写了一个tcp socket包含服务器和客户端,当用Ctrl + C后终止服务端后,再次启动时报错:
bind:address already in use
就是说端口重复占用了,寻思着是Ctrl + C暴力停止,没有调用close关闭socket。
close(server_sockfd);
close(client_sockfd);
加入上述函数后依然报这个错误,看来事情没有我想的那么简单。
这个时候根据进程名和端口号来查进程号,发现都查不到,不由得虎躯一震,背部发凉。
ps -ef | grep tcpServerDemo
lsof -i :8888
如果查找不到,可以试试
sudo lsof -i: 8888
后来查资料发现即使进程终止,socket也会有一个TIME-WAIT状态,它大概持续2-4分钟,当然也可能更长,过了这个时间就会释放这个端口号。
用这个命令可以看到处于TIME-WAIT状态的端口号:
netstat -an | grep 8888
那么怎么才能立即启动而不是等待这个TIME-WAIT结束呢?
my_addr.sin_family=AF_INET; //设置为IP通信
my_addr.sin_addr.s_addr=INADDR_ANY;//服务器IP地址--允许连接到所有本地地址上
my_addr.sin_port=htons(1234); //服务器端口号
//创建服务器端套接字--IPv4协议,面向连接通信,TCP协议
if((server_sockfd=socket(PF_INET,SOCK_STREAM,0))<0)
{
cout<<"socket error";
return ;
}
//使端口可以重复使用
int iSockOptVal = 1;
if (setsockopt(server_sockfd, SOL_SOCKET, SO_REUSEADDR, &iSockOptVal, sizeof(iSockOptVal)) == -1)
{
perror("setsockopt fail");
close(server_sockfd);
exit(EXIT_FAILURE);
}
//套接字绑定在服务器的网络地址下
if(bind(server_sockfd,(struct sockaddr *)&my_addr,sizeof(struct sockaddr))<0)
{
cout<<"bind error";
perror("bind error:");
return ;
}
其实就是添加一个setsockopt()函数,SO_REUSEADDR和iSockOptVal都是设置可重复使用的参数,其中iSockOptVal若为0则表示不能重复使用。
这个时候其实还是有TIME-WAIT,但是不影响我们重复使用同一个端口。
下面来了解一下TIME-WAIT:
这个有存在的必要吗,当然!存在就有它的合理性。
1.用来保证被动关闭的另一端也能正确关闭。
2.防止新启动的端口接收到旧的数据包。
存在的原因具体参考:TIME-WAIT原因
那么客户端和服务端谁先启动呢?
最好服务端,这样客户端发的每一条数据才能有反馈。
谁先关闭呢?
客户端关闭则服务端会收到服务端关闭的通知;但服务端关闭,则客户端需要发送一次请求才能得知服务端已经关闭。
因此先关闭客户端比较好,还有一个原因,这个服务端可能对应多个客户端,当然不能随便因为一个客户端不用了就关闭。