소켓 코딩시 실제적으로 주의해야할 사항 :: 게임제작[SSISO Community]
 
SSISO 카페 SSISO Source SSISO 구직 SSISO 쇼핑몰 SSISO 맛집
추천검색어 : JUnit   Log4j   ajax   spring   struts   struts-config.xml   Synchronized   책정보   Ajax 마스터하기   우측부분

게임제작
[1]
등록일:2018-07-31 11:20:03 (0%)
작성자:
제목:소켓 코딩시 실제적으로 주의해야할 사항
select(1, NULL, &fd_set, NULL, &time_out)의 결과로서 데이터 send가 가능하다고 된 최초의 시점에... 즉, connect(hSocket, ...)한 다음에
fd_set에 hSocket한 개만 넣고 위와 같이 했을 때 쓸 수 있다고 되면 최초의 쓰기 가능한 상태인지 보아서 최초였으면 OnConnect virtual함수를 호출하면 되겠죠.
따로 위의 함수를 connect되었는지 여부를 체크하기 위해 호출하기 보다는 전체 프로그램의 Idle(혹은WM_TIMER)타임에서 어차피 데이터 전송을 위해 체크해야 하기 때문에 그때 체크해서 해당 소켓 핸들에 대한 최초의 쓰기 가능인지 여부만 검사하면 됨...
- 물론 만약을 위해 데이터 리딩을 시도했을 때 데이터가 이미 와 있다면 이미 connect된 경우겠지요.
- 가능하면 여러 개의 소켓 핸들 전체에 대하여 한 번에 select함수를 호출해야함...
자세한 내용은 select함수 도움말을 보세요

 
- 언제 소켓 접속이 끊어진 것입니까?
    당연 send/recv 등등의 함수들을 호출 했을 때 오류를 리턴할 경우이겠죠...
    접속이 끊어지는 경우는 크게 세가지가 있습니다.
    1 - 이쪽에서 접속을 끊은 경우
    2 - 상대방에서 접속을 끊은 경우
    3 - 네트웍 서브시스템(혹은 인터넷 망사업자) 등등의 오류에 의한 강제 종료 <- 이 경우는 뒤에...
    1과 2의 경우는 실제적으론 동일할 경우이겠죠? 아래의 글을 읽어보시면 아시겠지만 둘이 동시에 접속 종료를 요구한 경우도 동일합니다...
    제대로 접속을 종료하는 방식은 다음과 같습니다.
  • 먼저 접속을 종료해야 겠다고 마음 먹은 측에서 데이터를 전부 보냈는지 확인(당연 서로 주고 받을게 없을 때 접속을 끊겠죠 ....;;)
    b. 더이상 데이터를 보낼게 없거나 통신/프로그램 오류가 발생하여 접속을 종료해야 하는게 확실한 시점에서 shutdown(hSocket, SEND?)를 호출.... 이후 이쪽에서 데이터를 보내려는 어떠한 시도도 send함수는 오류를 리턴함...
    c. 그래도 상대측에선 데이터를 계속해서 보내고 있을 수 있으므로 recv 함수를 계속 호출해야함...
    d. 상대측에서는 아직 이쪽에서 데이터 전송을 그만 둔지 모르기 때문에 recv를 계속 호출하고 있을 것임
    e. 이쪽(접속을 종료하겠다고 먼저 마음 먹은 쪽)에서 shutdown(hSocket, SEND?)를 통해 더이상 데이터를 안보내겠다고 한 사실이 상대측에 도착하면 상대측 recv 함수가 0을 리턴함...
    f. 위 e. 이후에 상대측에선 나머지 데이터를 다 보내거나 그만 둔 다음 적당한 시점에 shutdown(hSocket, BOTH? 혹은 SEND?)를 호출 어차피 상대측에서 SEND를 그만둔 상태이므로 이쪽에서 받든 안받든 차이가 없으므로 BOTH/SEND 아무거나 상관없음... 상대측에선 이 시점에 closesocket()를 불러서 socket핸들을 free하면 됨...
    g. 이쪽에선 c. 상태가 계속 되다가 recv 함수가 0을 리턴하면 상대방도 접속 종료에 동의한 상태 이므로 이 때 closesocket()를 불러서 소켓 핸들을 free하면 됨...
    - 위의 a~g까지의 단계를 보면 접속을 먼저 종료하겠다고 한 측은 shutdown(SEND?)를 불러서 상대방에 알리고 상대방도 shutdown(SEND?)를 통해 동의하면 그때 closesocket를 부르면 됨
    - 상대방이 접속을 종료하겠다고 먼저 shutdown(SEND?)를 호출한 것을 recv의 return값이 0인 것을 확인해서 알았다면 shutdown(SEND?)한 다음 바로 closesocket를 불러도 됨.
  • 위와 같이 shutdown함수를 closesocket함수 호출 이전에 불러줘야 제대로된 통신 종료가 되는 것인데 이러한 과정을 graceful shutdown이라고 합니다.
