📜 ⬆️ ⬇️

classic TCP server

My first TCP Server was created a couple of years ago. The basis for the creation was the book by R. Stevenson "Unix - Professional Programming." There are several approaches to the creation of TCP servers. In this post I want to talk about the classic TCP server.



When building a classic TCP Server, three components can be distinguished:

When working with sockets, you must:

Consider the code, see the comments:
1 struct sockaddr_in addr;
2 struct sockaddr_un local;
3
4 int err,len;
5
6 //
7 if( -1 == ( ls = socket( AF_INET, SOCK_STREAM, IPPROTO_TCP ) )) {
8 perror( "Socket can not created!\n");
9 return ;
10 }
11
12 // SO_REUSEADDR
13 // . bind()
14 setsockopt( ls, SOL_SOCKET, SO_REUSEADDR, &rc, sizeof(rc) );
15 memset( &addr , 0, sizeof(addr) );
16
17 //
18 addr.sin_family = AF_INET;
19 addr.sin_port = htons (port);
20 addr.sin_addr.s_addr = INADDR_ANY ;
21
22 //
23 if ( err = bind( ls, (struct sockaddr*) &addr, sizeof(addr) ) < 0 ) {
24 close(ls);
25 perror( "bind error!\n%s ", gai_strerror(err) );
26 return ;
27 }
28
29
30 //
31 if ( listen( ls, 25) < 0) { ;
32 close(ls);
33 perror( "listen error!\n");
34 return ;
35 }
36
37 //
38 pid_t pid = Demonize();
39
40 if ( pid==-1 ) {
41 close(ls);
42 perror( "demonize error!\n");
43 return;
44 }
45
46 //
47 while (true) {
48 // ,
49 rc = accept( ls, NULL, NULL );
50 //
51 pid = fork();
52
53 if ( pid < 0) syslog( LOG_ERR, " fork abort" );
54 if (pid == 0 ) { //
55 close( ls ); //
56
57 process(rc); // ,
58 //
59 close(rc);
60 return;
61 } else {
62 //
63 close( rc ); //
64 }
65
66 } // end while

')
And now some explanations:

Create a listening socket descriptor. Lines 7-10:

#include <sys / socket.h>
int socket (int domain, int type, int protocol);

domain constant:
AF_INET - IP Socket
AF_UNIX - UNIX socket

constant type:
SOCK_STREAM - persistent connection socket
SOCK_DGRAM - datagram socket

int protocol constant:
IPPROTO_TCP - protocol type

The socket function returns the descriptor number of the listening socket. If the result is less than 0, then the system error.

Lines 12-28 Assign an address to a socket.

#include <sys / socket.h>
int setsockopt (int socket, int level, int option_name, const void * option_value, socklen_t option_len);
sets the socket option:
SO_REUSEADDR reuse of local addresses for bind () function
If the result of the operation is less than 0, then the system error.

Lines 18-20 set the listening address and port in the sockaddr_in structure
addr.sin_family = AF_INET; // domain type INET
addr.sin_port = htons (port); // assign the port
addr.sin_addr.s_addr = INADDR_ANY; // any incoming address

The bind function associates an address with a nameless socket:
#include <sys / socket.h>
int bind (int socket, const struct sockaddr * address, socklen_t address_len);
int socket - socket descriptor
address - the address of the sockaddr structure with
address_len - the size of the sockaddr structure
If the result of the operation is less than 0, then the system error.

We start to listen to a socket of the Line 31-35. Using the listen function, the server declares its desire to accept connection requests:

#include <sys / socket.h>
int listen (int socket, int backlog);
int socket - socket descriptor
int backlog - the maximum length of the queue of pending connection requests
If the result of the operation is less than 0, then the system error.

Line 38-44, demonizing the process. More details in the cl. an article on demons processes.

Lines 47-66, processing the connection

Line 49, The accept function accepts a request and converts it to a connection.
#include <sys / socket.h>
int accept (int socket, struct sockaddr * restrict address, socklen_t * rest address address_len);
int socket - the descriptor of the listening socket, it is not connected to the connection and remains to listen to subsequent connections,
address - a pointer to the socket address structure, in which the client address will be stored at the end
address_len is the size of the buffer for placing the address structure, if address and address_len are NULL, then we are not interested in the client’s address.
The accept function returns a socket descriptor associated with the connection, or -1 in case of failure.

We can handle the connection and accept the following, but as a rule, the connections are processed for a long time and the server cannot wait; therefore, after the connection is established, the child process is generated in line 51 with the fork command. Next, in the parent process, close the connection descriptor, and in the child process, close the listening socket descriptor and call the user-defined function that will handle our connection.
The connection is processed using the read / write commands:
write to socket: write (rc, buff, len)
read from socket: read (rc, buff, len)
rc - connection descriptor
buff - read / write buffer
len is long.
At the end of the connection processing, the rc socket descriptor must be closed: close (rc).

What is not included and will be separately described in other articles: description of the fork command, signal processing, demonization of the process.

Source: https://habr.com/ru/post/70796/


All Articles