1.동기와 비동기의 이해
(1)동기화와 비동기화
<1>동기화된 입력 및 출력 함수의 호출
Application계층과 TCP계층 간의 완료를 의미한다.
데이터 전송시작------------------(Application계층에서의 데이터 전송종료)
send함수 호출 send함수리턴
데이터수신시작------------------------데이터수신종료
recv함수 호출 recv함수 리턴
A------------------------------------B
데이터 전송종료 라는 의미:
Application계층에서 Send함수 호출하면
보내고자하는 데이터가 TCP계층에 존재하는 출력버퍼로 완전히 전송된 상태
,이 상태에서 send함수가 리턴된다.
데이터수신종료 :
TCP계층에 존재하는 입력버퍼에서 Application계층으로 다 읽어들인 상태
A호스트가 30바이트가 보냈다고 하자
B호스트의 TCP계층의 입력버퍼에 5바이트가 들어와있다고 하자
그리고 A로부터 7바이트가 전송되고 있다고 하자. 그런데 전송되고 있는 7바이트는 신경쓰지 않는다.
여기서의 데이터 수신종료는
TCP계층에 존재하는 입력버퍼의 데이터를 Aplication계층이 다 읽어들인 상태 ,
이 상태에서 recv함수가 리턴된다.
send함수
send함수의 호출 - 데이터 전송 시작
send함수의 리턴 - 데이터 전송이 종료
==>즉 send함수는 동기화 되어 있다.
recv함수
recv함수의 호출 - 데이터 수신 시작
recv함수의 리턴 - 데이터 수신 종료
==>즉 recv함수는 동기화 되어 있다.
이와 달리
데이터전송 혹은 수신이 완료되는 시점과 리턴되는 시점과 다른 경우를 비동기라고 한다.
(2)동기화된 입력 및 출력함수의 문제점
A호스트의 Application계층 ---------A의 TCP(출력버퍼)-------------B의 TCP(입력버퍼)----------B의 Application계층
A가 데이터를 보냈는데 이미 B의 입력버퍼가 거의 찼는데도 B의 Application계층에서 데이터를 read가 느려서
A가 보낸 데이터를 수신할 수 없는 상황이 발생할 수 있다.
(3)비 동기화Notification
<1>동기화 Notification
-select함수를 기반으로 한 서버의 구현
-핸들에 변화가 발생해야 리턴이 된다. select함수의 리턴시기와 핸들의 변화시기가 일치한다 따라서 동기화 Notification이다
<2>비동기화 Notification
-WSAEventSelect함수를 기반으로 한 서버의 구현
-함수의 호출과 동시에 리턴한다. 따라서 핸들의 변화를 확인하기 위한 별도의 과정이 필요하다.
2.WSAEventSelect모델 서버
(1)구현 순서
비교대상:select함수의 특징: 설정(소켓설정) + 변화확인(알림)
<1>소켓생성:소켓을 생성한다.(관찰대상)
<2>이벤트생성:이벤트 WSACreateEvent함수를 호출해서 이벤트핸들을 생성한다.
(소켓을 위한 이벤트오브젝트를 만들어 소켓과 쌍을 이루어준다)
:소켓의 상태를 반영하는 오브젝트다. 소켓에 변화가 생기면 이벤트가 시그널드 상태가 된다.
왜 소켓마다 이벤트를 생성해야 하는가?
소켓도 자체적으로 상태를 가지지만 그 상태값은
소켓이 현재 실행중인지 종료었는지를 확인하는 용도로만 쓰이기 때문에
소켓의 변화를 확인하기 위해서는 사용할 수 없다.
<3>소켓과 이벤트 쌍설정:WSAEventSelect함수 호출
(소켓설정만 가능하다(select함수와 다르다),하나의 소켓핸들만 설정할 수 있다)
select함수와 달리 호출과 동시에 바로 리턴된다.
<4>소켓의 변화확인:WSAWaitForMultipleEvents함수 호출(변화를 확인(알림))
:핸들의 배열을 인자로 요구한다.넌시그널드상태로 시그널상태로 변경시 리턴된다.
<5>소켓변화의 이유확인 :WSAEnumNetworkEvents함수 호출(변화의 이유확인)
(2)함수
-이벤트생성
WSAEVENT WSACreateEvent(void);// 매뉴얼모드,넌시그널상태의 이벤트생성
-설정(소켓과 이벤트 연결)
int WSAEventSelect(SOCKET s , //감시대상이 되는 소켓
WSAEVENT hEventObject, //소켓의 변화를 확인하기 위한 Event오브젝트의 핸들
long lNetworkEvents); //확인하고자 하는 이벤트의 종류 (소켓이벤트 중 감시할 이벤트 선택)
-변화를 알림
DWORD WSAWaitForMultipleEvents(DWORD cEvents, //검사대상의 수
const WSAEVENT FAR* lphEvents, //검사대상의 배열
BOOL fWaitAll, //false인 겨우 하나의 변화로도 리턴된다.
DWORD dwTimeout, //타임아웃설정
BOOL fAlertable); //Completion Routine
-변화의 이유확인
int WSAEnumNetworkEvents(SOCKET s,
WSAEvent hEventObject, //변화가 생긴 소켓의 이벤트오브젝트 핸들
//두번째 인자로 전달하면 시그널드된 상태의 이벤트객체가 넌시그널이벤트객체로 바뀌게된다.
LPWSANETWORKEVENTS lpNetworkEvents); //발생한 변화에 대한 정보를 채우기 위한 포인터
typedef struct _WSANETWORKEVENTS
{
long lNetworkEvents; //변화가 발생한 이유가 여기로 들어온다.
int iErrorCode[FD_MAX_EVENTS];
}WSANETWORKEVENTS;
(3)WSAEventSelect예제
/***************** 1차 분석 ***************************/
/*
* WSAEventSelect_EchoServer.c
* Written by SW. YOON
*/
#include <stdio.h>
#include <string.h>
#include <winsock2.h>
#define BUFSIZE 100
void CompressSockets(SOCKET* hSockArray, int omitIndex, int total);
void CompressEvents(WSAEVENT* hEventArray, int omitIndex, int total);
void ErrorHandling(char *message);
int main(int argc, char **argv)
{
WSADATA wsaData;
SOCKET hServSock;
SOCKADDR_IN servAddr;
SOCKET hSockArray[WSA_MAXIMUM_WAIT_EVENTS];
SOCKET hClntSock;
int clntLen;
SOCKADDR_IN clntAddr;
WSAEVENT hEventArray[WSA_MAXIMUM_WAIT_EVENTS];
WSAEVENT newEvent;
WSANETWORKEVENTS netEvents;
int sockTotal=0;
int index, i;
char message[BUFSIZE];
int strLen;
if(argc!=2){
printf("Usage : %s <port>\n", argv[0]);
exit(1);
}
if(WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) /* Load Winsock 2.2 DLL */
ErrorHandling("WSAStartup() error!");
hServSock = socket(PF_INET, SOCK_STREAM, 0);
if(hServSock==INVALID_SOCKET)
ErrorHandling("socket() error");
servAddr.sin_family = AF_INET;
servAddr.sin_addr.s_addr = htonl(INADDR_ANY);
servAddr.sin_port = htons(atoi(argv[1]));
if(bind(hServSock, (struct sockaddr *) &servAddr, sizeof(servAddr) ) ==SOCKET_ERROR)
ErrorHandling("bind() error");
newEvent = WSACreateEvent();
if(WSAEventSelect(hServSock, newEvent, FD_ACCEPT) ==SOCKET_ERROR)
//서버소켓이벤트 설정
ErrorHandling("WSAEventSelect() error");
if(listen(hServSock, 5)==SOCKET_ERROR)
ErrorHandling("listen() error");
hSockArray[sockTotal]=hServSock;
hEventArray[sockTotal]=newEvent; //마지막 배열에 서버소켓에 대한 이벤트오브젝트 삽입
sockTotal++;
/***************** 2차 분석 *************************/
while(1)
{
index = WSAWaitForMultipleEvents(sockTotal, hEventArray, FALSE, WSA_INFINITE, FALSE);
//하나만 발생해도 리턴, 타임아웃설정하지 않음,
index = index-WSA_WAIT_EVENT_0; //실질적인 배열의 인덱스값을 얻을 수 있다.
//둘 이상의 소켓에서 동시에 이벤트가 발생할 수 있기 때문에 for문을 돌린다.
for(i=index; i<sockTotal; i++)
{
//변화확인
index=WSAWaitForMultipleEvents(1, &hEventArray[i], TRUE, 0, FALSE);
if((index==WSA_WAIT_FAILED || index==WSA_WAIT_TIMEOUT)) continue;
else //이유확인
{
index=i;
WSAEnumNetworkEvents(hSockArray[index], hEventArray[index], &netEvents);
if(netEvents.lNetworkEvents & FD_ACCEPT) //연결 요청의 경우.
{
if(netEvents.iErrorCode[FD_ACCEPT_BIT] != 0){
puts("Accept Error");
break;
}
clntLen = sizeof(clntAddr);
hClntSock = accept(hSockArray[index], (SOCKADDR*)&clntAddr, &clntLen);
newEvent=WSACreateEvent();
WSAEventSelect(hClntSock, newEvent, FD_READ|FD_CLOSE); //클라이언트 소켓에대한 이벤트 설정
hEventArray[sockTotal]=newEvent;
hSockArray[sockTotal]=hClntSock;
sockTotal++;
printf("새로 연결된 소켓의 핸들 %d \n", hClntSock);
} //if(NetworkEvents.lNetworkEvents & FD_ACCEPT) end
if(netEvents.lNetworkEvents & FD_READ) //데이터 전송의 경우.
{
if(netEvents.iErrorCode[FD_READ_BIT] != 0){
puts("Read Error");
break;
}
strLen=recv(hSockArray[index-WSA_WAIT_EVENT_0],
message, sizeof(message), 0);
send(hSockArray[index-WSA_WAIT_EVENT_0],
message, strLen, 0); // 에코 전송
}// if(netEvents.lNetworkEvents & FD_READ) end
if(netEvents.lNetworkEvents & FD_CLOSE) //연결 종료 요청의 경우.
{
if(netEvents.iErrorCode[FD_CLOSE_BIT] != 0) {
puts("Close Error");
break;
}
WSACloseEvent(hEventArray[index]);
closesocket(hSockArray[index]);
printf("종료 된 소켓의 핸들 %d \n", hSockArray[index]);
sockTotal--;
CompressSockets(hSockArray, index, sockTotal); //배열 정리.
CompressEvents(hEventArray, index, sockTotal);
}// if(netEvents.lNetworkEvents & FD_CLOSE) end
} //else end
} //for(i=index; i<sockTotal; i++) end
} //while(1) end
WSACleanup();
return 0;
}
/***************** 3차 분석 ***************************/
void CompressSockets(SOCKET* hSockArray, int omitIndex, int total)
{
int i;
for(i=omitIndex; i<total; i++)
hSockArray[i]=hSockArray[i+1];
}
void CompressEvents(WSAEVENT* hEventArray, int omitIndex, int total)
{
int i;
for(i=omitIndex; i<total; i++)
hEventArray[i]=hEventArray[i+1];
}
void ErrorHandling(char *message)
{
fputs(message, stderr);
fputc('\n', stderr);
exit(1);
}
'TCP/IP > 윈도우기반' 카테고리의 다른 글
쓰레드동기화 (0) | 2009.10.08 |
---|---|
Completion Port 입출력 모델 (0) | 2009.10.08 |
IOCP (0) | 2009.10.08 |
TCP 파일 전송 클라이언트 <윈도우 기반> (0) | 2009.07.01 |
TCP 파일 수신 서버<윈도우 기반> (0) | 2009.07.01 |
댓글