- 본 내용은 Linux (Ubuntu 14.04 lts)를 기반으로 제작되었습니다. -
- I/O Multiplexing
예를들어 네트워크 프로그램을 구현하다 보면, scanf중에는 printf가 되지 않는 현상들을 볼 수 있다.
즉, I/O가 병행적으로 진행되지 못하여 (I/O Blocking 발생) 그런 것인데, 이러한 방법을 해결하기 위해 다양한 방법들이 나타났다.
그중 Fork, 스레드에 대해서 이전 게시물에서 다루어 보았고,
이번에는 하나의 프로세스, 하나의 스레드를 이용하는 I/O Multiplexing에 대해 다루어 볼 예정이다.
I/O Multiplexing을 구현하기 위해서 이용되는 함수는 select, poll, epoll, kqueue등이 있다.
I/O Multiplexing 기능은 하나의 프로세스가 여러 파일 디스크립터를 모니터링해서
어떤 종류의 I/O 이벤트가 일어났는지 검사하고 각각의 파일 디스크립터가 Ready 상태가 되었는지 인지하는게 주요 목적이다.
이중 select와 poll에 대해서만 다룰 예정인데, 이번 게시물에서는 poll을 다루려 한다.
- poll이란?
select와 비슷한 기능을 하는 함수로 지정한 소켓의 변화를 확인하고자 사용되는 함수이다.
즉, 소켓셋에 저장된 소켓에 변화가 생길 때 까지 기다리고 있다가
소켓이 어떤 동작을 하면 동작한 소켓을 제외한 나머지 소켓을 모두 제거하고 해당되는 소켓에 대해 진행을 한다.
이러한 poll 함수는 여러개의 fd를 동시에 모니터링 하다가 한개라도 읽을 수 있는 상태가 되면 blocking을 해제한다.
- poll의 특징
poll은 거의 select와 동일하지만 다음과 같은 차이가 있다.
1. file descriptor가 무제한적이다.
2. 좀더 low level의 처리로 system call의 호출이 select보다 적고, 이식성 나쁘다.
- poll 함수 사용 법
#include <poll.h>를 이용한다.
int poll(struct pollfd *fds, nfds_t nfds, int timeout);
인자 설명
1. struct pollfd 구조체명[개수];
이러한 struct pollfd의 구성은 다음과 같다.
struct pollfd{
int fd; // firle descriptor
short events; // request events
short revents; // returned events
};
short events에 입력할 수 있는 상수 목록
POLLIN :: 읽을 데이터가 있을 때(events / revents)
POLLPRI :: 긴급 데이터(Out-of-band Data)를 읽을 것이 있을 때(events / revents)
POLLOUT :: 바로 쓸 수 있는 상태일 때(events / revents)
POLLWRBAND :: 긴급 데이터(Out-of-band data)를 쓸 수 있을 때(events / revents)
POLLERR :: 주어진 file descriptor에 오류가 있을 때(revents only)
POLLHUP :: 주어진 file descriptor에서 event가 지체되고 있을 때(revents only)
POLLNVAL :: 주어진 file descriptor이 유효하지 않을 때(revents only)
2. nfds_t nfds
fd의 개수를 입력하면 된다.
3. int timeout
millisecond 단위로 입력한다. (ex : 1000 = 1초)
-1 :: 무한 대기
0 :: 대기 없음
poll 함수의 리턴 값
1이상 :: Event가 발생한 fd의 개수를 의미
0 :: timeout 발생
-1 :: 오류 발생
poll을 이용한 통신 소스 코드
<header.h>
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | //header.h #include <stdio.h> #include <string.h> #include <stdlib.h> #include <netinet/in.h> #include <sys/socket.h> #include <arpa/inet.h> #include <unistd.h> #include <sys/select.h> #include <poll.h> #define PORT 20162 #define BUFFER_SIZE 100 #define LISTEN_QUEUE_SIZE 5 #define theNumberOfFDs 100 // This source code Copyright belongs to Crocus // If you want to see more? click here >> | Crocus |
<poll.c>
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 | #include "header.h" int main(int argc, char* argv[]) { int listenFD, connectFD; struct sockaddr_in listenSocket, connectSocket; socklen_t addrSz; // address size를 구할 변수 int i; ssize_t strLen; char buf[BUFFER_SIZE]; //if (argc != 2) //{ // printf("Usage : %s <port>\n", argv[0]); // exit(1); //} listenFD = socket(PF_INET, SOCK_STREAM, 0); memset(&listenSocket, 0, sizeof(listenSocket)); listenSocket.sin_family = AF_INET; listenSocket.sin_addr.s_addr = htonl(INADDR_ANY); listenSocket.sin_port = htons(PORT); if (bind(listenFD, (struct sockaddr *) &listenSocket, sizeof(listenSocket)) == -1) { printf("Can not bind.\n"); return -1; } if (listen(listenFD, LISTEN_QUEUE_SIZE) == -1) { printf("Listen fail.\n"); return -1; } // pollfd 배열 구조체 생성 struct pollfd pollFDs[theNumberOfFDs]; pollFDs[0].fd = listenFD; // 0번째 배열에는 listen을 지정 pollFDs[0].events = POLLIN; // 읽도록 만든다. pollFDs[0].revents = 0; // 처음에는 0으로 초기화 한다(아직 아무 일도 일어나지 않았으니) for (i = 1; i < theNumberOfFDs; i++) pollFDs[i].fd = -1; // 0번째 배열은 listen을 위한것이니 1번째부터 모두 -1로 초기화 while (1) { int result = poll(pollFDs, theNumberOfFDs, -1); // -1 :: 무한 대기 if (result > 0) { if (pollFDs[0].revents == POLLIN) { // 새로운 커넥션 요청이 들어왔을 때 connectFD = accept(listenFD, (struct sockaddr*)&connectSocket, &addrSz); for (i = 1; i < theNumberOfFDs; i++) { if (pollFDs[i].fd == -1) // 비어있는 fd슬롯을 찾아서 넣어준다. { pollFDs[i].fd = connectFD; pollFDs[i].events = POLLIN; pollFDs[i].revents = 0; break; // 모두 다 넣고 break를 통해 한번만 실행 } } } for (i = 1; i < theNumberOfFDs; i++) { switch (pollFDs[i].revents) { // no events case 0: break; // data is ready case POLLIN: strLen = read(pollFDs[i].fd, buf, BUFFER_SIZE); printf("%lu bytes read\n", strLen); buf[strLen] = '\0'; fputs(buf, stdout); fflush(stdout); write(pollFDs[i].fd, buf, strlen(buf)); // 슬롯 초기화 default: close(pollFDs[i].fd); pollFDs[i].fd = -1; pollFDs[i].revents = 0; } } } } close(listenFD); return 0; } // This source code Copyright belongs to Crocus // If you want to see more? click here >> |
<client.c>
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 | //client.cpp # include "header.h" int main(int argc, char** argv) { if (argc != 2) { printf("Usage: %s IPv4-address\n", argv[0]); return -1; } struct sockaddr_in connectSocket; memset(&connectSocket, 0, sizeof(connectSocket)); connectSocket.sin_family = AF_INET; inet_aton(argv[1], (struct in_addr*) &connectSocket.sin_addr.s_addr); connectSocket.sin_port = htons(PORT); int connectFD = socket(AF_INET, SOCK_STREAM, 0); if (connect(connectFD, (struct sockaddr*) &connectSocket, sizeof(connectSocket)) == -1) { printf("Can not connect.\n"); return -1; } else { int readBytes, writtenBytes; char sendBuffer[BUFFER_SIZE]; char receiveBuffer[BUFFER_SIZE]; //while(1) //{ //서버에 문자열을 보낸 뒤 서버가 보낸 echo를 받아 출력. printf("input :: "); fgets(sendBuffer,BUFFER_SIZE,stdin); write(connectFD, sendBuffer, strlen(sendBuffer)); readBytes = read(connectFD, receiveBuffer, BUFFER_SIZE); printf("%d bytes read\n", readBytes); receiveBuffer[readBytes] = '\0'; fputs(receiveBuffer, stdout); fflush(stdout); //} } close(connectFD); return 0; } // This source code Copyright belongs to Crocus // If you want to see more? click here >> | Crocus |
처음 서버를 열었을 때
서버에 하나의 클라이언트가 들어왔을
서버에 3개의 클라이언트가 들어왔을 때
'Applied > Network' 카테고리의 다른 글
네트워크 통신 프로그래밍 용어 및 설명 정리 - (2) (0) | 2016.12.09 |
---|---|
네트워크 통신 프로그래밍 용어 및 설명 정리 - (1) (0) | 2016.12.08 |
소켓 프로그래밍 - (27) I/O Multiplexing Select를 이용한 통신 (0) | 2016.11.20 |
소켓 프로그래밍 - (26) I/O Multiplexing Select 개념 및 소스코드 (0) | 2016.11.20 |
[부록] 두 수를 입력받아 더하는 서버 클라이언트 통신 (0) | 2016.11.17 |