IO 다중 복용의 select 총결산

1. 기본 개념
IO 다중 복용이란 프로세스가 지정한 하나 이상의 IO 조건을 읽으려고 하면 프로세스에 알립니다.IO 다중 경로 재사용은 다음과 같은 경우에 적용됩니다.
(1) 고객이 여러 개의 설명자를 처리할 때 (일반적으로 대화식 입력 및 네트워킹 인터페이스) 입출력 재사용을 사용해야 합니다.
(2) 한 고객이 여러 개의 인터페이스를 동시에 처리할 때 이런 상황은 가능하지만 매우 드물다.
(3) TCP 서버가 감청 커버 인터페이스와 이미 연결된 커버 인터페이스를 처리하려면 일반적으로 I/O 재사용이 필요합니다.
(4) 서버가 TCP를 처리하고 UDP를 처리하는 경우 일반적으로 입출력 재사용을 사용합니다.
(5) 한 서버에서 여러 서비스 또는 여러 프로토콜을 처리하려면 일반적으로 입출력 재사용을 사용합니다.
다중 프로세스와 다중 스레드 기술에 비해 I/O 다중 복용 기술의 가장 큰 장점은 시스템 비용이 적다는 것이다. 시스템은 프로세스/스레드를 만들 필요도 없고 이러한 프로세스/스레드를 유지할 필요도 없기 때문에 시스템의 비용을 크게 줄일 수 있다.
==>라인, 프로세스: 시스템 창설 비용 + 유지보수 시스템 비용
2. select 함수
이 함수 허가 프로세스는 여러 이벤트 중 하나를 기다리는 프로세스를 가리키며, 한 개 이상의 이벤트가 발생하거나 지정된 시간이 지나야만 깨울 수 있습니다.함수 원형은 다음과 같습니다.
#include <sys/select.h>
#include <sys/time.h>
int select(int maxfdp1,fd_set *readset,fd_set *writeset,fd_set *exceptset,const struct timeval *timeout)
 : , 0, -1

함수 매개변수는 다음과 같습니다.
(1) 첫 번째 매개 변수 maxfdp1은 테스트를 기다리는 설명자 개수를 지정합니다. 그 값은 테스트를 기다리는 최대 설명자 더하기 1입니다. (따라서 이 매개 변수를 maxfdp1로 명명합니다.) 설명자 0, 1, 2...maxfdp1-1 모두 테스트됩니다.
(2) 중간에 있는 세 개의 매개 변수readset, writeset, exceptset은 내부 테스트에서 읽기, 쓰기, 이상 조건에 대한 설명자를 지정합니다.만약 어떤 조건에 흥미가 없다면, 그것을 빈 지침으로 설정할 수 있다.struct fd_set은 하나의 집합으로 이해할 수 있습니다. 이 집합에는 파일 설명자가 저장되어 있으며 다음 네 개의 매크로를 통해 설정할 수 있습니다.
          void FD_ZERO(fd_set *fdset);//컬렉션 비우기
          void FD_SET(int fd, fd_set *fdset);//주어진 파일 설명자를 집합에 넣기
          void FD_CLR(int fd, fd_set *fdset);//지정된 파일 설명자를 컬렉션에서 삭제합니다.
          int FD_ISSET(int fd, fd_set *fdset);//컬렉션에 지정된 파일 설명자를 읽을 수 있는지 확인
(3)timeout은 내부 핵이 지정한 설명자 중 어느 것이든 준비가 얼마나 걸릴지 알려 줍니다.그 timeval 구조는 이 시간의 초수와 미초수를 지정하는 데 사용된다.
         struct timeval{
                   long tv_sec;  //seconds
                   long tv_usec; //microseconds
       };
이 매개 변수는 세 가지 가능성이 있다.
(1) 영원히 기다림: I/O가 준비되어 있을 때만 되돌아옵니다.이 매개변수를 빈 포인터 NULL로 설정합니다.
(2) 일정한 시간을 기다립니다: I/O가 준비되었을 때 되돌아오지만 이 매개 변수가 가리키는 timeval 구조에서 지정한 초와 미초를 초과하지 않습니다.
(3) 전혀 기다리지 않음: 설명 글자를 검사한 후 바로 되돌아오는 것을 윤문이라고 한다.이를 위해, 이 매개 변수는timeval 구조를 가리켜야 하며, 그 중의 타이머 값은 0이어야 한다.
3. 테스트 프로그램
TCP 리셋 프로그램을 작성합니다. 프로그램의 기능은 클라이언트가 서버에 정보를 보내고 서버가 수신하고 그대로 클라이언트에게 보내며 클라이언트가 수신한 정보를 표시합니다.
서버 프로그램은 다음과 같습니다.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>      //errno = EINTR

#include <netinet/in.h>    //sockaddr_in   sockaddr, socklen_t
#include <sys/socket.h>    // socket, listen ...
#include <sys/select.h>   //select
#include <unistd.h>
#include <sys/types.h>    
#include <arpa/inet.h>   //inet_pton  inet_ntoa

#define IPADDRESS  "127.0.0.1"
#define PORT       8787
#define MAXSIZE    1024
#define LISTENQ    5

static int socket_bind(const char* ip, int port);

// select
static void do_select(int listenfd);
// 
static void handle_connection(int* connfds, int num, fd_set *prset, fd_set* pallset);

int main(int argc, char **argv)
{
     int listenfd;
     
     listenfd = socket_bind(IPADDRESS, PORT);
     listen(listenfd, LISTENQ);

     do_select(listenfd);

     return 0;
}

