TCP-based network programming is divided into two parts: Server side and client, and the Common Core steps and processes are as follows.
Connect () function
For the client's connect () function, the function for the client to actively connect to the server, establish a connection is through three handshake, and This connection process is done by the kernel , not this function, this function is only to inform the Linux kernel , let the Linux kernel automatically complete the TCP three handshake connection (three handshake details, see "Talking about TCP three handshake"), and finally return the result of the connection to the function's return value (successful connection is 0, failure is-1).
the usual situation, Client-side The connect () function is blocked by default until the three handshake succeeds or the timeout fails (normally, the process is completed quickly).
Listen () function
For the server, it is passive connected. As an example of life, in general, mobile customer service (equivalent to a server) is waiting for the arrival of a customer (the equivalent of a client) phone. In this process, the listen () function needs to be called.
#include <sys/socket.h>int Listen (int sockfd, int backlog);
The main function of the listen () function is to turn the socket (SOCKFD) into a passive connection listening socket (passive waiting for the client's connection), and the function of the parameter backlog is to set the length of the connection queue in the kernel (what is the length of this, and then explain it in detail).
It is important to note that the listen () function does not block, it does the main thing , to tell the Linux kernel the length of the connection queue that corresponds to the socket and socket, and then the Listen () function ends.
In this case, when there is a client active connection (connect ()), the Linux kernel automatically completes the TCP 3 handshake, and the established link is automatically stored in the queue, so repeat.
Therefore, as long as the TCP server calls listen (), the client can establish a connection through connect () and the server, and the process of the connection is done by the kernel .
The following is the test server and client code, run the program, run the server first, and then run the client:
Server:
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys /socket.h> #include <netinet/in.h> #include <arpa/inet.h>int main (int argc, char *argv[]) {unsigned short Port = 8000;int SOCKFD;SOCKFD = socket (af_inet, sock_stream, 0);//Create Communication endpoint: Socket if (SOCKFD < 0) {perror ("socket"); exit (-1) ;} struct sockaddr_in my_addr;bzero (&my_addr, sizeof (MY_ADDR)); my_addr.sin_family = Af_inet;my_addr.sin_port = htons (port); my_addr.sin_addr.s_addr = htonl (inaddr_any); int Err_ Log = Bind (SOCKFD, (struct sockaddr*) &my_addr, sizeof (MY_ADDR)), if (err_log! = 0) {perror ("binding"); Close (SOCKFD); Exit (-1);} Err_log = Listen (SOCKFD), if (err_log! = 0) {perror ("Listen"), close (SOCKFD); exit (-1);} printf ("Listen client @port =%d...\n", port); sleep (10);//Delay 10ssystem ("Netstat-an | grep 8000 ");//view connection status return 0;}
client:
#include <stdio.h> #include <unistd.h> #include <string.h> #include <stdlib.h> #include < arpa/inet.h> #include <sys/socket.h> #include <netinet/in.h>int main (int argc, char *argv[]) {unsigned Short port = 8000; Server's port number char *server_ip = "10.221.20.12"; server IP address int sockfd;sockfd = socket (af_inet, sock_stream, 0);//Create Communication endpoint: Socket if (SOCKFD < 0) {perror ("socket"); exit (-1);} struct sockaddr_in Server_addr;bzero (&server_addr,sizeof (SERVER_ADDR)); Initialize server address server_addr.sin_family = Af_inet;server_addr.sin_port = htons (port); Inet_pton (Af_inet, SERVER_IP, & SERVER_ADDR.SIN_ADDR); int err_log = Connect (sockfd, (struct sockaddr*) &server_addr, sizeof (SERVER_ADDR)); Active Connection Server if (Err_log! = 0) {perror ("connect"); Close (SOCKFD); exit (-1);} System ("Netstat-an | grep 8000 ");//view connection status while (1); return 0;}
To run the
program, run the server first, and then run the client , and the result is as follows:
three-time handshake connection Queue
Here is a detailed description of the function of the second parameter of the Listen () function: tells the length of the kernel connection queue.
To better understand the backlog parameters, we must recognize that the kernel maintains two queues for any given listener interface:
1. The connection queue is not completed (incomplete connection queue), each of which corresponds to one of the following: A customer has been issued and reached the server, and the server is waiting to complete the corresponding TCP three-time handshake process. These sets of interfaces are in the SYN_RCVD state.
2. The connection queue (completed connection queue) has been completed and one of the customers who have completed the TCP three handshake process. These sets of interfaces are in the established state.
When the SYN from the customer arrives, TCP creates a new entry in the incomplete connection queue, and then responds with a second section of the three handshake: a SYN response from the server with a slight ACK to the customer syn (that is, Syn+ack), which remains in the incomplete connection queue. Until the third section of the three handshake (the customer's ACK to the server SYN) arrives or the item times out (the implementation that originated from Berkeley sets the timeout value for these outstanding connections to 75 seconds).
If the three-time handshake is completed normally, the entry is moved to the end of the completed connection queue without completing the connection queue.
The backlog parameter has historically been defined as the sum of the size of the above two queues, most implementations default to 5, and when the server takes away a connection that completes the connection queue, the position of the queue is empty again, so that it is dynamically balanced back and forth, but in high concurrent web This value in the server is obviously not enough.
Accept () function
The accept () function is to remove a completed connection from the head of the connection queue in the established state, and if the queue does not have a completed connection, the Accept () function blocks until the completed user connection is removed from the queue.
What happens if the server does not call accept () to take the completed connection in the queue in a timely way? UNP (Unix network programming) tells us that when the server's connection queue is full, the server will not answer the SYN that made the new connection, so the client's connect will return etimedout. But in fact, Linux is not like this!
The following is the test code, the server listen () function only specifies a queue length of 2, the client has 6 different sockets to actively connect to the server, at the same time, to ensure that the client's 6 connect () functions are first called, the server's Accpet () to start the call.
Server:
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys /socket.h> #include <netinet/in.h> #include <arpa/inet.h>int main (int argc, char *argv[]) {unsigned short Port = 8000;int SOCKFD = socket (af_inet, sock_stream, 0); if (SOCKFD < 0) {perror ("socket"); exit (-1);} struct sockaddr_in my_addr;bzero (&my_addr, sizeof (MY_ADDR)); my_addr.sin_family = Af_inet;my_addr.sin_port = htons (port); my_addr.sin_addr.s_addr = htonl (inaddr_any); int err_log = b IND (SOCKFD, (struct sockaddr*) &my_addr, sizeof (MY_ADDR)), if (err_log! = 0) {perror ("binding"), close (SOCKFD); exit ( -1);} Err_log = Listen (SOCKFD, 2);//wait queue is 2if (Err_log! = 0) {perror ("Listen"), close (SOCKFD); exit (-1);} printf ("After listen\n"), Sleep (20);//delay 20 seconds printf ("Listen client @port =%d...\n", port); int i = 0;while (1) {struct Sockaddr_in client_addr; Char Cli_ip[inet_addrstrlen] = ""; socklen_t Cliaddr_len = sizeof (CLIENT_ADDR); int CONNFD;CONNFD = Accept (SOCKFD, (struct sockaddr*) &client_addr, &cliaddr_len); if (CONNFD < 0) {perror ("accept"); continue;} Inet_ntop (Af_inet, &client_addr.sin_addr, Cli_ip, Inet_addrstrlen);p rintf ("-----------%d------\ n", ++i);p rintf ("Client ip=%s,port=%d\n", Cli_ip,ntohs (Client_addr.sin_port)), char recv_buf[512] = {0};while (recv (CONNFD, Recv_buf, sizeof (RECV_BUF), 0) > 0) {printf ("recv data ==%s\n", recv_buf); Close (CONNFD); Close connected sockets//printf ("Client closed!\n");} Close (SOCKFD); Turn off the listener socket return 0;}
Client:
#include <stdio.h> #include <unistd.h> #include <string.h> #include <stdlib.h> #include < arpa/inet.h> #include <sys/socket.h> #include <netinet/in.h>void test_connect () {unsigned short port = 8000; Server's port number char *server_ip = "10.221.20.12"; server IP address int sockfd;sockfd = socket (af_inet, sock_stream, 0);//Create Communication endpoint: Socket if (SOCKFD < 0) {perror ("socket"); exit (-1);} struct sockaddr_in Server_addr;bzero (&server_addr,sizeof (SERVER_ADDR)); Initialize server address server_addr.sin_family = Af_inet;server_addr.sin_port = htons (port); Inet_pton (Af_inet, SERVER_IP, & SERVER_ADDR.SIN_ADDR); int err_log = Connect (sockfd, (struct sockaddr*) &server_addr, sizeof (SERVER_ADDR)); Active Connection Server if (Err_log! = 0) {perror ("connect"); Close (SOCKFD); exit (-1);} printf ("Err_log =========%d\n", err_log); Char send_buf[100]= "This was for test"; Send (SOCKFD, Send_buf, strlen (SEND_BUF) , 0); Send information to the server//close (SOCKFD);} int main (int argc, char *argv[]) {pid_t pid;pid = fork (); if (0 = =PID) {test_connect ();//1pid_t PID = fork (); if (0 = = pid) {test_connect ();//2}else if (PID > 0) {test_connect ();//3}}else if (PID > 0) {test_connect ();//4pid_t PID = fork (); if (0 = = pid) {test_connect ();//5}else if (PID > 0) {test_connect (); 6}}while (1); return 0;}
The same is to run the server first, before running the client, the server accept () function delay of 20 seconds, to ensure that the client's connect () all calls completed before calling accept (), the results are as follows:
According to UNP, after the connection queue is full (where the length is set to 2, and 6 connections are made), the call to connect () should all fail at a later time, but in fact the test results are: Some connect () has been successfully returned immediately, and some have successfully returned after a significant delay. The same is true for server Accpet () functions: Some immediately return successfully, and some are returned successfully after a delay.
For the above server code, we changed the second parameter of Lisen () to a number greater than 6 (such as 10), rerun the program, and found that the client connect () immediately returned to success, the server Accpet () function also immediately returned to success.
After the TCP connection queue is full, Linux does not reject all the connections as stated in the book, some delay the connection, some will not connect (when the queue length is specified as 0), the second parameter of the server's listen () when writing the program is better to fill in, write too big bad (specifically can see CAT/ Proc/sys/net/core/somaxconn, the default maximum limit is 128), waste resources, write too small or bad, delay to establish a connection (also may not connect).
Test code Download please click here.
The relationship between connect (), listen (), and accept () in TCP network programming