qt定时器是阻塞的吗_吊打面试官 | 面试官:TCP真的可靠吗
生活随笔
收集整理的這篇文章主要介紹了
qt定时器是阻塞的吗_吊打面试官 | 面试官:TCP真的可靠吗
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
點擊藍字關注我哦
以下是本期干貨視頻視頻后還附有文字版本哦▼《面試官:TCP真的可靠嗎》▼
ps:請在WiFi環境下打開,如果有錢任性請隨意
TCP真的可靠嗎
面試官經常會問的一個問題是,如果TCP服務器宕機了,會發生什么?換句話說,TCP真的可靠嗎?這個問題要從兩個方面來回答:1.TCP是個可靠的協議,怎么保證它可靠的。2.TCP并不能保證它所發送數據的可靠傳輸。TCP如何保證可靠性?
首先,我們看看數據報不可靠有哪些問題,以及TCP是怎么解決的?1.差錯。TCP通過首部的校驗和,可以校驗首部和和數據。這是一種端到端的校驗,目的是檢測數據在傳輸過程中的任何變化,如果收到對端的校驗和有差錯,TCP將這個包丟棄并且不確認。2.丟包。TCP發出一個數據包后,啟動一個定時器,等待對端確認收到這個數據包,如果不能及時收到這個確認,將重發這個報文。3.失序。TCP承載于IP數據包來傳輸,IP包的到達可能會失序,因此TCP數據包的到達也可能失序,TCP對收到的數據包按照首部的序列號進行重新排序。4.重復。IP數據包會發生重復,TCP接收端根據TCP首部的序列號將重復的數據丟棄。此外,確認數據包,也不能是確認了一個數據包再發送下一個數據包,這不利于并行的批量發送,我們可以批量的發送,再批量的確認。這里有兩個問題需要考慮:1.接收方的處理能力,2.網絡的處理能力。1.首先來看看接收方的處理能力。當接收方的硬件能力不如發送方,或者是系統繁忙,那發送過去的報文只能丟棄。要限制發送方的發送速度,接收方就要告訴發送方它的處理能力,好讓發送發方限制它的發送速度就可以了,這就是滑動窗口的由來。2.下面來看看網絡處理能力。如果發送TCP數據包的速度快于中間某個路由器的發速度,路由器就開始丟包。導致較高的丟包率,如果TCP繼續保持這個速度送數據,那么網絡的性能就會極大的降低。這就需要擁塞控制算法。它分為兩分,一個是慢啟動,一個是擁塞避免。慢啟動指的就是TCP在一開始發送數據的時候以低速傳輸,只要能夠得到對應報文的ACK,就以指數級的速度提高速率。當增長到一個閾值時,增長速度就變成線性增長,而不是指數級的。或者是丟包嚴重了,說明網絡出現擁塞,要降低發送速率,進入擁塞避免階段。TCP并不能保證它所發送數據的可靠傳輸
可靠指的是什么,不可靠指的是什么?
上面我們討論了TCP通過很多機制保證可靠,這種可靠只是在端到端的通信上。假設數據從A進程送到B進程,數據從A進程通過它所在主機TCP/IP協議棧向下傳輸,經過若干臺路由器,通過進程B所在主機的TCP/IP協議棧向上傳輸,最后到達B進程。這些路由器沒有TCP層,只是轉發IP數據報,IP是個不可靠的協議。TCP能夠向進程B保證所有到達的數據是按序且未受損的。但有個問題, TCP已經ACK的數據包實際上不一定會抵達應用進程。比如,接收端TCP剛對數據進行ACK,但應用程序還沒有讀走,就崩潰了。針對TCP的ACK的數據報不能抵達目的應用程序的解決方案
我們的解決方案是應用層ACK。下面給一個簡單的實現,我們采用停等的方式來實現回射客戶服務器。這里設計成客戶端和服務器有兩條通道,主要的原因是想讓發送數據模塊和接收網絡數據模塊都能夠獲取網絡中對端的狀態,而不是將狀態混在一條通道上。這里給大家實現向外發送數據模塊。實現思路是:發送一條消息后,在定時器到之前必須接收對等實體發過來的應用層ACK,如果定時器時間到,我們就終止程序,當然,你可以實現的更復雜,比如重傳。#define APP_ACK 0x01int main( void ) { fd_set allset; fd_set readset; fd_set sockonly; struct timeval tv; struct timeval *tvp = NULL; const static struct timeval timeout = {2,0}; char buf[1024] = NULL; int sfd = tcp_client(9999); FD_ZERO(&allset); FD_ZERO(&readset); FD_ZERO(&sockonly); FD_SET(sfd, &allset); sockonly = allset; FD_SET(fileno(stdin), &allset); readset = allset; for ( ; ; ) { memset(buf, 0x00, sizeof(buf)); /* select分兩種情況: 1.接收來自鍵盤和網絡的事件。 2.當向網絡發送數據后,只能接收網絡事件,不能接收鍵盤事件,這時想接收網絡的對端應用ACK。 */ int ready = select(sfd+1, &readset, NULL, NULL, tvp); if ( ready < 0 ) perror("select"),exit(1); // 超時,不能及時獲取對端的應用ACK,打印超時并退出程序 if ( ready == 0 ) printf("message time out\n"),exit(1); // 接收網絡數據 if ( FD_ISSET(sfd, &readset) ) { int r = read(sfd, buf, sizeof(buf)); // 如果是錯誤或者EOF,終止程序 if ( r == 0 ) { printf("server close\n"); break; } else if ( r == -1 ) { perror("read"),exit(1); } else if ( r==1 && buf[0] != APP_ACK ) { printf("沒有收到對方的回應報文\n"); } // 收到確認報文,關閉定時器 tvp = NULL; // 允許接收來自網絡和鍵盤事件 readset = allset; } if ( FD_ISSET(fileno(stdin), &readset) ) { // 獲取鍵盤事件,發送網絡,啟動定時器,只接受網絡事件 int r = read(filene(stdin), buf, sizeof(buf)); if ( r == -1 ) perror("read"),exit(1); write(sfd, buf, r); tv = timeout; tvp = &tv; readset = sockonly; } }}當然這里設計的是停等協議,如果需要做的更好,像TCP內核協議棧一樣,可以考慮把應用程序做成事件驅動的,這是一個軟件設計的問題,就不在這里討論。故障類型
通過前面的討論我們可以看到網絡程序員不能認為TCP為我們做好了一切。我們可以把故障分為兩類:1.收不到FIN的故障,比如網絡掉線,或者主機崩潰都是這種情況。2.能收到FIN的故障,比如對方應用程序崩潰。收不到FIN的故障
先來說說沒有FIN的故障,分成四種情況:1.如果剛好阻塞在read函數上,這時沒法恢復。可以通過設置讀超時來解決。struct timeval tv;tv.tv_sec = 2;tv.tv_usec = 0;setsockopt(cfd, SOL_SOCKET, SO_RCVTIMEO, (const char *) &tv, sizeof tv);2.如果是先write,再read,協議棧會持續重傳。經過多次重傳不成功,協議棧會標記連接異常,阻塞的read就會得到TIMEOUT錯誤。3.如果是阻塞在select或epoll上,建議做心跳包。下面是一個有心跳功能的回射客戶服務器程序客戶端程序。十秒中如果沒有數據通信,就心跳,執行三次如果沒有應答,就退出。int main( void ) { int heartbeat = 0; fd_set allfd; fd_set readfd; struct timeval tv; msg_t msg; char buf[1024]; int cfd = tcp_client(); FD_ZERO(&allfd); FD_SET(cfd, &allfd); FD_SET(fileno(stdin), &allfd); tv.tv_sec = 10; tv.tv_usec = 0; for ( ; ; ) { readfd = allfd; memset(&msg, 0x00, sizeof(msg)); int ready = select(cfd+1, &readfd, NULL, NULL, &tv); if ( ready == -1 ) perror("select"),exit(1); if ( ready == 0 ) { printf("timeout %d\n", heartbeat); if ( ++heartbeat > 3 ) { printf("connection dead\n"); exit(0); } msg.type = htonl(MSG_HEARTBEAT); if ( write(cfd, (char*)&msg, sizeof(msg)) == -1 ) perror("write"),exit(1); tv.tv_sec = 2; continue; } if ( FD_ISSET(cfd, &readfd) ) { int ret = read(cfd, (char*)&msg, sizeof(msg)); if ( ret == 0 ) { printf("server close\n"); break; } if ( ntohl(msg.type) == MSG_ECHO ) { printf("=> %s\n", msg.data); heartbeat = 0; tv.tv_sec = 10; continue; } } if ( FD_ISSET(fileno(stdin), &readfd) ) { msg.type = htonl(MSG_ECHO); if ( fgets(msg.data, 100, stdin) == NULL ) break; if ( write(cfd, (char*)&msg, sizeof(msg)) == -1 ){ perror("write"); break; } } } close(cfd);}4.還有一種特殊情況就是,如果是主機崩潰又重啟了,這時對端主機得到RST錯誤。能收到FIN的故障
再來看看能收到FIN的故障,這里要意識到,從一個用程序角度,對端進程崩潰還是調用了close以及exit是無法區分的,在這兩種情況下TCP都會向我們發送一個FIN。1.如果是read,直接得到FIN信息,返回0。2.如果是write,則第一次調用會得到RST。3.收到RST,再多次調用write就得到SIGPIPE信號。總結
1.TCP通過序號和超時重傳保證了端到端的可靠。2.TCP并不能保證應用層的可靠。3.異常的情況分為,網絡故障,主機崩潰和進程崩潰。網絡故障和主機故障可以看作是一類故障,當然是指除了主機崩潰并在TCP放棄連接之前,就重啟了的情況。作者:李濤
審稿:王海斌
編輯:小丸子
點亮"在看",點亮"offer"
? ? ? ? ? ? ? ? ? ? ? ? ? ? ?原創不易,點個贊吧~
總結
以上是生活随笔為你收集整理的qt定时器是阻塞的吗_吊打面试官 | 面试官:TCP真的可靠吗的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 为什么vs会输出一个框作为结果_检测与分
- 下一篇: 微服务发展的历史_Spring Clou