저도 .Net을 초기버전부터 사용하던 사람이라 초기에 만든 'Client/Server'프로그램을 계속 고쳐서 사용했습니다.

 

그런데 이번에 슈퍼소켓을 사용하여 서버를 다시 만들려고 자료를 찾다 보니 '.Net 3.5' 부터는 'SocketAsyncEventArgs'를 사용하라고 권장하는 군요.

그래서 샘플을 찾는데.....원하는 셈플이 없어서 하나 만들었습니다.

(참고 : 'SocketAsyncEventArgs'를 이용한 채팅(Chatting) Client/Server 예제 0.7)

 

 

샘플을 만들면서 느낀점을 간단히 적어 보겠습니다.

 

1. 무한 루프여 안녕~

이전에는 서버에 리스너를 동작 시킨 후 무한루프를 돌려 'TcpListener.AcceptSocket()'을 사용하여 무한루프를 정지시켰다가 유저가 접속하면 동작시키는 방법을 사용 하였습니다.

 

이제는 'Socket.AcceptAsync()'에 함수를 연결해 두면 유저가 접속하면 자동으로 함수가 실행됩니다.

처리가 끝나면 다시 'Socket.AcceptAsync()'를 열고 기다리고 하면 됩니다.

 

//유저를 기다린다.
do
{
	//유저를 기다리다가 접속하면 유저클라이언트를 만들어준다.
	//do~while문이 서있는곳
	//유저가 접속해야 동작한다.
	claConnectUser insNewUser = new claConnectUser(tcplistenerServer.AcceptTcpClient());

	//새로 만든 유저클라이언트에 델리게이트를 붙여준다.
	insNewUser.Connected		+= new dgConnect(OnConnected);			//접속했을때 발생하는 이벤트 연결
	insNewUser.Disconnected	+= new dgDisconnect(OnDisconnected);	//끊겼을때 발생하는 이벤트 연결
	insNewUser.Messaged		+= new dgMessage(OnMessaged);			//메시지가 왔을때 발생하는 이벤트 연결
	insNewUser.Loged				+= new dgLog(OnLog);								//로그가 왔을때 발생하는 이벤트 연결

	insNewUser.Connect();	//접속을 했으니 접속을 호출

} while (true);	//end do

 

 

SocketAsyncEventArgs saeaUser = new SocketAsyncEventArgs();
//유저가 연결되었을때 이벤트
saeaUser.Completed += new EventHandler<SocketAsyncEventArgs>(Accept_Completed);
//유저 접속 대기 시작
socketServer.AcceptAsync(saeaUser);
private void Accept_Completed(object sender, SocketAsyncEventArgs e)
{
	//다시 클라이언트 접속을 기다린다.
	Socket socketServer = (Socket)sender;
	e.AcceptSocket = null;
	socketServer.AcceptAsync(e);
}

 

 

 

 

 

 

2. 콜백이여 안녕~

이전에는 비동기 메시지를 수신하고 처리하기 위해서 콜백을 사용했습니다.

콜백자체가 나쁜 건 아니지만, 가독성이 안 좋다는 문제가 있죠.

 

닷넷에서는 콜백도 있지만 비슷한 것으로 이벤트 연결도 있습니다.

(상황에 따라서 두 개가 다릅니다.)이런 경우는 이벤트를 연결하는 것이 더 깔끔한 코드가 됩니다.

 

//비동기 방식으로 메시지 수신 시작
AsyncCallback GetStreamMsgCallback = new AsyncCallback(GetStreamMsg);
MyTcpClient.GetStream().BeginRead(byteStreamData, 0, claGlobal.insNetData.uBufferSize, GetStreamMsgCallback, null);

 

 

//데이터 구조 생성
MessageData MsgData = new MessageData();
//리시브용 인스턴스 생성
SocketAsyncEventArgs saeaReceiveArgs = new SocketAsyncEventArgs();
//리시브용 데이터 구조 지정
saeaReceiveArgs.UserToken = MsgData;
//리시브용 데이터버퍼 설정
saeaReceiveArgs.SetBuffer(MsgData.GetBuffer(), 0, 4);
//유저한테서 넘어온 데이터 받음 완료 이벤트 연결
saeaReceiveArgs.Completed += new EventHandler<SocketAsyncEventArgs>(Recieve_Completed);
//데이터 받기 시작
m_socketMe.ReceiveAsync(saeaReceiveArgs);

 

 

코드만 봐서는 새로운 코드가 더 복잡해 보이지만 실제로는 'SocketAsyncEventArgs'를 생성하기 위한 코드 때문이고 이것을 제외하면 이벤트 연결과 리시브 열기 뿐이 없습니다.

코드가 직관적이라 추적할 때 좋습니다.

 

이전코드는 '.BeginRead'를 설정하고 나서 진행이 되면 'GetStreamMsgCallback'에 설정된 함수가 실행됩니다.

새로운 코드는 '.ReceiveAsync'를 호출하고 나면 연결된 이벤트가 실행됩니다.

 

3. 이벤트 단위 처리

이벤트 단위로 처리하니 코드가 깔끔해지는 장점이 있네요.

모든 소켓 비동기 작업은 'SocketAsyncEventArgs'를 통해 이벤트 단위로 처리되기 때문에 코드 추적이 간단합니다.

 

하지만 한 함수에서 시분할 없이 비동기 작업을 여러 개 하는 경우 데이터가 제대로 나오지 않는 경우가 있습니다.

이런 걸 해결하려면 당연하게도 쓰래드풀처럼 이벤트풀을 만들어서 돌려야 합니다.

(근데 테스트를 해보지 않아서 잘될지는 모르겠네요. 샘플을 보니까 잘될 거 같긴 하던데...)

 

마무리

제가 이걸 만들려고 만든 게 아니다 보니 분석을 대충에서 분석이 맞는 건지 알 수 없습니다 ㅎㅎㅎㅎ

성능은 모르겠는데 어차피 동접이 수백 명 단위라면 고만고만 할 겁니다.

요즘은 저렴하고 좋은 서버가 많아서 말이죠 ㅎㅎㅎ

 

 



 

 

댓글 작성

이름
패스워드
홈페이지
비밀글