tcp bind 에러 원인과 해결법과 SO_REUSEADDR

2019. 7. 22. 19:56드론

 

TIME_WAIT는 연결이 중심이 되는 TCP에서 끊어진 연결과 새로운 연결을 구분짓기 위해서(데이터 무결성을 위해) 생겨났다. 소켓이 닫힌 후 일정 시간 동안 커널에서 해당 포트 바인드를 막음으로써 위와 같은 문제를 해결할 수 있다

약 90초 동안의 TIME_WAIT 시간을 가져서 bind를 막게 되는데, UNIX에서는 이 시간을 건드리지 말라고 한다. 하지만 복잡하지 않는 TCP 소스코드라면 소켓 설정을 달리해서 bind 에러를 막을 수 있다

 

int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen);

 

level은 소켓, IP, TCP 레벨이 있으며, optname에 SO_REUSEADDR을 지정해 주면 같은 포트에 대해 다른 소켓이 bind()되는 것을 허락해 주기 때문에 bind()에러 없이 프로그램을 실행할 수 있다. 그리고 setsockopt에서는 optval에 옵션 값을 넣어주는데 실행을 시킬 것이기 때문에 1을 넣어서 전달하면 된다

 

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
void error_handle(char *msg);
int main(int argc, char **argv)
{
int serv_sock, cli_sock;
int opt;
struct sockaddr_in serv_addr, cli_addr;
socklen_t cli_addr_size;
char msg[256] = "Hello world!";
if(argc != 2) {
printf("usage : %s <port>\n", argv[0]);
exit(1);
}
serv_sock = socket(PF_INET, SOCK_STREAM, 0);
if(serv_sock == -1) {
error_handle("socket() error");
}
opt = 1;
setsockopt(serv_sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
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, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) {
error_handle("bind() error");
}
if(listen(serv_sock, 5) < 0) {
error_handle("listen() error");
}
cli_addr_size = sizeof(cli_addr);
cli_sock = accept(serv_sock, (struct sockaddr *)&cli_addr, &cli_addr_size);
if(cli_sock < 0){
error_handle("accept() error");
}
write(cli_sock, msg, strlen(msg));
close(cli_sock);
close(serv_sock);
return 0;
}
void error_handle(char *msg)
{
fputs(msg, stderr);
fputc('\n', stderr);
exit(1);
}
view raw binderror.c hosted with ❤ by GitHub

 

[출처] http://devbv.com/2017/04/01/timewait/