服务器端的 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 套接字程序中,客户端发送一些数据,但服务器需要多次读取。为啥?