프로그래밍의 기초/TCP | IP

에코 클라이언트의 해결책

Kim나현 2022. 1. 11. 16:22
반응형

- 에코 클라이언트의 경우 수신해야 할 데이터의 크기를 알고 있으므로 해결이 쉬움

- recv함수를 한번 호출하여 수신된 데이터를 한번에 받음 -> 수신받아야 할 데이터의 크기만큼 데이터를 수신받기 위해 recv함수를 반복 호출

// iterative echo client 
#define BUF_SIZE	1024
int main(int argc, char* argv[])
{
	char message[BUF_SIZE];
	char recv_m[BUF_SIZE];
	WSADATA wsaData;
	SOCKET clnt_sock, serv_sock;
	SOCKADDR_IN serv_addr;
	int wri_len;

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

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

	clnt_sock = socket(PF_INET, SOCK_STREAM, 0);
	if (clnt_sock == INVALID_SOCKET)
		err_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(clnt_sock, &serv_addr, sizeof(serv_addr)) == SOCKET_ERROR)
		err_handling("connect() error");
	else
		printf("connect....\n");

	while (strcmp(recv_m, "q\n") && strcmp(recv_m, "Q\n"))
	{
		fputs("Input messge(Q:exit): ", stdout); 
		fgets(message, BUF_SIZE, stdin);

		send(clnt_sock, message, strlen(message), 0);
		wri_len = 0;
		while (wri_len < strlen(message))			// while (wri_len != strlen(message))의 경우 오류 상황에서 무한루프에 빠지게 됨
		{
			wri_cnt = recv(clnt_sock, &recv_m[wri_len], 5, 0);
			if (wri_cnt == SOCKET_ERROR)
				err_handling("recv() error");
			wri_len += wri_cnt;
		}
		recv_m[wri_len] = 0;
		printf("receive data: %s", recv_m);
	}
	closesocket(clnt_sock);		// eof 전달: 연결의 끝
	WSACleanup();

	return 0;
}

 

  • 수신할 데이터의 크기를 파악하는 것이 불가능할 경우를 위해 어플리케이션 프로토콜의 정의
  • 데이터의 끝을 표현하거나 송수신될 데이터의 크기를 미리 알려줌

 

[여러 개의 숫자와 연산자 정보를 클라이언트로부터 전달받는 프로그램]

- 서버는 클라이언트로부터 숫자와 연산자 정보를 전달받아 숫자를 바탕으로 덧셈, 뺄셈, 곱셈을 계산하여 결과를 클라이언트에게 전달

- 클라이언트는 사용자로부터 숫자와 연산자 정보를 입력받고 서버로부터 받은 연산결과를 출력

  • 어플리케이션 프로토콜 정의

- 클라이언트는 서버에 접속하자마자 피연산자의 개수정보를 1바이트 정수형태로 전달

- 클라이언트가 서버에 전달하는 정수 하나는 4바이트로 표현 (피연산자)

- 정수를 전달한 다음에는 연산의 종류를 1바이트로 전달

- 문자 +-*중 하나를 선택해서 전달

- 서버는 연산결과를 4바이트 정수의 평태로 클라이언트에게 전달

- 연산결과를 얻은 클라이언트는 서버와의 연결을 종료

 

  피연산자 개수 연산자 피연산자 result
바이트 1바이트 1바이트 4바이트 4바이트
자료형 char (-128~127) char (-128~127) char[4] (int) char[4] (int)

// client

#define INT_SIZE	4	// 피연산자 바이트 수 
#define RLT_SIZE	4	// 결과 바이트 수 
#define MAX_SIZE	1024
int main(int argc, char* argv[])
{
	WSADATA wsaData;
	SOCKET clnt_sock;
	SOCKADDR_IN serv_addr;
	char opmsg[MAX_SIZE], data[MAX_SIZE];
	char oper[MAX_SIZE];
	unsigned int param, result;

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

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

	clnt_sock = socket(PF_INET, SOCK_STREAM, 0);
	if (clnt_sock == INVALID_SOCKET)
		err_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(clnt_sock, &serv_addr, sizeof(serv_addr)) == SOCKET_ERROR)
		err_handling("connect() error");

	fputs("connect....\n", stdout);

	fputs("OPerand cnt: ", stdout);
	scanf("%d", &param);
	opmsg[0] = (char)param;		// 1바이트, 피연산자 개수

	fgetc(stdin);
	fputs("operator cnt: ", stdout);
	scanf("%c", &opmsg[1]);		// 1바이트 연산자

	for (int i = 0; i < param; i++)
	{
		fputs("put number: ", stdout);
		// int형으로 형변환하여 4바이트 정수 저장
		scanf("%d", (int*)&opmsg[2 + i * INT_SIZE]);	// 4바이트 피연산자
	}
	send(clnt_sock, opmsg, 2 + param * INT_SIZE, 0);	// 패킷 묶음으로 서버에 전송

	recv(clnt_sock, &result, RLT_SIZE, 0);				// 4바이트 결과
	fputs("result: ", stdout);
	printf("%d", result);

	closesocket(clnt_sock);
	WSACleanup();
	return 0;
}

// server

// 연산자를 통해 연산을 진행하는 프로그램
#define MAX_SIZE	1024
#define RLT_SIZE	4
int calculate(int num, int oper[], char op)
{
	int result;
	result = oper[0];
	for (int i = 1; i < num; i++)
	{
		switch (op)
		{
		case '+':
			result += oper[i];
			break;
		case '-':
			result -= oper[i];
			break;
		case '*':
			result *= oper[i];
			break;
		case '/':
			result /= oper[i];
			break;
		default:
			break;
		}
	}
	return result;
}
int main(int argc, char* argv[])
{
	WSADATA wsaData;
	SOCKET clnt_sock, serv_sock;
	SOCKADDR_IN serv_addr, clnt_addr;
	int clnt_sz;

	int read_len = 0;
	char oper, param;
	char oper_list[MAX_SIZE];
	int sum = 0;

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

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

	serv_sock = socket(PF_INET, SOCK_STREAM, 0);
	if (serv_sock == INVALID_SOCKET)
		err_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_sock, (SOCKADDR*)&serv_addr, sizeof(serv_addr)) == SOCKET_ERROR)
		err_handling("bind() error");

	if (listen(serv_sock, 5) == SOCKET_ERROR)
		err_handling("listen() error");
	clnt_sz = sizeof(clnt_addr);
	for (int n = 0; n < 5; n++)
	{
		clnt_sock = accept(serv_sock, (SOCKADDR*)&clnt_addr, &clnt_sz);
		if (clnt_sock == INVALID_SOCKET)
			err_handling("accept() error");
		else
			printf("connect: %d\n", n + 1);

		recv(clnt_sock, &param, 1, 0);
		recv(clnt_sock, &oper, 1, 0);

		while (param * RLT_SIZE > read_len)		// 송신된 피연산자 배열의 길이를 초과할 때까지 반복
		{
			read_len += recv(clnt_sock, &oper_list[read_len], MAX_SIZE, 0);
		}
		sum = calculate(param, (int*)oper_list, oper);
		send(clnt_sock, (char*)&sum, sizeof(sum), 0);
		closesocket(clnt_sock);
	}
	closesocket(serv_sock);
	WSACleanup();
	return 0;
}

 

반응형