我们只关心代码中的业务逻辑和底层阻塞原因
客户端代码
发送数据
std::string message(len, 'S');
int nw = stream->sendAll(message.c_str(), message.size());
printf("sent %d bytes\n", nw);
接受服务端回显的数据
std::vector<char> receive(len);
int nr = stream->receiveAll(receive.data(), receive.size());
printf("received %d bytes\n", nr);
服务端代码
采用thread per connection 模型,每个链接分配一个线程来进行数据回显
InetAddress listenAddr(3007);
Acceptor acceptor(listenAddr);
printf("Accepting... Ctrl-C to exit\n");
int count = 0;
bool nodelay = argc > 1 && strcmp(argv[1], "-D") == 0;
while (true)
{
TcpStreamPtr tcpStream = acceptor.accept();
printf("accepted no. %d client\n", ++count);
if (nodelay)
tcpStream->setTcpNoDelay(true);
// C++11 doesn't allow capturing unique_ptr in lambda, C++14 allows.
std::thread thr([count] (TcpStreamPtr stream) {
printf("thread for no. %d client started.\n", count);
char buf[4096];
int nr = 0;
while ( (nr = stream->receiveSome(buf, sizeof(buf))) > 0)
{
int nw = stream->sendAll(buf, nr);
if (nw < nr)
{
break;
}
}
printf("thread for no. %d client ended.\n", count);
}, std::move(tcpStream));
thr.detach();
发送数据过大时,客户端阻塞的原因分析
在发送数据过大时,客户端会陷入长时间的阻塞状态,发送队列和接受队列的状态如上图,分析代码中的业务逻辑可知,由于客户端在发送完所有数据之后才会接受,在发送的时候将数据放入内核缓冲区,内核缓冲区长度固定,服务端是边读取边发送,所以服务端在向客户端发送回显数据时可能会将发送内核缓冲区填满(此时客户端没有发送完所有元素不会开始读,这样两边都在等待。。。)
解决方式 header + payload 方式进行数据交互
- 从应用层协议解决这个问题
每次发送数据之前进行分包,让接收方准备好对应的内存空间后进行接受,具体应用实例