TIME_WAIT状态总结

2022/3/21 0:01:53

本文主要是介绍TIME_WAIT状态总结,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

这个知识点在写代码上,感觉还是挺重要的,打算总结一下吧。
打算从以下5个方面去总结。
一:TIME_WAIT状态是什么
首先,这个是TCP状态转换图里面的某个状态。这个可以参考unix网络编程。
在这里插入图片描述
从图中可以看出,在一个客户端与服务器通信的过程当中,主动关闭的一方会进入这个状态。
二:TIME_WAIT状态怎么出现的
好了,其实第一点已经说明了,这个状态是怎么出现的了,就是,主动关闭的一方会进入这个状态。

#include <stdio.h>
#include <ctype.h>
#include <unistd.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

#define SERV_PORT 9000  //本服务器要监听的端口号,一般1024以下的端口很多都是属于周知端口,所以我们一般采用1024之后的数字做端口号

int main(int argc, char *const *argv)
{    
    //这些演示代码的写法都是固定套路,一般都这么写

    //服务器的socket套接字【文件描述符】
    int listenfd = socket(AF_INET, SOCK_STREAM, 0);    //创建服务器的socket,大家可以暂时不用管这里的参数是什么,知道这个函数大概做什么就行

    struct sockaddr_in serv_addr;                  //服务器的地址结构体
    memset(&serv_addr,0,sizeof(serv_addr));
    
    //设置本服务器要监听的地址和端口,这样客户端才能连接到该地址和端口并发送数据
    serv_addr.sin_family = AF_INET;                //选择协议族为IPV4
    serv_addr.sin_port = htons(SERV_PORT);         //绑定我们自定义的端口号,客户端程序和我们服务器程序通讯时,就要往这个端口连接和传送数据
    serv_addr.sin_addr.s_addr = htonl(INADDR_ANY); //监听本地所有的IP地址;INADDR_ANY表示的是一个服务器上所有的网卡(服务器可能不止一个网卡)多个本地ip地址都进行绑定端口号,进行侦听。

    int result;
    result = bind(listenfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr));//绑定服务器地址结构体
    if(result  == -1)
    {
        char *perrorinfo = strerror(errno); 
        printf("bind返回的值为%d,错误码为:%d,错误信息为:%s;\n",result,errno,perrorinfo);
        return -1;
    }
    result = listen(listenfd, 32);     //参数2表示服务器可以积压的未处理完的连入请求总个数,客户端来一个未连入的请求,请求数+1,连入请求完成,c/s之间进入正常通讯后,请求数-1
    if(result == -1)
    {        
        char *perrorinfo = strerror(errno); 
        printf("listen返回的值为%d,错误码为:%d,错误信息为:%s;\n",result,errno,perrorinfo);
        return -1;
    }

    int connfd;
    const char *pcontent = "I sent sth to client!\n"; //指向常量字符串区的指针
    for(;;)
    {
        //卡在这里,等客户单连接,客户端连入后,该函数走下去【注意这里返回的是一个新的socket——connfd,后续本服务器就用connfd和客户端之间收发数据,而原有的lisenfd依旧用于继续监听其他连接】        
        connfd = accept(listenfd, (struct sockaddr*)NULL, NULL);

        //发送数据包给客户端
        write(connfd,pcontent,strlen(pcontent)); //注意第一个参数是accept返回的connfd套接字
        printf("本服务器给客户端发送了一串字符~~~~~~~~~~~!\n");
        
        //只给客户端发送一个信息,然后直接关闭套接字连接;
        close(connfd); 
    } //end for
    close(listenfd);     //实际本简单范例走不到这里,这句暂时看起来没啥用
    return 0;
}

使用以上程序作为服务端,然后用telnet作为客户端,发起连接,这个时候,根据程序说明,会马上关闭这个连接,然后,我们用netstat命令观察可以看到除了有一个端口处于监听状态意外,还有的就是出现了TIME_WAIT这个状态了。
三:TIME_WAIT状态出现了有什么影响
感觉第二点出现的情况,如果我们马上关闭这个服务端程序,然后又重新启动,这个时候你会发现这个程序启动失败失败的原因是bind error了,提示地址已复用,也就是同一个地址和端口只能同时绑定一次。我们试想,如果在服务端处理大量连接的时候,某个时候,服务端挂掉了,这个时候由于TIME_WAIT状态的存在,导致程序运行不起来,那不坏菜了,服务端还怎么提供服务??
四:为什么要有TIME_WAIT状态
我们再来谈谈为什么需要这个状态?什么,居然要有这个状态??第三点不是说有这个状态会有不好的情况吗?当然,先别急,既然这个状态的出现就有他出现的道理,对于第三点出现时会有一些影响,当然,我们也是可以避免的,我们在第五点去谈这个问题。

