Several readers sent private messages saying that during the interview process at Tencent, the interviewer asked a question: "A tcp server and a tcp client. After the client and server establish a connection, the server keeps sleeping, and then the client keeps sending data. What

2024/06/2920:38:33 technology 1165

question led to

Several readers sent private messages saying that during the Tencent interview process, the interviewer asked a question: "A tcp server and a tcp client. After the client and server establish a connection, the server keeps sleeping. What will happen if the client keeps sending data?"

To answer this question, we need to understand the characteristics of the tcp protocol and the general process of sending data through tcp.

tcp data sending process

I am afraid that students who have been exposed to the Internet know that tcp is a reliable connection-oriented transmission protocol, which means that the data sent by the client can be received by the server, so there is no possibility of data discarding for the above problems. . Let's analyze the transmission process of tcp.

Several readers sent private messages saying that during the interview process at Tencent, the interviewer asked a question:

As shown in the figure, the transmission process of tcp data packets mainly has the following steps:

  • • 1. The application calls the write series of functions to send data. The data is first copied from the application buffer to the socket in the kernel of the sending end for sending. buffer, and then write returns successfully; it is important to note that the successful return of write only indicates that the data was successfully copied from the application process buffer to the socket send buffer, and does not mean that the data was sent to the opposite host.
  • • 2. The kernel protocol stack sends the data in the socket send buffer to the opposite host. This process is not controlled by the application program, but is completed by the sending end kernel protocol stack;
  • • 3. The data reaches the receiving end host. Socket receiving buffer, note that this receiving process is not controlled by the application, but is completed by the receiving end kernel protocol stack;
  • • 4. The data is copied from the socket receiving buffer to the receiving end application buffer, please note This process is completed by functions such as read.

clearly understands the transmission process of tcp. Now we will discuss the above issues according to the situation.

related video recommendations

Several readers sent private messages saying that during the interview process at Tencent, the interviewer asked a question:

blocking mode The default working mode of

write series functions is blocking mode: when the write function is called, the kernel copies data from the application process's buffer to the socket's send buffer. If there is no space in its send buffer, the process will sleep until there is space.

Therefore, in blocking mode, if the server keeps sleeping and does not receive data, and the client keeps writing, it can only perform the first three steps in the above process, so that the final socket reception buffer of the receiving end is connected to the sending end socket. The word send buffer is filled, so write cannot continue to copy data from the application to the sender's socket send buffer, and the sender process goes to sleep. This can be verified using the following procedure.

tcpClient.c is the client code used to send data. Each time the client writes successfully, it increases the counter count by 1 and outputs the number of bytes of successful write this time. count saves the number of successful write attempts by the client.

#include stdio.h#include string.h#include unistd.h#include sys/types.h#include sys/socket.h#include stdlib.h#include memory.h#include arpa/inet.h#include netinet /in.h#define PORT 8888#define Buflen 1024int main(int argc,char *argv[]){struct sockaddr_in server_addr;int n,count=0;int sockfd;char sendline[Buflen];sockfd= socket(AF_INET, SOCK_STREAM,0);memset(&server_addr,0,sizeof(server_addr));server_addr.sin_family = AF_INET;server_addr.sin_port = htons(PORT);server_addr.sin_addr.s_addr = htonl(INADDR_ANY);server_addr.sin_addr.s_addr = inet_add r (argv[1]);connect(sockfd,(struct sockaddr *)&server_addr,sizeof(server_addr));//Communicate with the server memset(sendline,'x',sizeof(Buflen));while ( (n= write(sockfd,sendline,Buflen))0 ){count++;printf("already write %d bytes -- %d\n",n,count);}if(n0)perror("write error");close( sockfd);}

The tcpServer.c below is the server program, and the server does not receive data.

