DevOps

[소켓] 다중소켓 처리 : 스레딩없이 서버에서 다중 클라이언트 처리

IT오이시이 2020. 12. 8. 23:44
728x90

 select () 명령을 이용하기

  • 파일 설명자 중 하나가 활성화 될 때까지 대기하면서 여러 파일 설명자를 모니터링 할 수 있다.
  • 예를 들어, 소켓 중 하나에서 읽을 데이터가 있으면 해당 정보를 제공합니다.
  • Select 는 파일 설명자가 데이터를 보내 자마자 활성화되는 인터럽트 핸들러처럼 작동합니다.

fd_set과 관련된 4가지 기능

fd_set readfds;

// Clear an fd_set
FD_ZERO(&readfds);  

// Add a descriptor to an fd_set
FD_SET(master_sock, &readfds);   

// Remove a descriptor from an fd_set
FD_CLR(master_sock, &readfds); 

//If something happened on the master socket , then its an incoming connection  
FD_ISSET(master_sock, &readfds); 

 

활성화된 select 관리하기  

소켓 중 하나에서 작업을 기다리십시오. 시간 초과가 되면 NULL이다.

activity = select( max_fd + 1 , &readfds , NULL , NULL , NULL);

 

코드 설명

클라이언트의 모든 활성 파일 설명자와 메인 서버 수신 소켓의 파일 설명자를 모니터링할 fd_set 변수 readfds를 만든다새로운 클라이언트가 연결될 때마다 master_socket이 활성화되고 해당 클라이언트에 대해 새로운 fd가 열릴 것이다. 그것의 fd를 client_list에 저장하고 다음 반복에서 우리는 그것을 readfds에 추가하여 클라이언트를 연결을 준비한다.
기존 클라이언트가 일부 데이터를 전송하면 readfd가 활성화되며, 기존 클라이언트 목록에서 데이터를 전송한 클라이언트를 확인한다.

 

//Example code: A simple server side code, which echos back the received message. 
//Handle multiple socket connections with select and fd_set on Linux 
#include <stdio.h> 
#include <string.h> //strlen 
#include <stdlib.h> 
#include <errno.h> 
#include <unistd.h> //close 
#include <arpa/inet.h> //close 
#include <sys/types.h> 
#include <sys/socket.h> 
#include <netinet/in.h> 
#include <sys/time.h> //FD_SET, FD_ISSET, FD_ZERO macros 
    
#define TRUE 1 
#define FALSE 0 
#define PORT 8888 
    
