目录
发送结构化的网络消息数据
【纯字符串网络消息】
- 优点:
- 处理简易命令方便快捷。
- 缺点:
- 传递大量数据是字符串解析消耗大。
- 企业中的应用方式:
- JSON、XML、自定义格式的字符数据形式。
【使用结构化的二进制数据流传输网络消息】
- 优点:
- 简单、方便、解析快,消耗低。
- 缺点:
- 需要严格的网络字节序一致。
- 关于 recv 函数接收网络消息
struct DataHeader { short dataLength; short cmd; };
- 固长数据,变长数据、粘包/拆包、少包/组包。
演示案例
- 服务端:
- 定义数据:
struct DataPackage { int age; char name[32]; };
- 发送数据,需要强制类型转换
if (0 == strcmp(cmdBuff, "GetInfo")) { DataPackage datapg = {6,"喜羊羊"}; send(sockAccpt, (const char *) &datapg, sizeof(DataPackage), 0); }
- 客户端:
- 定义数据:
struct DataPackage { int age; char name[32]; };
- 接收数据,需要强制类型转换
char recvBuff[128] = {}; int nlen = recv(sockCli, recvBuff, 128, 0); if (nlen > 0) { DataPackage* dpRecv =(DataPackage*) recvBuff; printf("接收到数据:年龄 = %d, 姓名 = %s \n", dpRecv->age, dpRecv->name); }
【完整代码】
- 服务端:
#define WIN32_LEAN_AND_MEAN #define _WINSOCK_DEPRECATED_NO_WARNINGS #include <windows.h> #include <WinSock2.h> #include <cstdio> #pragma comment(lib,"ws2_32.lib") struct DataPackage { int age; char name[32]; }; 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) { char cmdBuff[128] = {}; //5.接收客户端数据 int nLen = recv(sockAccpt, cmdBuff, 128, 0); if (nLen < 0) { printf("客户端已退出,任务结束\n"); break; } printf("收到命令:%s\n", cmdBuff); //6.处理请求 if (0 == strcmp(cmdBuff, "GetInfo")) { //7.向客户端发送数据 DataPackage datapg = {6,"喜羊羊"}; send(sockAccpt, (const char *) &datapg, sizeof(DataPackage), 0); } else { //7.向客户端发送数据 char msgBuff[] = "???."; send(sockAccpt, msgBuff, strlen(msgBuff) + 1, 0); } } //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") 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 { //5.向服务器发送请求 send(sockCli, cmdBuff, strlen(cmdBuff) + 1, 0); } //6.接收服务器信息 recv char recvBuff[128] = {}; int nlen = recv(sockCli, recvBuff, 128, 0); if (nlen > 0) { DataPackage* dpRecv =(DataPackage*) recvBuff; printf("接收到数据:年龄 = %d, 姓名 = %s \n", dpRecv->age, dpRecv->name); } } //7.关闭套接字 closesocket closesocket(sockCli); WSACleanup(); printf("结束任务\n"); getchar(); return 0; }
- 输出演示:
- 由上可知,当正确输入命令,则结果正确,当错误命令,服务器给客户端发送的是“???.” ,但是通过强制类型转换成DataPackage类型,输出就会出现错误,后面进一步探寻更好的解决方式。