1、前言
網(wǎng)絡(luò)編程中超時(shí)時(shí)間是一個(gè)重要但又容易被忽略的問(wèn)題,對(duì)其的設(shè)置需要仔細(xì)斟酌。
本文討論的是socket設(shè)置為阻塞模式,如果socket處于阻塞模式運(yùn)行時(shí),就需要考慮處理socket操作超時(shí)的問(wèn)題。所謂阻塞模式,是指其完成指定的操作之前阻塞當(dāng)前的進(jìn)程或線(xiàn)程,直到操作有結(jié)果返回.在我們直接調(diào)用socket操作函數(shù)時(shí),如果不進(jìn)行特意聲明的話(huà),它們都是工作在阻塞模式的,如 connect, send, recv等.
簡(jiǎn)單分類(lèi)的話(huà),可以將超時(shí)處理分成兩類(lèi):
連接(connect)超時(shí);發(fā)送(send), 接收(recv)超時(shí);
2、連接超時(shí)
從字面上看,連接超時(shí)就是在一定時(shí)間內(nèi)還是連接不上目標(biāo)主機(jī)。你所建立的socket連接其實(shí)最終都要進(jìn)行系統(tǒng)調(diào)用進(jìn)入內(nèi)核態(tài),剩下的就是等待內(nèi)核通知連接建立。所以自行在代碼中設(shè)置了超時(shí)時(shí)間(一般是叫connectTimeout或者socketTimeout),那么這個(gè)超時(shí)時(shí)間一到如果內(nèi)核還沒(méi)成功建立連接,那就認(rèn)為是連接超時(shí)了。如果他們沒(méi)設(shè)置超時(shí)時(shí)間,那么這個(gè)connectTimeout就取決于內(nèi)核什么時(shí)候拋出超時(shí)異常了。
因此,我們需要分析一下內(nèi)核是怎么來(lái)判斷連接超時(shí)的。
2.1內(nèi)核層的超時(shí)分析
我們都知道一個(gè)連接的建立需要經(jīng)過(guò)3次握手,所以連接超時(shí)簡(jiǎn)單的說(shuō)是是客戶(hù)端往服務(wù)端發(fā)的SYN報(bào)文沒(méi)有得到響應(yīng)(服務(wù)端沒(méi)有返回ACK報(bào)文)。
由于網(wǎng)絡(luò)本身是不穩(wěn)定的,丟包是很常見(jiàn)的事情(或者對(duì)方主機(jī)因?yàn)槟承┰騺G棄了該包),因此內(nèi)核在發(fā)送SYN報(bào)文沒(méi)有得到響應(yīng)后,往往還是進(jìn)行多次重試。同時(shí),為了避免發(fā)送太多的包影響網(wǎng)絡(luò),重試的時(shí)間間隔還會(huì)不斷增加。
在linux中,重試的時(shí)間間隔會(huì)呈指數(shù)型增長(zhǎng),為2的N次方,即:
第一次發(fā)送SYN報(bào)文后等待1s(2的0次冪)后再重試
第二次發(fā)送SYN報(bào)文后等待2s(2的1次冪)后再重試
第三次發(fā)送SYN報(bào)文后等待4s(2的2次冪)后再重試
第四次發(fā)送SYN報(bào)文后等待8s(2的3次冪)后再重試
第五次發(fā)送SYN報(bào)文后等待16s(2的4次冪)后再重試
第六次發(fā)送SYN報(bào)文后等待32s(2的5次冪)后再重試
第七次發(fā)送SYN報(bào)文后等待64s(2的6次冪)后再重試
對(duì)于重試次數(shù),由linux的net.ipv4.tcp_syn_retries來(lái)確定,默認(rèn)值一般是6(有些linux發(fā)行版可能不太一樣),我們可以通過(guò)sysctl net.ipv4.tcp_syn_retries查看。比如重試次數(shù)是6次,那么我們可以得出超時(shí)時(shí)間應(yīng)該是 1+2+4+8+16+32+64=127秒 (上面的第一條是第一次發(fā)送SYN報(bào)文,不算重試)。
如果我們想修改重試次數(shù),可以輸入命令sysctl -w net.ipv4.tcp_syn_retries=5來(lái)修改(需要root權(quán)限)。如果希望重啟后生效,將net.ipv4.tcp_syn_retries = 5放入/etc/sysctl.conf中,之后執(zhí)行sysctl -p 即可生效。
在一些linux發(fā)行版中,重試時(shí)間可能會(huì)變動(dòng)。如果想確定操作系統(tǒng)具體的超時(shí)時(shí)間,可以通過(guò)下面這條命令來(lái)判斷:
gaoke@ubuntu:~$ date; telnet 10.16.15.15 5000; date
Sat Apr 2 14:27:33 CST 2022
Trying 10.16.15.15...
telnet: Unable to connect to remote host: Connection timed out
Sat Apr 2 14:29:40 CST 2022
2.2綜合分析
如果應(yīng)用層面設(shè)置了自己的超時(shí)時(shí)間,同時(shí)內(nèi)核也有自己的超時(shí)時(shí)間,那么應(yīng)該以哪個(gè)為準(zhǔn)呢?答案是哪個(gè)超時(shí)時(shí)間小以哪個(gè)為準(zhǔn)。
個(gè)人認(rèn)為,在我們的實(shí)際應(yīng)用中,這個(gè)超時(shí)時(shí)間不宜設(shè)置的太長(zhǎng),通常建議2-10s。比如在分布式系統(tǒng)中,我們通常會(huì)在多臺(tái)節(jié)點(diǎn)中根據(jù)一定策略選擇一臺(tái)進(jìn)行連接。在有機(jī)器宕機(jī)的情況下,如果連接超時(shí)時(shí)間設(shè)置的比較長(zhǎng),而我們客戶(hù)端的線(xiàn)程池又比較小,就很可能大多數(shù)的線(xiàn)程都在等待建立連接,過(guò)了較長(zhǎng)時(shí)間才發(fā)現(xiàn)連接不上,影響應(yīng)用的整體吞吐量。
2.3connect系統(tǒng)調(diào)用
我們觀察一下此系統(tǒng)調(diào)用的kernel源碼,調(diào)用棧如下所示:
connect[用戶(hù)態(tài)]
|->SYSCALL_DEFINE3(connect)[內(nèi)核態(tài)]
|->sock->ops->connect

