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
在编写、运行服务端程序时,经常会遇到的一个错误是: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:* LISTEN2)第二次: [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:* LISTENb)重启监听服务器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)!!! ^Cd)此时在服务端快速地执行: [jiang@localhost server]$ netstat -an | grep 12500 tcp 0 0 192.168.44.144:12500 192.168.44.144:52656 TIME_WAITe)快速重启监听服务器: [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 |