#include stdio.h#include stdlib.h#include strings.h#include sys/types.h#include sys/socket.h#include memory.h#include unistd.h#include netinet/in.h#include arpa /inet.h#include string.h#define PORT 8888 //Define communication port #define BACKLOG 5 //Define listening queue length #define buflen 1024int listenfd,connfd;int main(int argc,char *argv[]){ struct sockaddr_in server_addr; //Storage server-side socket address structure struct sockaddr_in client_addr; //Storage client-side socket address structure pid_t pid;listenfd = socket(AF_INET,SOCK_STREAM,0);memset(&server_addr,0,sizeof(server_addr)); server_addr.sin_family = AF_INET; //Protocol family server_addr.sin_addr.s_addr = htonl(INADDR_ANY); //Local address server_addr.sin_port = htons(PORT);bind(listenfd,(struct sockaddr *)&server_addr,sizeof(server_addr)) ;listen(listenfd,BACKLOG);for(;;){socklen_t addrlen = sizeof(client_addr);connfd = accept(listenfd,(struct sockaddr *)&client_addr,&addrlen);if(connfd0)perror("accept error"); printf("receive connection\n");if((pid = fork()) == 0){close(listenfd);sleep(1000);//The child process does not receive data, sleep for 1000 seconds exit(0); }else{close(connfd);}}}

First compile and run the server, and then start the client. The running results are as follows:

Several readers sent private messages saying that during the interview process at Tencent, the interviewer asked a question:

You can see that the client got blocked after writing successfully 377 times. Note that it cannot be explained at this time. The sender's socket send buffer must be full, which only means that the available space in the socket send buffer is less than the number of writes in the write request - 1024.

Non-blocking mode

Let's take a look at how write works in the case of non-blocking sockets: For a non-blocking TCP socket, if there is no space in the sending buffer at all, the output function will immediately return an EWOULDBLOCK mistake. If there is some space in the send buffer, the return value will be the number of bytes the kernel was able to copy into the buffer. This number of bytes is also called the "short count".

In this way, you can know the effect of the server always sleeping and the client always writing data under non-blocking conditions: the client writes successfully at first, and as the client writes, the socket receiving buffer of the receiving end and the socket sending of the sending end are sent The buffer will fill up. When the available space of the sender's socket send buffer is less than the number of bytes written by the write request, write immediately returns -1 and sets errno to EWOULDBLOCK.

can be verified with the following program. The server program code is the same as the above example. We only look at the client non-blocking mode code:

#include stdio.h#include string.h#include unistd.h#include sys/types .h#include sys/socket.h#include stdlib.h#include memory.h#include arpa/inet.h#include netinet/in.h#include fcntl.h#include errno.h#define PORT 8888#define Buflen 1024int main(int argc,char *argv[]){struct sockaddr_in server_addr;int n,flags,count=0;int sockfd;char sendline[Buflen];sockfd= socket(AF_INET,SOCK_STREAM,0);memset(&server_addr, 0,sizeof(server_addr));server_addr.sin_family = AF_INET;server_addr.sin_port = htons(PORT);server_addr.sin_addr.s_addr = htonl(INADDR_ANY);server_addr.sin_addr.s_addr = inet_addr(argv[1]);connect( sockfd,(struct sockaddr *)&server_addr,sizeof(server_addr));flags=fcntl(sockfd,F_GETFL,0); //Set the connected socket to non-blocking mode fcntl(sockfd,F_SETFL,flags|O_NONBLOCK) ;memset(sendline,'a',sizeof(Buflen));while ( (n=write(sockfd,sendline,Buflen))0 ) { count++; printf("already write %d bytes -- %d\n", n,count); } if(n0){if(errno!=EWOULDBLOCK) perror("write error");else printf("EWOULDBLOCK ERROR\n"); } close(sockfd);}

Several readers sent private messages saying that during the interview process at Tencent, the interviewer asked a question:

First compile and run the server , and then start the client. The running results are as shown in the figure below.

Several readers sent private messages saying that during the interview process at Tencent, the interviewer asked a question:

Several readers sent private messages saying that during the interview process at Tencent, the interviewer asked a question:

Edit

and you can see that after the client successfully writes 185 times, there is insufficient space in the socket sending buffer, thus returning the EWOULDBLOCK error.We noticed that the same number of bytes (1024) can be written successfully every time in blocking mode. Why is it less in non-blocking mode?

This is because in blocking mode, writing will not block until the socket receiving buffer of the receiving end and the socket sending buffer of the sending end are full. In non-blocking mode, it may be that the second step of the sender's sending process is slow, causing the sender's socket send buffer to fill up quickly, while the receiver's socket receive buffer is not full yet, so write An error will be returned simply because the sender's socket send buffer is full.

Original address: https://mp.weixin.qq.com/s/rpNTjTUt19Bbyx6IWm2-ig

technology Category Latest News