Socket programming practices in Linux (8) Select restrictions and poll (preliminary knowledge of concurrency)

Source: Internet
Author: User
Tags socket error htons

Socket programming practices in Linux (8) Select restrictions and poll (preliminary knowledge of concurrency)
Select restrictions

The number of concurrent servers implemented by the select statement is generally restricted by the following two factors:

1) Maximum file descriptor limit that a process can open. This can be changed by adjusting the kernel parameters. You can use ulimit-n (number) to adjust or use the setrlimit function (root permission required). However, the maximum number of opened resources for a system is limited, which is related to the memory size, you can view it through cat/proc/sys/fs/file-max.

2) The fd_set set capacity limit in the select statement (FD_SETSIZE, usually 1024). This requires re-compiling the kernel to change.

For the first limit:

 

nclude 
 
  #include 
  
   int getrlimit(int resource, struct rlimit *rlim);int setrlimit(int resource, const struct rlimit *rlim);
  
 
Here, a resource value RLIMIT_NOFILE indicates a value greater than the maximum file description that can be opened by a process. If the value is exceeded, an EMFILE error occurs.

 

 

Rlim: struct describing the hardware and software constraints of resources. The prototype is struct rlimit {rlim_t rlim_cur;/* Soft limit */rlim_t rlim_max;/* Hard limit (ceiling for rlim_cur )*/};
Return description:
0 is returned when execution is successful. -1 is returned for failure, and errno is set to one of the following values
EFAULT: the space pointed by the rlim pointer is not accessible.
EINVAL: the parameter is invalid.
EPERM: when the resource limit value is added, the power cannot be

 

 

The soft limit is a built-in one. It is best not to go beyond the limit. If it goes beyond the limit, the system may send a signal to the process to terminate its operation.

The hard limit is generally the upper limit of the soft limit;

Resource available value

RLIMIT_AS

Maximum length of virtual memory available for a process, including stack, global variables, and dynamic memory

RLIMIT_CORE

Maximum size of core files generated by the kernel

RLIMIT_CPU

All cpu time used, in seconds

RLIMIT_DATA

Process DATA Segment (initialized DATA segment, not initialized BSS segment and heap) Restriction (in B)

RLIMIT_FSIZE

File Size Limit

RLIMIT_SIGPENDING

Maximum number of signals that can be suspended by the user

RLIMIT_NOFILE

Maximum number of opened files

RLIMIT_NPROC

Maximum number of processes that a user can create

RLIMIT_STACK

The process stack memory limit. If the memory limit is exceeded, the SIGSEGV signal is generated.

Process resource restrictions are generally established by the 0 # process at the beginning of the system. When changing the resource restrictions, the following three rules must be observed:

1. Any process can change a soft limit to a value smaller than or equal to its hard limit.

2. Any process can reduce its hard limit value, but it must be greater than or equal to its soft limit value. This reduction cannot be reversed for common users.

3. Only Super Users can raise the hard limit.

 

/** Example: getrlimit/setrlimit get/set the number of files opened by the process **/int main () {struct rlimit rl; if (getrlimit (RLIMIT_NOFILE, & rl) =-1) err_exit ("getrlimit error"); cout <"Soft limit:" <rl. rlim_cur <endl; cout <"Hard limit:" <rl. rlim_max <endl; cout <"------------------------->" <endl; rl. rlim_cur = 2048; rl. rlim_max = 2048; if (setrlimit (RLIMIT_NOFILE, & rl) =-1) err_exit ("setrlimit error"); if (getrlimit (RLIMIT_NOFILE, & rl) =-1) err_exit ("getrlimit error"); cout <"Soft limit:" <rl. rlim_cur <endl; cout <"Hard limit:" <rl. rlim_max <endl ;}

 

 

Test the maximum number of links that can be established. The following is the client code:

 

