锐单电子商城 , 一站式电子元器件采购平台!
  • 电话:400-990-0325

tcp 服务端如何判断客户端断开连接

时间:2022-09-26 08:00:00 快速断开连接器160484

一篇文章:

最近在做服务器端程序,C/S结构。功能相对简单client端与server端建立连接,然后发送消息server。我在server端会用专门的线程处理一个socket连接。如果涉及到一个问题,socket断开连接(异常,正常)后怎么感知?server端这边绝对被动,sever端不能主动断开连接。没有连接链路维护包等。client终端发送数据的时间也不确定。在socket断开连接后, server能够感知和释放资源。

在思考测试和询问同事之后,这个问题找到了一种做到这一点的方法。

当使用 select()函数测试一个socket如果可读的话select()函数返回值为1,使用recv()函数读取的数据长度为0 时,说明这个socket已经断开。

为更好的判断socket我判断是否断开recv()返回值小于等于0时,socket断开连接。但也需要判断。 errno是否等于 EINTR 。如果errno == EINTR 则说明recv程序收到信号后返回函数,socket连接仍然正常,不应该close掉socket连接。

PS:对于堵塞socket的recv函数将在以下三种情况下返回:

(1)recv返回到数据时。

(2)在整个程序接收到信号时,返回-1。errno = EINTR。除了程序开始时屏蔽信号。有些信号仍然无法屏蔽。

(3)socket当出现问题时,返回-1。查看具体的错误代码 man recv()

(4)一定要看 man 说明,非常详细,非常有帮助。

经过长时间的测试,这种方法是有效的。所以写出来让大家参考一下,请发表意见。

tcp连接会自动断开吗?

已经建立了TCP连接,并可能交换信息。但如果信息长时间不传递。TCP连接会自动断开吗?

如果能自动断开,这个时间是多少?

回答:TCP保活定时器能保证TCP但是一直保持,但是TCP不是每个保存定时器TCP/IP由于协议栈实现了,因此实现了协议栈RFC并不要求TCP必须实现保存定时器。

摘自《TCP/IP详解卷1第23章:保存不是TCP规范的一部分。HostRequirementsRFC提供3个不使用保存固定

时器的理由:(1)在短期错误的情况下,这可能会释放出非常好的连接;

(2)他们消耗不必要的带宽;(3)在互联网上花更多的钱,按分组计费。

然而,许多实现提供了保存定时器。

请参考更具体的信息RFC。

tcp/ip详细更全面的描述:

23.1介绍

闲着(idle)TCP连接上没有数据流,很多TCP/IP初学者对此感到惊讶。也就是说,如果TCP连接两端没有向对方发送数据的过程,所以在这两个过程中TCP模块之间没有数据交换。您可能会在其他网络协议中发现轮询(polling),但在TCP它不存在。言下之意是,只要我们启动客户端流程,我们就建立了与服务器相同的服务器TCP无论你离开几个小时、几天、几周或几个月,连接仍然存在。中间的路由器可能会崩溃或重启,电话线可能会崩溃或重启go down或者back up,只要连接两端的主机没有重启,连接依旧保持建立。

可以认为客户端和服务器端的应用程序都没有应用程序级(application-level)定时器探测连接的非活动状态(inactivity),结束任何应用程序。回忆在10.7结束,BGP每30秒向对方发送一次应用程序探测。这是一个应用程序定时器(application timer),与TCP不同的存活定时器。

然而,有时服务器需要知道客户端主机是否已经崩溃和关闭,或崩溃但重启。许多实现提供了生存定时器来完成这项任务。

存活(keepalive)并不是TCP规范的一部分。在Host Requirements RFC不使用列表有三个原因:(1)在短期故障期间,它们可能会导致良好的连接(good connection)被释放(dropped),(2)他们消耗了不必要的宽带,(3)他们(额外)在互联网上花钱,用数据包收费。然而,在许多实现中提供了生存定时器。

生存定时器是一个有争议的特征。很多人认为,即使需要这个特征,对方的轮询也应该由应用程序而不是TCP实现。有些人对这个话题表现出极大的热情,甚至达到宗教狂热。

如果两个终端系统之间的中间网络有暂时中断,则生存选项(option)它可以导致两个过程之间良好连接的终止。例如,如果在中间路由器崩溃和重启时发送生存检测,TCP客户端主机已经崩溃,但事实并非如此。

一些服务器应用程序可能代表客户端占用资源,需要知道客户端主机是否崩溃。生存定时器可以为这些应用程序提供探测服务。Telnet服务器和Rlogin许多版本的服务器默认提供生存选项。