*) 만약 위의 과정(graceful shutdown)을 거치지 않고 그냥 접속 종료하고 싶다고 바로 closesocket()을 호출하면 어찌되나요?
    상대측의 recv함수가 0을 리턴하지 않고 SOCKET_ERROR값을 리턴함(물론 운이 좋으면이죠... 대개는 그냥 아무일 없다고 나오죠...)
    이러한 경우는 aborty shutdown이라고 해서 접속 라인 상태 이상, 상대방 프로그램/시스템 down 등등과 구별하기가 어렵습니다...
    물론 이정도만이라면 그냥 그렇게 쓰셔도 되겠죠. 어차피 서로 빠빠이 하는 상태인데요 뭘...
    하지만 당연 이렇게 처리하시면 문제가 발생할 소지가 있습니다.
    이러한 경우 상대방 TCP/IP 처리부에서 이쪽에서 접속 종료한 사실을 모르기 때문에 계속 접속이 살아있게 되는 경우가 많습니다.-> 이것에 대한 처리를 하지 않으면 서버 소켓에서 더 이상 클라이언트  접속을 받지 않을 수 있다. (-,.-)
    - 자세한 내용은 모르겠지만 TCP/IP FIN(FINE==끝?) CODE인가를 서로 송수신해야 하는데 그게 전달이 제대로 안된다는 듯...
    - 그래서 결론은 바로 다음번 접속 시도때 서로 접속이 안되는 경우가 발생하게 됩니다 _
    - 이러한 경우 대개는 시스템을 리부트 해야만 접속이 다시 됩니다. 그전엔 벼라별 방법을 써도 잘 안되더군요.
    - 혹시 이러한 경우에 TCP/IP Keep Alive 옵션을 켜주면 제대로 접속 종료가 될지는 확인 못했네요... 한 번도 써본 적이 없는 옵션...
*) 네트웍 서브시스템(혹은 인터넷 망사업자) 등등의 오류에 의한 강제 종료는 어찌 처리합니까?
    뭐 천재지변이니 어쩔 수 없습니다. 바로 closesocket부르고 그만두면 됩니다.
    제대로된 오류 처리를 하고 싶으시다면closesocket 바로 뒤에 다시 socket 만들어서 connect 때려서 연결될 때까지 기다려서
    마치 오류가 없었던 듯이 행동하면 되겠죠 ;;;

     
