如果两端的 TCP 连接一直没有数据交互,达到了触发 TCP 保活机制的条件,那么内核里的 TCP 协议栈就会发送探测报文。
如果对端程序是正常工作的。当 TCP 保活的探测报文发送给对端, 对端会正常响应,这样 TCP 保活时间会被重置,等待下一个 TCP 保活时间的到来。
如果对端主机崩溃,或对端由于其他原因导致报文不可达。当 TCP 保活的探测报文发送给对端后,石沉大海,没有响应,连续几次,达到保活探测次数后,TCP 会报告该 TCP 连接已经死亡。
所以,TCP 保活机制可以在双方没有数据交互的情况,通过探测报文,来确定对方的 TCP 连接是否存活。
TCP保活相关参数如下:
SO_KEEPALIVE:是否开启保活
TCP_KEEPIDLE:Start keeplives after this period
TCP_KEEPINTVL:Interval between keepalives
TCP_KEEPCNT:Number of keepalives before death
开启一个简单的tcp服务器,代码如下
/*server.c*/
#include<stdio.h>
#include<stdlib.h>
#include<errno.h>
#include<string.h>
#include<sys/types.h>
#include<netinet/in.h>
#include<sys/socket.h>
#include<sys/wait.h>
#include <netinet/tcp.h>
#define PORT 4000//端口号
#define BACKLOG 5/*最大监听数*/
#define MAX_DATA 100//接收到的数据最大程度
int main(){
int sockfd,new_fd;/*socket句柄和建立连接后的句柄*/
struct sockaddr_in my_addr;/*本方地址信息结构体,下面有具体的属性赋值*/
struct sockaddr_in their_addr;/*对方地址信息*/
int sin_size;
char buf[MAX_DATA];//储存接收数据
sockfd=socket(AF_INET,SOCK_STREAM,0);//建立socket
if(sockfd==-1){
printf("socket failed:%d",errno);
return -1;
}
my_addr.sin_family=AF_INET;/*该属性表示接收本机或其他机器传输*/
my_addr.sin_port=htons(PORT);/*端口号*/
my_addr.sin_addr.s_addr=htonl(INADDR_ANY);/*IP,括号内容表示本机IP*/
bzero(&(my_addr.sin_zero),8);/*将其他属性置0*/
if(bind(sockfd,(struct sockaddr*)&my_addr,sizeof(struct sockaddr))<0){//绑定地址结构体和socket
printf("bind error");
return -1;
}
listen(sockfd,BACKLOG);//开启监听 ,第二个参数是最大监听数
while(1){
sin_size=sizeof(struct sockaddr_in);
new_fd=accept(sockfd,(struct sockaddr*)&their_addr,&sin_size);//在这里阻塞知道接收到消息,参数分别是socket句柄,接收到的地址信息以及大小
// 开启保活,1分钟内探测不到,断开连接
int keep_alive = 1;
int keep_idle = 3;
int keep_interval = 1;
int keep_count = 57;
if (setsockopt(new_fd, SOL_SOCKET, SO_KEEPALIVE, &keep_alive, sizeof(keep_alive))) {
perror("Error setsockopt(SO_KEEPALIVE) failed");
exit(1);
}
if (setsockopt(new_fd, IPPROTO_TCP, TCP_KEEPIDLE, &keep_idle, sizeof(keep_idle))) {
perror("Error setsockopt(TCP_KEEPIDLE) failed");
exit(1);
}
if (setsockopt(new_fd, SOL_TCP, TCP_KEEPINTVL, (void *)&keep_interval, sizeof(keep_interval))) {
perror("Error setsockopt(TCP_KEEPINTVL) failed");
exit(1);
}
if (setsockopt(new_fd, SOL_TCP, TCP_KEEPCNT, (void *)&keep_count, sizeof(keep_count))) {
perror("Error setsockopt(TCP_KEEPCNT) failed");
exit(1);
}
while(new_fd != -1) {
recv(new_fd,buf,MAX_DATA,0);//将接收数据打入buf,参数分别是句柄,储存处,最大长度,其他信息(设为0即可)。
printf("%s",buf);
}
}
return 0;
}
上述保活参数(int keep_alive = 1;int keep_idle = 3;int keep_interval = 1;int keep_count = 57;)表示3秒内无交互后,每隔1秒检测一次,57次都没得到响应时会断开连接。
编译可执行文件:gcc server.c -o server
服务端开启服务:./server
客户端连接服务:telnet 192.168.137.101 4000
这里服务端地址为192.168.137.101