使用个人计算机用户TCP/IP协议通过Telnet登录主机是一个常见的例子,可以解释需要使用生存定时器。如果用户在使用结束时只关闭电源而不取消电源(log off),然后他留下一半打开(half-open)的连接。在图18.16.我们可以看到如何通过发送一个半打开连接的数据来获得复位(reset)返回,但在客户端,是客户端发送的数据。如果客户端消失,留下服务器端半打开的连接,服务器在等待客户端的数据,等待将永远持续下去。生存特征的目的是检测服务器端的半开连接。

我的方法不一样。我用它。getsockopt判断还是挺准确的。

intSocketConnected(intsock)

{

if(sock<=0)

return0;

structtcp_info info;

intlen=sizeof(info);

getsockopt(sock,IPPROTO_TCP,TCP_INFO,&info,(socklen_t*)&len);

if((info.tcpi_state==TCP_ESTABLISHED))

{

//myprintf("socket connected\n");

return1;

}

else

{

//myprintf("socket disconnected\n");

return0;

}

}

tcp_info和TCP_ESTABLISHED在linux/tcp.h

包含

#include

#include

#include

#include

#include

#include

int SocketConnected(int sock)

{

if(sock<=0)

return 0;

struct tcp_info info;

int len=sizeof(info);

getsockopt(sock, IPPROTO_TCP, TCP_INFO, &info, (socklen_t *)&len);

if((info.tcpi_state==TCP_ESTABLISHED))

{

//myprintf("socket connected\n");

return 1;

}

else

{

//myprintf("socket disconnected\n");

return 0;

}

}

实现用户脱线检测的主要方法有三种:SO_KEEPALIVE ,SIO_KEEPALIVE_VALS 和Heart-Beat线程。

下面我就介绍一下以上三种方法。

(1)SO_KEEPALIVE 机制

这是socket设置接口是库提供的功能setsockopt API:

BOOL bSet=TRUE;

setsockopt(hSocket,SOL_SOCKET,SO_KEEPALIVE,(const char*)&bSet,sizeof(BOOL));

根据MSDN如果是socket设置了KEEPALIVE选项,TCP/IP栈在检测到对方掉线后,

任何在该socket上进行的调用(发送/接受调用)就会立刻返回,错误号是WSAENETRESET ;

同时,以后该怎么办?socket句柄的调用将立即失败,并返回WSAENOTCONN错误。

该机制的缺点也很明显:

默认情况下,空闲2小时内发送保持生存探测分段,不能保证实时检测!

当然,时间间隔参数也可以修改,但会影响所有打开此选项的套接口!

完成端口的关联socket该套接字选项可能被忽略。

(2)SIO_KEEPALIVE_VALS 机制

设置接口是WSAIoctl API:

DWORD dwError = 0L ;

tcp_keepalive sKA_Settngs = {0}, sReturned = {0} ;

sKA_Settings.onoff = 1 ;

sKA_Settings.keepalivetime = 5500 ; // Keep Alive in 5.5 sec.

sKA_Settings.keepaliveinterval = 3000 ; // Resend if No-Reply

if (WSAIoctl(skNewConnection, SIO_KEEPALIVE_VALS, &sKA_Settings,

sizeof(sKA_Settings), &sReturned, sizeof(sReturned), &dwBytes,

NULL, NULL) != 0)

{

dwError = WSAGetLastError() ;

}

实现时需要添加tcp_keepalive and SIO_KEEPALIVE_VALS的定义文件MSTCPiP.h

该选项不同于SO_KEEPALIVE 机制的就是它是针对单个连接的,对系统其他的套接

口并不影响。

针对完成端口的socket,设置了SIO_KEEPALIVE_VALS后,激活包由TCP STACK来负责。

当网络连接断开后,TCP STACK并不主动告诉上层的应用程序,但是当下一次RECV或者SEND操作

进行后,马上就会返回错误告诉上层这个连接已经断开了.如果检测到断开的时候,在这个连接

上有正在PENDING的IO操作,则马上会失败返回.

该机制的缺点:

不通用啦。MS的API只能用于Windows拉。不过,优雅一些^_^.

(3)Heart-Beat线程

没说的。自己写一个后台线程,实现Heart-Beat包,客户端受到该包后,立刻返回相应的反馈 包。

该方法的好处是通用,但缺点就是会改变现有的通讯协议!

/*Net check Make sure you have not used OUT OF BAND DATA AND YOU CAN use OOB*/

int netcheck(intfd)

{int buf_size = 1024;charbuf[buf_size];//clear OOB DATA

recv(fd, buf, buf_size);if(send(fd, (void *)"\0", 1, MSG_OOB) < 0)

{

fprintf(stderr,"Connection[%d] send OOB failed, %s", fd, strerror(errno));return -1;

}return 0;

}

/*Setting SO_TCP KEEPALIVE*/

