基于C++實現(xiàn)Socket交互式服務(wù)端
在 Windows 操作系統(tǒng)中,原生提供了強大的網(wǎng)絡(luò)編程支持,允許開發(fā)者使用 Socket API 進行網(wǎng)絡(luò)通信,通過 Socket API,開發(fā)者可以創(chuàng)建、連接、發(fā)送和接收數(shù)據(jù),實現(xiàn)網(wǎng)絡(luò)通信。本文將深入探討如何通過調(diào)用原生網(wǎng)絡(luò) API 實現(xiàn)同步遠程通信,并介紹了一個交互式 Socket 類的封裝,提升了編寫交互式服務(wù)器的便利性。
1. 交互式套接字類
為了更好地利用原生網(wǎng)絡(luò) API,我們引入了一個交互式 Socket 類的封裝。這個類抽象了底層的網(wǎng)絡(luò)細節(jié),提供了簡單而強大的接口,使得服務(wù)器端的交互式功能更容易實現(xiàn)。我們將詳細介紹這個類的設(shè)計和使用方法。
MySocket 類是一個 C++ 套接字類,封裝了在 Windows 平臺上使用原生網(wǎng)絡(luò) API 進行同步遠程通信的基本功能,該類需要使用多字節(jié)編碼模式,服務(wù)端與客戶端均需要引入此類,在項目頭文件中均需要新建MySocket.hpp文件。
完整代碼如下所示;
#pragma once
#include <WinSock2.h>
#pragma comment(lib, "ws2_32.lib")
class MySocket
{
protected:
SOCKET m_hSocket;
public:
// 獲取對端Socket用戶IP端口等
BOOL GetPeerName(char* rSocketAddress, UINT& rSocketPort)
{
sockaddr_in name = { AF_INET };
int lenname = sizeof(name);
if (getpeername(m_hSocket, (sockaddr*)&name, &lenname) < 0)
return false;
strcpy(rSocketAddress, inet_ntoa(name.sin_addr));
rSocketPort = htons(name.sin_port);
return true;
}
// 獲取本機Socket用戶IP端口等
BOOL GetSockName(char* rSocketAddress, UINT& rSocketPort)
{
sockaddr_in name = { AF_INET };
int lenname = sizeof(name);
if (getsockname(m_hSocket, (sockaddr*)&name, &lenname) < 0)
return false;
strcpy(rSocketAddress, inet_ntoa(name.sin_addr));
rSocketPort = htons(name.sin_port);
return true;
}
// 獲取當前用戶SocketID
BOOL GetSocketID()
{
return m_hSocket;
}
// 創(chuàng)建套接字
BOOL Create(UINT nSocketPort = 0, int nSockType = SOCK_STREAM, LPCTSTR lpszSocketAddress = NULL)
{
// 創(chuàng)建套接字
m_hSocket = socket(AF_INET, nSockType, 0);
if (m_hSocket == INVALID_SOCKET)
return false;
// 設(shè)置IP地址和端口
sockaddr_in sa = { AF_INET };
sa.sin_port = htons(nSocketPort);
if (lpszSocketAddress)
sa.sin_addr.s_addr = inet_addr(lpszSocketAddress);
// 綁定套接字和IP地址端口
return !bind(m_hSocket, (sockaddr*)&sa, sizeof(sa));
}
// 接受客戶請求
BOOL Accept(MySocket& rConnectedSock, LPSTR szIp = NULL, UINT* nPort = NULL)
{
sockaddr_in sa = { AF_INET };
int nLen = sizeof(sa);
rConnectedSock.m_hSocket = accept(this->m_hSocket, (sockaddr*)&sa, &nLen);
if (rConnectedSock.m_hSocket == INVALID_SOCKET)
return false;
if (szIp)
strcpy(szIp, inet_ntoa(sa.sin_addr));
if (nPort)
*nPort = htons(sa.sin_port);
return true;
}
// 連接服務(wù)端
BOOL Connection(LPCSTR lpszHostAddress, UINT nPort)
{
sockaddr_in sa = { AF_INET };
sa.sin_port = htons(nPort);
sa.sin_addr.s_addr = inet_addr(lpszHostAddress);
return !connect(m_hSocket, (sockaddr*)&sa, sizeof(sa));
}
// 偵聽
BOOL Listen(int nConnectionBacklog = 5)
{
return !listen(m_hSocket, nConnectionBacklog);
}
// 逐條發(fā)送
int Send(const void* lpBuf, int nBufLen, int nFlags = 0)
{
return send(m_hSocket, (LPCSTR)lpBuf, nBufLen, nFlags);
}
// 發(fā)送整個緩沖區(qū)
int SendTo(const void* lpBuf, int nBufLen, UINT nHostPort, LPCSTR lpszHostAddress = NULL,
int nFlags = 0)
{
sockaddr_in to = { AF_INET };
to.sin_port = htons(nHostPort);
to.sin_addr.s_addr = inet_addr(lpszHostAddress);
return sendto(m_hSocket, (LPCSTR)lpBuf, nBufLen, nFlags, (sockaddr*)&to, sizeof(to));
}
// 逐條接收
int Receive(void* lpBuf, int nBufLen, int nFlags = 0)
{
return recv(m_hSocket, (LPTSTR)lpBuf, nBufLen, nFlags);
}
// 接收整個緩沖區(qū)
int ReceiveFrom(void* lpBuf, int nBufLen, char* rSocketAddress, UINT& rSocketPort, int nFlags = 0)
{
sockaddr_in from = { AF_INET };
int lenFrom = sizeof(from);
int n = recvfrom(m_hSocket, (LPSTR)lpBuf, nBufLen, nFlags, (sockaddr*)&from, &lenFrom);
strcpy(rSocketAddress, inet_ntoa(from.sin_addr));
rSocketPort = htons(from.sin_port);
return n;
}
// 關(guān)閉套接字
void Close()
{
closesocket(m_hSocket);
m_hSocket = INVALID_SOCKET;
}
MySocket()
{
WSADATA wsaData;
WSAStartup(0x0202, &wsaData);
m_hSocket = INVALID_SOCKET;
}
~MySocket()
{
Close();
}
};以下是對該類的概括:
類名:MySocket
功能:提供了基本的網(wǎng)絡(luò)通信功能,包括創(chuàng)建套接字、獲取對端和本機的信息、接受客戶端連接、連接服務(wù)端、監(jiān)聽連接請求、發(fā)送和接收數(shù)據(jù)。
成員變量:
SOCKET m_hSocket:套接字句柄,用于標識一個套接字。
成員函數(shù):
Create:創(chuàng)建套接字,并可指定類型、本地端口和地址。
Accept:接受客戶請求,返回連接的套接字。
Connection:連接到服務(wù)端。
Listen:開始監(jiān)聽連接請求。
Send:逐條發(fā)送數(shù)據(jù)。
SendTo:發(fā)送整個緩沖區(qū)到指定地址。
Receive:逐條接收數(shù)據(jù)。
ReceiveFrom:接收整個緩沖區(qū),并獲取發(fā)送端地址和端口。
Close:關(guān)閉套接字。
初始化和清理:
構(gòu)造函數(shù) MySocket:初始化 Winsock 庫和套接字句柄。
析構(gòu)函數(shù) ~MySocket:關(guān)閉套接字。
使用注意事項:
適用于簡單的同步網(wǎng)絡(luò)通信場景。
該類提供了一些基本的網(wǎng)絡(luò)編程功能,適合用于創(chuàng)建簡單的服務(wù)器端和客戶端。需注意,這是一個同步實現(xiàn)的套接字類,適用于一些較為簡單的網(wǎng)絡(luò)通信需求。
2. 實現(xiàn)簡單的通信
通過具體的代碼示例,我們將演示如何使用交互式 Socket 類在 Windows 操作系統(tǒng)上實現(xiàn)同步遠程通信。代碼將包括服務(wù)器端和客戶端的實現(xiàn),以及它們之間的交互過程。通過這些示例,讀者將更好地理解如何在實際項目中應(yīng)用這些概念。
2.1 服務(wù)端流程
如下代碼是一個簡單的服務(wù)端程序,通過 MySocket 類建立基于 TCP 協(xié)議的服務(wù)器,通過sock.Create()創(chuàng)建套接字,然后通過sock.Accept()接收套接字,當有新的套接字連入時自動調(diào)用_beginthread()函數(shù)開啟一個子線程維持套接字的運行,每一個子線程內(nèi)部則都由ClientPro()函數(shù)來實現(xiàn)交互。
#define _CRT_SECURE_NO_WARNINGS
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#include <iostream>
#include <process.h>
#include "MySocket.hpp"
using namespace std;
void ClientPro(void* ptr)
{
// 初始化
MySocket* pSock = (MySocket*)ptr;
MySocket server_socket = *pSock;
server_socket.Send((const char *)"Welcome to LyServer", 19);
// 獲取客戶端信息
char sIp[20];
UINT nPort;
server_socket.GetPeerName(sIp, nPort);
while (true)
{
char szBuffer[4096] = { 0 };
// 接收客戶返回消息
int ref = server_socket.Receive(szBuffer, sizeof(szBuffer));
if (ref <= 0)
{
std::cout << "客戶: " << sIp << ":" << nPort << " [已斷開]" << std::endl;
break;
}
std::cout << "地址: " << sIp << ":" << nPort << " 接收命令: " << szBuffer << std::endl;
// 選擇不同的命令
if (strcmp(szBuffer, "list\n") == 0)
{
std::cout << "輸出文件" << std::endl;
}
else if (strcmp(szBuffer, "download\n") == 0)
{
std::cout << "下載文件" << std::endl;
}
else if (strcmp(szBuffer, "upload\n") == 0)
{
std::cout << "上傳文件" << std::endl;
}
// 返回給客戶端
server_socket.Send((char*)"ok", 2);
}
}
int main(int argc, char *argv[])
{
MySocket sock;
if (!sock.Create(8233, SOCK_STREAM, "127.0.0.1"))
{
return -1;
}
// 獲取本機信息
char sSevIp[20];
UINT nSevPort;
sock.GetSockName(sSevIp, nSevPort);
std::cout << "服務(wù)端: " << sSevIp << ":" << nSevPort << " 服務(wù)器啟動成功" << std::endl;
sock.Listen(5);
// 獲取客戶端信息
char sIp[20];
UINT nPort;
MySocket ptr;
while (true)
{
// 當有新用戶進來自動創(chuàng)建一個線程來維持會話
sock.Accept(ptr, sIp, &nPort);
std::cout << "客戶: " << sIp << ":" << nPort << " [已登錄]" << std::endl;
// 多線程
_beginthread(ClientPro, 0, &ptr);
}
return 0;
}以下是對該代碼的概括:
功能:實現(xiàn)一個簡單的基于 TCP 的服務(wù)器,監(jiān)聽指定端口(8233),接受客戶端連接,創(chuàng)建一個線程處理每個客戶端的會話。
主要函數(shù)和過程:
ClientPro 函數(shù):處理每個客戶端的會話。向客戶端發(fā)送歡迎消息,接收客戶端發(fā)送的命令,根據(jù)不同的命令執(zhí)行相應(yīng)的操作,并向客戶端發(fā)送響應(yīng)。該函數(shù)通過多線程在后臺運行,使得服務(wù)器能夠同時處理多個客戶端。
main 函數(shù):在主線程中創(chuàng)建 MySocket 類實例 sock,并調(diào)用 Create 函數(shù)創(chuàng)建服務(wù)器套接字。然后,通過 Listen 函數(shù)監(jiān)聽客戶端連接。在循環(huán)中,通過 Accept 函數(shù)接受客戶端連接,并為每個客戶端創(chuàng)建一個新線程,用于處理客戶端的會話。
通信協(xié)議:客戶端和服務(wù)器之間通過簡單的文本協(xié)議進行通信??蛻舳税l(fā)送不同的命令(“list”、“download”、“upload”),服務(wù)器接收命令并執(zhí)行相應(yīng)的操作,然后向客戶端發(fā)送響應(yīng)(“ok”)。
線程創(chuàng)建:使用 _beginthread 函數(shù)在每個新連接上創(chuàng)建一個線程,用于處理該客戶端的會話。
2.2 客戶端流程
如下代碼是一個簡單的客戶端程序,通過 MySocket 類實現(xiàn)與服務(wù)端的基于 TCP 協(xié)議的通信,通過sock.Connection()建立套接字鏈接,通過sock.Receive()接收數(shù)據(jù),通過sock.Send()發(fā)送數(shù)據(jù),其運行原理與原生套接字寫法保持一致。
#define _CRT_SECURE_NO_WARNINGS
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#include <iostream>
#include "MySocket.hpp"
using namespace std;
int main(int argc, char* argv[])
{
MySocket sock;
if (!sock.Create(0, SOCK_STREAM))
{
return -1;
}
// 獲取本機信息
char sClientIp[20];
UINT nClientPort;
sock.GetSockName(sClientIp, nClientPort);
std::cout << "服務(wù)端: " << sClientIp << ":" << nClientPort << " 服務(wù)器啟動成功" << std::endl;
if (!sock.Connection("127.0.0.1", 8233))
{
cout << "連接服務(wù)器失敗" << GetLastError() << endl;
return -1;
}
char szBuffer[4096] = { 0 };
int ref = sock.Receive(szBuffer, sizeof(szBuffer));
szBuffer[ref] = 0;
std::cout << "服務(wù)端回應(yīng): " << szBuffer << std::endl;
while (true)
{
// 循環(huán)接受輸入
input:
memset(szBuffer, 0, 4096);
std::cout << "Input CMD > ";
// 接收輸入命令
int inputLine = 0;
while ((szBuffer[inputLine++] = getchar()) != '\n');
if (strlen(szBuffer) == 1)
goto input;
// 發(fā)送數(shù)據(jù)
sock.Send(szBuffer, 4096, 0);
// 接收回顯
memset(szBuffer, 0, 4096);
sock.Receive(szBuffer, 4096, 0);
std::cout << "服務(wù)端回顯: " << szBuffer << std::endl;
}
sock.Close();
return 0;
}以下是對該代碼的概括:
功能:實現(xiàn)一個基于 TCP 的客戶端,連接到指定 IP 地址和端口(127.0.0.1:8233),與服務(wù)器建立連接后,可以輸入命令并發(fā)送到服務(wù)器,接收并顯示服務(wù)器的回顯。
主要函數(shù)和過程:
main 函數(shù):在主線程中創(chuàng)建 MySocket 類實例 sock,并調(diào)用 Create 函數(shù)創(chuàng)建客戶端套接字。然后,通過 Connection 函數(shù)連接到服務(wù)器。接著,通過 Receive 函數(shù)接收服務(wù)器發(fā)送的歡迎消息,并顯示在控制臺。
在一個無限循環(huán)中,通過標準輸入接收用戶輸入的命令,將命令發(fā)送到服務(wù)器,然后接收并顯示服務(wù)器的回顯。
通信協(xié)議:客戶端和服務(wù)器之間通過簡單的文本協(xié)議進行通信??蛻舳税l(fā)送用戶輸入的命令,服務(wù)器執(zhí)行命令并將結(jié)果回顯給客戶端。
輸入循環(huán):通過一個無限循環(huán),不斷接收用戶輸入的命令,并發(fā)送到服務(wù)器。如果用戶輸入空命令,程序會跳轉(zhuǎn)回 input 標簽重新接收輸入。
錯誤處理:在連接服務(wù)器失敗時,通過 GetLastError() 輸出詳細錯誤信息。
關(guān)閉套接字:在程序結(jié)束時,通過 sock.Close() 關(guān)閉套接字。
依次運行服務(wù)端和客戶端,然后當客戶端連接成功后此時的服務(wù)端即可收到連接請求,此時客戶端可以執(zhí)行各類簡單的命令,如下圖所示;

