기본 구조는 이전에 작성 하였던 http://blog.naver.com/spaciall/50103932789
기사에 바탕을 두고 있습니다.
서버만 비동기 구조로 바꾸어 보았습니다.
실질적으로 생성자에 전달해 주는 인자가 true/false냐에 따라 비동기/동기 모드를 모두 지원합니다.
C#에선 비동기로 통신을 하면 내부적으로 IOCP구조를 흉내내서, 효율이 좋다고 합니다.
올린 소스에 기초해서 비동기에 관련된 부분을 부분 부분 하나씩 띄어 설명하겠습니다.
클라이언트 소스, 서버 소스 2개 중 서버 소스를 보시며 읽어 주시면 됩니다.
아래 부분은 mFdes라는 Listen 소켓을 통해 비동기 Accept에 시동을 거는 함수 입니다.
new AsyncCallback(EndAcceptClient) 라고 써주면 Accept가 들어올 시, 현재 클래스의
EndAcceptClient 함수가 Callback으로 호출 됩니다.
그리고 Callback호출 된 함수에 mFdes를 인자로 넘겨준 다는 뜻 입니다.
mFdes.BeginAccept(new AsyncCallback(EndAcceptClient), mFdes);
연결이 들어오면 위에 말씀드린 대로, 아래의 함수가 자동 호출 됩니다. 그리고 인자로
넘어간 Listen 소켓을 아래와 같은 방식을 통해
Socket litenSocket = (Socket)ar.AsyncState; 라고 해서 넘겨 받게 됩니다.
그 후에 그 소켓으로, EndAccept()를 호출 하기만 하면 클라이언트를 하나 받는 것이죠.
그리고 나서 다시 Accept를 계속해서 받아야 하므로, 한번 더 BeginAccept를
통해 Accept에 시동을 걸어 두시면 되겠습니다.
public void EndAcceptClient(IAsyncResult ar)
{
Socket listenSocket = (Socket)ar.AsyncState;
Socket cltSocket = listenSocket.EndAccept(ar);.
.
.
mFdes.BeginAccept(new AsyncCallback(EndAcceptClient), mFdes);
위에서 빠진 내용이 하나 있는데요. 이번엔 날림이지만.. 제가 작성한 함수를 통으로 보겠습니다.
EndAccpet와 BeginAccept 사이에 BeginReceive를 하고 있는 것이 보입니다.
비동기 방식이므로 Accept가 끝났다면 바로 Receive를 걸어서 Receive를 받을 수 있도록
해 줘야 하기 때문입니다. 인자로는 1번재 인자에 버퍼, 2번째 인자에 받을 버퍼의 첫 위치,
3 번째 인자에 받을 길이, 4번째 인자로 소켓 속성, 5번째 인자로 Receive가 들어오면 호출될
Callback함수, 6번재 인자로 Callback함수에 인자로 넘어갈 객체가 되겠습니다. 보통 6번재 인자는
BeginReceive를 시도한 소켓이 됩니다.
public void EndAcceptClient(IAsyncResult ar)
{
Socket listenSocket = (Socket)ar.AsyncState;
Socket cltSocket = listenSocket.EndAccept(ar);
TcpClientSocket tcpClientSocket = (TcpClientSocket)new PacketSocket(cltSocket);
tcpClientSocket.SetClientManager(TcpClientList);
tcpClientSocket.SetServerSocket(this);
tcpClientSocket.GetSocket().BeginReceive(tcpClientSocket.GetTempStreamBuffer(), 0, TcpClientSocket.mTempStreamBufferSize, SocketFlags.None, new AsyncCallback(EndRecvClient), tcpClientSocket);
그리고 실제 이 소켓에 Receive가 들어오게 되면, 아래와 같이 인자로 넘어온
소켓 객체를 통해 가볍게 EndReceive해 주시면 받은 길이가 넘어오게 되고,
BeginReceive의 1번째 인자로 준 버퍼에 값이 들어오게
됩니다. 여기서 try, catch 처리를 해 줘야 하는데, 만일 어떤 이유에 의해,
연결이 끊겼다면 여기서 예외가 발생하기 때문입니다. 예외가 발생하면 그 소켓은
연결이 끊긴 것이므로 연결 끊김 처리를 알맞게 해 주시면 되겠습니다.
저는 혹시 몰라 size 값이 0 이하일 때도 연결 끊김 처리를 해 주었습니다.
public void EndRecvClient(IAsyncResult ar)
{
PacketSocket Client = (PacketSocket)ar.AsyncState;try
{
int size = Client.GetSocket().EndReceive(ar);
마지막으로 비 동기 Send를 살펴보겠습니다. 위의 비 동기 Receive를 이해하셨다면
Send는 더 쉬우므로 보시기만 해도 아실 겁니다. 인자 값은 위의 비 동기 Receive와
다를 바가 없습니다. 비 동기 Send가 끝나고 딱히 추가 작업이
있다면 불려지는 Callback 함수에서 무언가 작업을 해 주시면 됩니다.
보통 비 동기 Send가 끝난 후 딱히 할 일은 없습니다. 그래서 저는 아무 코드도 쓰지 않았습니다.
BeginSend시 try, catch로 묶어 두었는데요. 혹시 몰라서 해 두었습니다.
바로 전까지 유효하던 소켓 연결이 BeginSend를 하려는 바로 직전에 끊겨 버리면 예외가
발생할 테니까 저렇게 해 두었는데요. 저는 하지 않았지만 예외가 발생해 catch로 넘어간다면
소켓 연결을 정리하는 로직을 넣어주시는 것이 좋을 것 같습니다.
출처 : http://blog.naver.com/PostView.nhn?blogId=spaciall&logNo=50103998515
public IAsyncResult BeginSendClient(byte[] o, int size)
{
try
{
return mFdes.BeginSend(o, 0, size, SocketFlags.None, new AsyncCallback(EndSendClient), this);
}
catch (Exception e)
{
Console.WriteLine("From {0} : {1}", mFdes.Handle, e.Message.ToString());
return null;
}
}public void EndSendClient(IAsyncResult ar)
{
}
'네트워크' 카테고리의 다른 글
C# 비동기 클라이언트 소켓서버 (0) | 2013.05.14 |
---|---|
C# TCP 소켓통신 Server (0) | 2013.04.25 |
C# TCP 소켓통신 (0) | 2013.04.25 |
SuperSocket (0) | 2013.04.25 |
thread를 이용한 다중클라이언트 연결서버 (0) | 2013.04.24 |