网络数据报文的格式定义
- 报文有两个部分,包头和包体,是网络消息的基本单元。
- 包头:描述本次消息包的大小,描述数据的的作用。
- 包体:数据
- 下面数据定义,服务端与客户端共用。
enum CMD //命令枚举 { CMD_LOGIN, CMD_LOGOUT, CMD_ERROR }; //DataHeader struct DataHeader //数据包头 { short dataLength; short cmd; }; //DataPackage struct Login //登录 { char UserName[32]; char PassWord[32]; }; struct LoginResult //登录结果 { int lgResult; }; struct LogOut //退出登录 { char UserName[32]; }; struct LogOutResult //退出结果 { int lgOutResult; };
- 服务端数据处理
while (true) { DataHeader dbHeader = {}; //接收客户端数据 int nLen = recv(sockAccpt, (char*)&dbHeader, sizeof(DataHeader), 0); if (nLen < 0) { printf("客户端已退出,任务结束\n"); break; } printf("收到命令:%d ,数据长度: %d\n", dbHeader.cmd, dbHeader.dataLength); switch (dbHeader.cmd) { case CMD_LOGIN: { Login login = {}; recv(sockAccpt, (char*)&login, sizeof(Login), 0); //忽略对用户密码进行判断 LoginResult lgRet = { 0 }; send(sockAccpt, (char*)&dbHeader, sizeof(DataHeader), 0); send(sockAccpt, (char*)&lgRet, sizeof(LoginResult), 0); } break; case CMD_LOGOUT: { LogOut logout = {}; recv(sockAccpt, (char*)&logout, sizeof(LogOut), 0); //忽略对用户密码进行判断 LogOutResult lgOutRet = { 1 }; send(sockAccpt, (char*)&dbHeader, sizeof(DataHeader), 0); send(sockAccpt, (char*)&lgOutRet, sizeof(LogOutResult), 0); } break; default: dbHeader.cmd = CMD_ERROR; dbHeader.dataLength = 0; send(sockAccpt, (char*)&dbHeader, sizeof(DataHeader), 0); break; } }
- 客户端数据处理:
while (true) { //3.输入请求命令 char cmdBuff[128] = {}; scanf("%s", cmdBuff); //4.处理请求 if (0 == strcmp(cmdBuff, "exit")) { printf("接收到命令exit,退出 \n"); break; } else if (0 == strcmp(cmdBuff, "login")) { Login login = { "喜羊羊","123456" }; DataHeader header = { sizeof(login), CMD_LOGIN}; //5.向服务器发送请求 send(sockCli, (const char*)&header, sizeof(header), 0); send(sockCli, (const char *)&login, sizeof(login), 0); //接收服务器返回的数据 DataHeader retHeader = {}; LoginResult loginRet = {}; recv(sockCli, ( char*)&retHeader, sizeof(retHeader), 0); recv(sockCli, ( char*)&loginRet, sizeof(loginRet), 0); printf("LoginResult: %d\n", loginRet.lgResult); } else if (0 == strcmp(cmdBuff, "logout")) { LogOut logout = { "喜羊羊" }; DataHeader headerOut = {sizeof(logout), CMD_LOGIN}; //5.向服务器发送请求 send(sockCli, (const char*)&headerOut, sizeof(headerOut), 0); send(sockCli, (const char*)&logout, sizeof(logout), 0); //接收服务器返回的数据 DataHeader retHeaderOut = {}; LogOutResult logOutRet = {}; recv(sockCli, (char*)&retHeaderOut, sizeof(retHeaderOut), 0); recv(sockCli, (char*)&logOutRet, sizeof(logOutRet), 0); printf("LogOutResult: %d\n", logOutRet.lgOutResult); } else { printf("不支持该命令,请从新输入 \n"); } }
【服务端源码】
#define WIN32_LEAN_AND_MEAN #define _WINSOCK_DEPRECATED_NO_WARNINGS #include <windows.h> #include <WinSock2.h> #include <cstdio> #pragma comment(lib,"ws2_32.lib") enum CMD //命令枚举 { CMD_LOGIN, CMD_LOGOUT, CMD_ERROR }; //DataHeader struct DataHeader //数据包头 { short dataLength; short cmd; }; //DataPackage struct Login //登录 { char UserName[32]; char PassWord[32]; }; struct LoginResult //登录结果 { int lgResult; }; struct LogOut //退出登录 { char UserName[32]; }; struct LogOutResult //退出结果 { int lgOutResult; }; int main() { WORD ver = MAKEWORD(2, 2); WSAData dat; WSAStartup(ver, &dat); //1.创建socket套接字 SOCKET sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); //2,bind 绑定用于接收客户端连接的端口 sockaddr_in sinAddr = {}; sinAddr.sin_family = AF_INET; sinAddr.sin_port = htons(5678); //host to net unsigned short sinAddr.sin_addr.S_un.S_addr = INADDR_ANY; //inet_addr("127.0.0.1") if (SOCKET_ERROR == bind(sock, (sockaddr*)&sinAddr, sizeof(sockaddr_in))) { printf("Bind Error\n"); } else { printf("Bind Success\n"); } //3. listen 监听网络端口 if (SOCKET_ERROR == listen(sock, 5)) { printf("Listen Error\n"); } else { printf("Listen Success\n"); } //4.accept 接收客户端连接 sockaddr_in clientAddr = {}; int clAddrLen = sizeof(sockaddr_in); SOCKET sockAccpt = INVALID_SOCKET; sockAccpt = accept(sock, (sockaddr*)&clientAddr, &clAddrLen); if (INVALID_SOCKET == sockAccpt) { printf("Accept Error\n"); } else { printf("Accept Success\n"); } printf("新客户端加入:Socket = %d,IP = %s \n", (int)sockAccpt, inet_ntoa(clientAddr.sin_addr)); while (true) { DataHeader dbHeader = {}; //接收客户端数据 int nLen = recv(sockAccpt, (char*)&dbHeader, sizeof(DataHeader), 0); if (nLen < 0) { printf("客户端已退出,任务结束\n"); break; } printf("收到命令:%d ,数据长度: %d\n", dbHeader.cmd, dbHeader.dataLength); switch (dbHeader.cmd) { case CMD_LOGIN: { Login login = {}; recv(sockAccpt, (char*)&login, sizeof(Login), 0); //忽略对用户密码进行判断 LoginResult lgRet = { 0 }; send(sockAccpt, (char*)&dbHeader, sizeof(DataHeader), 0); send(sockAccpt, (char*)&lgRet, sizeof(LoginResult), 0); } break; case CMD_LOGOUT: { LogOut logout = {}; recv(sockAccpt, (char*)&logout, sizeof(LogOut), 0); //忽略对用户密码进行判断 LogOutResult lgOutRet = { 1 }; send(sockAccpt, (char*)&dbHeader, sizeof(DataHeader), 0); send(sockAccpt, (char*)&lgOutRet, sizeof(LogOutResult), 0); } break; default: dbHeader.cmd = CMD_ERROR; dbHeader.dataLength = 0; send(sockAccpt, (char*)&dbHeader, sizeof(DataHeader), 0); break; } } //closesocket 关闭套接字 closesocket(sock); closesocket(sockAccpt); WSACleanup(); printf("结束任务\n"); getchar(); return 0; }
【客户端源码】
#define WIN32_LEAN_AND_MEAN #define _WINSOCK_DEPRECATED_NO_WARNINGS #include <windows.h> #include <WinSock2.h> #include <cstdio> #pragma comment(lib,"ws2_32.lib") enum CMD //命令枚举 { CMD_LOGIN, CMD_LOGOUT, CMD_ERROR }; //DataHeader struct DataHeader //数据包头 { short dataLength; short cmd; }; //DataPackage struct Login //登录 { char UserName[32]; char PassWord[32]; }; struct LoginResult //登录结果 { int lgResult; }; struct LogOut //退出登录 { char UserName[32]; }; struct LogOutResult //退出结果 { int lgOutResult; }; struct DataPackage { int age; char name[32]; }; int main() { WORD ver = MAKEWORD(2, 2); WSAData dat; WSAStartup(ver, &dat); //1.建立一个socket SOCKET sockCli = socket(AF_INET, SOCK_STREAM, 0); if (INVALID_SOCKET == sockCli) { printf("Socket Error\n"); } else { printf("Socket Success\n"); } //2. connect连接服务器 sockaddr_in servAddr = {}; servAddr.sin_family = AF_INET; servAddr.sin_port = htons(5678); servAddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1"); int ret = connect(sockCli, (sockaddr*)&servAddr, sizeof(sockaddr_in)); if (SOCKET_ERROR == ret) { printf("Connect Error\n"); } else { printf("Connect Success\n"); } while (true) { //3.输入请求命令 char cmdBuff[128] = {}; scanf("%s", cmdBuff); //4.处理请求 if (0 == strcmp(cmdBuff, "exit")) { printf("接收到命令exit,退出 \n"); break; } else if (0 == strcmp(cmdBuff, "login")) { Login login = { "喜羊羊","123456" }; DataHeader header = { sizeof(login) ,CMD_LOGIN}; //5.向服务器发送请求 send(sockCli, (const char*)&header, sizeof(header), 0); send(sockCli, (const char *)&login, sizeof(login), 0); //接收服务器返回的数据 DataHeader retHeader = {}; LoginResult loginRet = {}; recv(sockCli, ( char*)&retHeader, sizeof(retHeader), 0); recv(sockCli, ( char*)&loginRet, sizeof(loginRet), 0); printf("LoginResult: %d\n", loginRet.lgResult); } else if (0 == strcmp(cmdBuff, "logout")) { LogOut logout = { "喜羊羊" }; DataHeader headerOut = { sizeof(logout), CMD_LOGIN }; //5.向服务器发送请求 send(sockCli, (const char*)&headerOut, sizeof(headerOut), 0); send(sockCli, (const char*)&logout, sizeof(logout), 0); //接收服务器返回的数据 DataHeader retHeaderOut = {}; LogOutResult logOutRet = {}; recv(sockCli, (char*)&retHeaderOut, sizeof(retHeaderOut), 0); recv(sockCli, (char*)&logOutRet, sizeof(logOutRet), 0); printf("LogOutResult: %d\n", logOutRet.lgOutResult); } else { printf("不支持该命令,请从新输入 \n"); } } //7.关闭套接字 closesocket closesocket(sockCli); WSACleanup(); printf("结束任务\n"); getchar(); return 0; }
【测试结果】