🌟 4-3 에코 클라이언트의 문제점
더보기
// echo_server.c
while((str_len = read(clnt_sock, message, BUF_SIZE)) != 0)
write(clnt_sock, message, str_len);
// 서버는 클라이언트로부터 데이터를 여러 번에 걸쳐 수신할 수 있다고 생각하고 read()를 반복 호출함.
// 수신한 만큼만 write()로 다시 전송함.
// ehco_client
write(sock, message, strlen(message));
str_len = read(sock, message, BUF_SIZE-1);
// 클라이언트는 문자열을 한 번에 전송함
// read()도 한 번만 호출해서 모든 데이터가 한 번에 수신되기를 기대함
해결책 1: read()를 반복해서 호출하여 원하는 길이만큼 수신
- 방법: 클라이언트가 write()를 통해 보낸 데이터의 길이만큼 수신이 완료될 때까지 read()를 반복 호출해야 함.
- 이유: read()는 최대 BUF_SIZE-1 바이트까지만 읽을 수 있고, 실제로는 그보다 더 적은 바이트가 수신될 수 있음. 따라서 read()를 한 번만 호출하면 불완전한 데이터 수신이 발생할 수 있음
// 서버에 메시지 전송 (write 반환값은 전송한 바이트 수)
int str_len = write(sock, message, strlen(message));
// 지금까지 수신한 총 바이트 수를 저장할 변수
int recv_len = 0, recv_cnt;
// 서버로부터 str_len 바이트를 모두 수신할 때까지 반복
while (recv_len < str_len) {
// 남은 공간에 데이터 수신 (최대 BUF_SIZE - 1 - recv_len 바이트)
recv_cnt = read(sock, &message[recv_len], BUF_SIZE - 1 - recv_len);
// read 오류 처리
if (recv_cnt == -1)
error_handling("read() error!");
// 누적 수신 바이트 수 갱신
recv_len += recv_cnt;
}
// 문자열 끝에 널 문자 추가 (C 문자열로 만들기 위함)
message[recv_len] = 0;
// 서버로부터 받은 메시지 출력
printf("Message from server: %s", message);
해결책 2: 응답 종료 조건을 지정 (ex. 종료 구분자 사용)
- 방법: 서버가 응답 데이터의 끝에 구분자 문자(예: \n, \0, "END" 등)를 추가로 전송하고 클라이언트는 그 구분자를 만날 때까지 read()를 반복
- 이유: 메시지의 길이를 사전에 알 수 없는 경우, 수신이 완료되었는지를 판단하기 위한 종료 기준이 필요함 종료 구분자를 사용하면 정확한 길이를 몰라도 수신을 마칠 수 있음
char ch; // 한 글자씩 수신할 문자 변수
int idx = 0; // message 배열의 인덱스
// 무한 루프: 종료 구분자('\n')가 수신될 때까지 계속 수신
while (1) {
// 1바이트 읽어서 ch에 저장
if (read(sock, &ch, 1) <= 0)
error_handling("read() error!");
// 읽은 문자를 message 배열에 저장
message[idx++] = ch;
// 개행 문자('\n')가 수신되면 루프 종료
if (ch == '\n')
break;
}
// 문자열 끝에 널 문자 추가 (C 문자열로 만들기 위함)
message[idx] = 0;
// 서버로부터 받은 메시지 출력
printf("Message from server: %s", message);
- TCP는 데이터를 바이트 스트림으로 전달하기 때문에 한 번에 보낸 메시지가 한 번에 도착하는 보장이 없음. 이로 인해 클라이언트에서 read()를 한 번만 출력하면 데이터가 일부 수신될 가능성↑
- 이를 해결할 방법 2가지. 첫째, write()로 보낸 데이터만큼 read()를 반복 호출하는 방식. 이 방식은 전송한 데이터 크기를 클라이언트가 알고 있을 경우 매우 적합. read()를 반복 호출하여 누적 수신 바이트 수가 전송한 바이트 수와 일치할 때까지 수신을 계속함. 이로 인해 위에 언급한 문제를 방지할 수 있음. 둘째, 서버가 메시지 끝에 구분자(예: \n, "END")를 붙이고, 클라이언트가 이를 만날 때까지 read()를 반복하는 방식! 이 방식은 구분자가 도착하면 수신이 완료됐음을 판단할 수 있기에 메시지 처리에 효과적이다. 다만 서버와 클라이언트가 구분자에 대한 사전 약속을 해야 함.
'C > Study' 카테고리의 다른 글
C언어 Newwork Study 5 (1) | 2025.07.11 |
---|---|
C언어 Newwork Study 4 (1) | 2025.07.11 |
C언어 Newwork Study 2 (1) | 2025.07.11 |
C언어 Newwork Study 1 (0) | 2025.06.30 |