最終調(diào)用的tcp_connect源碼如下:
int tcp_connect(struct sock *sk) {
......
// 發(fā)送SYN
err = tcp_transmit_skb(sk, buff, 1, sk->sk_allocation);
...
/* Timer for repeating the SYN until an answer. */
// 由于是剛建立連接,所以其rto是TCP_TIMEOUT_INIT
inet_csk_reset_xmit_timer(sk, ICSK_TIME_RETRANS,
inet_csk(sk)->icsk_rto, TCP_RTO_MAX);
return 0;
}
又上面代碼可知,在tcp_connect設(shè)置了重傳定時(shí)器之后return回了tcp_v4_connect再return到inet_stream_connect。
我們可以采用設(shè)置SO_SNDTIMEO來(lái)控制connect系統(tǒng)調(diào)用的超時(shí),如下所示:
setsockopt(sockfd, SOL_SOCKET, SO_SNDTIMEO, &timeout, len);
2.4不設(shè)置SO_SNDTIMEO
如果不設(shè)置SO_SNDTIMEO,那么會(huì)由tcp重傳定時(shí)器在重傳超過(guò)設(shè)置的時(shí)候后超時(shí),如下圖所示:

我們?nèi)绾尾榭磗yn重傳次數(shù)?:
cat /proc/sys/net/ipv4/tcp_syn_retries

