(C++服务器学习笔记):发送结构化的网络消息数据

目录

发送结构化的网络消息数据

演示案例

发送结构化的网络消息数据

【纯字符串网络消息】

  • 优点
    • 处理简易命令方便快捷。
  • 缺点
    • 传递大量数据是字符串解析消耗大。
  • 企业中的应用方式:
    • 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类型,输出就会出现错误,后面进一步探寻更好的解决方式。

猜你喜欢

转载自blog.csdn.net/baidu_41388533/article/details/111818420