설명

recv() 함수는 소켓으로부터 데이터를 수신합니다.

헤더 #include <sys/types.h>
#include
<sys/socket.h>
형태 int recv(int s, void *buf, size_t len, int flags);
인수
int s : 소켓 디스크립터
void *buf : 수신할 버퍼 포인터 데이터
size_t len : 버퍼의 바이트 단위 크기
int flags : 아래와 같은 옵션을 사용할 수 있습니다.
flags 옵션 설명
MSG_DONTWAIT 수신을 위해 대기가 필요하다면 기다리지 않고 -1을 반환하면서 바로 복귀
MSG_NOSIGNAL 상대방과 연결이 끊겼을 때, SIGPIPE 시그널을 받지 않도록 합니다.
반환
-1 이외 : 실제 수신한 바이트 수
-1 : 실패

TCP/IP 소켓 프로그램을 작성해 보겠습니다. 본 포스트는 동영상을 먼저 보신 후, 본문 내용을 보시면 이해하시기 편합니다.

이 포스트에는 동영상이 준비되어 있습니다. 동영상을 보시려면 를 클릭하여 주십시오.

TCP/IP 통신 함수 사용 순서

TCP/IP 예제 소개

  TCP/IP 예제를 서버와 클라이언트로 나누어서 설명을 드리도록 하겠습니다.

  1. 서버와 클라이언트는 port 4000번을 사용
  2. 클라이언트프로그램에서 서버에 접속하면 실행할 때 입력받은 문자열을 전송
  3. 서버는 클라이언트로부터 자료를 수신하면 문자열 길이와 함께 수신한 문자열을 클라이언트로 전송

서버 프로그램

  서버 프로그램에서 사용해야할 함수와 순서는 아래와 같습니다.

우선 socket 부터 만들어야 합니다. TCP/IP에서는 SOCK_STREAM을 UDP/IP에서는 SOCK_DGRAM을 사용하는 것을 참고하여 주십시오. socket()에 대한 더 자세한 말씀은 "Unix C Reference의 11장 7절 소켓 열고 닫기"를 참고하십시오.

int     server_socket;

