정리

네트워크 프로그래밍 할 때 종종 보는 에러들 정리

저장소/VC++

IOCP를 사용한 네트워크 프로그래밍에서 종종 보이는 에러들, 자주 보이는 순서대로 정리.

수신 대기 중 발생되는 에러 원인 찾다가 잘 정리된 내용 복사복사.


내용 출처 :

네이버, 구글 검색

내가 테스트 해본 것도 포함

http://javawork.egloos.com/2265358

http://cafe.naver.com/ongameserver/3758

http://newworlds.tistory.com/1178

http://www.devpia.com/MAEUL/Contents/Detail.aspx?BoardID=50&MAEULNo=20&no=345120&ref=336795

http://blog.naver.com/ddinggill/100110486640


http://coderoid.tistory.com/46

http://www.gpgstudy.com/forum/viewtopic.php?p=41368

http://mindsteeper.tistory.com/188




ERROR_SEM_TIMEOUT (121)

The semaphore timeout period has expired.

장비(방화벽 또는 라우터) 이상 또는 제한으로 인한 네트워크 단절(랜선을 뽑는 경우도 포함)이 발생했을 때

(어느쪽? 서버? 클라?)


ERROR_NETNAME_DELETED (64)

The specified network name is no longer available.

보통 IOCP에서 소켓 끊어짐의 상징은 0 byte read 입니다.

0 byte read가 통보되는 시점은 상대방 소켓에서 closesocket() 혹은 shutdown() 함수를 호출한 시점입니다.

즉, 상대방이 closesocket() 혹은 shutdown() 을 호출하지 않고 종료해버리면 일반적으로 0 byte read는 발생하지 않습니다.

이렇게 0 byte read가 발생하지 않은 상태에서 read() 혹은 write()를 시도하면 상대는 이미 종료 되었으므로

ERROR_NETNAME_DELETED 에러가 나게 됩니다. 이러한 경우를 우아한 종료와는 반대되는 개념으로 HardClose라고 합니다.


WSAENOTSOCK (10054)

An operation was attempted on something that is not a socket.

지정한 소켓은 정상 적이지 않습니다(INVALID_SOCKET). 별도의 종료처리없이 소켓을 닫으면 나옵니다. 


ERROR_IO_PENDING (997)

Overlapped I/O operation is in progress.

이건 더 수신(송신)할 내용이 있을 때 확인되는 값으로 에러로 보기 어렵다.

수신중이었다면 마저 수신처리 하면 된다.

데이터 수신량 확인 시 0 byte면 종료로 봐야하나.

아닌가? 모르겠다. 이건 볼 때마다 모르겠다. 뭐야 이거;; 무서워

허나 GetQueuedCompletionStatus 호출 후 결과는 성공인데 transferred bytes가 0 일 경우에도 이 에러가 확인 된다.

임의로 0 byte를 보내지 않는 이상 보편적으로 transferred bytes가 0 이면 연결이 끊긴 것으로 간주한다.

이럴 땐 transferred bytes의 값을 보고 판단하는게 맞는 것 같다.


ERROR_OPERATION_ABORTED (995)

The I/O operation has been aborted because of either a thread exit or an application request.

1. 작업이 취소된 사례. CreateIoCompletionPort에 추가한 핸들(소켓)이 닫혔을 때.

 클라이언트가 연결된 상태에서 서버가 먼저 종료하면 발생.(server socket이 close되어서)

2. 동시에 송수신할 때

 CreateIoCompletionPort에 핸들(소켓)을 추가하기 전에

 WSADuplicateSocket 이나 DuplicateHandle 를 이용해 핸들을 복사하여 분리해서 추가해주면 된다고 한다.(정말?? 진짜??)




GetQueuedCompletionStatus의 0 transferred bytes (4)

저장소/VC++

경험 부족이네, 확신이 없어 여기저기 퍼온다. ㅋ

긁어다 붙였는데 잘 붙네.. 원치 않는 것 까지.. ㅡㅜ


원문 : http://www.devpia.com/MAEUL/Contents/Detail.aspx?BoardID=50&MAEULNo=20&no=722711&ref=722690



 iocp 서버만들때 클라이언트가 연결을 끊었을경우  | VC++ 일반2008-05-18 오후 10:16:41
