[컴퓨터 네트워크] 네트워크 프로그래밍 인터페이스 소켓 API 해석(3)

  소켓은 네트워크 프로토콜 스택에 의해 프로그래머에게 노출되는 API로, 복잡한 컴퓨터 네트워크 프로토콜에 비해 API는 주요 작업 및 구성 데이터를 추상화하여 프로그래밍을 단순화합니다.

        이 기사에 설명된 소켓 내용은 Linux 배포판 centos 9의 man 도구에서 가져온 것이며 다른 플랫폼(예: os-x 및 다른 버전) 간에는 약간의 차이가 있습니다. 이 기사에서는 소켓 프로그래밍을 더 잘 이해하기 위해 주로 각 API를 자세히 소개합니다.


투표

poll()은 POSIX.1 - 2008을 준수합니다.

ppoll()은 Linux를 따릅니다.

1.도서관

标准 c 库,libc, -lc

2.헤더 파일

<poll.h>

3.인터페이스 정의

       int poll(struct pollfd *fds, nfds_t nfds, int timeout);
       int ppoll(struct pollfd *fds, nfds_t nfds,
                 const struct timespec *_Nullable tmo_p,
                 const sigset_t *_Nullable sigmask);

4.인터페이스 설명

        poll()은 select()와 동일한 작업을 수행하며 파일 설명자 세트가 I/O 준비가 될 때까지 기다립니다. Linux의 epoll()은 유사하지만 poll()보다 더 많은 기능을 제공합니다.

        fds 매개변수는 모니터링하도록 설정된 파일 설명자이며, 다음 구조의 배열입니다.

           struct pollfd {
               int   fd;         /* file descriptor */
               short events;     /* requested events */
               short revents;    /* returned events */
           };

        fds의 항목 수는 호출자가 지정합니다.

        구조의 fd에는 열린 파일 설명자가 포함되어 있습니다. 음수 값인 경우 events 매개변수가 무시되고 revents는 0을 반환합니다. (즉, fd를 보수로 설정하고 무시할 수 있습니다).

        이벤트 매개변수는 애플리케이션에 관심 있는 파일 설명자에 대한 이벤트를 식별하는 비트 마스크인 입력 매개변수입니다. 매개변수를 0으로 설정하면 POLLHUP/POLLERR/POLLNVAL 이벤트만 반환될 수 있습니다.

        revents는 발생한 실제 이벤트로 커널에 의해 채워지는 출력 매개변수입니다. 이러한 이벤트는 이벤트에 지정된 이벤트이거나 POLLHUP/POLLERR/POLLNVAL 중 하나일 수 있습니다. (이벤트에서 이 세 가지 이벤트에 해당하는 비트는 의미가 없습니다. 해당 조건이 발생하는 한 revents는 이벤트를 반환합니다.)

        요청한 이벤트(오류 포함)가 발생하지 않으면 이벤트가 발생할 때까지 poll()이 차단됩니다.

        timeout 매개변수는 파일 설명자가 준비될 때까지 poll()이 기다리는 시간(밀리초)을 지정합니다. 호출은 다음이 될 때까지 차단됩니다.

  • 파일 설명자 준비됨
  • 신호로 인해 통화가 중단되었습니다.
  • 시간 초과가 발생했습니다.

        마찬가지로 시간 초과 값도 시스템 클럭 세분성으로 상향 근사화되며 커널 스케줄링 지연으로 인해 차단되는 이벤트가 약간 더 많을 수 있습니다. 시간 초과가 음수 값인 경우 시간 초과가 무한하다는 의미입니다. timeout이 0으로 설정되면, 준비된 파일 설명자가 없더라도 poll()은 즉시 반환됩니다.

        이벤트 및 이벤트의 각 비트는 poll.h에 정의되어 있습니다.

    폴린

       읽을 데이터가 있습니다.

    여론조사 상

        파일 설명자에 예외가 있습니다. (1) TCP 소켓에 대역 외 데이터가 있습니다. (2) 메시지 모드의 의사 터미널 호스트가 슬레이브 상태 변경을 발견했습니다. (3) cgroup입니다. 이벤트 파일이 수정되었습니다.

        폴아웃

       현재 쓰기 가능하지만 소켓이나 파이프의 사용 가능한 공간보다 큰 데이터를 쓰면 여전히 차단이 발생합니다(O_NONBLOCK이 설정되지 않은 경우).

        POLLDHUP

        스트리밍 소켓 피어가 연결을 닫았거나 절반 연결을 쓰는 동안 종료되었습니다. 이 정의는 _GNU_SOURCE 매크로에 따라 다릅니다.

        폴러

       에러 발생됨. 파일 설명자가 파이프의 쓰기 끝을 가리키고 읽기 끝이 닫힌 경우에도 이 오류가 반환됩니다.

        폴훅

        끊으세요. 파이프 또는 스트림 소켓을 읽을 때 이 이벤트는 피어 측이 채널을 닫았음을 의미할 뿐이며, 후속 데이터를 읽을 때 채널의 데이터를 읽은 후 계속 읽으면 0(EOF)이 반환됩니다.

        설문조사

        잘못된 요청: fd가 열려 있지 않습니다.

        _XOPEN_SOURCE 매크로를 사용하여 컴파일할 때 다음 이벤트도 발생하지만 많은 정보를 제공하지는 않습니다.

        폴로드 노름

        POLLIN과 동일합니다.

        폴라드밴드

        우선순위 대역폭 데이터를 읽을 수 있습니다(일반적으로 Linux에서 사용됨).

        여론 조사

        POLLOUT과 동일

        폴워밴드

        우선순위 데이터가 기록될 수 있음

