TCP的定时器系列 — 超时重传定时器(有图有代码有真相!!!)

转载

主要内容:TCP定时器概述,超时重传定时器、ER延迟定时器、PTO定时器的实现。

内核版本:3.15.2

我的博客:http://blog.csdn.net/zhangskd

 

Q:一条TCP连接会使用多少个定时器呢?

A:目前的答案是9个:

超时重传定时器,持续定时器,ER延迟定时器,PTO定时器,ACK延迟定时器,

SYNACK定时器,保活定时器,FIN_WAIT2定时器,TIME_WAIT定时器。

 

数据结构

 

几种定时器的标识:

#define ICSK_TIME_RETRANS 1 /* Retransmit timer */

#define ICSK_TIME_DACK 2 /* Delayed ack timer */

#define ICSK_TIME_PROBE0 3 /* Zero window probe timer */

#define ICSK_TIME_EARLY_RETRANS 4 /* Early retransmit timer */

#define ICSK_TIME_LOSS_PROBE 5 /* Tail loss probe timer */

 

上述5种定时器分别为:超时重传定时器、ACK延迟定时器、持续定时器、ER延迟定时器、PTO定时器。

另外还有保活定时器、FIN_WAIT2定时器、TIME_WAIT定时器、SYNACK定时器。

 

虽然定义了9个定时器,但是内核中只用了4个实例(timer_list),所以有些定时器是共用一个实例的。

这4个实例分别是:

icsk->icsk_retransmit_timer:超时重传定时器、持续定时器、ER延迟定时器、PTO定时器。

icsk->icsk_delack_timer:ACK延迟定时器。

sk->sk_timer:保活定时器,SYNACK定时器,FIN_WAIT2定时器。

death_row->tw_timer:TIME_WAIT定时器。

 

创建和删除

 

(1) 定时器的创建

tcp_v4_init_sock

    |-> tcp_init_sock

             |-> tcp_init_xmit_timers

                        |-> inet_csk_init_xmit_timers

 

在初始化连接时,设置三个定时器实例的处理函数:

icsk->icsk_retransmit_timer的处理函数为tcp_write_timer()

icsk->icsk_delack_timer的处理函数为tcp_delack_timer()

sk->sk_timer的处理函数为tcp_keepalive_timer()

void tcp_init_xmit_timers(struct sock *sk)
{inet_csk_init_xmit_timers(sk, &tcp_write_timer, &tcp_delack_timer, &tcp_keepalive_timer);
}
/** Using different timers for retransmit, delayed acks and probes.* We may wish use just one timer maintaining a list of expire jiffies to optimize.*/void inet_csk_init_xmit_timers(struct sock *sk, void (*retransmit_handler) (unsigned long),void (*delack_handler) (unsigned long),void (*keepalive_handler) (unsigned long))
{struct inet_connection_sock *icsk = inet_csk(sk);setup_timer(&icsk->icsk_retransmit_timer, retransmit_handler, (unsigned long)sk);setup_timer(&icsk->icsk_delack_timer, delack_timer, (unsigned long)sk);setup_timer(&sk->sk_timer, keepalive_handler, (unsigned long)sk);icsk->icsk_pending = icsk->icsk_ack.pending = 0;
}

 

(2) 定时器的删除

tcp_done

tcp_disconnect

tcp_v4_destroy_sock

    |-> tcp_clear_xmit_timers

              |-> inet_csk_clear_xmit_timers

void inet_csk_clear_xmit_timers(struct sock *sk)
{struct inet_connection_sock *icsk = inet_csk(sk);icsk->icsk_pending = icsk->icsk_ack.pending = icsk->icsk_ack.blocked = 0;sk_stop_timer(sk, &icsk->icsk_retransmit_timer);sk_stop_timer(sk, &icsk->icsk_delack_timer);sk_stop_timer(sk, &sk->sk_timer);
}

 

激活

 

icsk->icsk_retransmit_timer和icsk->icsk_delack_timer的激活函数为inet_csk_reset_xmit_timer(),

共负责了5个定时器的激活工作。

