服务器端的 C++ TCP 套接字多次写入()被视为客户端的单次读取

Posted

技术标签:

【中文标题】服务器端的 C++ TCP 套接字多次写入()被视为客户端的单次读取【英文标题】:C++ TCP socket multiple write() on server side treated as single read on client side 【发布时间】:2015-11-27 17:40:54 【问题描述】:

我正在开发一个带有 TCP 连接的程序,它由一个服务器和多个客户端组成。

服务器端

write(sockFd,message,strlen(message));
write(sockFd,message2,strlen(message2));

客户端

char msg[256];

bzero(msg, 256);
n = read (listenFd, msg, 255);
cout << "Msg1 " << msg << endl;

bzero(msg, 256);
n = read (listenFd, msg, 255);
cout << "Msg2 " << msg << endl;

问题是在服务器 write() 两条消息之后,客户端上的第一个 read() 可能会读取来自服务器的所有消息。 例如,输出为 Msg 1 content-of-msg1content-of-msg2。为什么会发生?

【问题讨论】:

TCP 是一种流协议——没有消息边界。 有没有办法让客户端以不同的读取方式读取两条消息? @ZZZ 没有。收到后手动拆分消息。 顺便说一句,您可能不知道 send/recv 可能无法完全处理给定的长度。检查返回值并使用循环。 是的,也不是——“是”,因为您可以按照@deviantfan 所说的那样做并自己解析/拆分消息。 “不”是因为没有内置机制可以为您执行此操作。 【参考方案1】:

TCP 是一种流式协议,因此数据以流的形式出现,这意味着每次读取都将读取缓冲区中的数据(或您请求的数据)。

因此,如果您想将数据分解为数据包或数据报,则需要使用您自己的基于会话的协议。一种方法是在传出数据前加上 4 个字节的长度。这样读者可以先读取长度,这样就知道数据报的其余部分有多大。

【讨论】:

一个“基于会话”的协议? "这样读者可以先读取长度,这样就知道数据报的其余部分有多大。" -- 小心,因为即使是 4 字节的“长度”字段也可能被多个 recv() 调用拆分。【参考方案2】:

这是因为套接字写入和读取的流性质。您必须实现自己的消息区分技术。

一些变体是:

更糟糕的是:实现您自己的消息结束符号。 更好:在消息发送之前该消息的大小(以字节为单位)。首先读取大小,然后读取大小给定的字节数。

并且使用 C++11,今天是 2015 年,不要做旧的 C 风格。

例如(为简单起见,省略了错误检查):

服务器端:

// choose this type once and never change it after first release!
using message_size_t = uint64_t;

void write_message(int sockfd, const std::string & message)

    message_size_t message_size static_cast<message_size_t>(message.size()) ;

    // check error here:
    write(sockfd, &message_size, sizeof(message_size));

    // and here:
    write(sockfd, message.data(), message_size);


// use:
write_message(sockFd, message);
write_message(sockFd, message2);

客户端:

void read_bytes_internal(int sockfd, void * where, size_t size)

    auto remaining = size;

    while (remaining > 0) 
        // check error here
        auto just_read = recv(sockfd, where, remaining);
        remaining -= just_read;
    
 

std::string read_message(int sockfd)

    message_size_t message_size 0 ;
    read_bytes_internal(sockfd, &message_size, sizeof(message_size));

    std::string result message_size, 0 ;
    read_bytes_internal(sockfd, &result[0], message_size);

    return result;



// use:
cout << "Msg1 " << read_message(listenFd) << endl;
cout << "Msg2 " << read_message(listenFd) << endl;

(代码可能包含一些小错误,我是在***回答窗口中写的,并没有在IDE中进行语法检查。)

【讨论】:

以上是关于服务器端的 C++ TCP 套接字多次写入()被视为客户端的单次读取的主要内容,如果未能解决你的问题,请参考以下文章

使用 Boost Asio 在 TCP 套接字上执行异步写入操作

TCP阻塞模型下服务器和客户端的建立步骤

如何控制对等端的套接字[TCP打孔]

在 TCP 套接字程序中,客户端发送一些数据,但服务器需要多次读取。为啥?

如何在 VB.NET 中检查 TCP 服务器(套接字)与 TCP 客户端的连接状态

如何将字节数据从套接字写入 C 中的无符号字符向量? [关闭]