ppoll()

        ppoll()과 poll() 사이의 관계는 select()와 pselect() 사이의 관계와 유사합니다. ppoll()은 신호나 준비 이벤트를 기다리는 안전한 방법을 애플리케이션에 제공합니다.

        시간 초과 시간 정밀도의 차이를 제외하면 다음 두 코드는 거의 동일합니다.

           ready = ppoll(&fds, nfds, tmo_p, &sigmask);
           sigset_t origmask;
           int timeout;

           timeout = (tmo_p == NULL) ? -1 :
                     (tmo_p->tv_sec * 1000 + tmo_p->tv_nsec / 1000000);
           pthread_sigmask(SIG_SETMASK, &sigmask, &origmask);
           ready = poll(&fds, nfds, timeout);
           pthread_sigmask(SIG_SETMASK, &origmask, NULL);

          위의 코드는 timeout의 음수 값이 poll()에 의해 영원히 기다리는 것으로 해석되고 ppoll()의 *tmo_p의 음수 값이 오류를 보고하기 때문에 동일하다기보다는 거의 동일하다고 합니다.

        ppoll이 필요한 이유를 확인하려면 pselect(2)를 참조하세요.

        sigmask 매개변수가 NULL이면 신호 마스킹 작업이 발생하지 않으며, 이때 두 인터페이스의 유일한 차이점은 시간 정확도입니다.

        tmo_p는 ppoll()이 차단할 시간의 상한을 지정합니다. 이는 timespec 구조에 대한 포인터입니다. 포인터가 비어 있으면 ppoll()은 항상 차단합니다.

5.반환값

        성공하면 poll()은 이벤트가 있는 pollfds의 파일 설명자 수, 즉 해당 revent가 0이 아닌 값으로 업데이트되었는지를 나타내는 음수가 아닌 숫자를 반환합니다. 0을 반환하면 파일 설명자가 준비되지 않았거나 시간 초과되었음을 나타냅니다.

        오류가 발생하면 -1이 반환되고 오류 유형을 나타내도록 errno가 설정됩니다.

        오류 값은 다음과 같이 정의됩니다.

실패 fds는 프로세스 외부의 주소 공간을 가리킵니다.
EINTR 요청 이벤트가 발생하기 전에 신호가 발생하며 자세한 내용은 signal(7)을 참조하세요.
에이발 nfds 값이 RLIMIT_NOFILE 제한을 초과합니다.
단일 선택 *ppoll()의 tmo_P는 잘못된 값(음수)입니다.
ENOMEM 커널 데이터 구조를 할당할 메모리가 부족합니다.

일부 다른 UNIX 시스템에서는 Linux의 ENOMEM과 달리 커널이 커널 리소스를 할당하지 못하면 poll()이 EAGAIN 유형의 오류를 생성할 수 있습니다. POSIX에서는 이 동작을 허용합니다. 따라서 이식 가능한 프로그램은 EINTR과 마찬가지로 이 오류를 감지하고 다시 시도해야 합니다.

일부 구현에서는 poll()의 시간 초과로 사용되는 비표준 상수 INFTIM(-1)을 정의하지만 이 상수는 glibc에서 제공되지 않습니다.

6.주의

       poll()과 ppoll()의 동작은 O_NONBLOCK 플래그의 영향을 받지 않습니다.

        파일 설명자가 poll()에 의해 수신되고 있지만 다른 스레드에 의해 닫히는 상황에 대한 논의는 select(2)를 참조하십시오.

7.버그

        select(2)의 잘못된 준비 알림에 대한 설명을 참조하세요. 

