7-1. 소켓 종료의 문제점
파일 디스크립터를
이용한
close 하수의 호출은 연결의 완전 종료를 의미한다. 완전 종료라는 것은
데이터를
전소아는 것은 물론이거니와 수신하는 것 조차도 더 이상은 불가능하다는 것을
의미한다.
이러한 특성 때문에 경우에 따라서는 clsoe 함수의 호출을 통한 종료
자체가
우아하게 보이지 않을 수도 있다는 것이다.
만약, 양방향으로 통신을
하고
있는 두 호스트가 있을때, 호스트 A가 전송하고자 하는 마지막 패킷을
전송하고
나서 연결 종료를 하였다고 가정하다. 만약 그렇게 된다면 호스트 A는
더이상
어떤 패킷도 수신하지 못한다. 만약 호스트 B가 전송중인 패킷이 있다고
해도
말이다.
이러한 문제를 해결하기 위해서 소켓 스트림의 일부만 종료(half
close)하는
방법이 있다. 일부만 종료한다는 의미는, 전송은 가능하지만 수신은 불가능한 상황,
혹은
수신은 가능하지만 전송은 불가능한 상황을 말한다. 즉 말 그대로 스트림의
반만
닫는 것이다.
7-2. 우아한 소켓의 연결
1) 스트림 half-close 기능의 함수
shutdown 함수는 half-close 기능을 제공해
준다.
shutdown (성공 시 0, 실패 시 -1
리턴) (Language : c) #include <sys/socket.h>
int shutdown( int s, int how) ;
* s
:
종료하고자 하는 소켓의 파일 디스크립터를 전달한다.
* how : 종료
모드를
인자로 전달한다.
how 에 들어갈 수 있는 매크로 상수
상수값 |
모드
| 정의
==============================
0
|
SHUT_RD | 입력 스트림 종료
1
|
SHUT_WR
| 출력 스트림 종료
2
| SHUT_RDWR | 입,출력 스트림
종료
==============================
SHUT_RD 모드로 shutdown 함수를 호출하게 되면, 데이터 전송만
가능한
상태가 되고(입력 스트림 종료), SHUT_WR 모드로 shutdown 함수를 호출하게 되면
데이터
수신만 가능한 상태가 된다(출력 스트림 종료). 이러한 상태를 half-close 상태라
한다.
2) half-close 기능이 필요한 경우의 예제
서버는 클라이언트로의 파일
전송이
긑나면 모든 데이터가 전송되었음을 알려주기 위해서 소켓을 종료하게 될 것이다.
그러면
EOF도 전송이 되고 클라이언트도 데이터 수신을 완료하게 된다.
다음은
코드
이다.
file_server.c (Language : c)
/***************************************************************************
* file_server.c
*
* Sat Jan 5 17:35:10 2008
* Copyright 2008 pchero21
* pchero21@gmail.com
****************************************************************************/
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/socket.h>
#define BUFSIZE 30
void error_handling( char *message) ;
int main( int argc, char **argv)
{
int serv_sd;
int clnt_sd;
int fd;
char buf[ BUFSIZE] ;
struct sockaddr_in serv_addr;
struct sockaddr_in clnt_addr;
int clnt_addr_size;
int len;
if ( argc != 2 ) {
printf ( "Usage : %s <port> \n " , argv
[ 0 ] ) ;
exit( 1 ) ;
}
/* 접속해 오는 클라이언트에게 전송해 줄 파일 오픈 */
fd = open( "file_server.c" , O_RDONLY) ;
if ( fd == -1 )
error_handling( "File open error!" ) ;
serv_sd = socket( PF_INET, SOCK_STREAM, 0 ) ;
if ( serv_sd == -1 )
error_handling( "socket() error!" ) ;
memset( &serv_addr, 0 , sizeof ( serv_addr) ) ;
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr .s_addr = htonl( INADDR_ANY) ;
serv_addr.sin_port = htons( atoi( argv[ 1 ] ) ) ;
if ( bind( serv_sd, ( struct sockaddr*) &serv_addr, sizeof ( serv_addr) ) == -1 )
error_handling( "bind() error!" ) ;
if ( listen( serv_sd, 5 ) == -1 )
error_handling( "listen() error!" ) ;
clnt_addr_size = sizeof ( clnt_addr) ;
clnt_sd = accept( serv_sd, ( struct sockaddr*) &clnt_addr, &clnt_addr_size) ;
if ( clnt_sd == -1 )
error_handling( "accept() error!" ) ;
/* data send to client */
while ( ( len = read( fd, buf, BUFSIZE) ) != 0 ) {
write( clnt_sd, buf, len) ;
}
/* after data send, close the section(send section) of socket */
if ( shutdown( clnt_sd, SHUT_WR) == -1 )
error_handling( "shutdown() error!" ) ;
/* receive farewell message */
len = read( clnt_sd, buf, BUFSIZE) ;
write( 1 , buf, len) ;
close( fd) ;
close( clnt_sd) ;
return 0 ;
}
void error_handling( char *message)
{
fputs( message, stderr) ;
fputc( '\n ' , stderr) ;
exit( 1 ) ;
}
file_client.c (Language : c) /***************************************************************************
* file_client.c
*
* Sat Jan 5 18:17:47 2008
* Copyright 2008 pchero21
* pchero21@gmail.com
****************************************************************************/
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/socket.h>
#define BUFSIZE 30
void error_handling( char *message) ;
int main( int argc, char **argv)
{
int fd;
int sd;
char buf[ BUFSIZE] ;
int len;
struct sockaddr_in serv_addr;
if ( argc != 3 ) {
printf ( "Usage : %s <IP> <port> \n " , argv
[ 0 ] ) ;
exit( 1 ) ;
}
/* file open for save the receive data */
fd = open( "receive.dat" , O_WRONLY | O_CREAT | O_TRUNC) ;
if ( fd == -1 )
error_handling( "File open error!" ) ;
/* create socket for the connect server */
sd = socket( PF_INET, SOCK_STREAM, 0 ) ;
if ( sd == -1 )
error_handling( "socket() error" ) ;
memset( &serv_addr, 0 , sizeof ( serv_addr) ) ;
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr .s_addr = inet_addr( argv[ 1 ] ) ;
serv_addr.sin_port = htons( atoi( argv[ 2 ] ) ) ;
if ( connect( sd, ( struct sockaddr*) &serv_addr, sizeof ( serv_addr) ) == -1 )
error_handling( "connecct() error!" ) ;
/* receive data and save in file */
while ( ( len = read( sd, buf, BUFSIZE) ) != 0 ) {
write( fd, buf, len) ;
}
/* send thank you message to server for data sending */
write( sd, "Thank you, server\n " , 18 ) ;
close( sd) ;
close( fd) ;
return 0 ;
}
void error_handling( char *message)
{
fputs( message, stderr) ;
fputc( '\n ' , stderr) ;
exit( 1 ) ;
}
실행
결과