출처 http://www.gpgstudy.com/forum/viewtopic.php?t=16635&highlight=connectex

IOCP서버에서 서버간 connect를 하기 위해 Overlap방식으로 Connect 부분을 처리해주는
connectex함수를 사용해봤습니다. 원래는 소켓 재사용을 사용할 생각은 없어서
소켓을 생성해서 접속하고 끊을때는 소켓을 해제해주는 방식으로 했었는데 그부분은 잘됬습니다.
그런데 소켓 재사용을 위해서 Acceptex()함수를 쓸때 사용하던 Transmitfile함수를 옵션값을 그대로
사용해서 connectex의 소켓에 적용하고 다시 접속을 하려하면 에러가 납니다.

우선 소스를 보면서 말씀드리겠습니다.
코드:
// Connect함수는 이렇게 구성되어 있습니다.
// ( 복잡해 보일듯해서 로그 남기는 함수,GetLastError,기타 관련없는 코드 등은 삭제했습니다. )
BOOL CSession::Connect( const CHAR * szIp, const USHORT nPort,HANDLE & hIOCP )
{
INT nReturn;
BOOL bReturn;
DWORD dwBytes = 0;
LPFN_CONNECTEX lpfnConnectEx = NULL;
GUID GuidConnectEx = WSAID_CONNECTEX;
// Overlap의 Connection을 위한 함수포인터인 lpfnConnectEx을 얻어온다.
nReturn = ::WSAIoctl(
m_socketClient,
SIO_GET_EXTENSION_FUNCTION_POINTER,
&GuidConnectEx,
sizeof(GuidConnectEx),
&lpfnConnectEx,
sizeof(lpfnConnectEx),
&dwBytes,
NULL,
NULL
);
if( SOCKET_ERROR == nReturn )
{
return FALSE;
}
m_pOverlappedEx->m_emOperation = IO_CONNECT;
m_pOverlappedEx->m_pSession = this;
// Note : 소켓주소구조체를 구성해서 bind작업을 한다.( connect 작업이 되지 않음 )
// bind시에는 Local주소중 하나를 사용해주고
// Protocol family만 지정해주면 된다.
SOCKADDR_IN localAddr;
::ZeroMemory( &localAddr,sizeof( SOCKADDR_IN ) );
localAddr.sin_family = AF_INET;
localAddr.sin_addr.s_addr = inet_addr( GET_MY_IP() );
nReturn = ::bind( m_socketClient,( struct sockaddr * )&localAddr,sizeof( localAddr ) );
if( SOCKET_ERROR == nReturn )
{
return FALSE;
}
// 입력받은 서버의 ip와 port로 소켓주소 구조체를 구성한다.
SOCKADDR_IN si_addr;
::ZeroMemory( &si_addr,sizeof( SOCKADDR_IN ) );
si_addr.sin_family = AF_INET;
si_addr.sin_port = htons( nPort );
si_addr.sin_addr.s_addr = inet_addr( szIp );
// 입력받은 IOCP핸들과 socket핸들을 Associate한다.
if( !AssociateWithIOCP( hIOCP ) )
{
return FALSE;
}
// Note : Connect에 관한 Overlap작업을 요청한다.
bReturn = lpfnConnectEx(
m_socketClient,
(struct sockaddr * )&si_addr,
sizeof( struct sockaddr ),
NULL,
0,
NULL,
(LPOVERLAPPED)&m_pOverlappedEx );
if( FALSE == bReturn && ( WSAGetLastError() != WSA_IO_PENDING ) )
{
return FALSE;
}
return TRUE;
}


오류는 중간에 있는 bind함수에서 10022가 나옵니다.
물론 처음 connect사용시에는 잘되고요. 끊고 다시 접속할 때 이 소켓값(m_socketClient)을 그대로
사용하기 위해 TransmitFile함수를 써서 소켓 재사용 적용 후에 나온다는 겁니다.
그리고 TransmitFile함수를 break을 걸고 정상 리턴될때 보니 소켓값은 확실히 살아 있었습니다.
그리고 이 함수 사용후 접속도 끊기고요. 재사용이 안됩니다....

그래서 소켓 Reuse를 하는 시점에서 connectex를 호출할때 bind부분을 건너뛰도록 했더니...
AssociateWithIOCP()함수에서 FALSE가 리턴됬습니다. 아! 이 함수는 CreateCompletionPort함수를
이용해서 소켓값( m_socketClient )과 함수에서 받은 IOCP핸들값을 묶어주는 함수인데 NULL값이
리턴되서 FALSE가 나는 상황입니다.

이외에 bind()함수 전에 소켓 setsockopt로 SOCKREUSE옵션인가? 그것도 줘봤는데 실패...
MSDN에 나온것 처럼 전에 쓰던 소켓 옵션을 그대로 적용받기 위해서 lpfnConnectEx리턴된 후
SO_UPDATE_CONNECT_CONTEXT 옵션을 줘봤는데 역시 실패...입니다.

lpfnConnectEx함수를 호출하기 전에 계속 에러가 나는걸 봐서 뭔가 들어가야할 과정이
더 있을것도 같습니다만...구글링도 대답을 안해줍니다;;;

그리고 접속을 끊는 함수부분입니다..
코드:
//.... 생략....
m_pOverlappedEx->m_emOperation = IO_CLOSE;
m_pOverlappedEx->m_pSession = this;
::shutdown( m_socketClient,SD_BOTH );
// Note : TF_DISCONNECT 와 TF_REUSE_SOCKET 옵션값으로 소켓종료후 재사용이 가능토록한다.
bReturn = ::TransmitFile(
m_socketClient,
NULL,
0,
0,
(LPOVERLAPPED)&m_pOverlappedEx,
NULL,
TF_DISCONNECT | TF_REUSE_SOCKET
);
if( ( FALSE == bReturn ) && ( WSAGetLastError() != WSA_IO_PENDING ) )
{
return FALSE;
}


이 함수에서 혹시 에러가 걸리지 않을까 했는데 여기선 안걸리더군요. 정상적으로 실행됩니다...
( 참고로 이미 Acceptex함수와 맞춰 접속자의 소켓값을 재사용하면서 아무 문제없이 실행되던 코드입니다. )

혹시 이 부분을 구현해보셨거나 어디가 이상한지 보이시면 조언 좀 부탁드립니다.
아니면 어디 자료를 참고하면 된다는 거라도 좀 알려주십시요 ㅜ.ㅜ
저도 알게되면 여기 댓글로 남기겠습니다.

그럼..답변을 기다리며...마지막 제헌절 휴일 잘보내세요.....
위로
smurpe



가입: 2006년 2월 7일
올린 글: 1

올리기 올려짐: 2007-08-24 17:45
인용과 함께 답변 이 게시물을 del.icio.us에 추가

ConnectEx를 호출하기전에 bind를 하는데 이때 bind함수는 최초 한번만 호출이 되어야 합니다. 소켓 핸들을 사용후 반납후 재사용시 이미 bind가 되어 있던 소켓 핸들이라면 10022에러가 납니다.

Posted by 패스맨

댓글을 달아 주세요

// sosoth.cpp : 콘솔 응용 프로그램에 대한 진입점을 정의합니다.
//

#include "stdafx.h"
#include <iostream>
#include <time.h>

using namespace std;

int _tmain(int argc, _TCHAR* argv[])
{

 //__asm
 //{
 // mov eax ,1
 //}

 srand( ( unsigned) time( NULL));

 int ran[100];

 //초기화
 for(int i = 0; i< 100 ; ++i)
 {
  ran[i] = i;
 }
 
 int cnt = 0;
 while( 1)
 {
  int index = rand()%100;

  int tempNum = ran[index];

  ran[index] = ran[cnt];

  ran[cnt] = tempNum;

  cnt++;

  if( cnt ==99 )
   break;
 }

 for(int i = 0; i< 100 ; ++i)
 {
  cout<< ran[i]<<endl;
 }


 int allAvSum = 0;
 for (int i = 0; i< 1000; ++i)
 {
  int ran2[100];
  memcpy( ran2, ran, sizeof(ran2));
  cnt = 0;
  int rstSum = 0;
  while(1)
  {
   int index = rand()%100;

   if( ran2[index]  != 999 )
   {
    ran2[index] = 999;
    cnt++; // 이 갯수가 100개가 되면 모두 바뀐거.
   }

   rstSum++;

   if( cnt == 100)
    break;

  }


  allAvSum += rstSum;
  cout << rstSum <<endl;
 }

 cout<< "av:" <<1.0f* (float) ( allAvSum/ 1000  ) <<endl;

 return 0;
}

 

'Programing > 알고리즘' 카테고리의 다른 글

표준 rand()함수보다 유용한 랜덤 생성 알고리즘 – MT, WELL  (0) 2012.03.07
100개 따조 모으기  (0) 2012.01.16
중복하지 않는 난수생성..  (0) 2011.12.09
Crypt Kicker  (0) 2011.12.07
Jolly Jumpers  (0) 2011.12.07
DisplayLCD  (0) 2011.12.07
Posted by 패스맨

댓글을 달아 주세요


http://msdn.microsoft.com/en-us/library/26td21ds.aspx

'Programing > asm' 카테고리의 다른 글

x64에서의 인라인 어셈 불가  (0) 2012.01.13
어셈블리어  (0) 2011.11.29
OpCode of Intel Assembly 80x86  (0) 2011.03.07
Posted by 패스맨

댓글을 달아 주세요