안녕하세요. 허언증입니다.
WSAEventSelect() 함수 / WSAGETSELECTEVENT() 함수
FD_XXX를 자주 볼 수있는데 기초가 되는 select()를 통해 기초를 습득하자
C++ 소켓 통신을 하다가 몰랐던 부분을 찾아봤다.
♣ fd_set 구조체
fd_set 구조체는 File Descriptor (이하 FD) 를 저장하는 구조체이다.
안에 내용을 보면 그냥 배열로 여기면 편하다.
구조체 내용은 OS마다 조금씩 다른 듯 하다.
ubuntu14기준 - /usr/include/sys/select.h 에서 위 구조체를 확인할 수 있었다.
♣ FD_SET
fd_set 구조체에 2와 5의 FD 를 저장한다고 하면,
두번째 비트와 다섯번째 비트가 1로 변경된다.
값 저장은 FD_SET 매크로를 쓴다.
fd_set testFds ;
FD_SET(2, &testFds) ;
FD_SET(5, &testFds) ;
위 testFds 구조체의 배열값을 출력해 보면,
0 1 0 0 1 0 0 0 0 ~~~
위와 같이 2번째와 5번째 비트가 1 로 세팅된다.
♣ FD_ZERO
언급했듯이 fd_set 구조체의 주요 변수는 배열이다.
fd_set testFds ; 와 같이 선언만 하고 안에 배열값을 찍어보면 쓰레기값이 있을 수 있다.
이에 구조체를 초기화 해야 하며 아래와 같이 한다.
FD_ZERO(&testFds) ;
위에 FD_SET 예제에서도 변수 선언 후, 초기화를 해 주어야 한다.
fd_set testFds ;
FD_ZERO(&testFds)
FD_SET(2, &testFds) ;
FD_SET(5, &testFds) ;
♣ FD_CLR
FD_SET(2, &testFds) ;
FD_SET(3, &testFds) ;
FD_SET(5, &testFds) ;
위와 같이 입력 후, testFds 는 아래와 같이 세팅된다.
0 1 1 0 1 0 0 0 0 ~~~
이 때 만약 3번 FD 를 지우고 싶으면 FD_CLR 매크로를 쓴다.
FD_CLR(3, &testFds) ;
결과는 아래와 같다. 세번째 값이 0으로 초기화 되었다.
0 1 0 0 1 0 0 0 0 ~~~~
♣ FD_ISSET
이 매크로는 아래 select 함수와 함께 설명한다.
♣ select()
select 함수는 fd_set 구조체에 할당된 FD 의 이벤트가 발생하면
이를 감지하고 어떤 FD 이벤트가 발생했는지 알려준다.
ex)
fd_set testFds ;
FD_SET(2, &testFds) ;
FD_SET(5, &testFds) ;
select(6, &testFds, NULL, NULL, NULL) ;
이는 FD 이벤트는 6 미만값만 체크하며,
6 미만 중 2와 5 FD 의 이벤트가 발생했을 때만 깨어난다.
만약 2번 FD 의 이벤트가 발생하여 select 함수가 깨어나고,
select 함수 이후에 testFds 를 찍어보면,
0 1 0 0 0 0 0 0 0 ~~~~~~~
식으로 값이 변경된다. -> 이걸 몰라 한참 헤맸다.
즉 testFds 에 2, 5 FD 를 저장한 후,
저장된 FD (2,5) 중 이벤트가 발생하면,
해당 이벤트만 1 로 남기고 모두 0 으로 세팅한다.
이제 어떤 FD의 이벤트가 발생했는지 체크하여 FD_ISSET 매크로를 사용한다.
if(FD_ISSET(2, &testFds))
{
// 2번 FD 가 발생했다.
}
if(FD_ISSET(5, &testFds))
{
// 5번 FD 가 발생했다.
}
위와 같이 해당 FD에 대한 후처리를 진행하면 된다.
♣ 틀 정립
select 함수는 위 예제처럼 testFds 를 변경시킨다.
그럼 select 함수 전이나 후로 매번 testFds 를 다시 세팅해 주어야 한다.
이에 아래와 같은 틀이 될 것이다.
fd_set readFds;
FD_ZERO(&readFds);
FD_SET(2, &readFds);
FD_SET(5, &readFds);
while(1)
{
// select 함수에서 fd_set 이 변경되니 원본 대신 복사본을 select 함수에 전달한다.
checkFds = readFds;
printf("sleep - select()\n");
select(6, &checkFds, NULL, NULL, NULL);
printf("wake up - select()\n");
if(FD_ISSET(2, &checkFds))
{
// 2번 FD 이벤트에 대한 사후 처리.
}
if(FD_ISSET(5, &checkFds))
{
// 5번 FD 이벤트에 대한 사후 처리.
}
}
♣ 정리
이런 저런 코드를 보면서 원본 (readFds) 를 checkFds 로 복사하는 이유를 몰랐는데
select() 함수 내에서 fd_set 구조체 값을 변경하기 때문이다.
이를 위와 같이 정리하며 아래 내용이 빠져있다.
- select() 함수의 리턴값 처리
- select() 함수의 첫번째 인자 max FD 값의 처리
- select() 함수의 세번째, 네번째 인자 writeFds, exceptFds 에 대한 처리.
- select() 함수의 다섯번째 인자 타이머에 대한 처리.
출처: https://hahaite.tistory.com/290 [Hosang's Homepage]
'# Language > [ C & C++ ]' 카테고리의 다른 글
[C & C++] STL Vector 이용방법 정리 (선언, 함수) (0) | 2020.01.24 |
---|---|
[C & C++] C & C++ 자료형 총 집합 (0) | 2019.12.14 |
[C & C++] send(), recv() / sendto(), recvfrom() 정리 (0) | 2019.12.05 |
[C & C++] recv() , recvfrom() 차이점 (feat. network_socket) (0) | 2019.12.05 |
[C & C++] send() , sendto() 차이점 (feat. network_socket) (0) | 2019.12.05 |