對(duì)于系統(tǒng)調(diào)用,connect的超時(shí)時(shí)間為:
tcp_syn_retries |
timeout |
1 |
min(so_sndtimeo,3s) |
2 |
min(so_sndtimeo,7s) |
3 |
min(so_sndtimeo,15s) |
4 |
min(so_sndtimeo,31s) |
5 |
min(so_sndtimeo,63s) |
kernel代碼版本細(xì)微變化
值得注意的是,linux本身官方發(fā)布的2.6.32源碼對(duì)于tcp_syn_retries2的解釋和RFC并不一致,不同內(nèi)核小版本上的實(shí)驗(yàn)會(huì)有不同的connect timeout表現(xiàn)的原因(有的抓包到的重傳SYN時(shí)間間隔為3,6,12......)。
以下為代碼對(duì)比:
========================>linux 內(nèi)核版本2.6.32-431<========================
#define TCP_TIMEOUT_INIT ((unsigned)(1*HZ)) /* RFC2988bis initial RTO value */
?
static inline bool retransmits_timed_out(struct sock *sk,
unsigned int boundary,
unsigned int timeout,
bool syn_set)
{
......
unsigned int rto_base = syn_set ? TCP_TIMEOUT_INIT : TCP_RTO_MIN;
......
timeout = ((2 << boundary) - 1) * rto_base;
......
?
}
========================>linux 內(nèi)核版本2.6.32.63<========================
#define TCP_TIMEOUT_INIT ((unsigned)(3*HZ)) /* RFC 1122 initial RTO value */
?
static inline bool retransmits_timed_out(struct sock *sk,
unsigned int boundary
{
......
timeout = ((2 << boundary) - 1) * TCP_RTO_MIN;
......
}
另外,tcp_syn_retries重傳次數(shù)可以在單個(gè)socket中通過(guò)setsockopt設(shè)置。
3、發(fā)送超時(shí)
在tcp連接建立之后,寫(xiě)操作可以理解為向?qū)Χ税l(fā)送tcp報(bào)文的過(guò)程。在tcp的實(shí)現(xiàn)中,每一段報(bào)文都需要有對(duì)端的回應(yīng),即ACK報(bào)文。和連接時(shí)發(fā)送SYN報(bào)文一樣,如果超過(guò)一定時(shí)間沒(méi)有收到響應(yīng),內(nèi)核會(huì)再次重發(fā)該報(bào)文。和SYN報(bào)文的重試不同的是,linux有另外的參數(shù)來(lái)控制這個(gè)重試次數(shù),即net.ipv4.tcp_retries2,可以通過(guò)sysctl net.ipv4.tcp_retries2查看其值。
另外,這個(gè)數(shù)據(jù)報(bào)文重試時(shí)間間隔的計(jì)算方式也和SYN報(bào)文不一樣,由于計(jì)算方式比較復(fù)雜,這里就不詳細(xì)介紹。
一般linux發(fā)行版的net.ipv4.tcp_retries2的默認(rèn)值為5或者15,對(duì)應(yīng)的超時(shí)時(shí)間如下表:
tcp_retries2對(duì)端無(wú)響應(yīng)
525.6s-51.2s,根據(jù)動(dòng)態(tài)rto定
15924.6s-1044.6s,根據(jù)動(dòng)態(tài)rto定
和SYN報(bào)文的超時(shí)時(shí)間一樣,如果應(yīng)用層設(shè)置了超時(shí)時(shí)間,哪么具體的超時(shí)時(shí)間以?xún)?nèi)核和應(yīng)用層的超時(shí)時(shí)間的最小值為準(zhǔn)。
socket的write系統(tǒng)調(diào)用最后調(diào)用的是tcp_sendmsg,源碼如下所示:
int tcp_sendmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg,
size_t size){
......
timeo = sock_sndtimeo(sk, flags & MSG_DONTWAIT);
......
while (--iovlen >= 0) {
......
// 此種情況是buffer不夠了
if (copy <= 0) {
new_segment:
......
if (!sk_stream_memory_free(sk))
goto wait_for_sndbuf;
?
skb = sk_stream_alloc_skb(sk, select_size(sk),sk->sk_allocation);
if (!skb)
goto wait_for_memory;
}
......
}
......
// 這邊等待write buffer有空間
wait_for_sndbuf:
set_bit(SOCK_NOSPACE, &sk->sk_socket->flags);
wait_for_memory:
if (copied)
tcp_push(sk, flags & ~MSG_MORE, mss_now, TCP_NAGLE_PUSH);
// 這邊等待timeo長(zhǎng)的時(shí)間
if ((err = sk_stream_wait_memory(sk, &timeo)) != 0)
goto do_error;
......
out:
// 如果拷貝了數(shù)據(jù),則返回
if (copied)
tcp_push(sk, flags, mss_now, tp->nonagle);
TCP_CHECK_TIMER(sk);
release_sock(sk);
return copied;
out_err:
// error的處理
err = sk_stream_error(sk, flags, err);
TCP_CHECK_TIMER(sk);
release_sock(sk);
return err;
}
從上面的內(nèi)核代碼看出,如果socket的write buffer依舊有空間的時(shí)候,會(huì)立馬返回,并不會(huì)有timeout。但是write buffer不夠的時(shí)候,會(huì)等待SO_SNDTIMEO的時(shí)間(nonblock時(shí)候?yàn)?)。但是如果SO_SNDTIMEO沒(méi)有設(shè)置的時(shí)候,默認(rèn)初始化為MAX_SCHEDULE_TIMEOUT,可以認(rèn)為其超時(shí)時(shí)間為無(wú)限。那么其超時(shí)時(shí)間會(huì)有另一個(gè)條件來(lái)決定,我們看下sk_stream_wait_memory的源碼:
int sk_stream_wait_memory(struct sock *sk, long *timeo_p){
// 等待socket shutdown或者socket出現(xiàn)err
sk_wait_event(sk, ¤t_timeo, sk->sk_err ||
(sk->sk_shutdown & SEND_SHUTDOWN) ||
(sk_stream_memory_free(sk) &&
!vm_wait));
}
在write等待的時(shí)候,如果出現(xiàn)socket被shutdown或者socket出現(xiàn)錯(cuò)誤的時(shí)候,則會(huì)跳出wait進(jìn)而返回錯(cuò)誤。在不考慮對(duì)端shutdown的情況下,出現(xiàn)sk_err的時(shí)間其實(shí)就是其write的timeout時(shí)間,那么我們看下什么時(shí)候出現(xiàn)sk->sk_err。
SO_SNDTIMEO不設(shè)置,write buffer滿(mǎn)之后ack一直不返回的情況(例如,物理機(jī)宕機(jī))
物理機(jī)宕機(jī)后,tcp發(fā)送msg的時(shí)候,ack不會(huì)返回,則會(huì)在重傳定時(shí)器tcp_retransmit_timer到期后timeout,其重傳到期時(shí)間通過(guò)tcp_retries2以及TCP_RTO_MIN計(jì)算出來(lái)。
tcp_retries2的設(shè)置位置為:
cat /proc/sys/net/ipv4/tcp_retries2
SO_SNDTIMEO不設(shè)置,write buffer滿(mǎn)之后對(duì)端不消費(fèi),導(dǎo)致buffer一直滿(mǎn)的情況
和上面ack超時(shí)有些許不一樣的是,一個(gè)邏輯是用TCP_RTO_MIN通過(guò)tcp_retries2計(jì)算出來(lái)的時(shí)間。另一個(gè)是真的通過(guò)重傳超過(guò)tcp_retries2次數(shù)來(lái)time_out,兩者的區(qū)別和rto的動(dòng)態(tài)計(jì)算有關(guān)。但是可以大致認(rèn)為是一致的。
上述邏輯如下圖所示:

