TCP socket programming

Socket programming

       In the TCP/IP protocol, "IP address + TCP or UDP port number" uniquely identifies a process in network communication, and "IP address + port number" is called a socket.

       In the TCP protocol, the two processes that establish a connection each have a socket to identify, then the socket pair formed by the two sockets uniquely identifies a connection. "Socket" itself means "socket", which is used to describe the one-to-one relationship in network connections.

       The application layer programming interface designed for the TCP/IP protocol is called socket API.

       The function interface of TCP protocol and UDP protocol is introduced below.

 

TCP socket

□Big and small end

       The multi-byte data in the memory is divided into big endian and little endian relative to the memory address, and the offset address of the multi-byte data in the disk file relative to the file is also big endian and little endian. The network data stream is also divided into big-endian and small-endian. The address of the network data stream is stipulated: the data sent out first is the low address, and the data sent out later is the high address.

       The TCP/IP protocol stipulates that the network data stream should be in big-endian byte order, that is, the low address and the high byte. For example, in the UDP segment format in the previous section, the address 0~1 is the sixteen-digit source port number. If the port number is 1000 (0x3e8), then the address 0 is 0x03, the address 1 is 0xe8, the first address is 0, the second Send address 1. But if the sending host is in little-endian byte order, these 16 bits will be interpreted as 0x3e8 instead of 1000. Therefore, endianness conversion is required in the middle.

       To convert between network byte order and host byte order, you can call the following library functions:

 

       h means host

       n means network

       l represents a 32-bit long integer

       s represents a 16-bit short integer. For example, htonl means to convert a 32-bit long integer from host byte order to network byte order.

 

     The data type of the socket address and related functions. The socket API is an abstract network programming interface suitable for various underlying network protocols. However, the address formats of various network protocols are not the same.

sockaddr data structure:

        

 

       The address format of IPv4 and IPv6 is defined in netinet/in.h, IPv4 address is represented by sockaddr_in structure, and IPv6 address is represented by sockaddr_in6 structure. The address format of UNIX Domain Socket is defined in sys/un.h, represented by the sockaddr_un structure. The address types of IPv4, IPv6 and UNIXDomain Socket are defined as constants AF_INET, AF_INET6, AF_UNIX, respectively. Their structure is not the same, but the beginning is the same. In this way, as long as the first address of a certain sockaddr structure is obtained, the content of the structure can be determined according to the address type field without knowing the specific type of sockaddr structure. Therefore, the socket API can accept various types of sockaddr structure pointers as parameters, such as bind, accept, connect and other functions. The parameters of these functions should be designed as void * types in order to accept various types of pointers, but the implementation of the sock API is early Standardized in ANSI C, there was no void * type at that time, so the parameters of these functions are represented by the struct sockaddr * type, and the type conversion must be forced before passing the parameters:

         

 

 

       In socket network programming based on IPv4, the member struct in_addr sin_addr in sockaddr_in represents a 32-bit IP address. But we usually use dotted decimal string to represent IP address, the following function can convert between string representation and in_addr representation.

The function of converting a string to dotted decimal:

         

 

TCP protocol communication process:

 

       Server: After calling socket(), bind(), listen() to complete initialization, call accept() to block and wait, and it is in the state of listening port.

       Client: After calling socket() to initialize, call connect() to send the SYN segment and block waiting for the server to respond. The server responds with a SYN-ACK segment, and the client returns from connect() after receiving it, and responds with an ACK segment at the same time.

       Server: Return from accept() after receiving the ACK received by the client.

 

Simple TCP network program

tcp_server.c

         

        

        

 

 

 

       The socket APIs used in the program are:

       <1>

        

 

       socket() opens a network communication port. If it succeeds, it returns a file descriptor just like open(). The application can use read/write to send and receive data on the network like reading and writing files. If the socket() call fails, Returns -1. For IPv4, the family parameter is specified as AF_INET. For the TCP protocol, the type parameter is specified as SOCK_STREAM, which represents a stream-oriented transmission protocol.

 

        <2>

        

       The function of bind() is to bind the parameters sockfd and myaddr together, so that the file descriptor used for network communication, sockfd, monitors the address and port number described by myaddr. As mentioned earlier, struct sockaddr * is a general pointer type. The myaddr parameter can actually accept sockaddr structures of multiple protocols, and their lengths are different, so the third parameter addrlen is required to specify the length of the structure. The myaddr parameter in our program is initialized like this:

local.sin_family = AF_INET;

local.sin_port =htons(_port);

local.sin_addr.s_addr =inet_addr(_ip);

 

       <3>

        

       When a client initiates a connection, the accept() called by the server returns and accepts the connection. If a large number of clients initiate a connection and the server is too late to process, the client that has not yet accepted is in a connection waiting state, and listen() declares that sockfd is in Monitor state, and allow at most backlog clients to be in a connection waiting state, and ignore if more connection requests are received. listen() returns 0 on success and -1 on failure.

 

        <4>

        

       After the three-party handshake is completed, the server calls accept() to accept the connection. If there is no client connection request when the server calls accept(), it will block and wait until there is a client connection. addr is an outgoing parameter, and the client's address and port number will be sent out when accept() returns. The addrlen parameter is an incoming and outgoing parameter (value-result argument), the length of the buffer addr provided by the caller is passed in to avoid buffer overflow problems, and the actual length of the client address structure (there is It may not fill up the buffer provided by the caller). If you pass NULL to the addr parameter, it means you don't care about the client's address.

 

tcp_client.c

        

        

 

 

       Since the client does not need a fixed port number, bind() is not used, and the client's port number is automatically assigned by the kernel. The server does not have to call bind(), but if the server does not call bind(), the kernel will automatically assign a listening port to the server. The port number is different every time the server is started, and the client will have trouble connecting to the server.

 

        

 

       The client needs to call connect() to connect to the server. The parameters of connect and bind are in the same form. The difference is that the parameter of bind is its own address, and the parameter of connect is the address of the other party. connect() returns 0 if it succeeds, and returns -1 if it fails.

 

       First compile and run the server, then run the client, and send a message to the server:

        

 

       Server response message:

        

 

       Now use Ctrl C to terminate the server, then run the server immediately, the result is:

        bind error: Address already in use

        
       This is because although the server application program is terminated, the TCP protocol layer connection is not completely disconnected, so the same server port cannot be monitored again. Now use Ctrl C to terminate the client. The socket descriptor is automatically closed when the client terminates, and the server's TCP connection is in the TIME_WAIT state after receiving the FIN segment sent by the client.

       The TCP protocol stipulates that the party that actively closes the connection must be in the TIME_WAIT state, and wait for two MSLs before returning to the CLOSED state. Because we first Ctrl-C to terminate the server, the server is the party that actively closes the connection. It still cannot be during the TIME_WAIT period. Listen to the same server port again.

       You can use netstat -nltp to find its PID, and then use the kill command to kill it, you can re-bind:

        

 

 

Guess you like

Origin blog.csdn.net/wxt_hillwill/article/details/73723750