//int keep_alive = 1;//设定KeepAlive//int keep_idle = 1;//开始首次KeepAlive探测前的TCP空闭时间//int keep_interval = 1;//两次KeepAlive探测间的时间间隔//int keep_count = 3;//判定断开前的KeepAlive探测次数

void set_keepalive(int fd, int keep_alive, int keep_idle, int keep_interval, intkeep_count)

{int opt = 1;if(keep_alive)

{if(setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE,

(void*)&keep_alive, sizeof(keep_alive)) == -1)

{

fprintf(stderr,"setsockopt SOL_SOCKET::SO_KEEPALIVE failed, %s\n",strerror(errno));

}if(setsockopt(fd, SOL_TCP, TCP_KEEPIDLE,

(void *)&keep_idle,sizeof(keep_idle)) == -1)

{

fprintf(stderr,"setsockopt SOL_TCP::TCP_KEEPIDLE failed, %s\n", strerror(errno));

}if(setsockopt(fd,SOL_TCP,TCP_KEEPINTVL,

(void *)&keep_interval, sizeof(keep_interval)) == -1)

{

fprintf(stderr,"setsockopt SOL_tcp::TCP_KEEPINTVL failed, %s\n", strerror(errno));

}if(setsockopt(fd,SOL_TCP,TCP_KEEPCNT,

(void *)&keep_count,sizeof(keep_count)) == -1)

{

fprintf(stderr,"setsockopt SOL_TCP::TCP_KEEPCNT failed, %s\n", strerror(errno));

}

}

}

一篇文章:

keep alive VS heart beart:

这周在上班的路上看了本书《Effective TCP/IP Programming》,以下是一些读书笔记。顺带推荐一下这本书,写的很棒,适用于像我这样经常要写一些有一定质量的网络编程,但又没时间啃那些讲解TCPIP协议大部头书的人。

很多人都知道TCP并不会去主动检测连接的丢失,这意味着,如果双方不产生交互,那么如果网络断了或者有一方机器崩溃,另外一方将永远不知道连接已经不可用了。检测连接是否丢失的方法大致有两种:keepalive和heart-beat。

Keepalive是很多的TCP实现提供的一种机制,它允许连接在空闲的时候双方会发送一些特殊的数据段,并通过响应与否来判断连接是否还存活着(所谓keep~~alive)。我曾经写过一篇关于keepalive的blog ,但后来我也发现,其实keepalive在实际的应用中并不常见。为何如此?这得归结于keepalive设计的初衷。Keepalive适用于清除死亡时间比较长的连接。

比如这样的场景:一个用户创建tcp连接访问了一个web服务器,当用户完成他执行的操作后,很粗暴的直接拨了网线。这种情况下,这个tcp连接已经断开了,但是web服务器并不知道,它会依然守护着这个连接。如果web server设置了keepalive,那么它就能够在用户断开网线的大概几个小时以后,确认这个连接已经中断,然后丢弃此连接,回收资源。

采用keepalive,它会先要求此连接一定时间没有活动(一般是几个小时),然后发出数据段,经过多次尝试后(每次尝试之间也有时间间隔),如果仍没有响应,则判断连接中断。可想而知,整个周期需要很长的时间。

所以,如前面的场景那样,需要一种方法能够清除和回收那些在系统不知情的情况下死去了很久的连接,keepalive是非常好的选择。

但是,在大部分情况下,特别是分布式环境中,我们需要的是一个能够快速或者实时监控连接状态的机制,这里,heart-beat才是更加合适的方案。

Heart-beat(心跳),按我的理解,它的原理和keepalive非常类似,都是发送一个信号给对方,如果多次发送都没有响应的话,则判断连接中断。它们的不同点在于,keepalive是tcp实现中内建的机制,是在创建tcp连接时通过设置参数启动keepalive机制;而heart-beat则需要在tcp之上的应用层实现。一个简单的heart-beat实现一般测试连接是否中断采用的时间间隔都比较短,可以很快的决定连接是否中断。并且,由于是在应用层实现,因为可以自行决定当判断连接中断后应该采取的行为,而keepalive在判断连接失败后只会将连接丢弃。

关于heart-beat,一个非常有趣的问题是,应该在传输真正数据的连接中发送“心跳”信号,还是可以专门创建一个发送“心跳”信号的连接。比如说,A,B两台机器之间通过连接m来传输数据,现在为了能够检测A,B之间的连接状态,我们是应该在连接m中传输“心跳”信号,还是创建新的连接n来专门传输“心跳”呢?我个人认为两者皆可。如果担心的是端到端的连接状态,那么就直接在该条连接中实现“心跳”。但很多时候,关注的是网络状况和两台主机间的连接状态,这种情况下, 创建专门的“心跳”连接也未尝不可。

锐单商城拥有海量元器件数据手册IC替代型号,打造电子元器件IC百科大全!

相关文章