소켓을 close하지 않았을 때, 소켓의 TCP 상태는 무엇으로 변하는지 확인한다.
서버, 클라이언트 코드는 모두를 위한 리눅스 책 코드를 사용했다.
아래 repository의 daytime.c, daytimed.c 파일에서 코드를 볼 수 있다.
https://github.com/Jpub/Linux_for_Everyone
daytime.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
static int open_connection(char *host, char *service);
int main(int argc, char *argv[]) {
int sock;
FILE *f;
char buf[1024];
sock = open_connection((argc > 1 ? argv[1] : "localhost"), "daytime"); /* 1 */
f = fdopen(sock, "r");
if (!f) {
perror("fdopen(3)");
exit(1);
}
fgets(buf, sizeof buf, f); /* 5 */
fclose(f); /* 6 */
fputs(buf, stdout); /* 7 */
exit(0);
}
static int open_connection(char *host, char *service) {
int sock;
struct addrinfo hints, *res, *ai;
int err;
memset(&hints, 0, sizeof(struct addrinfo));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
if ((err = getaddrinfo(host, service, &hints, &res)) != 0) { /* 2 */
fprintf(stderr, "getaddrinfo(3): %s\n", gai_strerror(err));
exit(1);
}
for (ai = res; ai; ai = ai -> ai_next) {
sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); /* 3 */
if (sock < 0) {
continue;
}
if (connect(sock, ai -> ai_addr, ai -> ai_addrlen) < 0) { /* 4 */
close(sock);
continue;
}
freeaddrinfo(res);
return sock;
}
fprintf(stderr, "socket(2)/connect(2) failed");
freeaddrinfo(res);
exit(1);
}
daytimed.c
#if defined(__digital__) && defined(__unix__)
# ifdef _XOPEN_SOURCE
# undef _XOPEN_SOURCE
# endif
# define _XOPEN_SOURCE 500
# define _OSF_SOURCE
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <signal.h>
#define DEFAULT_PORT 13
static int listen_socket(int port);
int main(int argc, char *argv[]) {
struct sockaddr_storage addr;
socklen_t addrlen = sizeof addr;
int sock, server;
time_t t;
struct tm *tm;
char *timestr;
server = listen_socket(argc > 1? atoi(argv[1]) : DEFAULT_PORT); /* 1 */
for(;;) {
sock = accept(server, (struct sockaddr*)&addr, &addrlen); /* 6 */
if (sock < 0) {
perror("accept(2)");
exit(1);
}
time(&t);
tm = localtime(&t);
timestr = asctime(tm);
write(sock, timestr, strlen(timestr)); /* 7 */
close(sock); /* 8 */
}
close(server);
exit(0);
}
static int listen_socket(int port) {
struct addrinfo hints, *res, *ai;
int err;
char service[16];
memset(&hints, 0, sizeof(struct addrinfo));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE;
snprintf(service, sizeof service, "%d", port);
if ((err = getaddrinfo(NULL, service, &hints, &res))!= 0) { /* 2 */
fprintf(stderr, "%s\n", gai_strerror(err));
exit(1);
}
for (ai = res; ai; ai = ai->ai_next) {
int sock;
sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); /* 3 */
if (sock < 0) continue;
if (bind(sock, ai->ai_addr, ai->ai_addrlen) < 0) { /* 4 */
close(sock);
continue;
}
if (listen(sock, 5) < 0) { /* 5 */
close(sock);
continue;
}
freeaddrinfo(res);
fprintf(stderr, "listening on port %d...\n", port);
return sock;
}
fprintf(stderr, "cannot listen socket\n");
exit(1);
}
1.. client 코드에서 socket close하는 부분을 제거한다.
fgets(buf, sizeof buf, f);
fclose(f); /* ----> 제거 */
fputs(buf, stdout);
exit(0);
...
2.. 서버를 실행한다.
daytime 서버를 실행한다.
3.. 클라이언트를 실행한다.
클라이언트 프로세스는 서버 응답을 출력하고 종료한다.
4.. 소켓 상태를 확인한다.
클라이언트 → 서버 소켓은 리스트에 나오지 않는다.
서버 → 클라이언트 소켓은 TIME_WAIT 상태가 되었다.
클라이언트 프로세스가 종료하여, close 호출이 없더라도 커넥션이 정상적으로 close된 것 같다.
1.. 서버 코드에서 socket close하는 부분을 제거한다.
timestr = asctime(tm);
write(sock, timestr, strlen(timestr));
close(sock); /* -----> 제거 */
...
2.. 서버를 실행한다.
3.. 클라이언트를 실행한다.
4.. 소켓 상태를 확인한다.
client→ server 소켓은 FIN_WAIT_2 상태다.
FIN_WAIT_2: FIN 요청에 대한 ACK 응답은 받았지만, FIN 응답은 받지 못한 상태
server→client 소켓은 CLOSE_WAIT 상태다.
CLOSE_WAIT: active close한 쪽에서의 FIN 응답을 받았고, 그에 대한 ACK를 발송했지만 FIN을 아직 발송하지 못한 상태
server는 close 함수를 호출했는데, client에서는 close를 호출하기 전에 일정 시간 동안 대기한다면 어떻게 되는지 확인한다.
1.. client 코드에 sleep(100)을 추가한다.
fgets(buf, sizeof buf, f);
sleep(100); /* 100초동안 대기 */
fclose(f);
...
2.. 앞선 테스트 케이스의 2와 동일하다.
3.. 클라이언트를 실행한다.
클라이언트는 서버 호출 후 대기중이다.
4.. 소켓 상태를 확인한다.
server→client 소켓은 FIN_WAIT_2 상태다.
client→server 소켓은 CLOSE_WAIT 상태다.
이번에는 server가 active close했고, client가 close에 대한 응답을 하지 못했다.
server에서 close하기 전에 일정 시간동안 대기한다면 어떻게 되는지 확인한다.
1.. server 코드에 sleep(100)을 추가한다.
timestr = asctime(tm);
write(sock, timestr, strlen(timestr));
sleep(100); /* -----> 추가 */
close(sock);
...
client→server 소켓은 FIN_WAIT_2 상태다.
server→client 소켓은 CLOSE_WAIT 상태다.
client가 active close했고, server가 close에 대한 응답을 하지 못했다.