반응형
  • 일방적인 연결 종료의 문제점
  • close/ closesocket: 데이터 송수신 불가능한 종료 상황

- 호스트1이 마지막 데이터를 전송하고 연결을 종료했을 시 호스트1은 호스트2가 전송하는 데이터를 수신하는 함수를 호출할 수 없음

→ 데이터의 송수신에 사용되는 stream의 일부만 종료하여 송신만 닫거나 수신만 닫음: Half-close

 

  • stream은 한 방향으로만 데이터의 이동이 가능하므로 양방향 통신을 위해 두 개의 stream 필요

- 상호간의 데이터의 송수신이 가능한 상태: stream이 형성된 상태

- 두 호스트에 socket이 연결되면 호스트마다 입력 stream과 출력 stream이 하나씩 형성

(출력 stream은 입력 stream으로, 입력 stream은 출력 stream으로 연결)

 

  • shutdown함수: 하나의 stream만 끊는 함수

int shutdown(
  [in] SOCKET s,      // 종료할 socket의 file descriptor
  [in] int    how      // 종료 방법에 대한 정보 전달
);

- 매개변수 how의 종류

인자(Linux/ window) 의미
SHUT_RD/SD_RECEIVE 입력 스트림 종료
SHUT_WR/SD_SEND 출력 스트림 종료
(출력 버퍼에 전송되지 못한 데이터가 존재하면 전송)
SHUT_RDWR/SD_BOTH 입출력 스트림 종료

 

  • shutdown함수를 사용하는 이유

서버가 클라이언트에게 파일을 전송하고 클라이언트는 파일을 모두 받으면 thank you라는 메시지를 송신할 경우

- 클라이언트는 입력함수를 얼마나 많이 호출하여 파일 데이터를 모두 수신해야 할지 알 수 없고 계속 입력함수를 호출한다면 블로킹 상태(호출된 함수가 반환하지 않는 상태)에 빠짐

- 클라이언트는 파일의 끝을 의미하는 EOF를 수신해 확인이 가능하지만 서버에서 출력 stream을 종료해야지만 EOF 전송가능 → 서버가 입출력 stream을 모두 닫을 경우 클라이언트가 송신하는 thank you라는 메시지를 수신받지 못하므로 출력 stream만 닫는 shutdown함수 호출

 

  • half-close 기반의 파일전송 프로그램

//server

// half close
#define BUF_SZ		30
int main(int argc, char* argv[])
{
	SOCKET sock, cln_soc;
	SOCKADDR_IN serv_adr, clnt_adr;
	int clnt_sz, str_len;
	WSADATA wsaData;
	char message[BUF_SZ];
	FILE* fd;
	const char* name = "E:\\repos_n\\TCPex\\copy.txt";
	const char* mode = "r";

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

	if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
		err_handling("startup() error");
	
	fd = fopen(name, mode);
	sock = socket(PF_INET, SOCK_STREAM, 0);
	if (sock == INVALID_SOCKET)
		err_handling("socket() error");

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

	if (bind(sock, &serv_adr, sizeof(serv_adr)) == SOCKET_ERROR)
		err_handling("bind() error");

	if(listen(sock, 5)==SOCKET_ERROR)
		err_handling("listen() error");

	clnt_sz = sizeof(clnt_adr);

	cln_soc = accept(sock, &clnt_adr, &clnt_sz);
	if (cln_soc == INVALID_SOCKET)
		err_handling("accept() error");

	while (feof(fd) == 0)
	{
		str_len = fread(message, 1, BUF_SZ, fd);
		send(cln_soc, message, strlen(message), 0);
	}
	shutdown(cln_soc, SD_SEND);			// wr half close -> send eof to client

	recv(cln_soc, message, BUF_SZ, 0);
	printf("msg: %s\n", message);
	fclose(fd);
	
	closesocket(sock);
	WSACleanup();

	return 0;
}

// client

// half close
#define BUF_SZ		30
int main(int argc, char* argv[])
{
	FILE* fd;
	SOCKET sock;
	int str_len = 0;
	SOCKADDR_IN serv_adr, clnt_adr;
	WSADATA wsaData;
	char msg[BUF_SZ] = { 0 };
	const char* name = "data.txt";
	const char* mode = "wt";

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

	if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
		err_handling("startup() error");

	fd = fopen(name, mode);
	sock = socket(PF_INET, SOCK_STREAM, 0);
	if (sock == INVALID_SOCKET)
		err_handling("socket() eror");

	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]));

	if(connect(sock, &serv_adr, sizeof(serv_adr))==SOCKET_ERROR)
		err_handling("connect() eror");

	while ((str_len = recv(sock, msg, BUF_SZ - 1, 0)) != 0)
	{
		msg[str_len] = 0;
		fwrite(msg, 1, str_len, fd);
	}

	puts("done");
	send(sock, "thank you", 10, 0);
	fclose(fd);

	closesocket(sock);
	WSACleanup();
	return 0;
}
반응형

+ Recent posts