您的位置:

TCP长连接详解

一、什么是TCP长连接

TCP连接在网络通信中绝对是一个基础、关键而又常见的概念。而长连接又是TCP连接的一种特殊形态。所谓TCP长连接,指的是建立起来的TCP连接不会轻易的中断或关闭,而是一直保持连接状态,直至完成或出现意外断连。

与之对应的是短连接,即数据传输一次完成后,连接就会自动关闭。相比之下,长连接有很多优点,因此在实际开发中非常常用。我们可以通过几种方式来实现TCP长连接:保持TCP连接处于连接状态、服务器空闲状态发送心跳包来实现保活机制、应用层心跳等,后面我们会详细介绍这些内容。

二、TCP长连接的优点和缺点

长连接的优点主要有以下几点:

1、减少了TCP的握手次数,减少了资源的占用。

2、避免了服务端资源的频繁分配,提高了性能。

3、客户端可以更快速的获取到服务器上的数据,同时也提高了用户的体验。

然而,长连接也存在缺点:

1、可能存在部分数据丢失。长连接在通信过程中,因为网络的原因,一些数据可能会丢失,但不会造成太大的影响。

2、连接长久不关闭会对服务器端的性能造成威胁。因为长连接需要服务器端占用一定的资源保持状态,如果长时间不关闭就会对服务器造成一定的负担。

三、保持TCP连接处于连接状态

我们可以通过设置TCP的keep-alive属性让TCP连接一直处于连接状态。keep-alive会定时地给服务器发一个TCP包以判断连接是否终止,从而保持连接不断开。

// C语言实现,设置TCP_KEEPALIVE属性
int keepalive = 1;      //开启KEEPALIVE属性
int keepidle = 60;      //如果60秒内没有任何数据交互,TCP会发送检验包(称为保持存活探测分节)。
int keepinterval = 5;   //检验包的发送间隔默认为75秒收不到保持存活探测分节后,才会发送第一个探测分节
int keepcount = 3;      //如果第1次检测包发送失败,则每隔5秒发送第二个检测包,最多发送3个检测包。如果3次都没成功,就当连接失效断掉。

setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (void *)&keepalive, sizeof(keepalive));  //开启keepalive功能
setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, (void*)&keepidle, sizeof(keepidle));     //设置当有数据报要发送时Kernel会等待多长时间,才会发这个数据报。单位为秒。
setsockopt(fd, IPPROTO_TCP, TCP_KEEPINTVL, (void *)&keepinterval, sizeof(keepinterval));  //设置每次发送数据报的时间间隔,默认为75秒。
setsockopt(fd, IPPROTO_TCP, TCP_KEEPCNT, (void *)&keepcount, sizeof(keepcount));   //设置发送保持存活探测分节的次数。默认是9次。

四、服务器空闲状态发送心跳包实现保活机制

除了设置TCP的keep-alive属性之外,还可以采用心跳机制来保持TCP长连接。服务器不断地向客户端发送心跳包来判断连接的状态,从而实现保活机制。实现起来比设置TCP的keep-alive属性要容易一些。

//C语言实现
int SendData(int fd, char *buffer, int len, int flags)
{
    int nsend = 0;
    nsend = send(fd, buffer, len, flags);
    if (nsend == -1) 
    { 
        //send 发送失败
        if(errno == EINTR) /*信号中断*/
        {
            //稍后重发
        } 
        else if(errno == EAGAIN || errno == EWOULDBLOCK) /*一般错误类型*/
        {
            //稍后重发
        }
        else /*其他错误类型,关闭fd*/
        {
            //CLOSED端口fd
        }
    }
    return nsend;
}

int recv_with_timeout(int fd, char *buf, int data_size, int timeout)
{
    fd_set fds;
    struct timeval tv;
    int ret = 0;

    FD_ZERO(&fds);
    FD_SET(fd, &fds);

    tv.tv_sec = timeout;
    tv.tv_usec = 0;

    ret = select(fd+1, &fds, NULL, NULL, &tv);
    switch(ret)
    {
        case -1: /* error occurred */
            perror("select error:");
            return -1;

        case 0: /* timed out */

            return -2;

        default: /* data ready */
            if (FD_ISSET(fd, &fds))
                return recv(fd, buf, data_size, 0);
    }

    return -3;
}

char buffer[1024];
while(true)
{
    SendData(fd, "HeartBeat", sizeof("HeartBeat"), 0); //服务器定时发送心跳包
    int res = recv_with_timeout(fd, buffer, 4, 5); //设置5秒的等待时间
    if(res == -2) 
    {
        //客户端长时间未响应,服务端可以关闭连接
    }
}

五、应用层心跳

应用层心跳,就是通过应用程序在长连接上发送一定类型的消息,以达到保持连接状态的目的。在实际中,应用层心跳的灵活性和可控性都较高,但也需要我们自己来设计和实现心跳包的规则。

//C语言实现
while(true)
{
    SendData(fd, "HeartBeat", sizeof("HeartBeat"), 0); //发送应用层心跳包
    sleep(5);  //定时发送心跳
}

六、结束语

以上就是关于TCP长连接的详细讲解,包括了长连接的定义、优缺点、保持TCP连接处于连接状态、服务器空闲状态发送心跳包实现保活机制、以及应用层心跳等多个方面。我们可以结合实际项目和需求来选择不同的实现方式,以达到最佳的效果。