본문 바로가기
CS/컴퓨터네트워크

[열혈] half close

by sun__ 2020. 10. 24.

목적 : half close의 의미, 사용이유, 사용 예제코드의 이해

 

배경지식 : write시 출력버퍼에 쓰고, read시 입력 버퍼에서 읽어온다. tcp의 슬라이딩 윈도우에 의해 입력버퍼의 크기보다 큰 데이터가 들어오진 않는다.

 


통신의 종료를 원한다는 것은 더이상 전송할 데이터가 존재하지 않는 상황을 의미한다. 이때, 본인의 출력 스트림을 종료시켜도 된다. 하지만 상대방도 종료를 원하는지는 모르기 때문에, 본인의 입력 스트림은 종료하면 안되는 경우가 생긴다. 이런 경우 본인의 출력 스트림만 종료할 수 있다. 이걸 half close라 한다.

 

출력 스트림을 종료하면 상대 호스트로 EOF가 전송된다. 이걸로 수신받아야 하는 데이터의 끝을 알 수 있다.

 

#include <sys/socket.h>

int shutdown(int sock, int howto);
/*
성공 시 0, 실패 시 -1 반환
sock : 종료할 소켓의 파일 디스크립터
howto : 종료 방법
    SHUT_RD : 입력스트림
    SHUT_WR : 출력스트림
    SHUT_RDWR : 입출력스트림
*/

 

 


server는 본인의 코드를 client에게 보내고 다 보냈다면 half close로 eof신호를 보낸다. client는 eof 신호를 받았다면 정

상적으로 server의 코드를 수신한 것으로 판단하고 "thank you" 문자열을 전송한다.

 

<file_server.c>

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

#define BUF_SIZE 30
void error_handling(char* message);

int main(int argc, char** argv) {
	int serv_sd, clnt_sd;
	FILE* fp;
	char buf[BUF_SIZE];
	int read_cnt;
	
	struct sockaddr_in serv_adr, clnt_adr;
	socklen_t clnt_adr_sz;

	if (argc != 2) {
		printf("usage: %s <port>\n", argv[0]);
		exit(1);
	}

	fp = fopen("file_server.c", "rb");
	serv_sd = socket(PF_INET, SOCK_STREAM, 0);

	memset(&serv_adr, 0, sizeof(serv_adr));
	serv_adr.sin_family = AF_INET;
	serv_adr.sin_addr.s_addr = htonl(INADDR_ANY);
	serv_adr.sin_port = htons(atoi(argv[1]));

	bind(serv_sd, (struct sockaddr*)&serv_adr, sizeof(serv_adr));
	listen(serv_sd, 5);

	clnt_adr_sz = sizeof(clnt_adr);
	clnt_sd = accept(serv_sd, (struct sockaddr*)&clnt_adr, &clnt_adr_sz);

//////////////////////////////////////////////
	while (1)  {
    	//본인의 코드를 BUF_SIZE만큼씩 읽어서 client에게 송신한다.
		read_cnt = fread((void*)buf, 1, BUF_SIZE, fp);
		if (read_cnt < BUF_SIZE) {
			write(clnt_sd, buf, read_cnt);
			break;
		}
		write(clnt_sd, buf, BUF_SIZE);
	}
  
	shutdown(clnt_sd, SHUT_WR); 	//모두 송신했다면 half-close, eof전송
	read(clnt_sd, buf, BUF_SIZE);	//client로부터 메세지를 받는다.
	printf("Message from client: %s\n", buf);
   
//////////////////////////////////////////////// 

	fclose(fp);
	close(clnt_sd); close(serv_sd);
	return 0;
}

void error_handling(char* message) {
	fputs(message, stderr);
	fputc('\n', stderr);
	exit(1);
}

 

<file_client.c>

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

#define BUF_SIZE 30
void error_handling(char* message);

int main(int argc, char** argv) {
	int sd;
	FILE* fp;
	char buf[BUF_SIZE];
	int read_cnt;

	struct sockaddr_in serv_adr;

	if (argc != 3) {
		printf("usage: %s <IP> <port>\n", argv[0]);
		exit(1);
	}
/////////////////////////////////////////////////////////
	//서버의 코드를 받아적을 파일
	fp = fopen("receive.dat", "wb");
	sd = socket(PF_INET, SOCK_STREAM, 0);

	memset(&serv_adr, 0, sizeof(serv_adr));
	serv_adr.sin_family = AF_INET;
	serv_adr.sin_addr.s_addr = inet_addr(argv[1]);
	serv_adr.sin_port = htons(atoi(argv[2]));

	connect(sd, (struct sockaddr*)&serv_adr, sizeof(serv_adr));

	//server가 보낸 정보를 buf에 담아 fp가 가리키는 파일에 옮겨 적음
	while ((read_cnt = read(sd, buf, BUF_SIZE)) != 0)
		fwrite((void*)buf, 1, read_cnt, fp);

	puts("received file data");
	write(sd, "Thank you", 10);
   
///////////////////////////////////////////////////////// 
	fclose(fp);
	close(sd);
	return 0;
}

void error_handling(char* message) {
	fputs(message, stderr);
	fputc('\n', stderr);
	exit(1);
}