*) 위의 graceful shutdown과정을 순서대로 밟고 제대로 처리했는데 바로 다시 재접속을 하면 접속이 안돼요. 어찌된 것인가요?
    - 주로 클라이언트 쪽 보단 서버에서 listen소켓을 즉시 다시 만드는데 실패하는 경우임.
    먼저 linger옵션에 대해서 이해를 하셔야 합니다.
    어느쪽이든지 closesocket을 호출했다고 생각을 해보죠. 그렇다고 TCP/IP 처리부에서 바로 소켓 핸들을 날려버리면 문제가 생길 수도 있습니다.
    왜냐면 소켓 핸들에는 보내야 할 데이터 정보가 달려 있는데 closesocket부르기 전에 send한 데이터들이 아직 네트웍에 부하 혹은 모종의 이유로 처리가 안된 경우가 있을 수 있습니다. 이 때 개발자 측에서는 이미 소켓을 날려버렸지만 실제 하부 시스템에선 아직 살아있을 수가 있습니다. 그 상태에서 개발자가 바로 socket을 다시 만들고 같은 주소 같은 port로 바인드를 시도하면 당연 오류가 발생하죠...-> 이 것때문에 고생 많이 했습니다. ㅠ,.ㅜ
    closesocket함수를 호출해도 일정시간 하부시스템에서 핸들이 살아있게 되는데 이 시간이 linger타임입니다.
    이러한 옵션이 편하긴 하지만 방금처럼 바로 재접속 시도를 하는 경우엔 예기치 않은 오류메시지를 출력해야만 하는 경우가 될 수도 있습니다.
    따라서 제대로 처리를 하기 위해선 이 linger타임을 0로 설정하고 closesocket 호출을 그 시간 만큼 지연해서 하는 겁니다.
    즉 linger타임 액션을 하부 시스템에 맞기지 않고 직접 처리하는 것이죠.
    linger타임이 0이면 closesocket를 호출하는 순간 바로 socket핸들이 시스템에서 사라집니다.
    하지만 마지막에 보낸 데이터들이 충분히 전송될 시간 정도는 두고서 closesocket를 불러주어야 하겠죠...
    가장 제대로된 방법은 제일 마지막 패킷은 서로 접속종료를 확인하는 과정을 거치는 것이죠 그래서 상대방에게서 동의가 오면 바로 언제든지 접속을 종료해도 되기 때문에 문제가 되지는 않겠지요....->프로토콜 설계 및 구현 할때 종료 프로토콜을 클라이어트 서버로 만들어놓아야 한다.
    아래 두 줄이 linger타임을 설정하는 방법입니다.
struct linger opt_ling = { 1, 0 }; // ling.l_onoff, ling.l_lingererr = setsockopt(hSocket, SOL_SOCKET, SO_LINGER, (const char*)&opt_ling, sizeof(opt_ling)); 
*) socket함수를 불러 소켓을 만들고 함수를 부르면 블럭이 됩니다. 어떻게 non-blocked call을 할 수 있죠? 
클라이언트나 listen소켓 쪽에선 socket() 함수로 소켓을 만들자 마자 설정하면 됩니다.
    당연 서버측에선 accept 혹은 WSAAccept 함수를 통해 얻은 소켓 핸들에 대하여 설정하면 되겠죠
    뭐 단순히 소켓 핸들이 만들어지자 마자 항상 linger이나 non-blocked-IO 상태 등을 설정하시면 됩니다.
  #ifdef WIN32u_long s_ulOPT = 1;err = ioctl(hSocket, FIONBIO, &s_ulOPT);if (err == 0) { // don't use SOCKET_ERRORreturn TRUE;}#else // !WIN32 - 유닉스 계열 OS들에서...int flags = fcntl(hSocket, F_GETFL);flags |= O_NONBLOCK;err = fcntl(hSocket, F_SETFL, flags);if (err != -1) { // don't use SOCKET_ERRORreturn TRUE;}#endif /* WIN32 */
