프로그래밍의 기초/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", ¶m);
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, ¶m, 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;
}
반응형