为什么要有这个状态,参考了unix网络编程当中谈到的两点。
一::可靠地实现TCP全双工的终止。

在这里插入代码片

二:允许老的重复的TCP数据包在网络中消逝(丢弃)。

在这里插入代码片

五:如何避免TIME_WAIT状态
使用套接字选项可以避免这个状态的产生,从而可以让服务端程序在出现异常时可以快速的重启,恢复运行。
setsockopt,这个函数一般位于socket与bind之间,如果想探讨为什么一般位于这两个函数之间的,可以网上搜索。

#include <stdio.h>
#include <ctype.h>
#include <unistd.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

#define SERV_PORT 9000  //本服务器要监听的端口号,一般1024以下的端口很多都是属于周知端口,所以我们一般采用1024之后的数字做端口号

int main(int argc, char *const *argv)
{    
    //这些演示代码的写法都是固定套路,一般都这么写

    //服务器的socket套接字【文件描述符】
    int listenfd = socket(AF_INET, SOCK_STREAM, 0);    //创建服务器的socket,大家可以暂时不用管这里的参数是什么,知道这个函数大概做什么就行

    struct sockaddr_in serv_addr;                  //服务器的地址结构体
    memset(&serv_addr,0,sizeof(serv_addr));
    
    //设置本服务器要监听的地址和端口,这样客户端才能连接到该地址和端口并发送数据
    serv_addr.sin_family = AF_INET;                //选择协议族为IPV4
    serv_addr.sin_port = htons(SERV_PORT);         //绑定我们自定义的端口号,客户端程序和我们服务器程序通讯时,就要往这个端口连接和传送数据
    serv_addr.sin_addr.s_addr = htonl(INADDR_ANY); //监听本地所有的IP地址;INADDR_ANY表示的是一个服务器上所有的网卡(服务器可能不止一个网卡)多个本地ip地址都进行绑定端口号,进行侦听。


    //setsockopt():设置一些套接字参数选项;
    //参数2:是表示级别,和参数3配套使用,也就是说,参数3如果确定了,参数2就确定了;
    //参数3:允许重用本地地址
    int  reuseaddr=1; //开启
    if(setsockopt(listenfd,SOL_SOCKET,SO_REUSEADDR, (const void *) &reuseaddr,sizeof(reuseaddr)) == -1)
    {
         char *perrorinfo = strerror(errno); 
        printf("setsockopt(SO_REUSEADDR)返回值为%d,错误码为:%d,错误信息为:%s;\n",-1,errno,perrorinfo);
    }

    int result;
    result = bind(listenfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr));//绑定服务器地址结构体
    if(result  == -1)
    {
        char *perrorinfo = strerror(errno); 
        printf("bind返回的值为%d,错误码为:%d,错误信息为:%s;\n",result,errno,perrorinfo);
        return -1;
    }
    result = listen(listenfd, 32);     //参数2表示服务器可以积压的未处理完的连入请求总个数,客户端来一个未连入的请求,请求数+1,连入请求完成,c/s之间进入正常通讯后,请求数-1
    if(result == -1)
    {        
        char *perrorinfo = strerror(errno); 
        printf("listen返回的值为%d,错误码为:%d,错误信息为:%s;\n",result,errno,perrorinfo);
        return -1;
    }
    int connfd;
    const char *pcontent = "I sent sth to client!\n"; //指向常量字符串区的指针
    for(;;)
    {
        //卡在这里,等客户单连接,客户端连入后,该函数走下去【注意这里返回的是一个新的socket——connfd,后续本服务器就用connfd和客户端之间收发数据,而原有的lisenfd依旧用于继续监听其他连接】        
        connfd = accept(listenfd, (struct sockaddr*)NULL, NULL);

        //发送数据包给客户端
        write(connfd,pcontent,strlen(pcontent)); //注意第一个参数是accept返回的connfd套接字
        printf("本服务器给客户端发送了一串字符~~~~~~~~~~~!\n");
        
        //只给客户端发送一个信息,然后直接关闭套接字连接;
        close(connfd); 
    } //end for
    close(listenfd);     //实际本简单范例走不到这里,这句暂时看起来没啥用
    return 0;
}

可以使用以上程序配合telnet进行测试。



这篇关于TIME_WAIT状态总结的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程