*) OVERLAPPED IO가 잘 동작하지 않습니다.
    OVERLAPPED IO 사용시 Write하는 쪽만 사용하면 됩니다. Read하는 쪽까지 사용하기엔 프로그램 구조에 문제가 많습니다.
    Read야 시간 남을 때 정해진 버퍼 사이즈안에서 계속 무작정 받아 두면 되고, 패킷 처리부에서 시간 날 때마다 가져다 처리하면 됩니다.
    Q) OVERLAPPED IO결과가 성공이긴 한데 요구 되어진게 100 바이트 인데 50바이트만 보내고 50바이트는 못 보냈다고 나올 수 있나요?
    A) 현재까진 그런 경우는 본 적이 없습니다. 도큐먼트상으론 가능한데 All Or Nothing로 구현된듯 하네요...
    Q) select(fd_count, NULL, &fd_set, NULL, ...)을 이용하여 제대로 쓰기 가능 상태를 체크하는데 제대로 되지 않습니다.
    마치 OVERLAPPED IO처리가 조금씩 지연되고 있는듯 보입니다. 어찌해야 하나요?
    A) select(...) 호출 바로 앞에서 SleepEx(0, TRUE); 를 호출하여 OVERLAPPED IO결과가 프로그램에 전달될 수 있도록 해야 합니다.
    필요한 경우 SleepEx() 함수 안에서 바로 OVERLAPPED IO콜백 함수가 불립니다.
    Q) OVERLAPPED IO의 사용에 가장 큰 장점이 있다면 무엇?
    A) 각 소켓 연결마다 필요한 고정 사이즈의 중간 Send용 버퍼의 크기제한/낭비가 없어지며 속도가 빠르다... -> Windows NT Platform에서는 궁극의 소켓인 IOCP를 사용해야 합니다.
*) 자기만의 TCP/IP 용 라이브러리를 설계하고자 하는 사람이 반드시 알아야 할 한가지가 있다면?
    위에서 CAsyncSocket을 그대로 사용하지 마세요.란 제목으로 된 글의 보충
    일반적으로 통신 프로그램은 핑퐁시스템입니다. 이쪽에서 데이터를 보내면 상대방이 응답하죠. 바꾸어 말해도 같은 이야기고요.
    그런데 이 와중에 개발자는 항상 Send와 Recv 함수를 무수하게 사용하게 됩니다. 그리고 각각의 함수의 실행 결과에 대하여서는 항상 예외 처리를 해야 하고요. 이 때 위에서 예를 들었다 시피 어차피 .Send함수를 사용해서 성공했다고 했도 실제론 실패했을 수도 있기 때문에 프로그램에선 .Send가 성공했다고 모두가 성공적인 실행이라고 가정할 수 없습니다. 그러니 아예 모든 .Send문이 성공했다고 가정하는 것이죠. 즉 받은 데이터 패킷에 대하여 결과를 리턴한 내용들이 일단 무조건 성공했다고 가정합니다.
    .Send함수는 TCP/IP의 send함수를 바로 부르지 않고 그냥 정해진 버퍼에 쌓아 두기만 하는 거죠.
    그다음 프로그램의 Idle 타임에 실제 send() 함수를 호출하여 데이터를 전송하면 되는거죠.
    만약 이때 오류가 발생 한다면 송신부는 여기밖에 없기 때문에 단 한 곳의 오류 처리만 하면 됩니다. 더군다나 Idle루틴의 특성상 깊숙이 들어간 함수 호출이 아니기 때문에 상대적으로 오류처리도 쉽습니다. -> Socket Send 하는곳을 1곳에 둔다 ->성능 대신 안정성을 선택한것입니다. 즉 개발할때 성능과 안정성 두가지에서 Tradeoff를 해야 합니다. (아 어렵다 ㅠ,.ㅜ)
    송신할 데이터는 얼마가 될지 아무도 장담할 수가 없습니다. 그래서 가변 버퍼(혹은 OVERLAPPED IO)를 쓰면 좋을 듯 하고요.
    수신부는 고정크기의 버퍼를 가지면 됩니다. Idle루틴에서 항상 버퍼가 비어있는 만큼 읽으려 시도해서 가능하면 많이 읽어서 쌓아두면 패킷 처리부에서는 버퍼의 제일 앞에서부터 읽어보아서 처리할 만한 양의 데이터(일반적으로 한 개 이상의 완료된 패킷)가 다 도착한 다음에서야 읽기 버퍼에서 읽어서 지우면 됩니다. 읽기는 버퍼가 가득차면 더 이상 읽어가지 않으면 되기 때문에 버퍼 크기를 적당히 알아서 지정하면 되겠죠...
    이러한 시스템의 장점은 프로그램 전체를 통틀에서 실제 send/recv 함수 호출이 딱 한 군데라는 것이죠...
    오류처리가 무지하게 편해집니다.
    다만 Idle루틴은 여러가지 경우를 보아 윈도우에 Timer를 설정하여 WM_TIMER를 받아서 처리하는게 좋을 듯 싶군요.
    시간은 5-100밀리 세컨드 정도 편하신데로 하시면 됩니다. 어차피 늦게 보면 그만큼 많은 데이터가 처리 되어있을 것이기 때문에 문제 없겠네요
    CWinApp::OnIdle() 이 편하다고 생각하실지 모르지만 문제가 몇가지 있습니다.
    1 - CPU 점유율이 거의 항상 100%가 됩니다. 대부분... Timer를 5밀리 세컨으로 잡아도 한자리 수 단위로 떨어집니다.
    2 - Modal 대화상자가 뜨게 되었을 때 Idle루틴을 돌 수가 없어서 통신이 잠시 멈추게 됩니다.;;;