/** Reset the retransmissiion timer*/
static inline void inet_csk_reset_xmit_timer(struct sock *sk, const int what,unsigned long when,const unsigned long max_when)
{struct inet_connection_sock *icsk = inet_csk(sk);if (when > max_when) {
#ifdef INET_CSK_DEBUGpr_debug("reset_xmit_timer: sk=%p %d when=0x%lx, caller=%p\n",sk, what, when, current_text_addr());
#endifwhen = max_when;}if (what == ICSK_TIME_RETRANS || what == ICSK_TIME_PROBE0 || what == ICSK_TIME_EARLY_RETRANS || what == ICSK_TIME_LOSS_PROBE) {icsk->icsk_pending = what;icsk->icsk_timeout = jiffies + when; /*数据包超时时刻*/sk_reset_timer(sk, &icsk->icsk_retransmit_timer, icsk->icsk_timeout);} else if (what == ICSK_TIME_DACK) {icsk->icsk_ack.pending |= ICSK_ACK_TIMER;icsk->icsk_ack.timeout = jiffies + when; /*Delay ACK定时器超时时刻*/sk_reset_timer(sk, &icsk->icsk_delack_timer, icsk->icsk_ack.timeout);}
#ifdef INET_CSK_DEBUGelse {pr_debug("%s", inet_csk_timer_bug_msg);}  
#endif     
}

其中,超时重传定时器(ICSK_TIME_RETRANS)在以下几种情况下会被激活:

1. 发现对端把保存在接收缓冲区的SACK段丢弃时。

2. 发送一个数据段时,发现之前网络中不存在发送且未确认的段。

    之后每当收到确认了新数据段的ACK,则重置定时器。

3. 发送SYN包后。

4. 一些特殊情况。

 

超时处理函数

 

当icsk->icsk_retransmit_timer超时后,会调用其处理函数tcp_write_timer()进行处理。

staic void tcp_write_timer(unsigned long data)
{struct sock *sk = (struct sock *)data;bh_lock_sock(sk);if (! sock_owned_by_user(sk)) {  /* sk没被用户空间占用 */tcp_write_timer_handler(sk);} else { /* 否则先设置延迟标志,之后再处理 *//* delegate our work to tcp_release_cb() */if (! test_and_set_bit(TCP_WRITE_TIMER_DEFERRED, &tcp_sk(sk)->tsq_flags))sock_hold(sk);}bh_unlock_sock(sk);sock_put(sk);
}


icsk->icsk_retransmit_timer可同时作为:超时重传定时器、持续定时器、ER延迟定时器、PTO定时器,

所以需要判断是哪种定时器触发的,然后采取相应的处理措施。

void tcp_write_timer_handler(struct sock *sk)
{struct inet_connection_sock *icsk = inet_csk(sk);int event;/* 如果连接处于CLOSED状态,或者没有定时器在计时 */if (sk->sk_state == TCP_CLOSE || !icsk->icsk_pending)goto out;/* 如果定时器还没有超时,那么继续计时 */if (time_after(icsk->icsk_timeout, jiffies)) {sk_reset_timer(sk, &icsk->icsk_retransmit_timer, icsk->icsk_timeout);goto out;}event = icsk->icsk_pending; /* 用于表明是哪种定时器 */switch(event) {case ICSK_TIME_EARLY_RETRANS: /* ER延迟定时器触发的 */tcp_resume_early_retransmit(sk); /* 进行early retransmit */break;case ICSK_TIME_LOSS_PROBE: /* PTO定时器触发的 */tcp_send_loss_probe(sk); /* 发送TLP探测包 */break;case ICSK_TIME_RETRANS: /* 超时重传定时器触发的 */icsk->icsk_pending = 0;tcp_retransmit_timer(sk);break;case ICSK_TIME_PROBE0: /* 持续定时器触发的 */icsk->icsk_pending = 0;tcp_probe_timer(sk);break;}out:sk_mem_reclaim(sk);
}

 

如果是超时重传定时器触发的,就会调用tcp_retransmit_timer()进行处理。 

void tcp_retransmit_timer(struct sock *sk)
{struct tcp_sock *tp = tcp_sk(sk);struct inet_connection_sock *icsk = inet_csk(sk);/* Fast Open特性相关,暂时不做分析 */if (tp->fastopen_rsk) {...}/* 如果没有发送且未确认的数据段,没有等待哪来的超时,直接返回 */if (! tp->packets_out)goto out; /* 如果发送队列为空,显然不合理 */WARN_ON(tcp_write_queue_empty(sk));/* 发生RTO超时表明数据包的确丢失了,所以不用再进行额外检测了 */tp->tlp_high_seq = 0;/* 如果对端通告窗口为0,sk不处于DEAD状态,TCP连接不处于三次握手 *//* Receiver dastardly shrinks window. Our retransmits become zero probes,* but we should not timeout this connection. If the socket is an orphan, time it out,* we cannot allow such beasts to hang infinitely.  */if (! tp->snd_wnd && ! sock_flag(sk, SOCK_DEAD) && ! ((1 << sk->sk_state) & (TCPF_SYN_SENT | TCPF_SYN_RECV))) {struct inet_sock *inet = inet_sk(sk);if (sk->sk_family == AF_INET) {LIMIT_NETDEBUG(KERN_DEBUG pr_fmt("Peer %pI4:%u/ %u unexpectedly shrunk window %u:%u (repaired)\n",&inet->inet_daddr, ntohs(inet->inet_dport), inet->inet_num, tp->snd_una, tp->snd_nxt);}#if IS_ENABLED(CONFIG_IPV6)/* IPv6的处理,此处省略 */...
#endif/* 距离上次收到ACK的时间超过了最大超时时间,认为有错误发生了*/if (tcp_time_stamp - tp->rcv_tstamp > TCP_RTO_MAX) {tcp_write_err(sk); /* 报告错误,调用tcp_done终止连接 */goto out;}/* 进入Loss状态,标志丢失的数据段 */tcp_enter_loss(sk, 0);/* 重传发送队列的第一个数据段 */tcp_retransmit_skb(sk, tcp_write_queue_head(sk));__sk_dst_reset(sk); /* 更新路由缓存 */goto out_reset_timer;}/* 如果重传次数达到上限,报告错误并关闭套接口。* 如果资源使用达到上限,放弃本次重传。*/if (tcp_write_timeout(sk))goto out;/* 刚进入超时重传 */if (icsk->icsk_retransmits == 0) {int mib_idx;/* Recovery状态中发生超时 */if (icsk->icsk_ca_state == TCP_CA_Recovery) {if (tcp_is_sack(tp))mib_idx = LINUX_MIB_TCPSACKRECOVERYFAIL;elsemib_idx = LINUX_MIB_TCPRENORECOVERYFAIL;} else if (icsk->icsk_ca_state == TCP_CA_Loss) { /* Loss状态中发生超时 */    mib_idx = LINUX_MIB_TCPLOSSFAILURES;} else if (icsk->icsk_ca_state == TCP_CA_Disorder) || tp->sacked_out) {/* Disorder状态中发生超时 */if (tcp_is_sack(tp))mib_idx = LINUX_MIB_TCPSACKFAILURES;elsemib_idx = LINUX_MIB_TCPRENOFAILURES;} else { /* Open状态或CWR状态中发生超时 */mib_idx = LINUX_MIB_TCPTIMEOUTS;}NET_INC_STATS_BH(sock_net(sk), mib_idx);}/* 进入Loss状态,标志丢失的数据段。* 值得注意的是不再使用tcp_enter_frto_loss(),FRTO机制被重写了。*/tcp_enter_loss(sk, 0);/* 重传发送队列的第一个数据段,如果失败说明是本地拥塞,那么不进行指数退避 */if (tcp_retransmit_skb(sk, tcp_write_queue_head(sk)) > 0) {/* Retransmission failed because of local congestion, do not backoff. */if (! icsk->icsk_retransmits)icsk->icsk_retransmits = 1;inet_csk_reset_xmit_timer(sk, ICSK_TIME_RETRANS, min(icsk->icsk_rto, TCP_RESOURCE_PROBE_INTERVAL),TCP_RTO_MAX);goto out;}icsk->icsk_backoff++; /* 退避指数,采集到新的RTT样本时清零 */icsk->icsk_retransmits++; /* 超时次数,当确认了新数据时清零 */out_reset_timer:/* 对于符合条件的Thin stream不使用指数退避,重复超时6次后除外(太惨了:) */if (sk->sk_state == TCP_ESTABLISHED && (tp->thin_lto || sysctl_tcp_thin_linear_timeouts) &&tcp_stream_is_thin(tp) && icsk->icsk_retransmits <= TCP_THIN_LINEAR_RETRIES) {icsk->icsk_backoff = 0;icsk->icsk_rto = min(__tcp_set_rto(tp), TCP_RTO_MAX); /* RTO保持原样 */} else { /* Use normal (exponential) backoff */icsk->icsk_rto = min(icsk->icsk_rto << 1, TCP_RTO_MAX); /* RTO翻倍 */}/* 重置超时定时器,就是上文说的激活 */inet_csk_reset_xmit_timer(sk, ICSK_TIME_RETRANS, icsk->icsk_rto, TCP_RTO_MAX);/* 当重传多次时,需要根据重传时间间隔,决定是否更新路由缓存  */if (retransmits_timed_out(sk, sysctl_tcp_retries1 + 1, 0, 0))__sk_dst_reset(sk);out:;
}

 

连接的超时

 

很明显,重传不能无限的进行下去,当重传的次数超过设定的上限时,就会判定连接超时,关闭该连接。

此后相应的socket函数,比如connect和send,就会返回-1,errno设为ETIMEDOUT,表示连接超时。

 

判定连接是否超时,如果超过了最大等待时间,就放弃此连接。

/* A write timeout has occurred. Process the after effects. */ static int tcp_write_timeout (struct sock *sk)
{struct inet_connection_sock *icsk = inet_csk(sk);struct tcp_sock *tp = tcp_sk(sk);int retry_until;bool do_reset, syn_set = false;/* 如果超时是发生在三次握手期间 */if ((1 << sk->sk_state) & (TCPF_SYN_SENT | TCPF_SYN_RECV)) {if (icsk->icsk_retransmits) { /* 如果之前重传过了 */dst_negative_advice(sk); /* 更新目的路由缓存 *//* SYN携带Fast Open选项,或SYN携带数据 */if (tp->syn_fastopen || tp->syn_data)tcp_fastopen_cache_set(sk, 0, NULL, true);if (tp->syn_data)NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPFASTOPENACTIVEFAIL);}/* SYN的最大重传次数,由TCP_SYNCNT选项和tcp_syn_retries参数决定,默认是5次 */retry_until = icsk->icsk_syn_retries ?: sysctl_tcp_syn_retries;syn_set = true;} else {/* tcp_retries1默认为3,当重传次数超过此值时,表示可能遇到了黑洞,需要进行PMTU* 检测,同时更新路由缓存。*/if (retransmits_timed_out(sk, sysctl_tcp_retries1, 0, 0)) {/* Black hole detection */tcp_mtu_probing(icsk, sk); /* PMTU检测 */dst_negative_advic(sk); /* 更新路由缓存 */}retry_until = sysctl_tcp_retries2; /* 在断开TCP连接之前,最多进行多少次重传,默认值为15 *//* 如果当前套接口即将关闭 */if (sock_flag(sk, SOCK_DEAD)) {const int alive = (icsk->icsk_rto < TCP_RTO_MAX);retry_until = tcp_orphan_retries(sk, alive); /* 决定重传次数 */do_reset = alive || ! retransmits_timed_out(sk, retry_until, 0, 0);/* 如果当前的孤儿socket数量超过tcp_max_orphans,或者内存不够时,关闭此连接 */if (tcp_out_of_resources(sk, do_reset))return 1;}}/* 判定连接是否等待过久,即是否超时 */if (retransmits_timed_out(sk, retry_until, syn_set ? 0 : icsk->icsk_user_timeout, syn_set)) {/* Has it gone just too far ? */tcp_write_err(sk);return 1;}
}/* Calculate maximal number or retries on an orphaned socket. */
static int tcp_orphan_retries (struct sock *sk, int alive)
{int retries = sysctl_tcp_orphan_retries; /* May be zero. *//* We know from an ICMP that something is wrong. */if (sk->sk_err_soft && !alive)retries = 0;/* However, if socket sent something recently, select some safe number of retries.* 8 corresponds to > 100 seconds with minimal RTO of 200msec.*/if (retries == 0 && alive)retries = 8;return retries; 
}

 

判断连接是否超时,要分为3种情况。

1. SYN包:当SYN包的重传次数达到上限时,判定连接超时。(默认允许重传5次,初始超时时间为1s,总共历时31s)

2. 非SYN包,用户使用TCP_USER_TIMEOUT:当数据包发出去后的等待时间超过用户设置的时间时,判定连接超时。

3. 非SYN包,用户没有使用TCP_USER_TIMEOUT:当数据包发出去后的等待时间超过以TCP_RTO_MIN为初始超时

时间,重传boundary次所花费的时间后,判定连接超时。

 

如果返回值为真,判定连接超时,则关闭连接,把errno设置为ETIMEDOUT,Socket函数返回-1。

boundary为最大重传次数,timeout为用户设置的超时时间。(通过TCP_USER_TIMEOUT选项设置)

struct tcp_sock {.../* Timestamp of the last retransmit, also used in SYN-SENT to remember stamp of* the first SYN.*//* 上面的注释是错误的,变量的含义为:* 1. 原始SYN包的发送时间点,不是重传的。* 2. 本次重传时,第一个被重传的包,原始包的发送时间点。* 这个变量是用来计算连接的超时关闭时间:一个包发送多久还没有得到响应,就判断连接超时。*/u32 retrans_stamp; ...
};/* This function calculates a "timeout" which is equivalent to the timeout of a TCP connection* after " boundary unsuccessful, exponentially backed-off retransmissions with an initial RTO* of TCP_RTO_MIN or TCP_TIMEOUT_INIT if syn_set flag is set.*/
static bool retransmits_timed_out (struct sock *sk, unsigned int boundary, unsigned int timeout,bool syn_set)
{unsigned int linear_backoff_thresh, start_ts;/* 如果是SYN包,则默认的初始超时时间为1s,否则为200ms。*/unsigned int rto_base = syn_set ? TCP_TIMEOUT_INIT : TCP_RTO_MIN;if (! inet_csk(sk)->icsk_retransmits))return false; /* 如果之前没有重传过,直接返回false *//* start_ts为开始计时点,注意是原始包第一次被发送时的时间戳,不是重传包的 */if (unlikely(! tcp_sk(sk)->retrans_stamp)) start_ts = TCP_SKB_CB(tcp_write_queue_head(sk))->when;elsestart_ts = tcp_sk(sk)->retrans_stamp;/* 包括两种情况:* SYN包:timeout始终设置为0,因为在三次握手时TCP_USER_TIMEOUT是无效的。* 非SYN包:用户没有使用TCP_USER_TIMEOUT选项时。*/if (likely(timeout == 0)) { /* 当rto不超过最大值时,能够进行多少次指数退避。* 以SYN包为例,rto_base为1s,此值向上取整为7。*/linear_backoff_thresh = ilog2(TCP_RTO_MAX/rto_base); /* 从计时点开始,经过boundary次重传后,总共花费的时间 */if (boundary <= linear_backoff_thresh)timeout = ((2 << boundary) -1) * rto_base; elsetimeout = ((2 << linear_backoff_thresh) -1) * rto_base + (boundary - linear_backoff_thresh) * TCP_RTO_MAX;}

return (tcp_time_stamp - start_ts) >= timeout; /* 判断等待时间是否过长,即是否要放弃连接 */

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/459380.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Python try/except/finally等

Python try/except/finally等 [代码块]x abc def fetcher(obj, index): return obj[index] fetcher(x, 4) 输出&#xff1a; File "test.py", line 6, in <module> fetcher(x, 4) File "test.py", line 4, in fetcher retur…

TCP的定时器系列 — SYNACK定时器

转载 主要内容&#xff1a;SYNACK定时器的实现&#xff0c;TCP_DEFER_ACCPET选项的实现。 内核版本&#xff1a;3.15.2 我的博客&#xff1a;http://blog.csdn.net/zhangskd 在上一篇博客中&#xff0c;已经连带介绍了SYNACK定时器的创建和删除&#xff0c;所以本文直接从它的…

android -- 蓝牙 bluetooth (二) 打开蓝牙

4.2的蓝牙打开流程这一部分还是有些变化的&#xff0c;从界面上看蓝牙开关就是设置settings里那个switch开关&#xff0c;widget开关当然也可以&#xff0c;起点不同而已&#xff0c;后续的流程是一样的。先来看systemServer.java的代码&#xff0c;蓝牙服务开启的地方&#xf…

开始nodejs+express的学习+实践(8)

为什么80%的码农都做不了架构师&#xff1f;>>> 1.session使用 介绍的非常详细&#xff1a; http://www.cnblogs.com/chenchenluo/p/4197181.html 对比我们的app.js需要引入express-session模块和使用这个模块&#xff0c;在package依赖&#xff0c;并加载。 我们修…

TCP的定时器系列 — 保活定时器(有图有代码有真相!!!)

转载 主要内容&#xff1a;保活定时器的实现&#xff0c;TCP_USER_TIMEOUT选项的实现。 内核版本&#xff1a;3.15.2 我的博客&#xff1a;http://blog.csdn.net/zhangskd 原理 HTTP有Keepalive功能&#xff0c;TCP也有Keepalive功能&#xff0c;虽然都叫Keepalive&#xff0c…

浅谈 Scala 中下划线的用途

Scala 作为一门函数式编程语言&#xff0c;对习惯了指令式编程语言的同学来说&#xff0c;会不大习惯&#xff0c;这里除了思维方式之外&#xff0c;还有语法层面的&#xff0c;比如 underscore&#xff08;下划线&#xff09;就会出现在多种场合&#xff0c;令初学者相当疑惑&…

maven项目部署到linux上的奇葩问题

2019独角兽企业重金招聘Python工程师标准>>> 经常会遇到这样子的问题&#xff0c;maven项目在本地的eclipse配置的好好的&#xff0c;结果一到服务器就运行不起来。 当然遇到这种情况&#xff0c;我们首先会想到环境变量和相关的路径问题&#xff0c;但是当这两个条…

TCP的定时器系列 — 零窗口探测定时器(有图有代码有真相!!!)

转载 主要内容&#xff1a;零窗口探测定时器的实现。 内核版本&#xff1a;3.15.2 我的博客&#xff1a;http://blog.csdn.net/zhangskd 出现以下情况时&#xff0c;TCP接收方的接收缓冲区将被塞满数据&#xff1a; 发送方的发送速度大于接收方的接收速度。 接收方的应用程序未…

java中XPATH操作xml,非常便捷

<?xml version"1.0" encoding"UTF-8"?> <MessageList><item type"1"><template_id value"p2ItJPj0taTTP4QRXP-z51nYuD3aDNhgvLOusWGY4p0"/><topcolor value"#173177"/><first value&quo…

【python】r+,w+ 全局变量

来源&#xff1a;http://www.educity.cn/wenda/352188.html r&#xff1a;可读可写&#xff0c;若文件不存在&#xff0c;报错w: 可读可写&#xff0c;若文件不存在&#xff0c;创建文本模式&#xff1a;遇换行符时根据操作系统不同自动转换换行符&#xff0c;比如读文件时遇\n…

网络:TCP通讯之 time_wait 状态

基于TCP协议的通讯流程1、TCP建立连接2、TCP断开连接3、TCP状态转换TCP状态解释&#xff1a; SYN-RECVD&#xff1a;再收到和发送一个连接请求后等待对方对连接请求的确认 ESTABLISHED&#xff1a;代表一个打开的连接 FIN-WAIT-1&#xff1a;等待远程TCP连接中断请求&#xff0…

linux下echo与time服务的程序实现

一、针对ECHO服务的TCP客户软件的实现 1.网络拓扑结构&#xff1a; 2.源码&#xff1a; 1 #include <stdio.h>2 #include <stdlib.h>3 #include <unistd.h>4 #include <string.h>5 #include <stdarg.h>6 #include <sys/types.h>7 #include…

iOS UI-AlertView(警示框)和ActionSheet(选择框、操作表单)

1 #import "ViewController.h"2 3 interface ViewController ()<UIAlertViewDelegate,UIActionSheetDelegate>4 5 end6 7 implementation ViewController8 9 #pragma mark - 生命周期10 - (void)viewDidLoad {11 [super viewDidLoad];12 // 创建展示Al…

Linux高性能服务器编程:进程池和线程池原理及应用(有图有代码有真相!!!)

一、问题引入 在前面编写多进程、多线程服务器时通过动态创建子进程和子线程来实现并发服务器&#xff0c;这样做有以下缺点&#xff1a; 1&#xff09;动态创建进程、线程将会比较耗费时间&#xff0c;将导致较慢的客户响应。 2&#xff09;动态创建的子进程只为一个客户服…

Linux:多进程、多线程服务器的实现解析(有图有代码有真相!!!)

一、问题引入 阻塞型的网络编程接口 几乎所有的程序员第一次接触到的网络编程都是从 listen()、send()、recv()等接口开始的。使用这些接口可以很方便的构建服务器 /客户机的模型。 我们假设希望建立一个简单的服务器程序&#xff0c;实现向单个客户机提供类似于“一问一答”的…

数据结构:神奇的B树实现解析(有图有代码有真相!!!)

一、B树引入 二叉搜索树、平衡二叉树、红黑树都是动态查找树&#xff0c;典型的二叉搜索树结构&#xff0c;查找的时间复杂度和树的高度相关O(log2N)。 1&#xff09;数据杂乱无章-------线性查找--O&#xff08;n&#xff09; 2&#xff09;数据有序-------二分查找 ---O(lo…

Linux:dup/dup2 文件描述符重定向函数(有图有代码有真相!!!)

一、dup/dup2 有时我们希望把标准输入重定向到一个文件&#xff0c;或者把标准输出重定向到一个网络连接。系统调用dup和dup2能够复制文件描述符。dup返回新的文件文件描述符&#xff08;没有用的文件描述符最小的编号&#xff09;。 dup2可以让用户指定返回的文件描述符的值…

Linux:I/O多路转接之select(有图有代码有真相!!!)

一、select引入 一次 I/O 分为两个部分&#xff1a;1&#xff09;等待数据就绪 2&#xff09;进行数据转移 1、select 原理&#xff1a; select的原理就是减少等待数据就绪的比重&#xff0c;巧妙的利用等待队列机制让用户进程适当在没有资源可读/写时睡眠&#xff0c;有资…

Linux: I/O多路转接之poll(有图有代码有真相!!!)

一、poll()函数解析 不同与select使⽤用三个位图来表⽰示三个fdset的⽅方式&#xff0c;poll使⽤用⼀一个 pollfd的指针实现。pollfd结构包含了要监视的event和发⽣生的event&#xff0c; 不再使⽤用select“参数-值”传递的⽅方式。同时&#xff0c;pollfd并没有最⼤大数量限…

kalilinux装到u盘上的弊端_付费下载的歌曲,竟然无法在汽车上播放!原因在这里...

『使用某音乐播放器下载了周杰伦的110首歌曲&#xff0c;其中106首是kgm格式&#xff0c;4首mp3格式&#xff0c;装到U盘后&#xff0c;在其它设备播放只有4首mp3格式的可以播放&#xff0c;其它的均无法播放&#xff0c;请问该如何处理&#xff1f;』网友留言截图这是一位网友…