비동기 전송을 위한 설명과 예제
프로그래밍 :
2005/09/21 22:01
| 윈도우에서 비동기 전송용 소켓을 만드는 방법에는 아래와 같은 방법들이 있다. WSAAsyncSelect WSAEventSelect overlapped I/O 그렇지만 overlapped I/O방식은 최소 NT 계열의 운영체제를 사용하는 곳에서 밖에 사용이 불가능 하기 때문에 98, 95에서도 사용 할수는 없다. 그리고 WSAAsyncSelect 는 여러가지 소켓 입출력의 결과를 윈도우 메시지를 이용해서 윈도우 메시지 프로시저로 넘겨주는 방식이므로 무지 느리다. 따라서 비교적 성능이 괜찮은 WSAEventSelect을 이용한 비동기 처리를 알아 볼까 한다. 앞으로 말 할 주제는 네트워크 프로그래밍의 초보 수준을 말할려고 하는게 아니다. 최소한 소켓이 무엇이고 tcp에 대한 기본 개념이 있다는 가정하에 이야기 하도록 하겠다. 우선 WSAEventSelect를 사용할려면 이벤트를 만들고나서 비동기적으로 감시하고자 하는 소켓에 할당을 해야한다. 이러한 이벤트를 소켓과 연결시켜서 비동기적으로 감시하고자 할때 사용하는 함수가 WSAEventSelect이다. 이렇게 이벤트와 소켓을 연결해 놓고.. 이를 감시하기 위해서는 WSAWaitForMultipleEvents함수를 이용한다. 이 함수는 감시하고자 하는 이벤트가 해당 소켓에 발생했을 때나 기다림 값으로 주는 시간이 지났을 경우 리턴을 한다. 리턴을 할 경우 어떤 이벤트가 발생했는지 알기 위해서는 WSAEnumNetworkEvents를 이용해서 알면 된다. 여기까지는 기본적으로 WSAEventSelect를 이용하는 방법적인 설명이고.. 이제부터 좀 더 깊게 비동기 전송에 대해서 이야기 하고자 한다. 비동기 전송을 수행하고 알리기 위해서는 아래와 같은 이벤트 플래그가 쓰인다.(여기서 말하는 이벤트는 위에서 말한 소켓을 감시하기 위해 만든 OS 오브젝트인 이벤트를 말하는게 아니다.) FD_CONNECT FD_READ FD_WRITE FD_CLOSE 이것 말고도 더 있지만 가장 많이 쓰이는 이벤트들은 위에 것들과 같다. 이벤트의 이름에서도 알 수 있듯이 connect는 connect 함수를 호출 하고나서 연결이 정상적으로 연결되었는가 실패했는가를 알려주는 이벤트이고, read는 감시중인 소켓에 어떤 데이터가 들어 왔을 경우 recv함수를 이용해서 데이터를 가져가라는 의미이다. 그리고 write는 조금 후에 자세하게 다룰 wouldblock이 발생 했을 경우, 그 후에 소켓에 데이터를 써도 되는 경우 발생하는 이벤트이다. close는 자신이 소켓을 닫을 경우나 상대방이 접속을 끊었을 경우 발생하는 이벤트이다. 위에서 말한 4개의 이벤트 중 나머지는 그다지 이해하고 코딩하는데 그다지 문제가 없지만 wouldblock에 의해서 발생하는 write 이벤트(이하 write 이벤트)를 자세히 연구해 봐야한다. write 이벤트를 다루는 예제나 관련 정보는 msdn에도 자세히 나와있지 않을 뿐 아니라 일부 사람들도 잘못 알고 있는 정보가 있어서 나름대로 연구를 한 결과를 바탕으로 설명을 하겠다. msdn에서 write 이벤트는 비동기 소켓에 send함수를 호출하거나 recv 함수를 호출 했는데 SOCKET_ERROR를 리턴하고 나서WSAGetLastError를 호출했을때 이 함수가 WSAEWOULDBLOCK를 리턴후에 해당 소켓에 해당하는 소켓 버퍼가 비워졌을 경우 발생한다고 쓰여져있다. 간단한 recv의 경우를 알아보자면 이는 recv 함수를 비동기 소켓에 대해서 호출 했을 경우 해당 소켓 수신버퍼에 아무런 데이터가 없으면 발생 한다고 씌여져 있다. 하지만 우리가 사용하는 WSAEventSelect는 소켓 수신버퍼에 데이터가 도착하면 read이벤트가 발생하므로 이때 recv 함수를 호출하지 그 전에 일부러 recv를 호출하는 경우는 없으니까 이는 논외로 하자. 그렇다면 문제는 send함수를 호출 한 경우 발생하는 write 이벤트이다. send함수 또한 recv 함수같이 비동기 소켓에 대해서 데이터 전송을 요청했을때 소켓 송신버퍼가 가득차서 요청한 만큼의 데이터를 기록 할 수 없을때 발생한다. 문제는 이때 발생한다. msdn에는 내가 금방 말한 것 같이 데이터를 기록 할 수 없을때 발생한다.라고만 나와있지 전송을 요청한 데이터가 어떻게 된다고는 나와있지 않다. 그렇지만 내가 연구해본 결과로는 전송을 요청한 데이터는 실제 소켓 송신버퍼에 1바이트도 기록되지 않으므로 차후에 write 이벤트가 발생 할 시에 재전송을 요청해야 한다. 즉 어플리케이션은 send시 wouldblcok이 되면 send시 전송을 요청했던 데이터를 버리지 말고 가지고 있다가 write 이벤트가 발생하면 그때 다시 데이터 전송을 재개해야 한다는 이야기이다. 이에대한 근거는 send함수가 SOCKET_ERROR를 리턴하는 것에 있다. send함수는 소켓 송신버퍼에 성공적으로 전송할 데이터를 기록 했을 경우에 기록 한 만큼의 바이트수를 리턴한다. 그런데 SOCKET_ERROR(-1)을 리턴 한다는 의미는 버퍼에 전혀 기록을 하지 않았다는 의미가 된다. 이를 구현한 예제 코드는 아래와 같다 more.. |





댓글을 달아 주세요