Basic TCP Socket Programming
The basic socket functions required for a complete TCP client/server program are described.
1. Overview
In the entire TCP client/service program, the functions used are just a few, the overall block diagram is as follows:
2.socket function
In order to perform network I/O, what a process must do is call the socket function. Its function is declared as follows:
#include <sys/socket.h>int socket(int family ,intint protocol);
which
Family: Specifying the Protocol family
Type: Specify socket type
Protocol: Specify a protocol, set to 0, to select the system default values for the given family and type combinations.
These parameters have some specific values defined as follows:
| faimly |
Description |
| Af_inet |
IPV4 protocol |
| Af_inet6 |
IPV6 protocol |
| Af_local |
UNIX Domain protocol |
| Af_route |
Routing sockets |
| Af_key |
Key sockets |
Table 1 Family constant of the socket function
| type |
Description |
| Sock_stream |
BYTE stream sockets |
| Sock_dgram |
Datagram sockets |
| Sock_seqpacket |
Ordered group sockets |
| Sock_raw |
Raw sockets |
Table 2 type values for the socket function
| Protocol |
Description |
| Ipproto_tcp |
TCP Transport Protocol |
| Ipproto_udp |
UDP Transport Protocol |
| Ipproto_sctp |
SCTP Transfer Protocol |
Table 3 Protocol of the socket function af_inet or AF_INET6
When the socket function call succeeds, it returns a small nonnegative integer value, which becomes the socket descriptor, or SOCKFD. to get this descriptor, we developed the protocol family and socket type, and did not specify the local and remote protocol addresses.
In addition, the book also mentions a af_xxx (for the address family) and pf_xxx (for the Protocol family) of the difference, generally use AF, know this is okay.
3.connect function
The function declaration is as follows:
#include <sys/socket.h>int connect(intconststruct sockaddr *servaddr,socklen_t addrlen);
SOCKFD: The socket descriptor returned by the socket.
SERVADDR: Socket address structure that contains the server IP and port number.
Addrlen: Socket address structure size to prevent read out of bounds.
When the client calls connect, it initiates a three-way handshake connection to the server, which is not returned until the connection is established and the connection is faulted, and there may be several situations where an error is returned:
1) The TCP client did not receive a response from the SYN subsection. (If a SYN is issued, wait for the 6s to resend one if it is not responding, wait for 24s and then send one if it is still not responding.) Waiting for a total of 75s still not received returns error Etimedout)
2) If the response to the customer's SYN is RST, indicating that the server host does not have a process waiting to connect to it on the port we specify, the client receives the RST and returns a econnrefused error .
The three conditions for the RST are: The destination SYN arrives without a listening server, TCP wants to cancel an existing connection, and TCP receives a subsection that does not exist on the connection at all.
3) If the customer sends a SYN in the middle of a router on a "Destination unreachable" (Destination unreachable) ICMP error, it is considered a soft error, at a specified time (such as the above 75s) did not receive a response, the kernel will save the information as Ehostunreach or Enetunreach error is returned to the process.
If connect fails the socket is no longer available and must be closed, we cannot call the Connect function again on such a socket, and when the loop calls the function connect to try each IP address for a given host until one succeeds, after each connection fails, Must close the current socket descriptor and call the socket from the new connector.
4.bind function
The BIND function assigns a local protocol address to a socket. The protocol address is a combination of a 32-bit IPV4 address or a 128-bit IPV6 address and a 16-bit TCP/UDP port number.
The BIND function can be called to make a specific port number, or to make an IP address, or two to be specified, neither of which is specified.
The function declaration is as follows:
#include <sys/socket.h>int bind(intconststruct sockaddr * myaddr,socklen_t addrlen);
parameter Description:
SOCKFD: Socket Descriptor
MYADDR: Pointer to socket address structure
Addrlen: Length of the above structure to prevent the kernel from overstepping
Servers bundle their well-known ports at startup, such as Port 13 for time-fetching services. If you do not call the BIND function, TCP creates a temporary port when you call connect or listen, which is common to the client (after all, we have never seen the client program call the BIND function), and it is relatively rare for a TCP server. Because the TCP server is known through its well-known port.
A process can bind a specific IP address to its socket: this is not necessary for the client, because the kernel chooses the source IP address based on the outgoing network interface. For the server, this will limit the server to receiving only the client connections destined for that IP address .
For IPV4, the wildcard address is specified by a constant value of INADDR_ANY 0, which tells the kernel to select an IP address, so we often see the following statement:
struct sockaddr_in servaddr;servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
Similarly, when the port number is specified as 0 o'clock, the kernel chooses a temporary port when bind is invoked, but the bind function does not return the value selected by the kernel, and the second parameter has a const qualification. If you want the ephemeral port value to be selected by the kernel, it must be called getsockname to return the protocol address.
Finally, it is important to note that the bind binding requires Superuser privileges when the port number is reserved. This is why we have to add the server program under Linux sudo , and if there are no superuser privileges, the bindings will fail.
5.listen function
The Listen function is called by the TCP server and its function is declared as follows:
#include <sys/socket.h>int listen (intint backlog);
There are two main functions of the Listen function:
1. For parameter SOCKFD: When the socket function creates a socket, it is assumed to be an active socket. The Listen function converts the socket into a passive socket, indicating that the kernel should accept connection requests to that socket.
2. For parameter backlog: Specifies the maximum number of connections that the kernel should queue for the corresponding socket = Incomplete connection queue + completed connection queue
which
Connection queue not completed: socket (SYN_RCVD status) that indicates that the server received a SYN from the client but has not yet established a three-way handshake connection
Completed connection queue: A socket (established state) that represents the completion of the three-way handshake connection process
The process of combining three-way handshake:
1. Customer calls connect to send SYN sub-section
2. The server receives a SYN sub-section in the unfinished queue to establish an entry
3. Until the third section of the Sanlu handshake (the customer's ACK to the server SYN) arrives, the item never completes the queue and moves to the end of the completed queue.
4. When the process calls accept, the team is finished, and when the completed queue is empty, the Accept function blocks and the process sleeps until the team is completed.
So, if the three-way handshake is complete normally, the time that any of the items in the connection queue is not completed is the time at which the server receives the client's SYN and receives the client's ACK (RTT).
:
For a Web server, the RTT is 187ms.
There are also points to note about these two queues: When a client sends a SYN sub-section to the server, if the server's outstanding connection queue is full, the server ignores the SYN section and the server does not immediately respond to the client with a RST, because the client has its own retransmission mechanism, If the server sends an RST, then connect on the client side will return an error. In addition, the client cannot distinguish whether the RST means "the port does not have a server listening" or "the port has a server that is listening but its queue is full." ”
6.accept function
The TCP server calls the Accept function, which declares the following function:
#include<sys/socket.h>int accept (intstruct sockaddr *cliaddr ,socklen_t * addrlen);
parameter Description:
SOCKFD: Socket Descriptor
CLIADDR: The protocol address of the peer (customer)
Addr: Size
When the accept call succeeds, a new socket descriptor is returned, for example:
int connfd = Accept(listenfd,(SA*)NULL,NULL);
Where we call this listenfd a listener socket descriptor, which is called connfd a connected socket descriptor. , it is important to differentiate between these two sockets, a server process usually requires only one listener socket, but it can have many connected sockets (such as creating sub-processes by fork), which means that each client creates a connectfd when a connection is made, and when the connection is complete, The corresponding connected sockets will be closed.
Through the pointer we can get the socket information of the client, but if we are not interested in these can be other they are null, the book gives an example, the server corresponding connection, print the client's IP address and port number. The
Section code is as follows:
#include "Unp.h" #include <time.h> int Main ( int argc, char **argv) {//... for ( ; ; ) {len = sizeof (CLIADDR); CONNFD = Accept (LISTENFD, (SA *) &cliaddr, &len); //connected socket //CLIADDR Get the Client protocol address information. printf ( "connection from%s, Port%d\n" , Inet_ntop (af_inet, &cliaddr.sin_addr, Buff, sizeof (Buff)), Ntohs (Cliaddr.sin_port)); //... Close (CONNFD); }}
7.fork and EXEC functions
It is no stranger to use fork to create a subprocess, where a concurrent server program is written with fork.
#include <unisted.h>pid_t fork(void);
Returns 0 in the child process, returns the child process ID in the parent process, because there is this parent-child concept, so the fork function call is returned two times.
Some features about the fork function:
1. Any child process can have only one parent process, and the child process may get the parent process ID through Getppid
2. In the parent process, the descriptor that was opened before the fork is called and shared with the child process after the fork is applied to this feature in network communication.
Speaking of the 2nd feature above, we know that the server process often waits for the client to connect in the dead loop, using the feature 2, when the Accept function call returns a CONNFD, the child process can use it to read and write, and the parent process shuts down directly.
In short: The parent process creates a descriptor that the child process actually operates on.
The EXEC function is actually 6 functions, and their differences are mainly:
(a) whether the program file to be executed is specified by a file name or path name.
(b) Whether the parameters of the new program are listed or referenced by an array of pointers
(c) Whether to pass the environment of the calling process to the new program or to the new program to specify the new environment.
For more information about exec, refer to the following: Linux C programming exec function using
8. Concurrent Server
First a concept “迭代服务器” is called, for example:
for(;;){ connfd = Accept(listenfd,(SA*)NULL,NULL); ticks=time(NULL); snprintf(buff,sizeof(buff),"%.24s\r\n",ctime(&ticks)); Write(connfd,buff,strlen(buff)); Close(connfd);}
When a client connects, the server writes the time information to the client, closes the connected socket, and goes back to the top of the for loop to block waiting for the connection, so that every time the connection arrives, the service must be completed because it occupies the server process. But because the simple access time service itself quickly, a single service is completed immediately, so the impact is not small, but if it is very time-consuming service is not necessarily, we do not want the server by a single customer for a long time, but want the server to serve multiple users at the same time, So the simplest way to write a concurrent server program in UNIX is to fork a subprocess to serve each customer.
pid_t pid;int listenfd;int connfd;listenfd=Socket(/*...*/);Bind(listenfd,/*...*/);Listen(listenfd,/*...*/);for(;;){ connfd = Accept(listenfd,(SA*)NULL,NULL); if((pid=fork())==0)//子进程 { close(listenfd);//关闭监听套接字 doit(connfd);//服务 close(connfd); exit(0); } close(connfd);}
What I don't understand here is why do I have to turn off the listener socket (LISTENFD) in the subprocess?
This is related to fork knowledge:
1. First fork is not the parent process to run from start to finish, otherwise it will not be infinite.
2. The parent process at the call fork, the entire parent process space is copied to the child process as is, including instructions, variable values, program call stacks, environment variables, buffers, and so on.
3. In the example of a concurrent server, a child process copies a connected socket (CONNFD) and a listening socket (LISTENFD) to its own process space.
4. For socket descriptors, they all have a reference count, and the reference count becomes 2 when the descriptor is copied after the fork.
5. Therefore, we turn off CONNFD in the parent process (because we use CONNFD in the subprocess), turn off LISTENFD in the child process (because we listen in the parent process), and their reference count becomes 1.
6. Then, the desired state is that the parent process retains a listening socket that continues to wait for a client connection to service the client request through the connected socket in the child process.
7. Finally close the CONNFD in the subprocess, or exit(0) make the CONNFD really complete cleanup and resource release.
9.close function
The close function can be used to close the socket and terminate the TCP connection.
#include <unistd.h>int close(int sockfd);
As you can see from the concurrent server in the previous section, the close function is a reference count minus 1 for the socket descriptor, that is, if the reference count is not 0 when close is called, it will not cause the TCP four packet connection termination sequence, which is what the parent process expects from the concurrent server that the child process shares the connected socket. But if we do want to send a fin on a TCP connection, the shutdown function is called.
10.getsockname and Getpeername functions
#include <sys/socket.h>int getsockname(int sockfd,struct sockaddr*localaddr,socklen_t *addrlen);int getpeername(int sockfd,struct//若成功则为0,失败则为-1
The effect of these two functions:
1. First we know that the bind function is generally not used in TCP clients, and when getsockname Connect returns, it can return the client-side local IP address and the local port number.
2. If bind binds the port number 0 (kernel selection), because the bind parameter is const, it must be obtained by getsockname going to the local port number given by the kernel.
3. Get the address family of a socket
4. On a server with a wildcard IP address bind, after accept successfully returns, getsockname It can be used to return the local IP address that the kernel assigned to the connection. Where the socket descriptor parameter must be a connected socket descriptor.
11. Summary
This chapter describes the socket programming functions, both the client and the server side are socket started, the client then calls connect , and the server side has been called bind , listen and accept finally used close to close the descriptor.
UNIX Network programming note (3)-Basic TCP socket programming