정종현 (jjjjrr)  정종현님께 메시지 보내기정종현님을 내 주소록에 추가합니다.정종현님의 개인게시판 가기번호: 722690  / 읽음:143

안녕하세요

iocp 서버만들때

클라이언트가 연결을 끊었을경우

어떤이벤트가 발생하는지...

아래코드에서보면

if(dwBytesTrans == 0)   // 전송받은 것이 없다면 클라이언트가 종료한 경우
이경우가 클라이언트가 종료시의경우인데

bRet 가 false 이고

   ErrorCode = GetLastError();
   if (ERROR_NETNAME_DELETED == ErrorCode) 

인경우도 클라이언트의 연결이 끊어진경우라고하던데

이두경우의 차이점이 뭔가여?

 

 

  bRet = GetQueuedCompletionStatus(pIOCP->m_hIOCP, &dwBytesTrans, reinterpret_cast<ULONG_PTR*>(&pClient),
   reinterpret_cast<LPOVERLAPPED*>(&pIO), INFINITE);
  if(bRet)   // 에러상태를 점금한다
  {
   if((ULONG_PTR)pClient == THREAD_DIE)  // 스레드 강제종료
   {
          int ErrCode = WSAGetLastError();
    TRACE("IOCP 스레드 강제종료: %d\n", ErrCode);
       //pIOCP->CloseClient(pClient, FALSE);
    return FALSE;
   }
  }
  else
  {
   ErrorCode = GetLastError();
   if (ERROR_NETNAME_DELETED == ErrorCode) 

   {
   }
   continue;
  }

  if(dwBytesTrans == 0)   // 전송받은 것이 없다면 클라이언트가 종료한 경우
  {
   continue;
  }

이 글에 답변 등록하기
 [답변].2008-05-19 오전 12:02:01
박인규 (linuxian)  박인규님께 메시지 보내기박인규님을 내 주소록에 추가합니다.박인규님의 개인게시판 가기번호: 722698  

ERROR_NETNAME_DELETED은 IO작업중인 상태에서 접속이 끊긴 것입니다.

비정상적 종료라고 보시면 되겠구요.

 

recv 했을 때 버퍼의 크기가 0이라면 정상적인 종료로 보시면 됩니다.

 

이 글에 답변 등록하기
         [답변]클라이언트의 연결이 끊어진경우가2008-05-19 오전 7:22:09
정종현 (jjjjrr)  정종현님께 메시지 보내기정종현님을 내 주소록에 추가합니다.정종현님의 개인게시판 가기번호: 722710  

답변감사합니다

혹시 이두경우외에 클라이언트의 연결이 끊어진경우가

있을까여?

ERROR_NETNAME_DELETED 상태를 몰라서 한참헤멨는데

다른경우는 없을런지...

 

이 글에 답변 등록하기
 [답변].2008-05-19 오전 7:39:20
박인규 (linuxian)  박인규님께 메시지 보내기박인규님을 내 주소록에 추가합니다.박인규님의 개인게시판 가기번호: 722711  

GetQueuedCompletionStatus 이 함수가 거짓을 리턴되었다는 것은 요청한 IO 작업을 완료하지 못했다는 뜻입니다.

즉 IO 작업중에 또는 작업시도할때 상대편에 무슨 일이 생긴 것이지요.

왜냐하면 TCP는 기본적으로 연결을 보장하니까요.

이때 에러코드가 ERROR_NETNAME_DELETED 입니다. 솔직히 이건 문서에도 잘 나와있지 않은거라

저도 경험상 찾아낸 기억이 나네요.

 

 

GetQueuedCompletionStatus 은 TRUE를 리턴하면 IO작업을 완료했다는 뜻입니다.

Iocp에서 WSARecv를 걸어 두죠?

따라서 Recv에서 0바이트를 리턴(IO 완료)했다는 의미는 접속이 끊어진 걸로 간주하는 겁니다.

이것은 왜 그렇게 했느냐.. ㅎㅎ 처음 만든 사람이 그렇게 한거죠. 전송받은 바이트는 항상 0보다 클테니

