使用setsockopt进行端口复用(客户端主动断开连接和服务器主动断开连接的情况不同)

1、出现 情形:

使用ctrl+c终止程序:

(1)终止client.c ----->终止server.c  ------>启动server.c  -----> 启动client.c 不会出现问题(客户端主动断开连接情况

(2)终止server.c ----->终止client.c  ------>启动server.c ----->启动client.c   会出现问题(服务器主动断开连接的情况

使用 netstat -apn|grep 端口号  查看端口号的占用情况

2、原因:

先终止server.c的时候,端口号没有被释放。当再启动server.c的时候,就会报错端口被占用,错误信息如下:

bind函数会报错,Address already in use

进一步分析,这是由于服务器端先关闭的话,服务器会处于2MSL时长的TIME_WAIT状态(程序终止了,但是TCP协议层的连接没有完全断开),端口仍然在占用,故会报该错误。

先关闭客户端程序则不会出现这种问题,则是因为,虽然客户端先关闭后,客户端也会处于2MSL时长的TIME_WAIT状态,客户端的端口也会被占用,但是,因为客户端程序中并没有调用bind函数,即客户端的绑定的端口号是系统随机分配的,当再次启动客户端时,客户端又会被重新分配新的端口号,不会出现端口被占用的情况。

当先终止server.c后,查看网络连接状态,发现,服务器端的状态是FIN_WAIT2.,客户端的状态是CLOSE_WAIT

等待一会后,发现服务器端消失了,只剩下客户端的状态了。 

这是因为,server.c终止,会发送FIN给client.c,client发送ACK给server.c后,自身变为CLOSE_WAIT,而服务器端变为FIN_WAIT2.  客户端此时并没有关闭,不会向server.c发送FIN,状态也不会改变。

我们在先关闭服务器程序后,想立即重启服务器程序,不希望等待2MSL时长,那如何解决这个问题呢?这就用到了端口复用

3、端口复用

使用setsockopt()函数对socket进行端口复用。

#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>

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

int opt=1;
setsockopt(listenfd,SOL_SOCKET, SO_REUSEADDR,
                      &opt, sizeof(opt);

将SO_REUSEADDR的值赋值为1,则表示生效,赋值为0,则无效。 

放在socket函数之后,bind函数之前。

此时终止server.c后,再重启server.c,网络状态仍然为TIME_WAIT,但是不会报错,程序能够正常运行。

  • 2
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
以下是一个简单的示例程序,展示了如何使用C语言编写一个基本的socket通信程序,实现多个客户端连接一个服务器。 Server端代码: ```c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/socket.h> #include <arpa/inet.h> #include <unistd.h> #define PORT 8080 int main(int argc, char const *argv[]) { int server_fd, new_socket, valread; struct sockaddr_in address; int opt = 1; int addrlen = sizeof(address); char buffer[1024] = { 0 }; char *hello = "Hello from server"; // 创建socket if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) { perror("socket failed"); exit(EXIT_FAILURE); } // 设置socket选项 if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt))) { perror("setsockopt"); exit(EXIT_FAILURE); } // 设置socket地址结构 address.sin_family = AF_INET; address.sin_addr.s_addr = INADDR_ANY; address.sin_port = htons(PORT); // 绑定socket if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) { perror("bind failed"); exit(EXIT_FAILURE); } // 监听socket if (listen(server_fd, 3) < 0) { perror("listen"); exit(EXIT_FAILURE); } // 接受客户端连接 while (1) { if ((new_socket = accept(server_fd, (struct sockaddr *)&address, (socklen_t *)&addrlen)) < 0) { perror("accept"); exit(EXIT_FAILURE); } // 读取客户端发送的消息 valread = read(new_socket, buffer, 1024); printf("%s\n", buffer); // 向客户端发送消息 send(new_socket, hello, strlen(hello), 0); printf("Hello message sent\n"); // 关闭连接 close(new_socket); } return 0; } ``` Client端代码: ```c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/socket.h> #include <arpa/inet.h> #include <unistd.h> #define PORT 8080 int main(int argc, char const *argv[]) { int sock = 0, valread; struct sockaddr_in serv_addr; char *hello = "Hello from client"; char buffer[1024] = { 0 }; // 创建socket if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) { printf("\n Socket creation error \n"); return -1; } // 设置socket地址结构 serv_addr.sin_family = AF_INET; serv_addr.sin_port = htons(PORT); // 将IPv4地址从点数转换为二进制数 if (inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr) <= 0) { printf("\nInvalid address/ Address not supported \n"); return -1; } // 连接服务器 if (connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) { printf("\nConnection Failed \n"); return -1; } // 向服务器发送消息 send(sock, hello, strlen(hello), 0); printf("Hello message sent\n"); // 读取服务器返回的消息 valread = read(sock, buffer, 1024); printf("%s\n", buffer); // 关闭socket连接 close(sock); return 0; } ``` 使用方法: 1. 在终端中分别编译Server和Client代码: ```bash $ gcc server.c -o server $ gcc client.c -o client ``` 2. 在一个终端窗口中启动Server: ```bash $ ./server ``` 3. 在另一个终端窗口中启动Client: ```bash $ ./client ``` 4. 查看Server端终端窗口,应该可以看到输出了客户端发送的消息,并且向客户端发送了回复。 注意事项: - 程序中使用了本地IP地址(127.0.0.1)作为服务器地址,如果需要在局域网内使用,需要将其修改为服务器的实际IP地址。 - 程序中使用了固定的端口号(8080),如果需要使用其他端口号,需要将其修改为实际需要使用端口号。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值