int main(int argc , char *argv[]) 

    int opt = TRUE; 
    int master_socket , addrlen , new_socket , client_socket[30] , 
        max_clients = 30 , activity, i , valread , sd; 
    int max_sd; 
    struct sockaddr_in address; 
        
    char buffer[1025]; //data buffer of 1K 
        
    //set of socket descriptors 
    fd_set readfds; 
        
    //a message 
    char *message = "ECHO Daemon v1.0 \r\n"; 
    
    //initialise all client_socket[] to 0 so not checked 
    // 30개의 소켓 목록을 초기화 합니다.

    for (i = 0; i < max_clients; i++) 
    { 
        client_socket[i] = 0; 
    } 
        
    //create a master socket  : 메인 소켓을 생성합니다.
    if( (master_socket = socket(AF_INET , SOCK_STREAM , 0)) == 0) 
    { 
        perror("socket failed"); 
        exit(EXIT_FAILURE); 
    } 
    
    //set master socket to allow multiple connections , 
    //this is just a good habit, it will work without this 
    // master_socket 을 이용하여 클라이언트 요청을 대기합니다.
    if( setsockopt(master_socket, SOL_SOCKET, SO_REUSEADDR, (char *)&opt, 
        sizeof(opt)) < 0 ) 
    { 
        perror("setsockopt"); 
        exit(EXIT_FAILURE); 
    } 
    
    //type of socket created 
    address.sin_family = AF_INET; 
    address.sin_addr.s_addr = INADDR_ANY; 
    address.sin_port = htons( PORT ); 
        
    //bind the socket to localhost port 8888  : 8888 포트로 소켓 서버를 연결합니다.
    if (bind(master_socket, (struct sockaddr *)&address, sizeof(address))<0) 
    { 
        perror("bind failed"); 
        exit(EXIT_FAILURE); 
    } 
    printf("Listener on port %d \n", PORT); 
        
    //try to specify maximum of 3 pending connections for the master socket 
    // 3개의 대기요청 백로그로 소켓 연결을 기다립니다.
    if (listen(master_socket, 3) < 0) 
    { 
        perror("listen"); 
        exit(EXIT_FAILURE); 
    } 
        
    //accept the incoming connection 
    addrlen = sizeof(address); 
    puts("Waiting for connections ..."); 
        
    while(TRUE) 
    { 
        //clear the socket set 
        FD_ZERO(&readfds); 
    
        //add master socket to set 
        FD_SET(master_socket, &readfds); 
        max_sd master_socket
            
        //add child sockets to set 
        for ( i = 0 ; i < max_clients ; i++) 
        { 
            //socket descriptor 
            sd = client_socket[i]; 
                
            //if valid socket descriptor then add to read list 
            if(sd > 0) 
                FD_SET( sd , &readfds); 
                
            //highest file descriptor number, need it for the select function 
            if(sd > max_sd
                max_sd = sd; 
        } 
    
        //wait for an activity on one of the sockets , timeout is NULL , 
        //so wait indefinitely 
        activity = select( max_sd + 1 , &readfds , NULL , NULL , NULL); 
    
        if ((activity < 0) && (errno!=EINTR)) 
        { 
            printf("select error"); 
        } 
            
        //If something happened on the master socket , 
        //then its an incoming connection 
        if (FD_ISSET(master_socket, &readfds)) 
        { 
            if ((new_socket accept(master_socket
                    (struct sockaddr *)&address, (socklen_t*)&addrlen))<0) 
            { 
                perror("accept"); 
                exit(EXIT_FAILURE); 
            } 
            
            //inform user of socket number - used in send and receive commands 
            printf("New connection , socket fd is %d , ip is : %s , port : %d 
                \n" , new_socket , inet_ntoa(address.sin_addr) , ntohs 
                (address.sin_port)); 
        
            //send new connection greeting message 
            if( send(new_socket, message, strlen(message), 0) != strlen(message) ) 
            { 
                perror("send"); 
            } 
                
            puts("Welcome message sent successfully"); 
                
            //add new socket to array of sockets 
            for (i = 0; i < max_clients; i++) 
            { 
                //if position is empty 
                if( client_socket[i] == 0 ) 
                { 
                    client_socket[i] = new_socket; 
                    printf("Adding to list of sockets as %d\n" , i); 
                        
                    break; 
                } 
            } 
        } 
            
        //else its some IO operation on some other socket 
        for (i = 0; i < max_clients; i++) 
        { 
            sd = client_socket[i]; 
                
            if (FD_ISSET( sd , &readfds)) 
            { 
                //Check if it was for closing , and also read the 
                //incoming message 
                if ((valread = read( sd , buffer, 1024)) == 0) 
                { 
                    //Somebody disconnected , get his details and print 
                    getpeername(sd , (struct sockaddr*)&address , \ 
                        (socklen_t*)&addrlen); 
                    printf("Host disconnected , ip %s , port %d \n" , 
                        inet_ntoa(address.sin_addr) , ntohs(address.sin_port)); 
                        
                    //Close the socket and mark as 0 in list for reuse 
                    close( sd ); 
                    client_socket[i] = 0; 
                } 
                    
                //Echo back the message that came in 
                else
                { 
                    //set the string terminating NULL byte on the end 
                    //of the data read 
                    buffer[valread] = '\0'; 
                    send(sd , buffer , strlen(buffer) , 0 ); 
                } 
            } 
        } 
    } 
        
    return 0; 

 

[원문]socket-programming-in-cc-handling-multiple-clients-on-server-without-multi-threading/

 

Socket Programming in C/C++: Handling multiple clients on server without multi threading - GeeksforGeeks

A Computer Science portal for geeks. It contains well written, well thought and well explained computer science and programming articles, quizzes and practice/competitive programming/company interview Questions.

www.geeksforgeeks.org

 

728x90
반응형