*) CSocket을 가능하면(절대라고 표현해도 될 정도죠) 사용하지 마세요.
- CSocket을 이용하여 프로그램 하시는 분이 있다면 존경받아 마땅한 분이죠...
==> CSocket가 Blocked Call이기 때문에 쓰레드를 꼭 써야하는것도 아니죠....ui에 신경을 쓸필요가 없거나 소켓 옵션에 timeout을 걸어둘수도 있는 문제구욤....꼭 blockedcall이 나쁜건 아니죠..... 그리고 쓰레드을 이용한 코딩이 오브헤드가 그렇게 많지 않답니다. 그리고 telnet데몬들은 1user 1thread로 작성된게 거의 대부분이고 잘돌아 갑니다. 그리고 data sync도 따지고 보면 blocked나 non-blocked나 둘다 비슷할텐데요 ;
*) 가능하면 BSD소켓 API만을 사용하여 코딩하십시오...
==> 멀티 플래폼이 아닌 socket프로그램이라면 굳이 bsd소켓 api만 사용하지 않아도 될것입니다. 포팅을 고려한다면...물론 BSD소켓만 사용하시는게 좋겠죠....
윈속2.0 api의 경우 많은 기능과 더 향상된 성능(미미하겠지만)을 발휘한답니다.
*) connect함수를 호출한 다음 언제 상대방이 제대로 접속이 이루어 진 것인가요?
==> 굳이 select을 사용해서 체크할 필요 없을듯합니다 connect의 리턴 값만 체크하는것만으로도 충분하다고 생각됩니다. 이부분은 왜 굳이 select을 사용해서 체크하는지 이유가 있다면 알려주세욤 (전 그냥 connect의 리턴값 체크로도 잘되었습니당)
- 언제 소켓 접속이 끊어진 것입니까?
    당연 send/recv 등등의 함수들을 호출 했을 때 오류를 리턴할 경우이겠죠...
    접속이 끊어지는 경우는 크게 세가지가 있습니다.
==> 어느쪽이든 끊고 싶은쪽에서 closesocket해버려도 상관없을듯한데요 만약에 보내구 있던중 데이터의 확실한 전송을 원하시면 linger 을 on해주면 .....음.....타임아웃 값을 조금 늘려주든지 하면 될겁니다.
*) 네트웍 서브시스템(혹은 인터넷 망사업자) 등등의 오류에 의한 강제 종료는 어찌 처리합니까?
==> 이럴경우에 보통 감지 되지 않습니다. ping/pong으로 체크를 하셔서 종료처리를 서버쪽혹은 클라이언트쪽에서 ...하는거시
*) 위의 graceful shutdown과정을 순서대로 밟고 제대로 처리했는데 바로 다시 재접속을 하면 접속이 안돼요. 어찌된 것인가요?
==> ??? 재접속을 하면 안되는 이유는 다른데 있는듯합니다...
*) OVERLAPPED IO가 잘 동작하지 않습니다.
    OVERLAPPED IO 사용시 Write하는 쪽만 사용하면 됩니다. Read하는 쪽까지 사용하기엔 프로그램 구조에 문제가 많습니다.
    Read야 시간 남을 때 정해진 버퍼 사이즈안에서 계속 무작정 받아 두면 되고, 패킷 처리부에서 시간 날 때마다 가져다 처리하면 됩니다.
