Linux socket: how to make send () wait for recv ()

I am making a simple client-server application using the protocal TCP protocol.

I know that by default. recv() will block until the other side calls send() on this socket. But is it possible that send() blocks itself until the other side recv() edits msg instead of saving send() ing in the outgoing queue, and then, to find the other side recv() , received a whole bunch of messages, sent by multiple send() s

In other words. Is it possible for each send() expect the other side of recv() before it can call the other send() ?

To customize my question. I posted 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, '\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

 #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, '\0', sizeof(recvBuff)); recv(connfd, recvBuff, sizeof(recvBuff),0); printf( "%s\n", recvBuff); }while(++i<100); return 0; } 

What I expect from the result on the server side is to print:

 This is line 0 This is line 1 This is line 2 This is line 3 ... 

However, the actual result is as follows:

 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... 

However, this is easy to explain: when the client side issued send() , it did not wait for the server side to recv() finish, and for some reason the server side of recv() is slower than the client side send() . Thus, several send() on the client side can be collected together and received by the server as a whole. (is my explanation correct?)

Actually, it seems like a very stupid and free decision. just add sleep(1) (as I commented) after each send() in the loop. I know that this will make the code very inefficient, and if the recv() cycle takes longer to implement some other complex actions (this is obviously unpredictable when the program gets big), which will take more than 1 second. This solution does not work!

So, is there a more reliable way that allows both parties to interact with each other to ensure that msg is sent with one single send() received by one recv() ?

+6
source share
3 answers

client.c

 while(condition) { send() from client; recv() from server; } 

server.c

 recv() from client; while(condition) { send() from server; //acknowledge to client recv() from client; } 
+3
source

The only way that the sender should know that the receiver has received the message is to respond to the receiver.

In other words, if you want to synchronize the sender and receiver, you need to implement your own explicit flow control.

True, TCP requires receivers to acknowledge received packets, so you might think that the receiver might just ask for a TCP implementation if the data was confirmed. This is generally possible, although I do not believe that there is a cross-platform way to do this. For example, on Linux, you can use the ioctl SIOCOUTQ code to check the size of the outgoing buffer. But as far as I know, there is no blocking mechanism until the outgoing buffer is empty, so it would be best to periodically poll. This is unsatisfactory.

In any case, the result will be misleading, because there is also a buffer on the receiving side, and if processing is a bottleneck, the receive buffer will be the first to fill. Confirmation of low-level data only indicates that the data has reached the receiving TCP stack (i.e., the low-level IP interface, usually inside the kernel). It does not indicate that the data was processed by the receiving process, and there is no mechanism for the sender to request the use of a receiving buffer.

In addition, waiting for a confirmation message to be sent to the sending end will usually be a slow disconnect, unnecessarily, because it adds a transmission delay (receiver and sender) to each transaction. This is not as dramatic as your sleep solution, but it is somewhat similar.

If your messages are very short, you may encounter the Nagle algorithm, which delays small packets of data for a short period of time by the sender, in the hope that several packets can be combined. You can disable the Nagle algorithm for a TCP connection using setsockopt , with the TCP_NODELAY option. But do not do this unless you are sure that it will be useful. (Canonical use of TCP_NODELAY is a telnet connection, where the data consists of separate keystrokes.)

+7
source

If your problem is only processing one message at a time, how about including a length field? Sender

 int sendLen = strlen(sendBuff); send(sockfd, &sendLen, sizeof(sendLen), 0); send(sockfd, sendBuff, sendLen, 0); 

Receiver

 int len = 0; recv(connfd, &len, sizeof(len), 0); recv(connfd, recvBuff, len, 0); 

This will allow you to read one message at a time and leave the rest in the tcp buffer.

Remember that the code above makes many assumptions that your sending and receiving programs share endianess and int size. (If everything works on one computer, you're fine.) It also does not check for any errors, both send() and recv() have the possibility of errors.

+2
source

Source: https://habr.com/ru/post/957440/


All Articles