关于我们

质量为本、客户为根、勇于拼搏、务实创新

< 返回新闻公共列表

Linux 端口复用详解(附有案例代码)

发布时间:2023-06-26 23:00:20

1、端口复用用途


(1)防止服务器重启时之前绑定的端口还未释放;

(2)程序突然退出而系统没有释放端口;

说明:TCP 通信后,在四次挥手时,主动发送断开连接方必须处于TIME_WAIT一段时间,目的是确保另一方能够接收到主动发送放最后的ACK,如果没接收到,则会重新发送;主动关闭方重新发送的最终 ACK 并不是因为被动关闭方重传了 ACK(它们并不消耗序列号, 被动关闭方也不会重传),而是因为被动关闭方重传了它的 FIN。事实上,被动关闭方总是 重传 FIN 直到它收到一个最终的 ACK。


端口复用解释:主动断开方,在最后会有一个TIME_WAIT时间,在这个时间段内,主动断开方在等待,并没有及时释放,所以其他设备无法及时的与主动段开放进行再次连接;


2、端口复用 API


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

   

说明:如果想使用端口复用,设置的时机是在服务器绑定端口之前;即在绑定端口之前调用 setsockopt 函数;


3、端口复用案例


(1)客户端


#include#include#include#include#include int main() {   // 创建socket  int fd = socket(PF_INET, SOCK_STREAM, 0);  if(fd == -1) {  perror("socket");  return -1;  }   struct sockaddr_in seraddr;  inet_pton(AF_INET, "127.0.0.1", &seraddr.sin_addr.s_addr);  seraddr.sin_family = AF_INET;  seraddr.sin_port = htons(9999);   // 连接服务器  int ret = connect(fd, (struct sockaddr *)&seraddr, sizeof(seraddr));   if(ret == -1){  perror("connect");  return -1;  }   while(1) {  char sendBuf[1024] = {0};  fgets(sendBuf, sizeof(sendBuf), stdin);   write(fd, sendBuf, strlen(sendBuf) + 1);   // 接收  int len = read(fd, sendBuf, sizeof(sendBuf));  if(len == -1) {  perror("read");  return -1;  }else if(len > 0) {  printf("read buf = %s\n", sendBuf);  } else {  printf("服务器已经断开连接...\n");  break;  }  }   close(fd);   return 0; }

   


(2)服务端


#include#include#include#include#include#include int main(int argc, char *argv[]) {   // 创建socket  int lfd = socket(PF_INET, SOCK_STREAM, 0);   if(lfd == -1) {  perror("socket");  return -1;  }   struct sockaddr_in saddr;  saddr.sin_family = AF_INET;  saddr.sin_addr.s_addr = INADDR_ANY;  saddr.sin_port = htons(9999);   //端口复用api,该函数必须在端口连接之前调用才有效  int optval = 1;  setsockopt(lfd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));   int optval = 1;  setsockopt(lfd, SOL_SOCKET, SO_REUSEPORT, &optval, sizeof(optval));   // 绑定  int ret = bind(lfd, (struct sockaddr *)&saddr, sizeof(saddr));  if(ret == -1) {  perror("bind");  return -1;  }   // 监听  ret = listen(lfd, 8);  if(ret == -1) {  perror("listen");  return -1;  }   // 接收客户端连接  struct sockaddr_in cliaddr;  socklen_t len = sizeof(cliaddr);  int cfd = accept(lfd, (struct sockaddr *)&cliaddr, &len);  if(cfd == -1) {  perror("accpet");  return -1;  }   // 获取客户端信息  char cliIp[16];  inet_ntop(AF_INET, &cliaddr.sin_addr.s_addr, cliIp, sizeof(cliIp));  unsigned short cliPort = ntohs(cliaddr.sin_port);   // 输出客户端的信息  printf("client's ip is %s, and port is %d\n", cliIp, cliPort );   // 接收客户端发来的数据  char recvBuf[1024] = {0};  while(1) {  int len = recv(cfd, recvBuf, sizeof(recvBuf), 0);  if(len == -1) {  perror("recv");  return -1;  } else if(len == 0) {  printf("客户端已经断开连接...\n");  break;  } else if(len > 0) {  printf("read buf = %s\n", recvBuf);  }   // 小写转大写  for(int i = 0; i < len; ++i) {  recvBuf[i] = toupper(recvBuf[i]);  }   printf("after buf = %s\n", recvBuf);   // 大写字符串发给客户端  ret = send(cfd, recvBuf, strlen(recvBuf) + 1, 0);  if(ret == -1) {  perror("send");  return -1;  }  }   close(cfd);  close(lfd);   return 0; }

   


4、补充


查看网络相关信息命令:netstat

参数:
    -a 所有的 socket
    -p 显示正在使用 socket 的程序的名称
    -n 直接使用 IP 地址,而不通过域名服务器
    -apn 和 -anp 是一个意思

eg:netstat -anp | grep 9999         //9999是端口号


/template/Home/leiyu/PC/Static