0을 리턴하면 접속 종료로 하자 머 이런..

GetQueuedCompletionStatus의 0 transferred bytes (3)

저장소/VC++

음.. 요렇게도 쓰는구나. +ㅁ+

근데 내가 찾는 것과는 다르다.



원문 출처 : http://ykpk3733.egloos.com/8780



IOCP 를 공부중에 우연히 0 size buffer 란 기법을 알게 되고 여기저기 자료를 보면서 공부한 내용을 정리한것이다.

 

조금이라도 도움이 되었으면 하는 마음으로 포스팅을 한다.

 

-----------------------------------------------------------------------------------

1. "기본 WSAOVERLAPPED의 구조"

다음 구조체는 WSARecv() 혹은 WSASend() 함수에 넘겨주어야 하는 일반적인WSAOVERLAPPED 구조체이다.

 

typedef struct PER_IO_DATA : public WSAOVERLAPPED {
 char  Buffer[DATA_BUF_SIZE];
 int operation;
}PER_IO_DATA;

 

이 구조체에는 Buffer라는 변수가 포함되어 있다.

즉, 이것은 0 SIZE 버퍼가 아닌 클라이언트마다 각각의 버퍼를 할당해주는 방식이다.


2. "0 SIZE 버퍼"

1번과 같은 방식은 대량 연결용 서버의 경우엔 불필요한 메모리를 너무 많이 잡아 먹고, 관리도 힘들어진다.

이런 경우에는 "0 SIZE 버퍼" 를 이용하는 방법이 있다.

아래는 "0 SIZE 버퍼" 의 구조체 정보이다.

 

typedef struct PER_IO_DATA : public WSAOVERLAPPED {
 int operation; // OP_RECV, OP_SEND
}

 

보는바 와 같이 버퍼를 아예 제거 해버렸다.

그리고, 다음과 같이 사용 하면 된다.

 

pData = new PER_IO_DATA;
if( pData == NULL )
 return FALSE;

pData->Operation = OP_RECV;

wsabuf.buf = NULL; // 버퍼를 아예 제공하지 않는다.
wsabuf.len = 0;  // Recv할 최대 크기를 0으로 만든다.
flag = 0;
ret = WSARecv( sock, &wsabuf, 1, &&bytes, &flag, pData, NULL );
if( ret == SOCKET_ERROR && WSAGetLastError() != ERROR_IO_PENDING ){
 delete pData;
 return FALSE;
}

 

이렇게 하면, 해당 소켓으로 데이터가 들어왔을때 IOCP는 Worker thread를 깨워

처리하도록 한다. 그런데, 문제는 버퍼가 없으니 받은 데이터가 없다.

3. Worker thread에서 데이터 처리하기

아래 코드는 Worker thread의 핵심이 되는 함수이다.

 

ret = GetQueuedCompletionStatus(
 m_cp,    // IOCP Handle
 &bytes,    // 송,수신된 byte 수
 (LPWORD)&sock,   // Completion Key
 (LPWSAOVERLAPPED *)&pid, // WSAOVERLAPPED 데이터
 INFINITE );

 

3번째 PARAMETER인 sock은 Completion Key 로 SOCKET HANDLE 을 바로 사용한 경우이다.

일반적으로 bytes 값이 "0"이면 연결이 끊긴 것으로 처리를 하였다.

하지만 "0 SIZE 버퍼"를 사용했을 경우엔 무조건 bytes 값이 "0" 로 리턴된다.

따라서, 이 경우에는 끊긴 것으로 판단하지 말고 일단 데이타가 들어온 것이라고 판단해야 한다.

버퍼를 0로 만들었기 때문에 IOCP는 소켓으로 수신된 데이터를 아직 손대지 않은 상태이다.

이제 이것을 읽어 내야 하는데, 방법은 간단하다.

일반적인 송,수신 소켓 함수로 읽어내면 된다.

프로토콜을 이쁘게 정의하면, 수신되어야할 데이터가 얼마인지를 알 수 있는 경우가 있는데,

이 경우는 그 만큼 읽혀 질때까지 recv()를 하면되고,

만일 모를 경우는 nonblocking 방식으로 WSAEWOULDBLOCK이 될 때까지 recieve를 하면 된다.