==> 현재 서버구현에서 가장 많이 쓰이는게 iocp(windows nt기반에서)을 이용해서 overlapped i/o입니당..-,-a 그리고 보통 overlapped로 read/write한다구 해서 문제가 되는건 아님니당....오히려 성능이 좋다고 설명되어있습니다(win32네트워크프로그래밍;직접 벤치마킹해보진 않았음) 님이 말씀하신건 혹시 리눅스의 aio_의 단점이긴합니당;
    Q) OVERLAPPED IO결과가 성공이긴 한데 요구 되어진게 100 바이트 인데 50바이트만 보내고 50바이트는 못 보냈다고 나올 수 있나요?
    A) 현재까진 그런 경우는 본 적이 없습니다. 도큐먼트상으론 가능한데 All Or Nothing로 구현된듯 하네요...
==> 이건 저두 확실히 알지 못하지만 all Or Nothing라구 하긴 문제가 있지 않을까요 (10M~100M)을 보내보셍 그럼 100M가 가야하는데 그렇지 않을듯.....저건 스트리밍i/o인 tcp의 특성인거 같기도 하고.....이부분은 저도 확실하지 않습니당.
    Q) select(fd_count, NULL, &fd_set, NULL, ...)을 이용하여 제대로 쓰기 가능 상태를 체크하는데 제대로 되지 않습니다.
    마치 OVERLAPPED IO처리가 조금씩 지연되고 있는듯 보입니다. 어찌해야 하나요?
    A) select(...) 호출 바로 앞에서 SleepEx(0, TRUE); 를 호출하여 OVERLAPPED IO결과가 프로그램에 전달될 수 있도록 해야 합니다.
    필요한 경우 SleepEx() 함수 안에서 바로 OVERLAPPED IO콜백 함수가 불립니다.

*) select와 overlapped i/o와 아무 상관이 없으며 SleepEx을 쓰는 이유도 타당해보이지 않는뎅....;; 리고 버퍼에 넣는 이유는 오류처리의 불편함도 있을수 있겟지만 tcp가 스트리밍io 여서이기도 하고 버퍼링(똑같은 말이네...-..-;모아서 보내면 자잘히 많이 보내는것보다 좋겠져...-,-;)과 동기화등 다른 이유가 더 큰거 같은뎅; --이규승()
*)접속이 끊어진건 오류뿐 아니라 recv가 0을 리턴해도 종료된게 아닌지요? --조해진 ->MSDN
*)몇몇 부분에서 수긍가지 않는것두있긴하지만 나름대로 각자의 스타일이라 생각하고 넘어갑니다 좋은글 감사합니다 --이규승
*)overlapped io에서 recv시 미리 사이즈를 주고 받아야 한다는 건 무슨 소리인지 이해가 안됩니다. 잘못아신듯 --최인호
 

