C/C++:TCP bind error:Address already in use

您所在的位置:网站首页 嘻哈帮街舞教学视频 C/C++:TCP bind error:Address already in use

C/C++:TCP bind error:Address already in use

2023-09-23 11:18| 来源: 网络整理| 查看: 265

C/C++:TCP bind error:Address already in use

在编写、运行服务端程序时,经常会遇到的一个错误是:Address already in use.

Address already in use 是在调用bind系统调用时出现的错误。

原因有两个:

1.bind一个已经listen的端口

例如:

当前主机已经有服务器进程调用bind以及listen,在当前主机监听12500端口:

[jiang@localhost ~]$ netstat -an | grep 12500 tcp 0 0 0.0.0.0:12500 0.0.0.0:* LISTEN

你尝试在程序中再次调用bind,将12500端口和你的socket进行绑定,此时会产生系统调用错误:

[jiang@localhost ~]$ ./server/server socket bind error=98(Address already in use)!!! Code: #include #include #include #include #include #include #include #include #include #include #define BACKLOG 16 int main() { // socket int listen_fd = socket(AF_INET, SOCK_STREAM, 0); if (listen_fd printf("socket bind error=%d(%s)!!!\n", errno, strerror(errno)); exit(1); } // listen if (listen(listen_fd, BACKLOG) // accept int client_fd = accept(listen_fd, NULL, NULL); if (client_fd printf("send msg to client error!!!\n"); exit(1); } close(client_fd); connIdx++; } // never close(listen_fd); return 0; }

编译 && 生成可执行文件:

[jiang@localhost server]$ gcc -o server server.c

连续调用两次server:

1)第一次:

[jiang@localhost server]$ ./server server init ok, start to accept new connect...

bind成功,服务器进程正常运行,用netstat查看12500端口状态:

[jiang@localhost server]$ netstat -an | grep 12500 tcp 0 0 0.0.0.0:12500 0.0.0.0:* LISTEN

2)第二次:

[jiang@localhost server]$ ./server socket bind error=98(Address already in use)!!!

bind失败,进程退出。

2.bind的目标端口是本主机本地socket连接中的端口

例如:

a)主机启动一个监听服务器; b)连接请求到达,派生一个子进程来处理这个客户端连接; c)监听服务器终止,但是子进程继续为现有连接的客户提供服务; d)重启监听服务器;

在d)步骤之前,已经有一条(多条)正在连接的TCP连接:

五元组=》

客户端IP:客户端PORT:TCP:服务端IP:服务端PORT(12500)

当想要再次启动监听服务器对 bind 12500 进行调用将会失败,导致重启监听服务器失败。

也就是说,本机已经有一个连接的本端socket是12500端口,要再 bind 12500 将失败。

请看一个有意思的示例程序。

Code:

服务端程序见上文。

客户端程序:

#include #include #include #include #include #include #include #include #include #include int main() { int client_fd = socket(AF_INET, SOCK_STREAM, 0); if (client_fd printf("inet_pton error!!!\n"); exit(1); } if (connect(client_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) printf("read error=%d(%s)!!!\n", errno, strerror(errno)); exit(1); } msg[rbytes] = 0; // null terminate printf("%s", msg); int finRead = read(client_fd, msg, sizeof(msg)-1); if (finRead msg[finRead] = 0; printf("finRead(%d): read FIN-Segment\n", finRead); } else { printf("finRead(%d): %s\n", msg); } close(client_fd); return 0; }

编译 && 生成可执行文件:

[jiang@localhost client]$ gcc -o client client.c [jiang@localhost client]$ ll total 16 -rwxrwxr-x. 1 jiang jiang 8476 May 14 14:09 client -rw-rw-r--. 1 jiang jiang 1374 May 14 13:18 client.c

步骤如下:

a)启动监听服务器server:

[jiang@localhost server]$ ./server server init ok, start to accept new connect... [jiang@localhost ~]$ netstat -an | grep 12500 tcp 0 0 0.0.0.0:12500 0.0.0.0:* LISTEN

b)重启监听服务器server:

[jiang@localhost server]$ ./server server init ok, start to accept new connect... ^C [jiang@localhost server]$ ./server server init ok, start to accept new connect... [jiang@localhost ~]$ netstat -an | grep 12500 tcp 0 0 0.0.0.0:12500 0.0.0.0:* LISTEN

我们看到,按下Ctrl+C宕掉进程,并重启监听服务器成功。

c)执行客户端程序,链接服务端的服务端口,然后宕掉服务端进程:

[jiang@localhost client]$ ./client connect to server ok! connIdx=0 finRead(0): read FIN-Segment [jiang@localhost server]$ ./server server init ok, start to accept new connect... accept one new connect(0)!!! ^C

d)此时在服务端快速地执行:

[jiang@localhost server]$ netstat -an | grep 12500 tcp 0 0 192.168.44.144:12500 192.168.44.144:52656 TIME_WAIT

e)快速重启监听服务器:

[jiang@localhost server]$ ./server socket bind error=98(Address already in use)!!!

重启失败,Address already in use。

f)稍等1分钟左右,重启监听服务器:

[jiang@localhost server]$ ./server server init ok, start to accept new connect...

重启成功。

概括来说:

1)如果没有客户端连接到服务器,那么可以不间断地重启服务器; 2)如果有客户端连接到服务器,那么服务端宕掉后,需要等待一段时间才可以 bind 12500 成功,重启成功。

原因:

监听服务程序中,是服务器主动对连接进行close,主动关闭连接,那服务器一侧就将进入到TIME_WAIT状态,持续2MSL。

当监听服务器宕掉时,连接还是会被内核所记录,也就是说,四次挥手还未完成,这个连接还未完整地走完四次挥手,还处于“连接中”的状态。

这种情况,实际和一条运行中的正常的TCP连接没啥区别,都未完成四次挥手流程。

当监听服务器进程重启,12500还被内核持有,也就 bind 12500 失败,重启监听服务器失败。

直到连接在2MSL时间之后(通常2MSL=1min)连接被内核释放,再次 bind 12500 将会成功。

如果没有客户端连接到服务器,当然可以随意起宕服务器!因为服务器一侧没有连接处于TIME_WAIT状态!12500随时可以串行地 bind 12500 成功。

如何避免?

设置套接字选项:SO_REUSEADDR。

在《UNIX网络编程:卷一 套接字联网API》一书中写到:

所有的TCP服务器都应当指定SO_REUSEADDR选项。

修改代码,在 bind 12500 前新增:

int flag = 1; if (setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(flag))


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3