티스토리 뷰

소켓 통신에 대해 공부를 하고 windows환경에서 개발을 하려고 보니 리눅스 소켓과 윈도우 소켓은 많이 다르다는 것을 알게 되었다.

그중 하나가 리눅스에서 멀티플렉싱(Multiplexing)을 위해 사용하던 select를 윈도우에서 그대로 사용할 수 없다는 것이다.


Multiplexing

하나의 프로세스가 하나의 전송로를 사용해서 여러 사용자들과 통신하여 효율성을 높이는 기술


리눅스에서는 멀티플렉싱을 구현하기 위해 select 함수를 사용하였다.


int select(int nfds, // 최대 소켓 번호+1

fd_set* read_fds, // 읽기를 감지할 fds

fd_set* write_fds, // 쓰기를 감지할 fds

fd_set* error_fds, // 예외를 감지할 fds

struct timeval* timeout); // 대기 시간 (0=INFINITE)


select 함수는 fd_set을 사용하는데 리눅스에서는 socket이 파일과 함께 관리되기 때문에 select로 input, output, error를 감지할 수 있었다.


리눅스 시스템에 예약되어있는 file discripter

0 : st_input

1 : st_output

2 : st_error


위처럼 0, 1, 2번 fd(file discripter)가 예약이 되어있어서 소켓을 생성하면 일반적인 경우 3번부터 생성이 되고 아래와 같은 코드로 input을 감지할 수 있었다.


FD_SET(0, &read_fds);

 

select(nfds, &read_fds, NULL, NULL, NULL);

 

if(FD_ISSET(0, &read_fds)){

// do

}






winsock은 리눅스 소켓과 다르게 소켓과 파일을 다르게 인식한다. 즉 위와 같은 코드는 10038에러가 발생한다.


10038(WSAENOTSOCKET)

소켓이 아닌 곳에 시도를 할 때 발생하는 error


따라서 winsock으로 멀티 플렉싱을 하려면 다른 방식을 사용해야 한다.



WSAEventSelect

윈도우의 이벤트 객체를 소켓마다 지정해서 이벤트가 발생하는 것을 기다린다.

이벤트 타입이 다를 뿐 리눅스의 select와 비슷한 원리를 가지고 있다.


동작 순서는 다음과 같다.


1. 소켓을 생성할 때 마다 WSACreateEvent() 로 이벤트 생성한다.


WSAEVECT WSACreateEvent();


2. WSAEventSelect() 로 소켓과 이벤트를 연결한다.


int WSAEvectSelect(SOCKET s, // 설정할 소켓

WSAEVENT hEvectObject, // 연결할 이벤트

long INetworkEvect);// 네트워크 이벤트


등록할 수 있는 이벤트

FD_READ : 데이터 수신이 준비되었을 때

FD_WRITE : 데이터 전송이 준비되었을 때

FD_ACCEPT : 접속 요청이 들어왔을 때

FD_CONNECT : 접속 요청이 완료되었을 때

FD_CLOSE : 접속이 종료되었을 때


3. WSAWaitForMultipleEvent() 로 이벤트를 기다린다.


DWORD WSAWaitForMultipleEvents(DWORD cEvents, // 대기할 이벤트 개수

const WSAEVENT* lphEvents, // 대기할 이벤트 배열

BOOL fWaitAll, // TRUE=모든 이벤트 배열이 signal되면 return FALSE=하나라도 signal되면 return

DWORD dwTimeout, // 대기시간 (WSA_INFINITE=이벤트가 발생할 때까지 대기)

BOOL fAlertable);// I/O completion routine과 관련된 부분 (false)




사용 예

while(1){

index = WSAWaitForMultipleEvents(numSocket, hEvents, FALSE, WSA_INFINITE, FALSE);

// ...

}


4. WSAEnumNetworkEvents() 로 발생한 이벤트를 알아내어 처리한다.


int WSAEnumNectworkEvents(SOCKET s, // 알고자 하는 소켓

WSAEVENT hEventObject, // 알고자 하는 이벤트

LPWSANETWORKEVENTS lpNetworkEvents);// 네트워크 이벤트 정보 구조체 포인터



사용 예

for(int i=0; i<numSockets; i++){

WSAEnumNetworkEvents(sockets[i], hEvents[i], &nwEvent);

if(nwEvent.lNetworkEvent & FD_READ)

recv(s, recvLine, MAX_LINE, 0);

}


사용하지 않는 이벤트는 WSACloseEvent() 로 삭제한다.


WSAEvectSelect() 를 사용하면 소켓이 자동으로 비동기 모드로 설정된다. 그래서 recv/send 함수를 사용해야 한다. read/write 함수를 사용하면 WSAEWOULDBLOCK 에러가 발생할 수 있다.

댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/05   »
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
글 보관함