답변 글을 달아 주셔서 감사합니다.
먼저 제가 글을 쓰는 상황에 대해서 밝히지 않은게 조금 문제가 된 듯 합니다.
어느날 웹서핑을 하다가 데브피아란 곳이 생각나서;;; 들어와보니 의외로 소켓에 관한 질문을 하시는 분들이 꽤 되더군요..
전 지금까지 만 15년간 코딩을 해오고 있습니다. 그리고 그 중 소켓 코딩은 약 5년 동안 해왔고요...
물론 처음엔 CSocket/CAsyncSocket/BSD API 를 이용한 전형적인 순서를 밟아서 나름대로 경험을 쌓은 것이고요.
제가 글을 처음 쓸 때는 이것저것 생각하지 않고 글의 제목대로 소켓코딩(정확히 말하면 자기만의 Library)을 함에 있어서 실제 제가 겪고 남의 질문에 답하는 과정에서 쌓인 생각이 자연스럽게 표출된 것입니다. 아무 생각없이 뜬금없이 글을 써 올린 제게 좀 문제가 있네요
* 위의 글에서 소켓 코딩은 그냥 TCP/IP(UDP 제외한...)를 이용한 코딩을 의미합니다.
* 최소한 CSocket/CAsyncSocket은 써보고 대안을 찾는 사람을 대상으로 한 글입니다.
* 소켓코딩을 해서 실제 프로그램을 제작했는데 문제가 생긴 경우 혹시 프로그램상의 문제가 있다면 오류를 찾는데 도움을 드리기 위한 겁니다.
* 그래서 대부분의 글 내용이 BSD소켓 API를 이용한 async(non-blocked) call에 관한 내용입니다. 물론 win32 환경에서는 오버랩드 I/O를 사용하고 있다고 생각하시면 됩니다.
* 그리고 개인적으로 온라인 게임 서버/클라이언트, 메신저의 다자간 채팅 서버/클라이언트, 웹 서비스 회사의 ActiveX/CGI/인증서버 등등 많은 소켓 응용 프로그램을 제작한 경험상, 대부분의 상황이 접속 요구가 굉장히 빈번하고 수많은 오류 처리를 해야하는 상황하의 코딩들만 해보았기 때문에 CSocket/CAsyncSocket을 이용하는 경우나, 구조적 안정성이나 프로그래밍적인 편리성 등이 필요없는 상황(그저 대충대충해도 뭐든지 가능한;;)은 솔직히 전혀 생각해 본 적이 없었더군요... --정창모

논점1: blocked call / timeout / 오류처리
특수한 목적, 주로 1 대 1 접속을 목표로 하는 경우엔 CSocket이 아주 괜찮은 선택이긴 합니다.
단 상용제품 등으로 내지 않는 상황에서요.... 왜 그런고 하니 실제 작성된 프로그램이 안정성 있게 돌아가려면 수많은 예외처리를 해야하는데 CSocket을 이용한 프로그램의 속성상 프로그램 여기/저기서 send/recv를 남발하게 됩니다. 더구나 Blocked call 인 CSocket을 이용하면서 오류에 대처하기 위해 모든 송/수신 호출부에서 타임아웃을 건다고 생각해보죠... 조금 코메디가 되죠 자기 혼자 쓰는 프로그램이 아닌 경우는 거의 모든 경우에 안정성과 매끄러운 오류 처리 등이 필수적입니다. 결국 웬만한 정도의 안정성을 확보하기 위해서 넣는 오류코드가 장난이 아니게 되며 Async콜 사용에 비해 무지 깔끔했던 코드들이 결국은 누더기가 될 가능성이 농후합니다. 특히 CSocket의 사용시의 문제점은 그걸 사용해 무지하게 오랜 시간을 코딩했다 해도 개발자의 경험이 늘지 않는다는 겁니다. 아마도 궁극적으로 CSocket을 마스터한 사람이 쓰는 코딩 방식은 모든CSocket API에 극히 적은 타임아웃을 주고 마치 CAsyncSocket처럼 사용하게될 겁니다.
    - 1:1접속이 아닌 1:다 의 경우 mmorpg가 아닌경우엔 순간전송량이 많고 접속자수가 그리 많지않은(ftp나 file 관리서버)같은경우엔 blocked call이 훨씬 좋을수 있습니다.
    ->MMORPG 개발 경험상 무조건 ASynchronizatio이고 IOCP를 사용해야 합니다.
    - 그리고 CSocket이 recv/send을 남발한다구 했는데 그건 Asyncsocket두 마찬가지아닌가요 이건 어떻게 소켓을가지고 프로그래밍 했느냐의 문제일듯...
    - timeout에 대해서.... 실제루 그렇게 사용되구 있구요(ftp서버나 telnetd, httpd) 그리소 송수신 호출부에 타임아웃을 거는게 아니고 소켓의 옵션을 말했던겁니다.
