技术标签: tcp服务器accept的作用
1. 设置TCP_DEFER_ACCEPT
int val = 10; // time_out
if (setsockopt(sock_descriptor, IPPROTO_TCP, TCP_DEFER_ACCEPT, &val, sizeof(val))== -1)
{perror("setsockopt");
exit(1);}
2. TCP_DEFER_ACCEPT的效果 正常的tcp三次握手过程:
第一次握手:建立连接时,客户端发送syn包(syn=j)到服务器,并进入SYN_SEND状态,等待服务器确认;
第二次握手:服务器收到syn包,必须确认客户的SYN(ack=j+1),同时自己也发送一个SYN包(syn=k),即SYN+ACK包,此时服务器进入SYN_RECV状态;
第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1),此包发送完毕,客户端和服务器进入ESTABLISHED状态,完成三次握手
设置TCP_DEFER_ACCEPT后
第一次握手:建立连接时,客户端发送syn包(syn=j)到服务器,并进入SYN_SEND状态,等待服务器确认;
第二次握手:服务器收到syn包,必须确认客户的SYN(ack=j+1),同时自己也发送一个SYN包(syn=k),即SYN+ACK包,此时服务器进入SYN_RECV状态;
第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1),此包发送完毕,连接并不进入ESTABLISHED状态,而是在第一个真正有数据的包到达后才进入ESTABLISHED,完成连接的建立。TCP_DEFER_ACCEPT的超时在第三次握手时候,如果客户段迟迟不发送数据,服务器 连接将一直处于syn_recv状态。此时内核会重传 syn_ack ,重传的次数可以通过 sysctl -w net.ipv4.tcp_synack_retries=3来设置,如果3次重传后,客户端依然没有数据,在等待 设置TCP_DEFER_ACCEPT时候指定的超时时间后(这个时间单位为s,可是测试看来并不精准的执行),系统将回收连接,并不对客户端发出rst或者fin包。
内核中源码如下:
第三次握手会调用函数tcp_v4_hnd_req:
static struct sock *tcp_v4_hnd_req(struct sock*sk, struct sk_buff*skb)
{
......
struct request_sock *req = inet_csk_search_req(sk,&prev, th->source,
iph->saddr, iph->daddr);//查找半连接队列,返回req
if (req)
return tcp_check_req(sk, skb, req, prev);//ack的处理
......
}
我们看函数tcp_check_req
struct sock *tcp_check_req(struct sock*sk,struct sk_buff*skb,
struct request_sock *req,
struct request_sock **prev)
{
......
/*If TCP_DEFER_ACCEPT is set, drop bare ACK.*/
if (inet_csk(sk)->icsk_accept_queue.rskq_defer_accept&&
TCP_SKB_CB(skb)->end_seq== tcp_rsk(req)->rcv_isn+ 1) {//如果选项设置了,并且是裸
ack,丢弃该ack;选项值得默
认为1
inet_rsk(req)->acked= 1;
return NULL;
}
child = inet_csk(sk)->icsk_af_ops->syn_recv_sock(sk, skb,
req, NULL);//如果非裸ack或没设置选项则建立连接(req从半连接
队列到连接队列及tcp状态变为ESTABLISHED)
......
}
我们在用户层写socket程序时,可以通过setsockopt来设置TCP_DEFER_ACCEPT选项:
val = 5;
setsockopt(srv_socket->fd, SOL_TCP, TCP_DEFER_ACCEPT,&val, sizeof(val));
里面 val 的单位是秒,注意如果打开这个功能,kernel 在 val 秒之内还没有收到数据,不会继续唤醒进程,而是直接丢弃连接。
在内核空间会调用:
static int do_tcp_setsockopt(struct sock*sk, int level,
int optname, char __user*optval, int optlen)
{
struct tcp_sock *tp = tcp_sk(sk);
struct inet_connection_sock *icsk = inet_csk(sk);
int val;
...... if (get_user(val, (int __user *)optval))//拷贝用户空间数据
return -EFAULT;
......
case TCP_DEFER_ACCEPT:
icsk->icsk_accept_queue.rskq_defer_accept= 0;
if (val> 0) {//如果setsockopt中设置val为0,则不开始TCP_DEFER_ACCEPT选项
/* Translate valuein seconds to number of
* retransmits */
while (icsk->icsk_accept_queue.rskq_defer_accept
val > ((TCP_TIMEOUT_INIT / HZ) <<
icsk->icsk_accept_queue.rskq_defer_accept))//根据设置的val决定重传次数,譬
如val=10,重传次数为3;后面我们可以看到,只有
/proc/sys/net/ipv4/tcp_synack_retries的
值小于等于通过val算出的重传次数时,这个val才
起作用
icsk->icsk_accept_queue.rskq_defer_accept++;
icsk->icsk_accept_queue.rskq_defer_accept++;
}
break;
......
}
内核是通过函数inet_csk_reqsk_queue_prune进行重传synack:
void inet_csk_reqsk_queue_prune(struct sock*parent,
const unsigned long interval,
const unsigned long timeout,
const unsigned long max_rto)
{
struct inet_connection_sock *icsk = inet_csk(parent);
struct request_sock_queue *queue = &icsk->icsk_accept_queue;
struct listen_sock *lopt = queue->listen_opt;
int max_retries = icsk->icsk_syn_retries? : sysctl_tcp_synack_retries;//默认synack
重传次数为5
int thresh = max_retries;
unsigned long now = jiffies;
struct request_sock **reqp,*req;
int i, budget;
......
if (queue->rskq_defer_accept)
max_retries = queue->rskq_defer_accept;//设定支持选项时候的重传次数
budget = 2 * (lopt->nr_table_entries/ (timeout / interval));
i = lopt->clock_hand;
do {
reqp=&lopt->syn_table[i];
while ((req =*reqp) != NULL){
if (time_after_eq(now, req->expires)){
if ((req->retrans
(inet_rsk(req)->acked&& req->retrans
&&!req->rsk_ops->rtx_syn_ack(parent, req, NULL)){//如果重传次数小于设定
的重传次数,就重传synack;这里可以看出两个并列的判断条件:req->retrans < thres
h和(inet_rsk(req)->acked && req->retrans < max_retries),第一个是当前req
的重传次数小于设定的最大重传次数,这里是5;第二个则是TCP_DEFER_ACCEPT;inet_rs
k(req)->acked则是在函数tcp_check_req中设定的,上面讨论过了,而max_retries则
为通过val计算的值,默认为1。这个重传次数决定了synack包的重传次数及最长超时时间,
显然两者中较大者起到决定性的作用。譬如,默认重传为2,通过val计算出的max_retries
值为3,则将发送3次重传的synack及超时时间为12秒后,关闭连接
unsigned long timeo;
if (req->retrans++== 0)
lopt->qlen_young--;
timeo = min((timeout<retrans), max_rto);
req->expires= now + timeo;//每重传一次,超时值就按初始值
timeout(TCP_TIMEOUT_INIT)比值为2的等比
数列增加,如3 6 12 24 48 96
reqp = &req->dl_next;
continue;//继续循环
}
/* Drop this request*/
如果超时,如超过例子中的96秒,就将req从半连接队列里删除,丢弃连接
inet_csk_reqsk_queue_unlink(parent, req, reqp);
reqsk_queue_removed(queue, req);
reqsk_free(req);
continue;
}
reqp = &req->dl_next;
}
i = (i+ 1) & (lopt->nr_table_entries- 1);
} while(--budget> 0);
lopt->clock_hand= i;
if (lopt->qlen)
inet_csk_reset_keepalive_timer(parent, interval);
}
那么TCP_DEFER_ACCEPT选项有什么好处呢,我们知道服务端处于监听时,客户端connect;服务端会收到syn包,并发送synack;当客户端收到synack并发送裸ack时,服务端accept创建一个新的句柄,这是不支持TCP_DEFER_ACCEPT选项下的流程。如果支持TCP_DEFER_ACCEPT,收到裸ack时,不会建立连接,操作系统不会Accept,也不会创建IO句柄。操作系统应该在若干秒后,会释放相关的链接;但没有同时关闭相应的端口,所以客户端会一直以为处于链接状态,如果Connect后面马上有后续的发送数据,那么服务器会调用Accept接收这个连接。
函数inet_csk_reqsk_queue_prune是通过tcp_synack_timer,是它在定时器中起作用的
static void tcp_synack_timer(struct sock*sk)
{
inet_csk_reqsk_queue_prune(sk, TCP_SYNQ_INTERVAL,
TCP_TIMEOUT_INIT, TCP_RTO_MAX);
}
方式一:[cpp] view plain copy#include #include using namespace std; using namespace cv; int main() { //创建一个2*2 类型为8位的uchar类型三通道的 颜色为黄色 Mat
Layout POJ - 3169题意: 有 N 头奶牛正在排队,它们的编号为 1 到 N,约翰要给它们安排合适的排队位置,满足以下条件:• 首先,所有奶牛要站在一条直线上。由于是排队,所以编号小的奶牛要靠前,不能让编号大的奶牛插队。但同一个位置可以容纳多头奶牛,这是因为它们非常苗条的缘故• 奶牛喜欢和朋友靠得近点。朋友关系有 F 对,其中第 Ai 头奶牛和第 Bi 头奶牛是第 i 对...
FlexSim软件PF模块标识详细解释工艺流程实体概述标识生成基本子流程显示实体任务序列列表资源区域协同先占展示工艺流程实体概述工艺流程模块在实体库中有一组独特的实体。当我们在FlexSim中打开并激活工艺流程视图时,实体库将会改变并显示这些实体。标识生成与3D仿真模型中的发生器类似,这些活动创建将工艺流程的临时标识。大多数工艺流程将从某些标识生成活动开始。基本基本活动在各种工艺流程中有许多普遍的用途。子流程为了创建和运行分支流程,需要子流程活动。有关更多信息,请参阅子流程活动。显
在多台机器互联的场景下,可能会出现无法通信的情形,可能原因有:代码出错,地址出错,防火墙。对于防火墙的命令,后面会提及。 对于前两种错误debug就可以解决。如果这两种情况都不是,那么极有可能是防火墙的问题,我们可以通过wireshark来抓包进行分析。在wireshark抓包中,我们看到当fedora作为服务器接受到客户端的请求之后,给主机发回的信息会被host administratively
Vue -渐进式JavaScript框架介绍vue 中文网 vue github Vue.js 是一套构建用户界面(UI)的渐进式JavaScript框架库和框架的区别我们所说的前端框架与库的区别?Library库,本质上是一些函数的集合。每次调用函数,实现一个特定的功能,接着把控制权交给使用者代表:jQuery jQuery这个库的核心:DOM操作,即:封装DOM操...
4. 计算机网络性能指标4.1 速率(1)速率:指每秒钟传输的比特数量,称为数据率或比特率,单位b/s或bit/s(比特/秒),有时也写为bps(bit per second)。(2)其它单位的换算关系 ①通信领域: 1kb/s=1000b/s、1Mb/s=1000kb/s、1Gb/s=1000Mb/s、1Tb/s=1000Gb/s(注意,k、b均为小写) ②计算机领域:1...
1.Jigloo SWT/Swing GUI Builder : 让Eclipse可以像JBuilderX一样使用拖拉方式建构GUI的外挂: http://cloudgarden.com/jigloo/index.html ;下载此版本:Jigloo plugin for Eclipse (using Java 1.4 or 1.5) 安装后即可由档案->新建->其它->GUI Form选取要建构
呱哥这篇问答在知乎第一次点赞破百,按当前点赞和收藏比来看,刷个几百赞只是时间问题。最近刚从不到100人的小公司跳到一大家大公司,先说说大厂和小厂有啥区别。之前在小厂对了对抗成长焦虑,我才选...
6.1 离散空间和连续空间在之前的实例中,状态和动作的数量受到限制。使用小的,有限的马尔可夫决策过程(MDP),可以用表,字典或其他有限结构来表示动作值函数。例如,考虑下面的非常小的gridworld。假设世界有四个可能的状态,并且代理有四个可能的操作可供使用(上,下,左,右)。您在前面的课程中了解到,我们可以在表中表示估计的最佳操作值函数,每个状态对应一个行,每个动作对应一个列。我们将此表称为Q表。[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BnBR6zFE-16
1、常用的数据结构index相关Xapian::WritableDatabase 用于建立索引。Xapian::TermGenerator 非常简单的切词、建索引器,不是必须使用的,可用其他替代,但是提供了一些帮助函数,非常好用。search相关:Xapian::Database 用于读取索引。Xapian::Enquire 提供检索服务,与Xapian::Da
UPT (Unified Prompt Tuning) few-shot 文本分类Towards Unified Prompt Tuning for Few-shot Text Classification
1. 新建虚拟机,百度文库有一篇《使用VMware安装OpenWrt》,地址:http://wenku.baidu.com/link?url=NkvaQpTf2dR8FpYn7JD9A7-uaw6XQvLafQ64Mqg9SNEZk8M_JCQTuLELR0GfWlBdmrbA3_EuUxnwQGsRz-JBA6-Jx6QHmYbVcJKJ-V0oX__装好后,复制下载的openwr...