上图可以看到每隔1秒有保活报文,且客户端正常响应。
接下来我们将192.168.137.102的网卡down掉
可以看到客户端已经无响应了,经过1分种左右就可看见服务端的连接断掉了

几个思考点:
是否开启keepalive?
是否存在数据交互?
进程崩溃;
客户端主机宕机(客户端网络不可用 是同种情况);
keepalive开启,服务器会探测客户端,不会出现问题。
keepalive未开启,存在以下情况
使用 kill -9 来模拟进程崩溃的情况,发现在 kill 掉进程后,服务端会发送 FIN 报文,与客户端进行四次挥手。
所以,即使没有开启 TCP keepalive,且双方也没有数据交互的情况下,如果其中一方的进程发生了崩溃,这个过程操作系统是可以感知的到的,于是就会发送 FIN 报文给对方,然后与对方进行 TCP 四次挥手。
第一种,客户端主机宕机,又迅速重启,会发生什么?
第二种,客户端主机宕机,一直没有重启,会发生什么?
客户端主机宕机,又迅速重启
在客户端主机宕机后,服务端向客户端发送的报文会得不到任何的响应,在一定时长后,服务端就会触发超时重传机制,重传未得到响应的报文。
服务端重传报文的过程中,刚好客户端主机重启完成,这时客户端的内核就会接收重传的报文,:
如果客户端主机上没有进程监听该 TCP 报文的目标端口号,由于找不到目标端口,客户端内核就会回复 RST 报文,重置该 TCP 连接;
如果客户端主机上有进程监听该 TCP 报文的目标端口号,由于客户端主机重启后,之前的 TCP 连接的数据结构已经丢失了,客户端内核里协议栈会发现找不到该 TCP 连接的 socket 结构体,于是就会回复 RST 报文,重置该 TCP 连接。
所以,只要有一方重启完成后,收到之前 TCP 连接的报文,都会回复 RST 报文,以断开连接。
客户端主机宕机,一直没有重启
这种情况,服务端超时重传报文的次数达到一定阈值后,内核就会判定出该 TCP 有问题,然后通过 Socket 接口告诉应用程序该 TCP 连接出问题了。
那具体重传几次呢?
在 Linux 系统中,提供了一个叫 tcp_retries2 配置项,默认值是 15,如下:
[root@node01 ~]# cat /proc/sys/net/ipv4/tcp_retries2 15
这个内核参数是控制,在 TCP 连接建立的情况下,超时重传的最大次数。
不过 tcp_retries2 设置了 15 次,并不代表 TCP 超时重传了 15 次才会通知应用程序终止该 TCP 连接,内核还会基于「最大超时时间」来判定。
每一轮的超时时间都是倍数增长的,比如第一次触发超时重传是在 2s 后,第二次则是在 4s 后,第三次则是 8s 后,以此类推。
内核会根据 tcp_retries2 设置的值,计算出一个最大超时时间。
在重传报文且一直没有收到对方响应的情况时,先达到「最大重传次数」或者「最大超时时间」这两个的其中一个条件后,就会停止重传。
如果客户端主机崩溃了,服务端是无法感知到的,在加上服务端没有开启 TCP keepalive,又没有数据交互的情况下,服务端的 TCP 连接将会一直处于 ESTABLISHED 连接状态,直到服务端重启进程。
在没有使用 TCP 保活机制,且双方不传输数据的情况下,一方的 TCP 连接处在 ESTABLISHED 状态时,并不代表另一方的 TCP 连接还一定是正常的。
TCP是面向连接的协议,连接的建立和释放是每一次面向连接的通信中必不可少的过程。TCP连接的管理就是使连接的建立和释放都能正常地进行。三次握手TCP连接的建立—三次握手建立TCP连接①若主机A中运行了一个客户进程,当它需要主机B的服务时,就发起TCP连接请求,并在所发送的分段中用SYN=1表示连接请求,并产生一个随机发送序号x,如果连接成功,A将以x作为其发送序号的初始值:seq=x。主机B收到A的连接请求报文,就完成了第一次握手。客户端发送SYN=1表示连接请求客户端发送一个随机发送序号x,如果连接成功,A将以x作为其发送序号的初始值:seq=x②主机B如果同意建立连接,则向主机A发送确认报
我正在使用Ruby-Tk为OSX开发一个桌面应用程序,我想为该应用程序提供一个AppleEvents接口(interface)。这意味着应用程序将定义它将响应的AppleScript命令的字典(对应于发送到应用程序的Apple事件),并且用户/其他应用程序可以使用AppleScript命令编写Ruby-Tk应用程序的脚本。其他脚本语言支持此类功能——Python通过位于http://appscript.svn.sourceforge.net/viewvc/appscript/py-aemreceive/的py-aemreceive库和Tcl通过位于http://tclae.source
Method#unbind返回对该方法的UnboundMethod引用,稍后可以使用UnboundMethod#bind将其绑定(bind)到另一个对象.classFooattr_reader:bazdefinitialize(baz)@baz=bazendendclassBardefinitialize(baz)@baz=bazendendf=Foo.new(:test1)g=Foo.new(:test2)h=Bar.new(:test3)f.method(:baz).unbind.bind(g).call#=>:test2f.method(:baz).unbind.bind(h).
我在这里尝试使用yarn,遇到了一个可能与ruby相关的问题。在执行任何yarn命令,我收到错误.../.rvm/gems/ruby-2.3.0/gems/yarn-0.1.1/lib/yarn/server.rb:14:in':uninitializedconstantSocket::SOL_TCP(NameError)错误堆栈:$yarn.../.rvm/gems/ruby-2.3.0/gems/yarn-0.1.1/lib/yarn/server.rb:14:in':uninitializedconstantSocket::SOL_TCP(NameError)Didyoume
我正在尝试创建一个使用一次的HTTP服务器来处理单个回调,并且需要帮助在Ruby中找到一个空闲的TCP端口。这是我正在做的事情的框架:require'socket't=STDIN.readport=8081whiles=TCPServer.new('127.0.0.1',port).acceptputss.getss.print"HTTP/1.1200/OK\rContent-type:text/plain\r\n\r\n"+ts.closeexitend(它回显标准输入到第一个连接然后死掉。)如何自动找到空闲端口进行监听?这似乎是在远程服务器上启Action业然后使用唯一作业ID回调
显示等待需要用到两个类:WebDriverWait和expected_conditions两个类WebDriverWait:指定轮询间隔、超时时间等expected_conditions:指定了很多条件函数(也可以自定义条件函数)具体可以参考官网:selenium.webdriver.support.expected_conditions—Selenium4.5documentationfromseleniumimportwebdriverfromselenium.webdriver.common.byimportByfromselenium.webdriver.support.uiimpor
首先请注意,我在StackOverflow和网络上的文章中发现了几个类似的问题,但没有一个能帮助我解决我的问题:PGErrorcouldnotconnecttoserver:ConnectionrefusedIstheserverrunningonport5432?PG::ConnectionBad-couldnotconnecttoserver:Connectionrefusedpsql:couldnotconnecttoserver:Connectionrefused问题来了:我有一个非常棒的Rails应用程序。我和我的合作者使用GitHub一起工作。我们有一个master和一个m
承接上篇文章(十分钟了解关于TCP/IP网络的基础知识)五.ARP(地址解析协议) 虽说使用IP地址确实方便了我们使用者记忆以及整理归类、寻找信息的发送目的地,但是最终接收数据的地方,还是MAC地址,于是乎,为了实现有IP地址到MAC地址的转换,引入了名为ARP(AddressResolutionProtocol)又称之为地址解析协议。 ARP通过广播(Broadcast,这是个专业名词,后面还会继续提起)的方式对LAN中所有的计算机提问:“哎,谁IP地址是10.165.7.116(上篇文章中的例子)呀?你MAC地址多少啊,快过来登记一下!”,如果有哪台计算机回复了MA
几周前Jekyll对我来说工作正常,但现在突然出现以下错误:TCPServerError:Addressalreadyinuse-bind(2)INFOWEBrick::HTTPServer#start:pid=7300port=4000%lsof-i:4000即使端口上没有任何运行。以下是详细信息:%jekyll--versionJekyll0.11.2%wherejekyll/home/bhaarat/.rvm/gems/ruby-1.9.2-p290/bin/jekyll/usr/bin/jekyll%ruby--versionruby1.9.2p290(2011-07-09re
我收到错误AWS::S3::Errors::InvalidRequest不支持您提供的授权机制。请使用AWS4-HMAC-SHA256.当我尝试将文件上传到新法兰克福地区的S3存储桶时。所有适用于USStandard区域。脚本:backup_file='/media/db-backup_for_dev/2014-10-23_02-00-07/slave_dump.sql.gz's3=AWS::S3.new(access_key_id:AMAZONS3['access_key_id'],secret_access_key:AMAZONS3['secret_access_key'])s3_