write_timeout表格
tcp_retries2 |
buffer未滿(mǎn) |
buffer滿(mǎn) |
5 |
立即返回 |
min(SO_SNDTIMEO,(25.6s-51.2s)根據(jù)動(dòng)態(tài)rto定 |
15 |
立即返回 |
min(SO_SNDTIMEO,(924.6s-1044.6s)根據(jù)動(dòng)態(tài)rto定 |
4、接收超時(shí)
在tcp協(xié)議中,讀的操作和寫(xiě)操作的邏輯是相通的。
tcp連接建立后,兩邊的通信無(wú)非就是報(bào)文的互傳。寫(xiě)操作是將數(shù)據(jù)放到tcp報(bào)文中發(fā)送給對(duì)端,然后等待對(duì)端響應(yīng),一定時(shí)間沒(méi)有得到響應(yīng)就是超時(shí)。而讀操作其實(shí)就是發(fā)送一個(gè)讀取數(shù)據(jù)的報(bào)文給對(duì)端,然后對(duì)端返回帶有數(shù)據(jù)的報(bào)文,一定時(shí)間沒(méi)有收到對(duì)端的報(bào)文則認(rèn)為超時(shí)。對(duì)于tcp協(xié)議而言,其實(shí)不會(huì)分辨他們發(fā)送的報(bào)文具體是要干嘛,因此readTimeout的判斷邏輯和writeTimeout基本一樣。它的重傳次數(shù)也是由參數(shù)net.ipv4.tcp_retries2控制。在應(yīng)用層面也一般是統(tǒng)一叫socketTimeout。
read系統(tǒng)調(diào)用
socket的read系統(tǒng)調(diào)用最終調(diào)用的是tcp_recvmsg, 其源碼如下:
int tcp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
size_t len, int nonblock, int flags, int *addr_len)
{
......
// 這邊timeo=SO_RCVTIMEO
timeo = sock_rcvtimeo(sk, nonblock);
......
do{
......
// 下面這一堆判斷表明,如果出現(xiàn)錯(cuò)誤,或者已經(jīng)被CLOSE/SHUTDOWN則跳出循環(huán)
if(copied) {
if (sk->sk_err ||
sk->sk_state == TCP_CLOSE ||
(sk->sk_shutdown & RCV_SHUTDOWN) ||
!timeo ||
signal_pending(current))
break;
} else {
if (sock_flag(sk, SOCK_DONE))
break;
?
if (sk->sk_err) {
copied = sock_error(sk);
break;
}
// 如果socket shudown跳出
if (sk->sk_shutdown & RCV_SHUTDOWN)
break;
// 如果socket close跳出
if (sk->sk_state == TCP_CLOSE) {
if (!sock_flag(sk, SOCK_DONE)) {
/* This occurs when user tries to read
* from never connected socket.
*/
copied = -ENOTCONN;
break;
}
break;
}
.......
}
.......
?
if (copied >= target) {
/* Do not sleep, just process backlog. */
release_sock(sk);
lock_sock(sk);
} else /* 如果沒(méi)有讀到target自己數(shù)(和水位有關(guān),可以暫認(rèn)為是1),則等待SO_RCVTIMEO的時(shí)間 */
sk_wait_data(sk, &timeo);
} while (len > 0);
......
}
上面的邏輯如下圖所示:

重傳以及探測(cè)定時(shí)器timeout事件的觸發(fā)時(shí)機(jī)如下圖所示:

如果內(nèi)核層面ack正常返回而且對(duì)端窗口不為0,僅僅應(yīng)用層不返回任何數(shù)據(jù),那么就會(huì)無(wú)限等待,直到對(duì)端有數(shù)據(jù)或者socket close/shutdown為止,如下圖所示:

很多應(yīng)用就是基于這個(gè)無(wú)限超時(shí)來(lái)設(shè)計(jì)的,例如activemq的消費(fèi)者邏輯。
ReadTimeout超時(shí)表格
C系統(tǒng)調(diào)用:
tcp_retries2 |
對(duì)端無(wú)響應(yīng) |
對(duì)端內(nèi)核響應(yīng)正常 |
5 |
min(SO_RCVTIMEO,(25.6s-51.2s)根據(jù)動(dòng)態(tài)rto定 |
SO_RCVTIMEO==0?無(wú)限,SO_RCVTIMEO) |
15 |
min(SO_RCVTIMEO,(924.6s-1044.6s)根據(jù)動(dòng)態(tài)rto定 |
SO_RCVTIMEO==0?無(wú)限,SO_RCVTIMEO) |
Java系統(tǒng)調(diào)用
tcp_retries2 |
對(duì)端無(wú)響應(yīng) |
對(duì)端內(nèi)核響應(yīng)正常 |
|
5 |
min(SO_TIMEOUT,(25.6s-51.2s)根據(jù)動(dòng)態(tài)rto定 |
SO_TIMEOUT==0?無(wú)限,SO_RCVTIMEO |
|
15 |
min(SO_TIMEOUT,(924.6s-1044.6s)根據(jù)動(dòng)態(tài)rto定 |
SO_TIMEOUT==0?無(wú)限,SO_RCVTIMEO |
5、對(duì)端物理機(jī)宕機(jī)之后的超時(shí)
對(duì)端物理機(jī)宕機(jī)后還依舊有數(shù)據(jù)發(fā)送
對(duì)端物理機(jī)宕機(jī)時(shí)對(duì)端內(nèi)核也gg了(不會(huì)發(fā)出任何包通知宕機(jī)),那么本端發(fā)送任何數(shù)據(jù)給對(duì)端都不會(huì)有響應(yīng)。其超時(shí)時(shí)間就由上面討論的min(設(shè)置的socket超時(shí)[例如SO_TIMEOUT],內(nèi)核內(nèi)部的定時(shí)器超時(shí)來(lái)決定)。
對(duì)端物理機(jī)宕機(jī)后沒(méi)有數(shù)據(jù)發(fā)送,但在read等待
這時(shí)候如果設(shè)置了超時(shí)時(shí)間timeout,則在timeout后返回。但是,如果僅僅是在read等待,由于底層沒(méi)有數(shù)據(jù)交互,那么其無(wú)法知道對(duì)端是否宕機(jī),所以會(huì)一直等待。但是,內(nèi)核會(huì)在一個(gè)socket兩個(gè)小時(shí)都沒(méi)有數(shù)據(jù)交互情況下(可設(shè)置)啟動(dòng)keepalive定時(shí)器來(lái)探測(cè)對(duì)端的socket。如下圖所示:

大概是2小時(shí)11分鐘之后會(huì)超時(shí)返回。keepalive的設(shè)置由內(nèi)核參數(shù)指定:
cat /proc/sys/net/ipv4/tcp_keepalive_time 7200 即兩個(gè)小時(shí)后開(kāi)始探測(cè)
cat /proc/sys/net/ipv4/tcp_keepalive_intvl 75 即每次探測(cè)間隔為75s
cat /proc/sys/net/ipv4/tcp_keepalve_probes 9 即一共探測(cè)9次
可以在setsockops中對(duì)單獨(dú)的socket指定是否啟用keepalive定時(shí)器。
對(duì)端物理機(jī)宕機(jī)后沒(méi)有數(shù)據(jù)發(fā)送,也沒(méi)有read等待
和上面同理,也是在keepalive定時(shí)器超時(shí)之后,將連接close。所以我們可以看到一個(gè)不活躍的socket在對(duì)端物理機(jī)突然宕機(jī)之后,依舊是ESTABLISHED狀態(tài),過(guò)很長(zhǎng)一段時(shí)間之后才會(huì)關(guān)閉。
6、進(jìn)程宕機(jī)后的超時(shí)
物理機(jī)突然宕機(jī)和進(jìn)程宕掉的表現(xiàn)不一樣。一個(gè)tcp連接建立后,如果一端的物理機(jī)突然宕機(jī),另外一端是完全不知情的,它會(huì)像往常一樣繼續(xù)發(fā)送相關(guān)報(bào)文,直到超時(shí)時(shí)間到了才返回。另外,一般操作系統(tǒng)會(huì)有機(jī)制檢測(cè)來(lái)釋放該tcp連接。而如果只是進(jìn)程宕掉,在進(jìn)程退出的時(shí)候,操作會(huì)負(fù)責(zé)回收這個(gè)進(jìn)程所屬的所有tcp連接,在這時(shí)會(huì)向這些tcp連接的對(duì)端發(fā)送FIN報(bào)文,表示要關(guān)閉連接了,這時(shí)候?qū)Χ耸强梢灾肋B接已經(jīng)關(guān)閉的。(如果進(jìn)程退出后還收到來(lái)自對(duì)端的報(bào)文,那么內(nèi)核會(huì)立馬發(fā)送reset給對(duì)端,從而不會(huì)卡住對(duì)端的線(xiàn)程資源)
所以如果僅僅是對(duì)端進(jìn)程宕機(jī)的話(huà)(進(jìn)程所在內(nèi)核會(huì)close其所擁有的所有socket),由于fin包的發(fā)送,本端內(nèi)核可以立刻知道當(dāng)前socket的狀態(tài)。如果socket是阻塞的,那么將會(huì)在當(dāng)前或者下一次write/read系統(tǒng)調(diào)用的時(shí)候返回給應(yīng)用層相應(yīng)的錯(cuò)誤。如果是nonblock,那么會(huì)在select/epoll中觸發(fā)出對(duì)應(yīng)的事件通知應(yīng)用層去處理。如果fin包沒(méi)發(fā)送到對(duì)端,那么在下一次write/read的時(shí)候內(nèi)核會(huì)發(fā)送reset包作為回應(yīng)。
nonblock
設(shè)置為nonblock=true后,由于read/write都是立刻返回,且通過(guò)select/epoll等處理重傳超時(shí)/probe超時(shí)/keep alive超時(shí)/socket close等事件,所以根據(jù)應(yīng)用層代碼決定其超時(shí)特性。定時(shí)器超時(shí)事件發(fā)生的時(shí)間如上面幾小節(jié)所述,和是否nonblock無(wú)關(guān)。nonblock的編程模式可以讓?xiě)?yīng)用層對(duì)這些事件做出響應(yīng)。
7、總結(jié)
網(wǎng)絡(luò)編程中超時(shí)時(shí)間是個(gè)重要但又容易被忽略的問(wèn)題,這個(gè)問(wèn)題只有在遇到物理機(jī)宕機(jī),偶爾的網(wǎng)絡(luò)抖動(dòng)等平時(shí)遇不到的現(xiàn)象時(shí)候才會(huì)凸顯。希望本篇文章可以對(duì)讀者在以后遇到類(lèi)似超時(shí)問(wèn)題時(shí)有所幫助,只要涉及到阻塞式的網(wǎng)絡(luò)請(qǐng)求,就一定要加超時(shí),否則你將進(jìn)入自己曾經(jīng)種下的苦果,加班,掉的不是汗水,是頭發(fā)。
原文https://zhuanlan.zhihu.com/p/535405145
★文末名片可以免費(fèi)領(lǐng)取音視頻開(kāi)發(fā)學(xué)習(xí)資料,內(nèi)容包括(FFmpeg ,webRTC ,rtmp ,hls ,rtsp ,ffplay ,srs)以及音視頻學(xué)習(xí)路線(xiàn)圖等等。文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-663138.html
見(jiàn)下方!↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-663138.html
到了這里,關(guān)于深入剖析阻塞式socket的timeout的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!