설명

listen() 함수는 소켓을 통해 클라이언트의 접속 요청을 기다리도록 설정합니다.

헤더#include <sys/socket.h>
형태int listen(int s, int backlog);
인수
int s: 소켓 디스크립터
int backlog: 대기 메시지 큐의 개수
반환
0: 성공
-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 == bindserver_socket, (struct sockaddr*)&server_addr, sizeof( server_addr) ) )
    {
       printf( "bind() 실행 에러\n");
       exit( 1);
    }

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

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

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

▶ 클라이언트 접속 요청에 따라 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_socketbuff_rcv, BUFF_SIZE);

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

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

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

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

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

closeclient_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. writeclient_socket, argv[1], strlen( argv[1])+1); // +1: NULL까지 포함해서 전송

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

  5. socket 을 소멸하여 통신 작업을 완료합니다.
  6. closeclient_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);
   }

   if( -1 == listen(server_socket, 5))
   {
      printf( "listen() 실행 실패\n");
      exit( 1);
   }

   while( 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);
      }

      read ( client_socket, buff_rcv, BUFF_SIZE);
      printf( "receive: %s\n", buff_rcv);
      
      sprintf( buff_snd, "%d : %s", strlen( buff_rcv), buff_rcv);
      write( client_socket, buff_snd, strlen( buff_snd)+1);          // +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);
   }
   write( client_socket, argv[1], strlen( argv[1])+1);      // +1: NULL까지 포함해서 전송
   read ( client_socket, buff, BUFF_SIZE);
   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 test_string      // 클라이언트를 문자열을 입력하여 실행
receive: test_string
11 : test_string
]$ ./client forum.falinux.com
receive: forum.falinux.com
17 : forum.falinux.com
]$