C语言 Linux 套接字:如何让 send() 等待 recv()
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/19794764/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Linux socket: How to make send() wait for recv()
提问by user2151995
I am making a simple client-server application using TCP protocal.
我正在使用 TCP 协议制作一个简单的客户端 - 服务器应用程序。
I Know that by default. recv()will block until the other side call a send()to this socket.
But is it possible that send()block itself until the other side has recv()ed the msg instead of keeping send()ing to outgoing queue and later to find the other side recv()got a whole bunch of msg sent by multiple send()s
我知道默认情况下。recv()将阻塞,直到另一端调用send()此套接字。但是是否有可能send()阻塞自己,直到另一端发送recv()了 msg 而不是保持send()在传出队列中,然后发现另一端recv()收到了多个send()s发送的一大堆 msg
In other words. Is it possible to let every send()wait for the other side's recv()before it can call another send()?
换句话说。是否有可能让每个人都send()等待对方的recv()才能呼叫另一个人send()?
To ilustate my question. I'll post a simple code here:
为了说明我的问题。我将在这里发布一个简单的代码:
client.c
客户端
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <netdb.h>
#include <stdio.h>
#include <poll.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <arpa/inet.h>
int main(int argc, char *argv[])
{
int sockfd = 0;
char sendBuff[1024];
struct sockaddr_in serv_addr;
int i;
if(argc != 2)
{
printf("\n Usage: %s <ip of server> \n",argv[0]);
return 1;
}
if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
printf("\n Error : Could not create socket \n");
return 1;
}
memset(&serv_addr, '0', sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(5000);
if(inet_pton(AF_INET, argv[1], &serv_addr.sin_addr)<=0)
{
printf("\n inet_pton error occured\n");
return 1;
}
if( connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0)
{
printf("\n Error : Connect Failed \n");
return 1;
}
do{
memset(sendBuff, '#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <time.h>
int main(int argc, char *argv[])
{
int listenfd = 0, connfd = 0;
struct sockaddr_in serv_addr;
char sendBuff[1025];
char recvBuff[100];
int i = 0;
listenfd = socket(AF_INET, SOCK_STREAM, 0);
memset(&serv_addr, '0', sizeof(serv_addr));
memset(sendBuff, '0', sizeof(sendBuff));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
serv_addr.sin_port = htons(5000);
bind(listenfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
listen(listenfd, 10);
connfd = accept(listenfd, (struct sockaddr*)NULL, NULL);
do{
memset(recvBuff, 'This is line 0
This is line 1
This is line 2
This is line 3
...
', sizeof(recvBuff));
recv(connfd, recvBuff, sizeof(recvBuff),0);
printf( "%s\n", recvBuff);
}while(++i<100);
return 0;
}
', sizeof(sendBuff));
sprintf(sendBuff, "This is line %d", i);
send(sockfd, sendBuff, strlen(sendBuff), 0);
//sleep(1);
}while(++i<100);
return 0;
}
server.c
服务器.c
This is line 0
This is line 1This is line 2This is line3This is line 4
This is line 5This is line 6This is line 7This is line 8This is line 9This is line 10
This is line 11This is line 12...
What I expect on the server side outcome is to print:
我对服务器端结果的期望是打印:
while(condition)
{
send() from client;
recv() from server;
}
However, the actual outcome is like this:
然而,实际的结果是这样的:
recv() from client;
while(condition)
{
send() from server; //acknowledge to client
recv() from client;
}
However this is easy to explain: when the client side issued a send(), it didn't wait for the server side's recv()to finish, and for some reason, the server side recv()loop is slower than client side's send(). Thus several send()s on client side may get piled together and received by server as a whole. (is my explanation right?)
然而这很容易解释:当客户端发出 a 时send(),它没有等待服务器端recv()完成,并且由于某种原因,服务器端recv()循环比客户端慢send()。因此send(),客户端的几个s 可能会堆积在一起并作为一个整体被服务器接收。(我的解释对吗?)
Actually there seems to be a very silly and loose solution. just add a sleep(1)(as I commented out) after each send()in the loop. I know this will make the code very inefficient and if the recv()loop will take longer time to implement some other complicated actions( This is obviously unpredictable when the program gets large) which will take longer than 1 sec. This solution fails!
实际上似乎有一个非常愚蠢和松散的解决方案。只需在循环中的sleep(1)每个之后添加一个(正如我注释掉的那样)send()。我知道这会使代码非常低效,并且如果recv()循环需要更长的时间来实现一些其他复杂的操作(当程序变大时这显然是不可预测的),这将花费超过 1 秒的时间。此解决方案失败!
So is there a better solid way to let the two sides communicate with each other to ensure the msg sent by one single send()received by a single recv()?
那么有没有更好的办法让双方互相沟通,保证一个人发的msg被一个人send()收到recv()呢?
采纳答案by ravi
client.c
客户端
int sendLen = strlen(sendBuff);
send(sockfd, &sendLen, sizeof(sendLen), 0);
send(sockfd, sendBuff, sendLen, 0);
server.c
服务器.c
int len = 0;
recv(connfd, &len, sizeof(len), 0);
recv(connfd, recvBuff, len, 0);
回答by rici
The only way for the sender to know that the receiver has received the message is for the receiver to reply.
发送方知道接收方已收到消息的唯一方法是接收方回复。
In other words, if you want to keep the sender and receiver in sync, you need to implement your own explicit flow control.
换句话说,如果要保持发送方和接收方同步,则需要实现自己的显式流量控制。
It is true that the TCP protocol requires that receivers acknowledge received packets, so you might think that it is possible for the receiver to simply ask the TCP implementation if the data has been acknowledged. This is generally possible, although I don't believe there is a cross-platform way of doing so. On Linux, for example, you can use the ioctlcode SIOCOUTQto check the size of the outbound buffer. But as far as I know there is no mechanism for blocking until the outbound buffer is empty, so the best you could do would be to periodically poll. That is not satisfactory.
TCP 协议确实要求接收方确认收到的数据包,因此您可能认为接收方可以简单地询问 TCP 实现是否数据已被确认。这通常是可能的,尽管我不相信有跨平台的方式这样做。例如,在 Linux 上,您可以使用ioctl代码SIOCOUTQ检查出站缓冲区的大小。但据我所知,在出站缓冲区为空之前没有阻塞机制,所以你能做的最好的事情就是定期轮询。这并不令人满意。
In any event, the result would be misleading because there is also a buffer on the receiving end, and if processing is the bottleneck, the receiving buffer will be the first to fill up. Low-level data acknowledgement only indicates that the data has reached the receiving TCP stack (that is, the low-level IP interface, generally inside of the kernel). It does not indicate that the data has been processed by the receiving process, and there is no mechanism for the sender to query the usage of the receiving buffer.
在任何情况下,结果都会产生误导,因为在接收端也有一个缓冲区,如果处理是瓶颈,那么接收缓冲区将首先被填满。低级数据确认仅表示数据已到达接收 TCP 堆栈(即低级 IP 接口,一般在内核内部)。并不表示数据已经被接收进程处理过,也没有发送方查询接收缓冲区使用情况的机制。
Furthermore, waiting for the acknowledge message to arrive at the sending end will generally slow communications down unnecessarily, since it adds a transmission delay (receiver → sender) to every transaction. This is not so dramatic as your sleepsolution, but it is somewhat similar.
此外,等待确认消息到达发送端通常会不必要地减慢通信速度,因为它为每个事务增加了传输延迟(接收方→发送方)。这不像您的sleep解决方案那么戏剧化,但有点相似。
If your messages are very short, you might be running into the Nagle algorithm, which causes small data packets to be retained for a small amount of time by the sender, in the hopes that several packets can be consolidated. You can turn the Nagle algorithm off for a TCP connection using setsockopt, with the TCP_NODELAYoption. But don't do this unless you are sure that it will be useful. (The canonical use of TCP_NODELAYis a telnet-type connection, where the data consists of individual keystrokes. )
如果您的消息很短,您可能会遇到Nagle 算法,该算法会导致发送方将小数据包保留一小段时间,希望可以合并多个数据包。您可以使用setsockopt, 和TCP_NODELAY选项为 TCP 连接关闭 Nagle 算法。但是不要这样做,除非你确定它会有用。( 的规范使用TCP_NODELAY是 telnet 类型的连接,其中数据由单个按键组成。)
回答by Macattack
If your problem is just processing a single message at a time, how about include a length field? Sender
如果您的问题只是一次处理一条消息,那么包含一个长度字段怎么样? 发件人
##代码##Receiver
接收者
##代码##That would allow you to read a single message at a time and leave the rest in the tcp buffer.
这将允许您一次读取一条消息,并将其余消息留在 tcp 缓冲区中。
Bear in mind, the code above makes a lot of assumptions about your sending and receiving programs sharing endianess and int size. (If its all running on one machine you're fine) It also doesn't check any error returns, both send()and recv()have the possibility of errors.
请记住,上面的代码对您的发送和接收程序共享字节顺序和整数大小做了很多假设。(如果它的一台机器你没事上所有正在运行),它也不会检查任何错误再次出现,都send()和recv()有错误的可能性。

