What is the basic process of a TCP request?
socket
Used to specify the protocol type for communication. The return value is socket Descriptor
The function is defined as int socket(int family,int type,int protocol), defined in sys/socket.h.
- Family: specifies a protocol family. For example, AF_INET indicates IPv4 and AF_INET6 indicates IPv6
- Type: indicates the socket type. For example, SCOK_STREAM indicates a byte stream socket and SCOK_DGRAM indicates a datagram socket
- Protocol: constant value of a protocol type. The value is usually 0, indicating that the default values of all family and type are used. IPROTO_TCP indicates TCP, and IPROTO_UDP indicates UDP
connect
The client is used to establish a connection to the TCP server. Its call triggers the TCP three-way handshake, which moves the current socket from the CLOSED state to the SYN_SENT state, and then to the ESTABLISHED state if successful. Returns only if the connection is established or an error occurs.
If connect fails, the socket is no longer available and must be closed. To reconnect, the socket must be called again
In what cases does CONNECT fail?
- The client did not receive a SYN response and returned ETIMEDOUT error.
If the 4.4bSD kernel sends SYN, wait 6s for no response, wait 24s for no response, and return an ETIMEDOUT error if it waits 75s in total
- The client receives a SYN response as RST and returns an ECONNREFUESED error.
This is a hard error. RST may be received if: no server is listening to the connection port; TCP wants to cancel the connection; TCP received a node on a nonexistent connection
- The router raised the ‘Destination unreachable’ ICMP error.
This is a soft error
bind
Assigns a local protocol address to a socket.
Local protocol address: a combination of an IPv4 or IPv6 address and a port
The port and address for calling bind can be specified, neither, or only one. If the port number is not specified, the kernel selects a temporary port when bind is called.
Int bind(int sockfd,const struct *myaddr,socklen_t addrlen); The first argument is the socket descriptor returned by the socket, the second argument is a pointer to the protocol-specific address structure, and the third argument is the length of the address structure. Since the address structure is a constant, it cannot be returned if it is a kernel-specified port, so to get a temporary kernel-specified port, you must call getsockName to return the protocol address
listen
Do two things
- Indicates that the kernel should accept connection requests to this socket, and the TCP state changes from CLOSED to LISTEN
- Specifies the maximum number of connections that the kernel should queue for the corresponding socket
By default, a socket created by a socket is used to initiate requests, that is, to call connect. Listen turns the socket into a passive socket, which is used to receive requests
A listening socket queue maintained by the kernel
The backlog value varies depending on the operating system
- Incomplete queue: A SYN packet sent by a client reaches the server, and the server is waiting to complete the TCP three-way handshake.
- Completed queue: one of the entries for each client that has completed the TCP three-way handshake
The three handshakes completed normally move the uncompleted connection pair to the end of the completed queue. When a process calls Accept, the head of the completed queue is returned to the process, and if the completed queue is empty, the process is put to sleep, which is the default blocking mode, until TCP puts an item in the queue.
When a client SYN arrives, TCP ignores the packet if the queue is full, causing the client to retransmit
accept
Used to return the next completed connection from the completed connection queue header. If accept succeeds, the return value is a new descriptor automatically generated by the kernel to represent the TCP connection established with the client.
A server typically creates only one listening socket, which lasts for the declared life of the service. However, a connection socket is created for each client connection and closed when the service to the client is complete
Accept generates a new descriptor to handle the connected request process
First, the server in the listening state listens for the connection request from the client
All descriptors opened in the parent process before fork is called are shared with the child process after fork returns.
Concurrent server
In Unix, the simplest way to write a concurrent server is to fork a sub-process to serve each client. The general implementation is as follows:
for(;;) { connfd=Accept(listenfd,..) // a call to fork returns twice. Returns a value of 0 once in the child process; Returns the process ID of the new child process once in the calling process, that is, the parent process.if((pid=Fork())==0){ Close(listenfd); // Stop the child process without listeningdoSomething(connfd); // Process client request Close(connfd); // Close the connection after processing the client requestexit(0); } Close(connfd) // The parent process can be disconnected}Copy the code
A new connection was closed in the parent process. Why can the child process still process connection requests?
Each file or socket has a reference count. Maintained in a file table, this represents the number of currently open descriptors that reference the file or socket. The file entry associated with Listenfd on socket returns has a reference count of 1, as does Connfd returned by Accept. After the fork, the two file descriptors are shared between the parent and child processes, so the reference count becomes 2, so that when the parent closes connfd, only the reference count changes from 2 to 1, and the actual resource cleaning and freeing takes place only when it goes to 0.
close
Used to close the socket, and if the file’s reference count happens to be zero at this point, a FIN packet is sent, terminating the TCP connection.
If you want to terminate it directly, use shutdown