논점2: winsock2을 사용할것인지 bsd호환 api을 사용할것인지?
전적으로 동의합니다... 하지만 엄청난 성능 향상(오버랩드 I/O는 정말 쓸만하죠)이 이루어지는 것이 아닌데 굳이 윈도우즈에서만 사용되는 API만을 사용하면 웬지 이상하지 않느냐는 정도로 이해하시면 될듯...
엄청난 성능향상은 없지만 제가 알기론 overlapped I/O을 사용하는 WSASend/Recv의 경우엔 30%의 성능향상이 있으며...(network programming for mcrosoft windows와 개인적인 테스트)
iocp나 기타 winsock2에서만 지원하는 각각의 객체들도 winsock1대에서는 사용할수 없기때문에 성능상으로 따지고 보면 많은 향상이 있다는것입니다. 실제로 예전 테스트에서 select을 사용하는것보다 winsock2에서 제일 비슷한 eventselect가 성능상 우위에 있었습니다. -> 생각 보다 WInsock2 APID Eventselect 성능이 뛰어나다 만약 극단적인 성능대신 개발에 용이성으로 Tradeoff를 한다면 나는 EventSelect로 개발 한다.
논점3: overlapped i/o
소켓 프로그램 구조적으로 write는 오버랩드 I/O에 아주 잘 맞습니다. 하지만 문제는 read쪽인데 프로그램에서 read할 데이터의 사이즈가 명백한 상황에서는 오버랩드 I/O를 이용한 코딩이 아주 도움이 되겠죠.. 하지만 대개의 경우 소켓코딩에서는 읽어야할 데이터의 수가 미리 고정되어 있지 않습니다.-> 프로토콜 설계를 잘 해야 한다. 그저 상대방이 보내주는 데이터를 읽어야 하는데 패킷의 크기도 가변이 태반이기 때문에 미리 사이즈를 주고 읽어야 하는 오버랩드 I/O와는 맞지 않는다는 겁니다. 고작 고정 사이즈의 작은 패킷 헤더를 읽기 위해 쓰기엔 아깝죠... 다만 파일처리쪽에선 많이 다르겠죠. 파일처리시엔 이미 데이터의 크기를 알고 있기 때문에 사용하면 무지 편하겠죠...
참고로 overlapped i/o는 제가 님의 말씀을 듣고 찾아본결과 현재 수면위로 올라온 버그는 없습니다. 그리고 커맨드가 가변일경우에 아깝다구 했는데....왜 그런지 이해하기 힘들구 제 생각에 님 기법이 데이타 사이즈을 recv 그후에 데이터를 recv해서 파씽하시나 본데 그렇게 하지 않고 그냥 일정 버퍼크기만큼을 받아서 파씽하는 방법을 보통 사용하죠 (overlapped i/o시)
논점4: socket가 스트리밍i/o라는 말이었는뎅...
스트리밍 I/O 처럼 속도까지 필요한 경우엔 독립적인 Read/Write 쓰레드 까지 사용될 수 있으므로 동기화 때문에라도 버퍼의 필요가 극대화 되겠죠.
다들 아시는것처럼 데이터량이 많으나 적으나 버퍼링으로 얻는 이점은 많답니다; --이규승
-> 윈도우 OS Kernel에서도 결국은 데이터를 Page or NonpagePool Memory management that This point is good at use Buffer for Winsock application! -aiwisdom@gmail.com
[본문링크] 소켓 코딩시 실제적으로 주의해야할 사항
[1]
코멘트(이글의 트랙백 주소:/cafe/tb_receive.php?no=34764
작성자
비밀번호

 

SSISOCommunity

[이전]

Copyright byCopyright ⓒ2005, SSISO Community All Rights Reserved.