#include 
 
   #include 
  
    #include 
   
     #include  #include 
    
      #include 
     
       #include 
      
        #include 
       
         #include 
        
          #define ERR_EXIT(m) \ do \ { \ perror(m); \ exit(EXIT_FAILURE); \ } while( 0) int main( void) { int count = 0; while( 1) { int sock; if ((sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) { sleep( 4); ERR_EXIT( "socket"); } struct sockaddr_in servaddr; memset(&servaddr, 0, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_port = htons( 5188); servaddr.sin_addr.s_addr = inet_addr( "127.0.0.1"); if (connect(sock, ( struct sockaddr *)&servaddr, sizeof(servaddr)) < 0) ERR_EXIT( "connect"); struct sockaddr_in localaddr; socklen_t addrlen = sizeof(localaddr); if (getsockname(sock, ( struct sockaddr *)&localaddr, &addrlen) < 0) ERR_EXIT( "getsockname"); printf( "ip=%s port=%d\n", inet_ntoa(localaddr.sin_addr), ntohs(localaddr.sin_port)); printf( "count = %d\n", ++count); } return 0; }
        
       
      
     
    
   
  
 

 

Let's take a look at the server output:

Recv connect ip = 127.0.0.1 port = 57430
Count = 2039
Recv connect ip = 127.0.0.1 port = 57431
Count = 2040
Recv connect ip = 127.0.0.1 port = 57432
Count = 2041
Recv connect ip = 127.0.0.1 port = 57433
Count = 2042
Recv connect ip = 127.0.0.1 port = 57434
Count = 2043
Recv connect ip = 127.0.0.1 port = 57435
Count = 2044
Recv connect ip = 127.0.0.1 port = 57436
Accept error: Too enabled open files

Resolution: for the client, you can only open up to 1021 connection sockets, because a total of 1024 files can be opened in Linux, for example, 0, 1, 2 must be removed. The server can only return 1020 connected sockets with accept, because in addition to and 2, there is also a listening socket listenfd, a socket on the client (not necessarily the last one) although a connection has been established and is in the completed connection queue, the maximum descriptor limit is reached when accept is returned, an error is returned, and a prompt message is printed.

 

When the client returns-1 in socket (), it calls sleep (4) Resolution.

When the client calls the socket to prepare to create 1,022nd sockets, an error will also be prompted as shown above. At this time, the socket function returns-1 error. If there is no sleep 4s, then exit the process. What is the problem? If you exit the process directly, all sockets opened by the client are closed, that is, many FIN segments are sent to the server. At this time, the server may still be in accept, that is, the connected socket is returned from the connected queue. In this case, the server not only listens to the readable events of the socket, but also cares about the readable events of the socket that has been connected, read returns 0, so many client close fields are included in the output of the entry. Another problem is that the server closes its connected socket because read returns 0, then, a client connection may be returned by accept, that is, the real concurrency capacity of the server cannot be tested;

Poll call

Poll does not have the second limit of select, that is, FD_SETSIZE. You do not need to modify the kernel, but the first limit is unavoidable for the time being;

 

#include 
 
    int poll(struct pollfd *fds, nfds_t nfds, int timeout);
 

 

Parameter nfds: The number of events to be detected. the struct array size (which can also be expressed as the number of file descriptors) (The caller shoshould specify the number of items in the fds array in nfds .)

Timeout: the timeout time (unit: milliseconds, Ms). If the value is-1, the timeout will never be reached.


 

Poll and select are very similar. The important difference is that the number of concurrent poll tasks is not related to FD_SETSIZE, but only to the number of file descriptors that a process can open, you can modify the select Program to a poll program. Before running the server program, use ulimit-n 2048 to change the limit to 2048. Note that you also need to change it on the terminal that runs the client process, because the client also has some restrictions, this is only a temporary change, because the child process will inherit this environment parameter, and we start the program in the bash command line, so during the process running, the maximum number of file descriptors is 2048.

The server program using the poll function is as follows, which is similar to the select statement:

 

