프로그래밍의 기초/TCP | IP
Iterative 기반의 서버, 클라이언트 구현
Kim나현
2022. 1. 10. 19:17
반응형
- 에코 서버: 클라이언트가 전송하는 문자열 데이터를 그대로 재전송
- Iterative 서버의 함수호출 순서
- 계속된 클라이언트의 연결요청을 수락하기 위해 accept함수를 반복
- 한 순간 하나의 클라이언트의 socket을 대상으로 하지만 프로세스, 쓰레드를 통해 둘 이상의 클라이언트의 socket을 대상으로 할 수 있음
[Iterative 형태로 동작하는 에코 서버, 에코 클라이언트]
- 서버는 한 순간 하나의 클라이언트와 연결되어 에코 서비스를 제공
- 서버는 총 다섯 개의 클라이언트에게 순차적으로 서비스를 제공하고 종료
- 클라이언트는 프로그램 사용자로부터 문자열 데이터를 입력 받아 서버에 전송
- 서버는 전송받은 문자열 데이터를 클라이언트에게 재전송 (에코)
- 서버와 클라이언트간의 문자열 에코는 클라이언트가 Q를 입력할 때까지 계속
// iterative echo server
#define BUF_SIZE 1024
int main(int argc, char* argv[])
{
WSADATA wsaData;
SOCKET clnt_sock, serc_sock;
SOCKADDR_IN serv_addr, clnt_addr;
socklen_t clnt_sz;
char message[BUF_SIZE];
int read_len;
if (argc != 2)
{
printf("usage: %s <port>\n", argv[0]);
}
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
err_handling("start up() error");
serc_sock = socket(PF_INET, SOCK_STREAM, 0);
if (serc_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(serc_sock, (SOCKADDR*)&serv_addr, sizeof(serv_addr)) == SOCKET_ERROR)
err_handling("bind() error");
if(listen(serc_sock, 5)== SOCKET_ERROR)
err_handling("listen() error");
clnt_sz = sizeof(clnt_addr);
int n = 0;
while (n < 5)
{
clnt_sock = accept(serc_sock, (SOCKADDR * )&clnt_addr, &clnt_sz);
if (clnt_sock == INVALID_SOCKET)
err_handling("accept() error");
else
printf("connect: %d\n", ++n);
while ((read_len = recv(clnt_sock, message, BUF_SIZE, 0)) != 0)
send(clnt_sock, message, read_len, 0);
closesocket(clnt_sock);
}
closesocket(serc_sock);
WSACleanup();
return 0;
}
- 클라이언트 socket에서 closesocket이 호출되면 상대 socket에 EOF가 전달되므로 recv 함수에서 0 반환
// 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 = recv(clnt_sock, recv_m, BUF_SIZE - 1, 0);
recv_m[wri_len] = 0;
printf("receive data: %s", recv_m);
}
closesocket(clnt_sock); // eof 전달: 연결의 끝
WSACleanup();
return 0;
}
// 클라이언트 실행 창
connect....
Input messge(Q:exit): echo
receive data: echo
Input messge(Q:exit): r
receive data: r
Input messge(Q:exit): q
receive data: q
// 서버 실행 창
connect: 1
connect: 2
connect: 3
connect: 4
connect: 5
- 에코 클라이언트의 문제점
1. TCP 클라이언트는 데이터의 경계가 존재하지 않기 때문에 클라이언트에서 둘 이상의 write 함수호출로 문자열 정보가 묶여 한번에 서버로 전달되면 클라이언트는 한번에 둘 이상의 문자열 정보를 서버로부터 돌려받을 수 있음
2. 서버는 한 번의 send함수로 전송을 명령했지만 전송할 데이터의 크기가 크다면 운영체제에서 내부적으로 여러 개의 조각으로 나눠 클라이언트에게 전송할 수 있음 (데이터의 모든 조각이 클라이언트에게 전송완료되지 않은 상태에서 recv함수를 호출할 수 있음)
반응형