static int  socket_bind(const char* ip, int port)
{
     int listenfd;
     struct sockaddr_in servaddr;
    
     listenfd = socket(AF_INET, SOCK_STREAM,0);
     if(listenfd == -1)
     {
          perror("socket error");
          exit(1);
     }
    
     bzero(&servaddr,sizeof(servaddr));
     servaddr.sin_family = AF_INET;
     inet_pton(AF_INET, ip, &servaddr.sin_addr);
     servaddr.sin_port = htons(port);
     
     if( bind(listenfd, (struct sockaddr*)&servaddr,sizeof(servaddr)) == -1)
     {
          perror("bind error.
");           exit(1);      }             return listenfd; } static void do_select(int listenfd) {     int connfd;     struct sockaddr_in  cliaddr;     socklen_t     cliaddrlen;          fd_set rset, allset;     int maxfd,maxi;     int clientfds[FD_SETSIZE];   //     int nready;          int i;          for(i = 0; i< FD_SETSIZE; i++)     {           clientfds[i] = -1;       }             maxi = -1;          FD_ZERO(&allset);     FD_SET(listenfd, &allset);     maxfd = listenfd;          for(;;)     {          rset = allset;          nready = select(maxfd+1,&rset,NULL,NULL,NULL);          if(nready == -1)          {              perror("select error.
");              exit(1);          }          if(FD_ISSET(listenfd, &rset))  //          {               // , arguent error               bzero(&cliaddr,sizeof(cliaddr));               cliaddrlen = sizeof(cliaddr);               if( (connfd = accept(listenfd,(struct sockaddr*)&cliaddr,&cliaddrlen)) == -1)               {                     if(errno ==  EINTR)                         continue;                     else                     {                           perror("accept error.
");                           exit(1);                     }               }               fprintf(stdout,"accept a new  client: %s:%d
",inet_ntoa(cliaddr.sin_addr),cliaddr.sin_port);                              for(i = 0; i < FD_SETSIZE; i++)               {                    if(clientfds[i] < 0)                    {                         clientfds[i] = connfd;                         break;                    }               }               if(i == FD_SETSIZE)               {                    fprintf(stderr,"too many clients.
");                    exit(1);               }                              FD_SET(connfd, &allset);               maxfd = ((connfd > maxfd) ? connfd:maxfd);                              maxi = ( (i > maxi) ? i : maxi );                              if(--nready < 0)                  continue;            }                        handle_connection(clientfds,maxi,&rset,&allset);  //     }    } static void  handle_connection(int* connfds, int num, fd_set* prset, fd_set* pallset) {      int i,n;      char buf[MAXSIZE];      memset(buf,0,MAXSIZE);            for(i = 0; i<= num; i++)      {           if(connfds[i] < 0)               continue;                          if(FD_ISSET(connfds[i],prset))           {                 n = read(connfds[i],buf,MAXSIZE);                 if(n == 0)                 {                     close(connfds[i]);                     FD_CLR(connfds[i],pallset);                                          connfds[i] = -1;                     continue;                 }                 printf("read msg is:");                 write(STDOUT_FILENO,buf,n);                                  write(connfds[i], buf,n);           }      } }

클라이언트 프로그램은 다음과 같습니다.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/select.h>
#include <unistd.h>
#include <sys/types.h>

#define MAXSIZE     1024
#define IPADDRESS   "127.0.0.1"
#define SERV_PORT   8787

#define max(a,b) (a > b) ? a:b

static void handle_connection(int sockfd);

int main(int argc, char** argv)
{ 
     int sockfd;
     
     struct sockaddr_in servaddr;
     sockfd = socket(AF_INET, SOCK_STREAM, 0);
     
     bzero(&servaddr, sizeof(servaddr));
     servaddr.sin_family = AF_INET;
     servaddr.sin_port   = htons(SERV_PORT);
     inet_pton(sockfd, (struct sockaddr*)&servaddr, sizeof(servaddr));
     
     connect(sockfd, (struct sockaddr*)&servaddr,sizeof(servaddr));
    
     handle_connection(sockfd);

     return 0;
     
}

static void handle_connection(int sockfd)
{
     char sendline[MAXSIZE],recvline[MAXSIZE];
     int maxfdp;
     
     fd_set rset;
     int n;
     
     FD_ZERO(&rset);
     for(;;)
     {
          FD_SET(STDIN_FILENO, &rset);
          FD_SET(sockfd, &rset);
          
          maxfdp = max(STDIN_FILENO, sockfd);
          select(maxfdp+1, &rset,NULL,NULL,NULL);
          
          if(FD_ISSET(sockfd, &rset))
          {
               n = read(sockfd, recvline,MAXSIZE);
               if(n == 0)
               {
                     fprintf(stderr,"client: server is closed.
");                      close(sockfd);                      FD_CLR(sockfd,&rset);                }                write(STDOUT_FILENO, recvline,n);           }           if(FD_ISSET(STDIN_FILENO, &rset))           {                 n = read(STDIN_FILENO, sendline, MAXSIZE);                 if(n == 0)                 {                      FD_CLR(STDIN_FILENO, &rset);                      continue;                 }                 write(sockfd, sendline ,n);           }      } }

참조:
http://konglingchun.is-programmer.com/posts/12146.html
http://blog.163.com/smileface100@126/blog/static/27720874200951024532966/

좋은 웹페이지 즐겨찾기