소켓프로그래밍 깊이 보기 1 : 접속이 닫힌 후 읽을 수 있는 버퍼

Quiz 하나를 생각해보자.

클라이언트가 서버에 접속하여 뭔가를 전송하고 있다. 클라이언트는 1000 바이트를 전송하고나서 바로 소켓을 종료하였는데, 서버는 1 byte 씩 읽으면서 행의 끝을 판단하는 구조로 되어 있다. 서버가 10 바이트를 읽었는데, 실상 접속은 종료되었다. 서버쪽 프로그램은 11 바이트를 읽을 때, 접속 종료를 바로 알 수 있을까?

싱겁지만, 답은 서버쪽에서 1001번째를 읽기 시도할 때 비로소 안다는 것이다. 그 이유는 TCP는 데이터의 정확한(?) 전송을 보장하도록 되어 있기 때문인데, 끊어진 클라이언트에게 일단 1000 바이트에 대해 받았다는 신호를 보냈기 때문에, 서버 프로그램에 안정적으로 데이터를 올려 보낸후 접속이 종료되었음을 알려주는 것이다.

아래의 예는 위 문제를 구현한 것은 아니지만, 느린 수신에서 일어나는 현상을 설명하는 것이다.
보내는 쪽은 프로그램은 이미 종료되었지만, 받는 쪽은 계속 진행하고 있다.

$ ./server &
$ ./client
Send: elapsed 0 sec: sent 100 bytes
Send: elapsed 0 sec: sent 100 bytes
Send: elapsed 0 sec: sent 100 bytes
Send: elapsed 0 sec: sent 100 bytes
Send: elapsed 0 sec: sent 100 bytes
Send: elapsed 0 sec: sent 100 bytes
Recv: elapsed 0 sec: received 200 bytes
Send: elapsed 0 sec: sent 100 bytes
Send: elapsed 0 sec: sent 100 bytes
Send: elapsed 0 sec: sent 100 bytes
Send: elapsed 0 sec: sent 100 bytes
End of client
Recv: elapsed 1 sec: received 200 bytes
Recv: elapsed 2 sec: received 200 bytes
Recv: elapsed 3 sec: received 200 bytes
Recv: elapsed 4 sec: received 200 bytes
End of server

$ cat server.c
#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>

void do_hojin2( int sock )
{
       char buf[200];
       int len;
       time_t t = time(0);
       while( ( len = recv( sock, buf, sizeof buf, 0 ) ) > 0 )
       {
               printf("Recv: elapsed %ld sec: received %d bytes\n", time(0) - t, len );
               sleep(1);
       }
}

int main()
{
       struct sockaddr_in addr;
       int sock, worksock;
       int len;
       int val = 1;

       sock = socket( PF_INET, SOCK_STREAM, 0 );

       memset( & addr, 0, sizeof addr );
       addr.sin_family = AF_INET;
       addr.sin_port = htons( 4000 );
       len = sizeof addr;

       setsockopt( sock, SOL_SOCKET, SO_REUSEADDR, & val, sizeof val );
       bind( sock, (struct sockaddr *) & addr, len );
       listen( sock, 5 );

       worksock = accept( sock, (struct sockaddr *) & addr, & len );
       do_hojin2( worksock );
       close( worksock );
       close( sock);
       printf("End of server\n");
       return 0;
}

$ cat client.c
#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>

void do_hojin1( int worksock )
{
       int i;
       time_t t = time(0);
       for( i=0; i<10; i++ )
       {
               char buf[100];
               int sent;

               sleep(0);
               memset( buf, 'x', sizeof buf );
               sent = send( worksock, buf, sizeof buf, 0 );
               printf("Send: elapsed %ld sec: sent %d bytes\n", time(0) - t, sent );
               fflush( stdout );
       }
}

int main()
{
       struct sockaddr_in addr;
       int sock, worksock;
       int len;
       int val = 1;

       int buf[32768];

       sock = socket( PF_INET, SOCK_STREAM, 0 );

       memset( & addr, 0, sizeof addr );
       addr.sin_family = AF_INET;
       addr.sin_port = htons( 4000 );
       len = sizeof addr;

       connect( sock, (struct sockaddr *) & addr, len );
       do_hojin1( sock );
       close( sock );
       printf("End of client\n");
       return 0;
}

신고
크리에이티브 커먼즈 라이선스
Creative Commons License

+ Recent posts