8.코드 예시

        프로그램은 명령줄 매개변수에 의해 전달된 파일 이름을 열고 해당 POLLIN 이벤트를 모니터링합니다. 프로그램은 주기적으로 poll()을 호출하여 파일 설명자를 모니터링하고 준비된 파일 설명자의 수를 인쇄합니다. 각 준비된 파일 설명자에 대해 프로그램은 다음을 수행합니다.

  • 반환된 항목을 사람이 읽을 수 있는 형식으로 표시합니다.
  • 파일 설명자가 준비되면 파일 설명자에서 일부 데이터를 읽고 인쇄합니다.
  • 파일 설명자를 읽을 수 없지만 다른 이벤트(예: POLLHUP)가 발생하면 파일 설명자를 닫습니다.

        터미널에서 프로그램을 실행하고 FIFO를 열도록 요청한다고 가정해 보겠습니다.

       $ mkfifo myfifo
       $ ./poll_input myfifo

        다른 터미널에서 FIFO를 열고 일부 데이터를 쓴 다음 FIFO를 닫습니다.

       $ echo aaaaabbbbbccccc > myfifo

                 프로그램을 실행하는 터미널에서 다음 정보를 볼 수 있습니다.

           Opened "myfifo" on fd 3
           About to poll()
           Ready: 1
             fd=3; events: POLLIN POLLHUP
               read 10 bytes: aaaaabbbbb
           About to poll()
           Ready: 1
             fd=3; events: POLLIN POLLHUP
               read 6 bytes: ccccc

           About to poll()
           Ready: 1
             fd=3; events: POLLHUP
               closing fd 3
           All file descriptors closed; bye

         위에서 우리는 poll()이 세 번 반환되는 것을 볼 수 있습니다:

  • 첫 번째 반환은 파일 설명자를 읽을 수 있음을 나타내는 POLLIN이고, 다른 반환은 파일 설명자의 다른 쪽 끝이 닫혀 있음을 나타내는 POLLHUP입니다. 그런 다음 프로그램은 사용 가능한 일부 입력 데이터를 읽습니다.
  • 두 번째 반환은 동일한 두 이벤트로, 여전히 일부 사용 가능한 데이터를 소비합니다.
  • 마지막으로 poll()이 반환되면 POLLHUP 이벤트만 발생하고 파일 설명자가 닫히고 프로그램이 종료됩니다.
       /* poll_input.c

          Licensed under GNU General Public License v2 or later.
       */
       #include <fcntl.h>
       #include <poll.h>
       #include <stdio.h>
       #include <stdlib.h>
       #include <unistd.h>

       #define errExit(msg)    do { perror(msg); exit(EXIT_FAILURE); \
                               } while (0)

       int
       main(int argc, char *argv[])
       {
           int            ready;
           char           buf[10];
           nfds_t         num_open_fds, nfds;
           ssize_t        s;
           struct pollfd  *pfds;

           if (argc < 2) {
              fprintf(stderr, "Usage: %s file...\n", argv[0]);
              exit(EXIT_FAILURE);
           }

           num_open_fds = nfds = argc - 1;
           pfds = calloc(nfds, sizeof(struct pollfd));
           if (pfds == NULL)
               errExit("malloc");

           /* Open each file on command line, and add it to 'pfds' array. */

           for (nfds_t j = 0; j < nfds; j++) {
               pfds[j].fd = open(argv[j + 1], O_RDONLY);
               if (pfds[j].fd == -1)
                   errExit("open");

               printf("Opened \"%s\" on fd %d\n", argv[j + 1], pfds[j].fd);

               pfds[j].events = POLLIN;
           }

           /* Keep calling poll() as long as at least one file descriptor is
              open. */

           while (num_open_fds > 0) {
               printf("About to poll()\n");
               ready = poll(pfds, nfds, -1);
               if (ready == -1)
                   errExit("poll");

               printf("Ready: %d\n", ready);

               /* Deal with array returned by poll(). */

               for (nfds_t j = 0; j < nfds; j++) {
                   if (pfds[j].revents != 0) {
                       printf("  fd=%d; events: %s%s%s\n", pfds[j].fd,
                              (pfds[j].revents & POLLIN)  ? "POLLIN "  : "",
                              (pfds[j].revents & POLLHUP) ? "POLLHUP " : "",
                              (pfds[j].revents & POLLERR) ? "POLLERR " : "");

                       if (pfds[j].revents & POLLIN) {
                           s = read(pfds[j].fd, buf, sizeof(buf));
                           if (s == -1)
                               errExit("read");
                           printf("    read %zd bytes: %.*s\n",
                                  s, (int) s, buf);
                       } else {                /* POLLERR | POLLHUP */
                           printf("    closing fd %d\n", pfds[j].fd);
                           if (close(pfds[j].fd) == -1)
                               errExit("close");
                           num_open_fds--;
                       }
                   }
               }
           }

           printf("All file descriptors closed; bye\n");
           exit(EXIT_SUCCESS);
       }

추천

출처blog.csdn.net/BillyThe/article/details/132774658