TCP粘包解决方案

TCP粘包解决方案

黏包的在客户端发送频率低的情况下粘包不明显,下面是原来的服务器中epoll一个事件可读的回调函数recvdata(event_infor *infor),当调用这个函数然后recv()函数读缓冲区的数据,指定大小为BUFF_MAX-1,而BUFF_MAX我设置为1024,一般单次接收数据小于这个值,所以基本上就是读取了所有缓冲区数据,接收过程:收到数据->epoll可读->调用回调函数->读取缓冲区所有数据->空缓冲区等待下一次接收。在处理客户端发来的聊天消息时还没有发生粘包现象,处理文件传输时一直粘包,问题分析是客户端高速循环调用send()发送数据,也就是说服务器其实recv()多条数据,比如说发送{"code":1}{"code":2}服务器读一次可能是{"code":1}{"co
原服务器代码:

void Server::recvdata(event_infor *infor) {
    int n = recv(infor->fd, infor->buff, BUFF_MAX - 1, 0);
    if (n > 0) { //收到的数据
        infor->buff[n] = '\0';
        LOG(INFO) << infor->ip << " 发来一条消息: " << infor->buff;
        _messageQueue->push(infor->buff, infor);
    } else if (n == 0) {
        LOG(INFO) << "fd: " << infor->fd << " 连接关闭";
        close(infor->fd);
        infor->status = false;
    } else {
        close(infor->fd);
        infor->status = false;
        LOG(INFO) << "fd: " << infor->fd << " 连接关闭";
    }
    bzero(infor->buff, BUFF_MAX - 1);
}

解决办法见下:

//结构体定义
struct MessageStruct{
    int jsonLen;
    char json[0]; //不能换成char *json
};

客户端代码:

string data = "{\"code\":0,\"data\":{\"passwd\":\"123456\",\"name\":\"daimiaopeng\"}}";
    int len = sizeof(MessageStruct) + data.size();
    MessageStruct *messageStruct = static_cast<struct MessageStruct *>(malloc(len));
    messageStruct->jsonLen = data.size();
    strncpy(messageStruct->json, data.c_str(), data.size());
    //也可以用memcpy(),这里是string->char* 而不是char*->char*
    for (int i = 0; i < 1000; i++) {
        send(sockfd, messageStruct, len, 0);
        }
    free(messageStruct);

改进后的服务器代码:

void Server::recvdata(event_infor *infor) {
    MessageStruct messageStruct;
    //读取结构体
    int n = recv(infor->fd, &messageStruct, sizeof(MessageStruct), 0);
    if (n > 0) { //收到的数据
        //获取结构体中的json长度字段
        char *jsonData = static_cast<char *>(malloc(messageStruct.jsonLen));
        //再读json数据
        if (jsonData== nullptr)
            return;
        recv(infor->fd, jsonData, messageStruct.jsonLen, 0);
        _messageQueue->push(string(jsonData, messageStruct.jsonLen), infor);
        free(jsonData);
    } else if (n == 0) {
        LOG(INFO) << "fd: " << infor->fd << " 连接关闭";
        close(infor->fd);
        infor->status = false;
    } else {
        close(infor->fd);
        infor->status = false;
        LOG(INFO) << "fd: " << infor->fd << " 连接关闭";
    }
}

总的来说是通过定义一个结构体然后二进制发送,读messageStruct的大小数据,sizeof(messageStruct)大小就是int类型的大小,为4,实际上后面还跟了数据,char json[0]不占用空间,而char *json是占用8个字节的,所以不能用char *json代替char json[0]这里是个坑注意一下,转换完成读取jsonLen的值,再读jsonLen大小的数据这样就完成分包了。这里比较难理解,建议网上多看几篇通过结构体解决粘包的文章,结合本文的代码理解。

你可能感兴趣的:(C++,socket)