이렇게 데이터를 모두 읽어서 처리하였다면,

다시 WSARecv() 를 사용하여 IOCP에 요청을 한다.

물론 생성된 pid는 이곳에서 제거를 해도 좋고, 계속 사용해도 좋다.


GetQueuedCompletionStatus의 0 transferred bytes (2)

저장소/VC++

음.. 


원문 출처 : http://copynull.blog.me/10078931630


네트워크 접속이 비정상으로 끊겼을 때는 
GetQueuedCompletionStatus()함수가 FALSE를 리턴하고 수신바이트 크기가 0(zero) 입니다. 

정상적으로 끊겼을 때는 
GetQueuedCompletionStatus()함수가 TRUE를 리턴하고 수신바이트 크기가 0(zero) 입니다. 
이 때 GetQueuedCompletionStatus()에서 Overlapped Recv I/O 가 리턴됩니다. 

 

비정상적종료 :   리턴값 false, 수신바이트 0

정상적인종료 :   리턴값 true,  수신바이트 0

 

즉 간단히 코드로 설명해보면

 

BOOL result = GetQueuedCompletionStatus(......);

 

if( result == FALSE || recvBytes == 0 )

{

        if( result == FALSE )

        {      비정상 종료 알려주는 코드  WSAGetLastError()로 알림   }

      // 정상 종료 코드

      // 종료를 진행한다....

}

 

IO_TYPE로 검사후 진행되는 코드의 경우 아래와 같이 진행하면 되겠다.

if ( !result || ( result && !recvBytes ) )
  {
   if (OverlappedEx->IoType == IO_ACCEPT)    // 데이터가 없어도 IO_ACCEPT라면 접속함수로 넘김
    OnIoConnected(Object);
   else                                                    // 그것도 아니면 완전 모두 에러처리
    OnIoDisconnected(Object);

   continue;
  }

GetQueuedCompletionStatus의 0 transferred bytes (1)

저장소/VC++

내가 틀리지 않았당께!! 아닌가? 



원문 출처 : http://javawork.egloos.com/2265358


 ERROR_NETNAME_DELETED는 에러 번호로는 64번이고 한글로는 "지정된 네트워크 이름을 더 이상 사용할 수 없습니다." 정도로 해석됩니다. 구글에 ERROR_NETNAME_DELETED로 검색해보면 거의 IOCP와 관련된 글들이 나옵니다. 어떤 상황에서 이 에러를 만나게 되는지, 해결책은 무엇인지 적어봅니다.

 보통 IOCP에서 소켓 끊어짐의 상징은 0 byte read 입니다. 0 byte read가 통보되는 시점은 상대방 소켓에서 closesocket() 혹은 shutdown() 함수를 호출한 시점입니다. 즉, 상대방이 closesocket() 혹은 shutdown() 을 호출하지 않고 종료해버리면 일반적으로 0 byte read는 발생하지 않습니다. 이렇게 0 byte read가 발생하지 않은 상태에서 read() 혹은 write()를 시도하면 상대는 이미 종료 되었으므로 ERROR_NETNAME_DELETED 에러가 나게 됩니다. 이러한 경우를 우아한 종료와는 반대되는 개념으로 HardClose라고 합니다.

ACE에서는 ACE_Asynch_Read_Stream.read() 나 ACE_Asynch_Write_Stream.write() 에서 0 이 아닌 다른 정수를 리턴하고(에러), ACE_OS::last_error() 하면 64번이 리턴됩니다. ACE를 사용하지 않는 경우에는 Read/Write 혹은 GetQueuedCompletionStatus에서  에러가 발생하고 GetLastError하면 64번이 리턴되겠죠.

다른 분의 설명으로는 accept에서도 발생가능한데, AccpEx호출 시 backlog에 있던 소켓이 accept가 되기 전에 접속을 끊어도 이 에러가 발생한다고 합니다.  아마도 이 글이 그러한 상황인 것 같습니다.

요약하면 ERROR_NETNAME_DELETED가 발생하는 경우는 상대방이 HardClose한 상황 이므로 그냥 종료처리하면 된다 입니다.