server_socket = socket( PF_INET, SOCK_STREAM, 0);
if (-1 == server_socket)
{
   printf( "server socket 생성 실패");
   exit( 1) ;
}

  bind() 함수를 이용하여 socket에 server socket 에 필요한 정보를 할당하고 커널에 등록

  1. 만들어진 server_socket 은 단지 socket 디스크립터일 뿐입니다.
  2. 이 socket에 주소를 할당하고 port 번호를 할당해서 커널에 등록해야 합니다.
  3. 커널에 등록해야 다른 시스템과 통신할 수 있는 상태가 됩니다.
  4. 더 정확히 말씀드린다면 커널이 socket 을 이용하여 외부로부터의 자료를 수신할 수 있게 됩니다.
  5. socket에 주소와 port 를 할당하기 위해 sockaddr_in 구조체를 이용합니다.
  6. struct sockaddr_in server_addr;

    memset( &server_addr, 0, sizeof( server_addr);
    server_addr.sin_family      = AF_INET;              // IPv4 인터넷 프로토롤
    server_addr.sin_port        = htons( 4000);         // 사용할 port 번호는 4000
    server_addr.sin_addr.s_addr = htonl( INADDR_ANY);   // 32bit IPV4 주소

    if( -1 == bind( server_socket, (struct sockaddr*)&server_addr, sizeof( server_addr) ) )
    {
       printf( "bind() 실행 에러\n");
       exit( 1);
    }

  7. htonl( INADDR_ANY) 는 주소를 지정해 주는 것으로 inet_addr( "내 시스템의 IP ")로도 지정할 수 있습니다. 그러나 프로그램이 실행되는 시스템 마다 IP 가 다를 것이므로 주소 지정을 고정 IP로 하지 않고 htonl( INADDR_ANY) 를 사용하는 것이 편리합니다.

? 이제 listen() 함수로 클라이언트 접속 요청을 확인합니다.

if( -1 == listen( server_socket, 5))
{
    printf( "대기상태 모드 설정 실패\n");
    exit( 1);
}

  1. listen() 함수를 호출하면 클라이언트의 접속 요청이 올 때 까지 대기 상태가 됩니다. 즉, 블록된 모습이 되죠.
  2. 함수가 리턴이 되었을 때에는 클라이언트의 접속이 요청 되었다든지, 아니면 에러가 발생했을 경우입니다.
  3. 에러 없이 함수가 복귀했다면 클라이언트의 접속 요청입니다.
  4. 접속 요청을 허락합니다.

? 클라이언트 접속 요청에 따라 accept()로 접속을 허락합니다.

  1. accept()로 접속 요청을 허락하게 되면 클라이언트와 통신을 하기 위해서 커널이 자동으로 소켓을 생성합니다.
  2. 이 소켓을 client socket이라고 하겠습니다.
  3. client socket 정보를 구하기 위해 변수를 선언합니다.  그리고 client 주소 크기를 대입합니다.
  4. int     client_addr_size;

    client_addr_size = sizeof( client_addr);

  5. accept()를 호출 후에 에러가 없으면 커널이 생성한 client socket 을 반환해 줍니다.
  6. client_socket = accept( server_socket, (struct sockaddr*)&client_addr,
                                                              &client_addr_size);

    if ( -1 == client_socket)
    {
       printf( "클라이언트 연결 수락 실패\n");
       exit( 1);
    }

? 이제 client socket까지 만들어 졌으므로 read(), write() 함수를 이용하여 자료를 송수신 할 수 있습니다. read() 함수를 이용하여 클라이언트로부터 전송되어 오는 자료를 읽어 들입니다.

read ( client_socket, buff_rcv, BUFF_SIZE);

  1. read() 를 이용하여 클라이언트로부터 전송된 자료를 읽어 들입니다.
  2. 만일 클라이언트로부터 전송된 자료가 없다면 송신할 때 까지 대기하게 됩니다. 즉, 블록된 모습이 됩니다.
? 이번에는 wirte() 함수를 이용하여 클라이언트도 데이터를 전송합니다.

  1. 수신된 데이터의 길이를 구하여 전송 데이터를 준비합니다.
  2. sprintf( buff_snd, "%d : %s", strlen( buff_rcv), buff_rcv);

  3. write() 를 이용하여 클라이언트로 자료를 송신합니다.

    write( client_socket, buff_snd, strlen( buff_snd)+1); // +1: NULL까지 포함해서 전송

? 작업이 완료되면 close() 를 이용하여 client socket 을 소멸 시켜 데이터 통신을 종료합니다.

close( client_socket);

클라이언트 프로그램

  클라이언트 프로그램은 서버에 비해 간단합니다. 바로 설명 들어갑니다.

  socket() 을 이용하여 소켓을 먼저 생성합니다.

int     client_socket;

client_socket = socket( PF_INET, SOCK_STREAM, 0);
if( -1 == client_socket)
{
   printf( "socket 생성 실패\n");
   exit( 1);
}

  connect()를 이용하여 서버로 접속을 시도합니다.

  1. 주소 정보에 서버의 주소와 포트번호를 지정하고
  2. 서버와의 연결을 시도합니다.
  3. 예제에서는 시스템 자기를 가르키는 IP, 127.0.0.1 을 사용했습니다.
  4. struct sockaddr_in    server_addr;

    memset( &server_addr, 0, sizeof( server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons( 4000);
    server_addr.sin_addr.s_addr= inet_addr( "127.0.0.1");  // 서버의 주소

    if( -1 == connect( client_socket, (struct sockaddr*)&server_addr, sizeof( server_addr) ) )
    {
       printf( "접속 실패\n");
       exit( 1);
    }

  1. 접속에 성공하면 데이터를 전송합니다.
  2. write( client_socket, argv[1], strlen( argv[1])+1); // +1: NULL까지 포함해서 전송

  3. 자료를 수신하고 화면에 출력합니다.
  4. read ( client_socket, buff, BUFF_SIZE);
    printf( "%s\n", buff);

  5. socket 을 소멸하여 통신 작업을 완료합니다.
  6. close( client_socket);

서버 프로그램 소스

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>

#define  BUFF_SIZE   1024

int   main( void)
{
   int   server_socket;
   int   client_socket;
   int   client_addr_size;

   struct sockaddr_in   server_addr;
   struct sockaddr_in   client_addr;

   char   buff_rcv[BUFF_SIZE+5];
   char   buff_snd[BUFF_SIZE+5];



   server_socket  = socket( PF_INET, SOCK_STREAM, 0);
   if( -1 == server_socket)
   {
      printf( "server socket 생성 실패\n");
      exit( 1);
   }

   memset( &server_addr, 0, sizeof( server_addr));
   server_addr.sin_family     = AF_INET;
   server_addr.sin_port       = htons( 4000);
   server_addr.sin_addr.s_addr= htonl( INADDR_ANY);

   if( -1 == bind( server_socket, (struct sockaddr*)&server_addr, sizeof( server_addr) ) )
   {
      printf( "bind() 실행 에러\n");
      exit( 1);
   }

   while( 1)
   {
      if( -1 == listen(server_socket, 5))
      {
         printf( "대기상태 모드 설정 실패\n");
         exit( 1);
      }

      client_addr_size  = sizeof( client_addr);
      client_socket     = accept( server_socket, (struct sockaddr*)&client_addr,
                                          &client_addr_size);

      if ( -1 == client_socket)
      {
         printf( "클라이언트 연결 수락 실패\n");
         exit( 1);
      }

      recv  ( client_socket, buff_rcv, BUFF_SIZE, 0);
      printf( "receive: %s\n", buff_rcv);
      
      sprintf( buff_snd, "%d : %s", strlen( buff_rcv), buff_rcv);
      send( client_socket, buff_snd, strlen( buff_snd)+1, 0);       // +1: NULL까지 포함해서 전송
      close( client_socket);
   }
}

클라이언트 프로그램 소스

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>

#define  BUFF_SIZE   1024

int   main( int argc, char **argv)
{
   int   client_socket;

   struct sockaddr_in   server_addr;

   char   buff[BUFF_SIZE+5];

   client_socket  = socket( PF_INET, SOCK_STREAM, 0);
   if( -1 == client_socket)
   {
      printf( "socket 생성 실패\n");
      exit( 1);
   }

   memset( &server_addr, 0, sizeof( server_addr));
   server_addr.sin_family     = AF_INET;
   server_addr.sin_port       = htons( 4000);
   server_addr.sin_addr.s_addr= inet_addr( "127.0.0.1");

   if( -1 == connect( client_socket, (struct sockaddr*)&server_addr,
                           sizeof( server_addr) ) )
   {
      printf( "접속 실패\n");
      exit( 1);
   }
   send( client_socket, argv[1], strlen( argv[1])+1, 0); // +1: NULL까지 포함해서 전송
   recv( client_socket, buff, BUFF_SIZE, 0);
   printf( "%s\n", buff);
   close( client_socket);
   
   return 0;
}
]$ gcc server.c -o server    // 서버 프로그램을 server 이름으로 컴파일
]$ gcc client.c -o client    // 클라이언트 프로그램을 client 이름으로 컴파일
]$ ./server &                // 서브 프로그램을 백그라운드로 실행
[1] 25869
]$ ./client forum.falinux.com
receive: forum.falinux.com
17 : forum.falinux.com
]$ ./client "test string"
receive: test string
11 : test string
]$