티스토리 뷰
서버가 여러 클라이언트를 처리하기 위한 방법 중 입출력 다중화를 사용하는 방법을 알아보자.
입출력 다중화란 여러 입출력 대상이 있을 때 그 대상들을 검사하며 입출력이 필요할 때마다 작업을 수행하는 것을 말한다. 이 방법은 멀티 프로세싱, 멀티 스레딩 방법과 다르게 싱글 프로세스, 싱글 스레드에서도 동작한다. 하지만 완전히 동시에 처리하지는 못하는 단점을 가지고 있다.
select() 함수
리눅스는 select() 함수로 입출력 다중화를 구현할 수 있다. select()는 지정한 범위의 file discripter중 FD_SET으로 1로 set되어있는 파일을 순회하면서 변화가 있는 file discripter를 반환한다.
파일에 변화가 있다면 그대로 1로 두고, 변화가 없다면 0으로 set
멀티 플랙싱이 가능하게 한다.
* 멀티 플렉싱 : 하나의 프로세스로 여러 클라이언트와 통신하는 방법
file discripter
- select()를 이해하기 위해서는 리눅스에서 파일을 관리하는 방법을 알아야 한다. 리눅스는 파일을 file discripter로 관리하며, 리눅스 시스템에서 파일을 열면 해당 파일에 대한 정보를 담은 구조체를 가리키는 포인터들이 table 형태로 저장되는데 이 table의 색인 번호가 바로 file discripter다.
- 리눅스는 소켓도 파일로 다루기 때문에 편리하게 사용할 수 있다.
- 리눅스에서 0, 1, 2번 파일은 기본설정으로 지정되어있으며 임의의 파일들은 3부터 fd를 부여받는다. 소켓도 파일로 다루기 때문에 소켓을 생성하면 3 이상의 fd(file discripter)를 부여 받는다.
fd set
크기가 1024인 '비트 필드' 이며 0과 1값을 가진다.
int select(int n, // 검사할 파일 디스크립터 번호 중 가장 큰 값+1 (배열인자로 접근하기 때문에)
fd_set* readfds, // 읽기를 검사할 fd_set
fd_set* writefds, // 쓰기를 검사할 fd_set
fd_set* exceptfds, // 예외를 검사할 fd_set
const struct timeval* timeout); // 검사하는 시간에 제한을 둔다. NULL이면 무한정 대기한다.
// return값 : 0 <= 변경이 생긴 파일의 수, -1 = 실패
select()의 핵심은 fd_set을 제어하는 것이다. fd_set은 파일 디스크립터 번호를 배열로 가지는 '비트 필드'구조체이다. select()는 fd_set을 순회하며, fd_set값이 1인 파일에 대해 읽기 또는 쓰기를 검사하고 변화가 생긴 fd_set을 1로 한다. ( 없다면 당연히 0으로 한다. )
fd_set을 제어하는 매크로 함수는 다음과 같다.
FD_ZERO(*fds) |
fd_set을 초기화한다. 모든 값을 0으로 한다. |
FD_SET(fdnum, *fds) |
fdnum(파일 번호)을 fd_set에서 1로 한다. select()는 이 파일을 검사한다. |
FD_ISSET(fdnum, *fds) |
fdnum이 fd_set에서 1인지 검사한다. |
FD_CLR(fdnum, *fds) |
fd_set에서 fdnum을 제거한다. 더이상 관리할 필요가 없을 때 사용한다. |
이제 select()로 소켓들의 입출력을 관리하는 방법을 알아보자.
먼저 7개의 소켓을 만들었고, 각 소켓은 3~9까지 fdnum을 부여받았다고 가정하자. 그럼 fd_set의 모습은 다음과 같을 것이다.
fd_set* readfds
fdnum |
0 |
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
bit | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
이제 3,5,8 소켓에 대해 읽기를 검사하기 위해 3, 5, 8번을 등록한다.
FD_SET(3, &readfds);
FD_SET(5, &readfds);FD_SET(8, &readfds);
fd_set의 모습은 다음처럼 된다.
fd_set* readfds
fdnum | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
bit | 0 | 0 | 0 | 1 | 0 | 1 | 0 | 0 | 1 | 0 |
select()로 fd_set을 검사한다.
nfds가 9인 이유는 검사할 비트가 배열 번호상 9번비트 까지이기 대문이다.
select(9, &readfds, (fd_set*)0, (fd_set*)0, NULL);
예를들어 3번과 5번에서 변화가 생겼다면 fd_set의 모습은 다음처럼 변한다.
fd_set* readfds
fdnum | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
bit | 0 | 0 | 0 | 1 | 0 | 1 | 0 | 0 | 0 | 0 |
* fd_set은 이전 상태를 유지하지 않으므로 다시 또 3, 5, 8번 소켓을 검사하고 싶다면 다시 FD_SET으로 등록시키고 사용해야 한다.
select로 모든 소켓 중에 뭔가 변화가 있는 소켓이 있는지 알아냈으니 이제 FD_ISSET으로 어떤 소켓에 변화가 있는지 검사를 할 수 있다
FD_SET(int fd, fd_set* fdset) // fdset의 fd번째 인자를 1로 set (fd번 소켓을 1로 올린다는 소리)
FD_ISSET(int fd, fd_set* fdset) // fdset의 fd번째 인자 값을 return
위 예에서 3번과 5번 소켓에 변화가 있었으니
read_fds의 fd_arrya는 [0][0][0][1][0][1][0] ...
위처럼 구성이 되어있을 것이다
그러므로
FD_ISSET(3, &read_fds) 와 FD_ISSET(5, &read_fds)의 값은 1이 될 것이고
반복문을 돌면서 소켓 0번부터 최대 소켓번호 n 까지 순회하며 read_fds를 검사하면 어떤 소켓이 데이터를 수신 받았는지 알 수 있다.
'Programming > Socket' 카테고리의 다른 글
socket : socket이란? (0) | 2017.01.30 |
---|---|
socket : TCP/IP란? (0) | 2017.01.30 |
윈속(Winsock)으로 채팅 프로그램을 만들어보자 (0) | 2016.08.13 |
윈속(Winsock) 멀티플렉싱(Multiplexing) 함수 WSAEventSelect() (0) | 2016.08.07 |
소켓 종료 명령 - close()와 shutdown() (2) | 2016.07.26 |
- Total
- Today
- Yesterday
- 데이터베이스
- ios
- 국내여행
- OS
- database
- Git
- C++
- C/C++
- SwiftUI
- Java
- Cocos2d-x
- scala
- 드라마
- C
- 알고리즘
- SOCKET
- winsock
- DesignPattern
- machine learing
- 운영체제
- ue4
- swift
- Spring
- JSP
- 수학
- SHADER
- game
- 자료구조
- rxswift
- mongoDB
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |