우린 이 전장까지 소켓을 생성하고 바인딩하여 클라이언트의 연결요청을 수락하는 방법까지 배웠습니다. 이제 실질적으로 중요한 데이터 전송, 수신 함수인 send 함수와 recv 함수를 배울겁니다.
send 함수는 데이터를 해당 소켓으로 보내는 함수이고 recv 함수는 해당 소켓으로부터 데이터를 받는 함수입니다. 이 단원 마지막에는 간단한 에코서버와 아직 배우진 않았지만 클라이언트가 연결을 요청하는 프로그램의 소스를 코딩해 볼겁니다. 중요한 부분이니 눈 크게 뜨고 따라오세요.
우선 두 함수의 원형부터 살펴볼까요?
int send(
SOCKET s,
const char* buf,
int len,
int flags
);
int recv(
SOCKET s,
char* buf,
int len,
int flags
);
함수에 들어가는 파라미터가 똑같군요. 그렇지만 조금씩 다른 뜻을 가지고 있습니다.
먼저 send함수와 recv함수의 첫번째 파라미터로 각각 소켓형을 가지는데 연결된 소켓을 넣어줍니다. 당연한 얘기지만 send함수엔 데이터를 보낼 대상의 소켓을 넣어주고, recv함수엔 데이터를 받을 대상의 소켓을 넣어줘야 합니다.
그리고 send/recv 두번째 인수는 char형의 포인터인데 char형이므로 send함수로 어떤 데이터든지 보낼 수 있습니다. 구조체던지 int형이던지 적절한 캐스팅 연산자를 사용해 데이터를 보낼 수 있죠. 반면 recv함수도 어떤 데이터든지 받을 수 있는데 send함수에서 캐스팅한 데이터를 보내주었다면 send함수에서도 적절한 캐스팅으로 데이터를 변환해야겠죠? recv함수는 send함수로 인해 보내어진 데이터를 받을 버퍼의 포인터를 명시하면 됩니다.
세번째 파라미터는 두번째 파라미터 버퍼의 바이트 크기인데 send함수에서는 두번째 인자의 크기만큼 sizeof를 사용해 넣어주면 되고 recv함수에서는 어느정도의 크기를 받을지 모르니 버퍼의 크기를 적당히 크게 잡아주고 그 크기를 세번째 파라미터에 바이트 크기로 명시해 줍니다.
네번째 파라미터는 함수 호출시에 여러가지 옵션을 설정하기 위한 플래그인데 특별한 옵션을 주지 않으려면 0 을 넣어줍니다.
send함수가 호출성공되면 데이터를 보낸 양만큼의 바이트 크기를 리턴하고 recv함수는 받은 만큼의 바이트 크기를 리턴합니다. 에러가 나면 SOCKET_ERROR를 리턴하고 이에 관한 자세한 에러 세부내용은 WSAGetLastError를 호출해서 알아볼 수 있습니다.
accept장에서 언급한것과 같이 위 함수들은 '블록함수'입니다. 해당일이 끝날때까지 리턴되지 않고 무작정 대기하는 함수죠. 쉽게 생각해서 서버측에서 send로 어떤 클라이언트에게 데이터를 전송했습니다. 그런데 클라이언트측에서 recv함수로 서버가 보낸 데이터를 받지 않는다면 대기상태로 들어가게 됩니다. 블록함수를 사용할때는 클라이언트와 서버프로그램간의 실행구조를 잘 생각해서 작성하도록 해야합니다.
아래는 간단한 클라이언트 서버 프로그램입니다. 이 서버프로그램은 서버소켓을 생성하고 클라이언트의 연결요청을 기다리다가 클라이언트가 연결요청을 해오면 클라이언트의 새로운 소켓을 accept로 리턴받고 그 소켓으로부터 데이터를 받아와 그 데이터를 다시 클라이언트로 보냅니다.
//=======================================================
// Echo server( TCP )
#include <winsock2.h>
#include <stdio.h>
#define BUFSIZE 1024
void main( int argc, char* argv[] )
{
WSADATA wsaData;
SOCKET servSock;
SOCKET clntSock;
SOCKADDR_IN servAddr;
SOCKADDR_IN clntAddr;
int clntAddrSize;
char Msg[BUFSIZE];
int strLen;
if( argc != 2 )
{
puts( "Usage : %s <port>\n", argv[0] );
return;
}
if( WSAStartup( MAKEWORD(2, 2), &wsaData ) != 0 )
return;
servSock = socket( PF_INET, SOCK_STREAM, 0 );
if( servSock == INVALID_SOCKET )
return;
servAddr.sin_family = AF_INET;
servAddr.sin_port = htons( atoi(argv[1]) );
servAddr.sin_addr.s_addr = htonl( INADDR_ANY );
if( bind( servSock, ( struct sockaddr* )&servAddr, sizeof(servAddr) )
== SOCKET_ERROR )
{
closesocket( servSock );
return;
}
if( listen( servSock, 5 ) == SOCKET_ERROR )
{
closesocket( servSock );
return;
}
// 클라이언트의 연결요청을 기다림(혹은 수락)
clntAddrSize = sizeof(clntAddr);
clntSock = accept( servSock, ( struct sockaddr* )&clntAddr, &clntAddrSize );
if( clntSock == INVALID_SOCKET )
{
closesocket( servSock );
return;
}
puts( "Client accepted" );
// 루프를 돌면서 클라이언트의 메시지를 받아 다시 클라이언트에게 전송
// 만일 클라이언트가 연결을 종료한다면 recv는 0(eof)를 리턴하게 된다
while( ( strLen = recv( clntSock, Msg, BUFSIZE, 0 ) ) != 0 )
{
send( clntSock, Msg, strLen, 0 );
}
closesocket( clntSock );
closesocket( servSock );
WSACleanup();
}
//=========================================================
// Echo client
#include <winsock2.h>
#include <stdio.h>
#include <string.h>
#define BUFSIZE 1024
void main( int argc, char* argv[] )
{
WSADATA wsaData;
SOCKET sSock;
char Msg[BUFSIZE];
int strLen;
SOCKADDR_IN servAddr;
if( argc != 3 )
{
printf( "Usage : %s <IP> <Port>\n", argv[0] );
return;
}
if( WSAStartup( MAKEWORD(2, 2), &wsaData ) != 0 )
return;
sSock = socket( PF_INET, SOCK_STREAM, 0 );
if( sSock == INVALID_SOCKET )
return;
// 접속할 서버의 sockaddr 구조체 정보를 채움
servAddr.sin_family = AF_INET;
servAddr.sin_port = htons( atoi(argv[2]) );
servAddr.sin_addr.s_addr = inet_addr( argv[1] );
if( connect( sSock, ( struct sockaddr* )&servAddr, sizeof(servAddr) )
== SOCKET_ERROR )
{
closesocket( sSock );
return;
}
// 루프를 돌면서 서버로 메시지를 전송
while( 1 )
{
fputs( "Enter message( q to quit ) : ", stdout );
fgets( Msg, BUFSIZE, stdin );
if( !strcmp( Msg, "q\n" ) )
break;
send( sSock, Msg, strlen( Msg ), 0 );
strLen = recv( sSock, Msg, BUFSIZE - 1, 0 );
Msg[strLen] = 0;
printf( "Message from server : %s \n", Msg );
}
closesocket( sSock );
WSACleanup();
}
- Reference
'[ Windows Program ] > Windows API' 카테고리의 다른 글
비동기소켓 서버구현 (0) | 2011.03.07 |
---|---|
비동기 소켓 - WSAAsyncSelect (1) | 2011.03.07 |
클라이언트의 연결을 처리하는 accept 함수 (2) | 2011.03.07 |
클라이언트의 접속을 가능하게 하는 listen 함수 (0) | 2011.03.07 |
주소할당 함수 bind의 사용 (0) | 2011.03.07 |