# Include
 
  
# Include
  
   
# Include
   
    
# Include
    
     
# Include
     
      
# Include
      
        # Include
       
         # Include
        
          # Include
         
           # Include
          
            # Include
           
             # Include "read_write.h" # define ERR_EXIT (m) \ do {\ perror (m); \ exit (EXIT_FAILURE); \} while (0) int main () {int count = 0; signal (SIGPIPE, SIG_IGN); int listenfd; // passive socket (file descriptor), that is, only accept is allowed, listener socket if (listenfd = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP) <0) // listenfd = socket (AF_INET, SOCK_STREAM, 0) ERR_EXIT ("socket error "); struct sockaddr_in servaddr; memset (& servaddr, 0, sizeof (servaddr); servaddr. sin_family = AF_INET; servaddr. sin_port = htons (5188); servaddr. sin_addr.s_addr = htonl (INADDR_ANY); int on = 1; if (setsockopt (listenfd, SOL_SOCKET, SO_REUSEADDR, & on, sizeof (on) <0) ERR_EXIT ("setsockopt error"); if (bind (listenfd, (struct sockaddr *) & servaddr, sizeof (servaddr) <0) ERR_EXIT ("bind error "); if (listen (listenfd, SOMAXCONN) <0) // listen should be after socket and bind, and ERR_EXIT ("listen error") before accept; struct sockaddr_in peeraddr; // The outgoing parameter socklen_t peerlen = sizeof (peeraddr); // The initial value of int conn is required for passing in the outgoing parameter; // The connected socket is changed to an active socket, that is, you can connect actively) int I; struct pollfd client [2048]; int maxi = 0; // client [I] subscript for (I = 0; I <2048; I ++) client [I]. fd =-1; int nready; client [0]. fd = listenfd; client [0]. events = POLLIN; while (1) {/* poll detection [0, maxi + 1) */nready = poll (client, maxi + 1,-1 ); if (nready =-1) {if (errno = EINTR) continue; ERR_EXIT ("poll error");} if (nready = 0) continue; // if a readable event occurs on the listener interface, if (client [0]. revents & POLLIN) {conn = accept (listenfd, (struct sockaddr *) & peeraddr, & peerlen); // accept no longer blocks if (conn =-1) ERR_EXIT ("accept error"); for (I = 1; I <2048; I ++) {if (client [I]. fd <0) {client [I]. fd = conn; if (I> maxi) maxi = I; break ;}}if (I = 2048) {fprintf (stderr, "too allow clients \ n "); exit (EXIT_FAILURE);} printf ("count = % d \ n", ++ count); printf ("recv connect ip = % s port = % d \ n ", inet_ntoa (peeraddr. sin_addr), ntohs (peeraddr. sin_port); client [I]. events = POLLIN; if (-- nready <= 0) continue;} for (I = 1; I <= maxi; I ++) {conn = client [I]. fd; if (conn =-1) continue; // a readable event occurs in the connected interface if (client [I]. revents & POLLIN) {char recvbuf [1024] = {0}; int ret = readline (conn, recvbuf, 1024); if (ret =-1) ERR_EXIT ("readline error"); else if (ret = 0) // The client closes {printf ("client close \ n"); client [I]. fd =-1; close (conn);} fputs (recvbuf, stdout); writen (conn, recvbuf, strlen (recvbuf); if (-- nready <= 0) break ;}} return 0;}/* poll is restricted only by the maximum file descriptor that a process can open. This can be adjusted using ulimit-n */
           
          
         
        
       
      
     
    
   
  
 

We can see that the maximum number of connections is already 2045, although a connection on the server side does not return an accept. That is, poll can withstand more concurrent connections than select, and is limited by the maximum number of file descriptors that a process can open. It can be modified through ulimit-n, but the number of file descriptors that a system can open is also limited, which is related to the memory size of the system. Therefore, it cannot be infinitely concurrent, as we mentioned at the beginning of this article, you can use cat/proc/sys/fs/file-max to check the local capacity.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.