3.實現(xiàn)登錄服務(wù)器
上述代碼只是一個簡單的演示案例,用來演示如何使用套接字編寫交互程序,如下我們將繼續(xù)完善這段代碼,實現(xiàn)一個簡單的帶有登錄功能的登錄服務(wù)器程序,使用戶可以在執(zhí)行命令前具備簡單的登錄認證功能。
3.1 服務(wù)端流程
如下代碼是一個簡單的基于 Windows 的多線程服務(wù)器程序,通過 MySocket 類實現(xiàn)與客戶端的基于 TCP 協(xié)議的通信,在交互模式下用戶可輸入多種命令,登錄登出以及登陸后的命令執(zhí)行功能。
#define _CRT_SECURE_NO_WARNINGS
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#include <iostream>
#include <process.h>
#include <vector>
#include "MySocket.hpp"
using namespace std;
// 登錄狀態(tài)記錄
typedef struct
{
char UserName[32];
int SocketID;
}loginPool;
// ------------------------------------------------------------------------
// 用戶登錄驗證代碼部分
std::vector<loginPool> login_pool_vect;
// 檢查用戶ID是否存在與容器內(nèi),如果存在則返回用戶名
bool is_login(std::vector<loginPool> &ptr, int socket_id)
{
for (int x = 0; x < ptr.size(); x++)
{
if (ptr[x].SocketID == socket_id)
{
return true;
}
}
return false;
}
// 用戶登錄驗證
bool login(char *username, char *password, int socket_id)
{
if ((strcmp(username, "lyshark") == 0) && (strcmp(password, "123123") == 0))
{
// 如果在則增加一個socket登錄標志
loginPool pool_ptr;
pool_ptr.SocketID = socket_id;
strcpy(pool_ptr.UserName, "lyshark");
login_pool_vect.push_back(pool_ptr);
return true;
}
else if ((strcmp(username, "admin") == 0) && (strcmp(password, "123456") == 0))
{
// 如果在則增加一個socket登錄標志
loginPool pool_ptr;
pool_ptr.SocketID = socket_id;
strcpy(pool_ptr.UserName, "lyshark");
login_pool_vect.push_back(pool_ptr);
return true;
}
return false;
}
// 根據(jù)傳入ID從容器內(nèi)彈出一個節(jié)點
bool logout(std::vector<loginPool> &ptr, int socket_id)
{
for (vector<loginPool>::iterator it = ptr.begin(); it != ptr.end(); it++)
{
if (it->SocketID == socket_id)
{
// 彈出指定結(jié)構(gòu)體
ptr.erase(it);
return true;
}
}
return false;
}
// ------------------------------------------------------------------------
// 響應(yīng)客戶端的子線程(主要功能實現(xiàn)部分)
void ClientPro(void* ptr)
{
// 初始化
MySocket* pSock = (MySocket*)ptr;
MySocket server_socket = *pSock;
server_socket.Send((const char *)"Welcome to LyShark Mini Server", 31);
// 獲取客戶端信息
char sIp[20];
UINT nPort;
server_socket.GetPeerName(sIp, nPort);
while (true)
{
char szBuffer[4096] = { 0 };
int sid = pSock->GetSocketID();
int ref = server_socket.Receive(szBuffer, sizeof(szBuffer));
if (ref <= 0)
{
logout(login_pool_vect, sid);
std::cout << "客戶: " << sIp << ":" << nPort << " [已斷開]" << std::endl;
break;
}
std::cout << "地址: " << sIp << ":" << nPort << " 接收命令: " << szBuffer << std::endl;
// 用戶登錄
if (strcmp(szBuffer, "login\n") == 0)
{
char recv_username[32] = { 0 };
char recv_password[32] = { 0 };
// 接收用戶名和密碼
pSock->Receive(recv_username, 32, 0);
pSock->Receive(recv_password, 32, 0);
// 驗證登錄狀態(tài)
bool login_flag = login(recv_username, recv_password, sid);
if (login_flag == TRUE)
{
std::cout << "用戶: " << recv_username << " 已登錄" << std::endl;
pSock->Send("已登錄", sizeof("已登錄"), 0);
}
else
{
pSock->Send("賬號或密碼錯誤", sizeof("賬號或密碼錯誤"), 0);
}
}
// 用戶登出
else if (strcmp(szBuffer, "logout\n") == 0)
{
// 驗證是否登錄成功
int login_flag = is_login(login_pool_vect, sid);
if (login_flag == TRUE)
{
std::cout << "用戶已登出" << std::endl;
logout(login_pool_vect, sid);
pSock->Send("用戶已登出", sizeof("用戶已登出"), 0);
}
else
{
std::cout << "請先登錄" << std::endl;
pSock->Send("請先登錄", sizeof("請先登錄"), 0);
}
}
// 遍歷本機文件
else if (strcmp(szBuffer, "list\n") == 0)
{
// 驗證是否登錄成功
int login_flag = is_login(login_pool_vect, sid);
if (login_flag == TRUE)
{
std::cout << "用戶已登錄,輸出本機文件" << std::endl;
pSock->Send("認證通過", sizeof("認證通過"), 0);
// 循環(huán)輸出數(shù)據(jù)包
for (int x = 0; x < 10; x++)
{
char sz[1024] = { 0 };
sprintf(sz, "count -> %d", x);
pSock->Send(sz, sizeof(sz), 0);
}
}
else
{
std::cout << "請先登錄" << std::endl;
pSock->Send("請先登錄", sizeof("請先登錄"), 0);
}
}
}
}
int main(int argc, char *argv[])
{
MySocket sock;
if (!sock.Create(8233, SOCK_STREAM, "127.0.0.1"))
{
return -1;
}
// 獲取本機信息
char sSevIp[20];
UINT nSevPort;
sock.GetSockName(sSevIp, nSevPort);
std::cout << "服務(wù)端: " << sSevIp << ":" << nSevPort << " 服務(wù)器啟動成功" << std::endl;
sock.Listen(5);
// 獲取客戶端信息
char sIp[20];
UINT nPort;
MySocket ptr;
while (true)
{
sock.Accept(ptr, sIp, &nPort);
std::cout << "客戶: " << sIp << ":" << nPort << " [已登錄]" << std::endl;
// 多線程
_beginthread(ClientPro, 0, &ptr);
}
return 0;
}以下是對該代碼的概括:
功能:
通過 MySocket 類實現(xiàn)基于 TCP 協(xié)議的多線程服務(wù)器,可以處理多個客戶端的連接。
實現(xiàn)了用戶登錄驗證功能,支持用戶登錄、登出和查看本機文件列表的操作。
主要結(jié)構(gòu)和功能:
登錄狀態(tài)記錄結(jié)構(gòu)體 (loginPool):記錄用戶登錄狀態(tài),包括用戶名和套接字 ID。
用戶登錄驗證相關(guān)函數(shù):
is_login:檢查指定套接字 ID 是否已登錄。
login:驗證用戶名和密碼,如果驗證通過則將用戶信息加入登錄池。
logout:根據(jù)套接字 ID 從登錄池中移除用戶。
子線程主要處理函數(shù) ClientPro:
初始化后發(fā)送歡迎消息給客戶端。
接收客戶端命令,處理用戶登錄、登出和查看本機文件列表的請求。
針對不同的命令進行相應(yīng)的處理和回復。
主線程 main:
創(chuàng)建服務(wù)器套接字,并通過 Create 函數(shù)創(chuàng)建服務(wù)器套接字。
獲取本機信息,包括 IP 地址和端口,并顯示在控制臺。
通過 Listen 函數(shù)監(jiān)聽客戶端連接。
接受客戶端連接,創(chuàng)建子線程處理每個客戶端連接。
通信協(xié)議:服務(wù)器與客戶端之間通過簡單的文本協(xié)議進行通信,支持用戶登錄、登出和查看本機文件列表的操作。
多線程處理:通過 _beginthread 創(chuàng)建子線程處理每個客戶端的連接,實現(xiàn)了多客戶端并發(fā)處理。
用戶登錄驗證:支持用戶登錄驗證功能,通過用戶名和密碼驗證用戶身份,記錄登錄狀態(tài),處理用戶登錄、登出的請求。
3.2 客戶端流程
如下代碼是一個基于 Windows 的客戶端程序,通過 MySocket 類實現(xiàn)與服務(wù)器的基于 TCP 協(xié)議的通信。
#define _CRT_SECURE_NO_WARNINGS
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#include <iostream>
#include "MySocket.hpp"
using namespace std;
int main(int argc, char* argv[])
{
MySocket sock;
if (!sock.Create(0, SOCK_STREAM))
{
return -1;
}
// 獲取本機信息
char sClientIp[20];
UINT nClientPort;
sock.GetSockName(sClientIp, nClientPort);
if (!sock.Connection("127.0.0.1", 8233))
{
cout << "連接服務(wù)器失敗" << GetLastError() << endl;
return -1;
}
char szBuffer[4096] = { 0 };
int ref = sock.Receive(szBuffer, sizeof(szBuffer));
szBuffer[ref] = 0;
std::cout << "服務(wù)端回應(yīng): " << szBuffer << std::endl;
while (true)
{
input:
memset(szBuffer, 0, 4096);
std::cout << "CMD > ";
// 發(fā)送命令
int inputLine = 0;
while ((szBuffer[inputLine++] = getchar()) != '\n');
if (strlen(szBuffer) == 1)
goto input;
// 執(zhí)行登錄
if (strcmp(szBuffer, "login\n") == 0)
{
// 發(fā)送命令
sock.Send(szBuffer, 4096, 0);
char input_username[32] = { 0 };
char input_password[32] = { 0 };
// 發(fā)送用戶名
printf("用戶名: ");
scanf("%s", &input_username);
sock.Send(input_username, 32, 0);
// 發(fā)送密碼
printf("密碼: ");
scanf("%s", &input_password);
sock.Send(input_password, 32, 0);
// 獲取登錄狀態(tài)
char recv_message[64] = { 0 };
sock.Receive(recv_message, 64, 0);
std::cout << recv_message << std::endl;
}
// 登出用戶
else if (strcmp(szBuffer, "logout\n") == 0)
{
// 發(fā)送命令
sock.Send(szBuffer, 4096, 0);
// 獲取返回消息
char recv_message[64] = { 0 };
sock.Receive(recv_message, 64, 0);
std::cout << recv_message << std::endl;
}
// 遍歷本機文件
else if (strcmp(szBuffer, "list\n") == 0)
{
// 發(fā)送命令
sock.Send(szBuffer, 4096, 0);
// 獲取返回消息
char recv_message[64] = { 0 };
sock.Receive(recv_message, 64, 0);
std::cout << recv_message << std::endl;
if (strcmp(recv_message, "請先登錄") == 0)
{
goto input;
}
// 循環(huán)接收數(shù)據(jù)包
for (int x = 0; x < 10; x++)
{
char sz[1024] = { 0 };
sock.Receive(sz, 1024, 0);
std::cout << sz << std::endl;
}
}
}
sock.Close();
return 0;
}以下是對該代碼的概括:
功能:
通過 MySocket 類實現(xiàn)基于 TCP 協(xié)議的客戶端,可以與服務(wù)器進行通信。
支持用戶通過命令行輸入與服務(wù)器進行簡單的交互,包括登錄、登出和查看本機文件列表的操作。
主要結(jié)構(gòu)和功能:
用戶交互循環(huán):
使用一個循環(huán),通過命令行輸入命令,將命令發(fā)送給服務(wù)器,并根據(jù)服務(wù)器的回應(yīng)進行相應(yīng)的操作。
支持登錄、登出和查看本機文件列表的操作。
命令處理:
對用戶輸入的不同命令,通過 sock.Send 將命令發(fā)送給服務(wù)器,并通過 sock.Receive 接收服務(wù)器的回應(yīng)。
具體命令包括登錄、登出和查看本機文件列表。
登錄交互:
當用戶輸入 “login” 命令時,程序會提示用戶輸入用戶名和密碼,并將輸入的用戶名和密碼發(fā)送給服務(wù)器進行登錄驗證。
接收服務(wù)器的回應(yīng),輸出相應(yīng)的登錄狀態(tài)信息。
登出交互:
當用戶輸入 “logout” 命令時,程序向服務(wù)器發(fā)送登出命令,接收服務(wù)器的回應(yīng)并輸出相應(yīng)的信息。
查看本機文件列表交互:
當用戶輸入 “list” 命令時,程序向服務(wù)器發(fā)送查看本機文件列表的命令,接收服務(wù)器的回應(yīng)并輸出相應(yīng)的信息。
如果用戶未登錄,則輸出 “請先登錄” 提示,并繼續(xù)等待用戶輸入。
通信協(xié)議:客戶端與服務(wù)器之間通過簡單的文本協(xié)議進行通信,服務(wù)器回應(yīng)的信息通過控制臺輸出。
與之前的程序不同,這段代碼增加了簡單的用戶認證模式,當用戶直接執(zhí)行命令時則會提示客戶端請先登錄,無法執(zhí)行命令;

此時通過login命令,并輸入用戶名lyshark密碼123123則會提示已登錄,此時就可以執(zhí)行任意的命令參數(shù)了,如下圖所示,當結(jié)束時還需要使用logout退出當前會話;

以上就是基于C++實現(xiàn)Socket交互式服務(wù)端的詳細內(nèi)容,更多關(guān)于C++ Socket交互式服務(wù)端的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
C語言創(chuàng)建鏈表錯誤之通過指針參數(shù)申請動態(tài)內(nèi)存實例分析
這篇文章主要介紹了C語言創(chuàng)建鏈表錯誤之通過指針參數(shù)申請動態(tài)內(nèi)存,是鏈表創(chuàng)建過程中非常常見的經(jīng)典錯誤。實例中做了較為詳盡的分析,需要的朋友可以參考下2014-09-09
C++實現(xiàn)將數(shù)據(jù)寫入Excel工作表的示例代碼
直觀的界面、出色的計算功能和圖表工具,使Excel成為最流行的個人計算機數(shù)據(jù)處理軟件。在本文中,您將學習如何使用?Spire.XLS?for?C++?創(chuàng)建?Excel?文檔,以及如何將數(shù)據(jù)寫入?Excel?工作表2023-03-03

