套接字(Socket)編程(二) 內(nèi)部通信原理

起初我覺(jué)得學(xué)習(xí)套接字并不需要知道內(nèi)部的通信原理,因?yàn)檫@些都是由系統(tǒng)來(lái)處理骡显,但是隨著后來(lái)的深入我發(fā)現(xiàn)了這個(gè)錯(cuò)誤的想法施籍,后面有關(guān)套接字的相關(guān)設(shè)置都與內(nèi)部通信原理息息相關(guān)(包括后來(lái)的設(shè)置套接字可選項(xiàng)兢哭、套接字I/O緩存)诅挑,今天一起來(lái)探究一下套接字的內(nèi)部的通信原理

上一篇我們聊了Socket連接創(chuàng)建及通信流程的一些基本函數(shù) 《套接字(Socket)編程(一) 函數(shù)概念篇》,這篇我們將來(lái)聊一聊TCP的連接和斷開(kāi)甲棍、TCP套接字I/O緩沖區(qū)简识、UDP套接字I/O緩沖區(qū)UDP套接字的無(wú)連接和有連接模式

關(guān)于下面說(shuō)到的TCP套接字連接握手感猛、斷開(kāi)握手以及中間的數(shù)據(jù)交互涉及到的 SYN七扰、ACK、FIN陪白、SEQ等關(guān)鍵詞請(qǐng)參考我后面一片文章《TCP/IP 協(xié)議及數(shù)據(jù)格式》颈走,文章 “3.2 TCP協(xié)議數(shù)據(jù)報(bào)的頭”部分,里面有詳細(xì)的講解咱士;關(guān)于連接立由、斷開(kāi)和通信過(guò)程中ACK和SEQ的數(shù)值變化以及交互的細(xì)節(jié)可以參考這篇文章里面的 “3.3 TCP通信數(shù)據(jù)交互細(xì)節(jié)和實(shí)踐”部分轧钓,里面有詳細(xì)的數(shù)據(jù)抓取和講解。

一锐膜、TCP連接的三次握手和斷開(kāi)的四次握手

在講到下面函數(shù)之前毕箍,我們不得不先說(shuō)下連接過(guò)程和端口的過(guò)程,雖然我們用C代碼寫(xiě)連接的時(shí)候看不到握手的過(guò)程道盏,但是里面的參數(shù)我們是可以通過(guò)相關(guān)函數(shù)調(diào)用進(jìn)行設(shè)置而柑,后面講的函數(shù)很多關(guān)乎到這里面的參數(shù)設(shè)置

首先來(lái)說(shuō)下TCP套接字連接進(jìn)程中的10種狀態(tài)

狀態(tài) 描述
LISTEN 偵聽(tīng)來(lái)自遠(yuǎn)端TCP協(xié)議端口的連接請(qǐng)求
SYN-SENT 發(fā)送連接請(qǐng)求后,等待匹配的連接請(qǐng)求
SYN-RECEIVED 收到和發(fā)送一個(gè)連接請(qǐng)求后荷逞,等待確認(rèn)
ESTABLISHED 連接已經(jīng)打開(kāi)媒咳,可以發(fā)送或接收數(shù)據(jù)
FIN-WAIT-1 發(fā)送了中斷連接請(qǐng)求,等待對(duì)方確認(rèn)
FIN-WAIT-2 收到對(duì)方對(duì)于中斷請(qǐng)求的確認(rèn)种远,等待對(duì)方的中斷請(qǐng)求
TIME-WAIT 等待足夠的時(shí)間涩澡,以保證遠(yuǎn)端收到連接中斷請(qǐng)求的確認(rèn)
CLOSE-WAIT 等待本地應(yīng)用層發(fā)來(lái)中斷請(qǐng)求
LAST-ACK 等待遠(yuǎn)端TCP協(xié)議對(duì)連接中斷的確認(rèn)
CLOSED 沒(méi)有任何連接
1.1 TCP連接時(shí)的三次握手
TCP建立的三次握手.png

步驟:
①第一條消息為SYN消息Synchronization同步消息,表示收發(fā)數(shù)據(jù)前傳輸?shù)耐较?br> SEQ = x:表示現(xiàn)在傳遞的數(shù)據(jù)包的序號(hào)為x院促,如果接收無(wú)誤筏养,請(qǐng)通知我向你傳遞x+1號(hào)數(shù)據(jù)包
②接著服務(wù)器會(huì)回復(fù)SYN+ACK類(lèi)型數(shù)據(jù)消息,服務(wù)器對(duì)客戶(hù)端首次傳輸?shù)臄?shù)據(jù)包確認(rèn)(ACK)和服務(wù)器傳輸數(shù)據(jù)做準(zhǔn)備的同步消息(SYN)捆綁發(fā)送
ACK = x+1:表示剛才客戶(hù)端的序號(hào)為x的數(shù)據(jù)包接收無(wú)誤常拓,接下來(lái)請(qǐng)傳輸序號(hào)為x+1的數(shù)據(jù)包
SEQ = y:表示現(xiàn)傳遞的數(shù)據(jù)包序號(hào)為y,如果接收無(wú)誤辉浦,請(qǐng)通知我向你傳輸y+1號(hào)數(shù)據(jù)包
③客戶(hù)端回復(fù)服務(wù)器ACK消息
SEQ = x+1:表示向服務(wù)器傳遞序號(hào)為x+1數(shù)據(jù)包
ACK = y+1:表示接受服務(wù)器端的序號(hào)為y的數(shù)據(jù)包接收無(wú)誤弄抬,接下來(lái)可以傳輸序號(hào)為y+1的數(shù)據(jù)包

連接三次握手為了防止已失效的連接請(qǐng)求報(bào)文段突然又傳到了服務(wù)器,導(dǎo)致服務(wù)器誤認(rèn)為客戶(hù)端想請(qǐng)求連接而發(fā)生連接的錯(cuò)誤
舉個(gè)例子:在兩次握手的前提下宪郊,Client發(fā)出連接請(qǐng)求掂恕,但因?yàn)閬G失了,故而不能收到Server的確認(rèn)弛槐。于是Client重新發(fā)出請(qǐng)求懊亡,然后收到確認(rèn),建立連接乎串,數(shù)據(jù)傳輸完畢后店枣,釋放連接,Client發(fā)了2個(gè)叹誉,但是鸯两,某種情況下,Client發(fā)出的第一個(gè)連接請(qǐng)求在某個(gè)節(jié)點(diǎn)滯留了长豁,延誤到達(dá)Server钧唐。假設(shè)此時(shí)Server已經(jīng)釋放連接,那么Server在收到此實(shí)現(xiàn)的連接請(qǐng)求后匠襟,就誤認(rèn)為Client又發(fā)出一次連接請(qǐng)求钝侠,在兩次握手的情況下(Client發(fā)生請(qǐng)求该园,Server接受請(qǐng)求并確認(rèn)),Server就認(rèn)為Client又發(fā)出一次新連接請(qǐng)求帅韧。此時(shí)Server就又給Client發(fā)生一個(gè)確認(rèn)爬范,表示同意建立連接。因?yàn)槭莾纱挝帐秩醴耍珻lient收到后青瀑,也不再次發(fā)出確認(rèn)連接。此時(shí)Server會(huì)等待Client發(fā)送的數(shù)據(jù)萧诫,而Client本來(lái)就沒(méi)有要求發(fā)送數(shù)據(jù)斥难,肯定也無(wú)動(dòng)于衷。此時(shí)Server的資源就被浪費(fèi)了帘饶。

1.2 TCP數(shù)據(jù)交換

通過(guò)第一步的三次握手過(guò)程完成數(shù)據(jù)交換哑诊,下面就正式開(kāi)始收發(fā)數(shù)據(jù),其默認(rèn)方式如下圖

TCP數(shù)據(jù)交換工作原理.png

如圖:客戶(hù)端分兩次向服務(wù)器傳遞了200字節(jié)的過(guò)程及刻,首先客戶(hù)端通過(guò)一個(gè)數(shù)據(jù)包發(fā)送100個(gè)字節(jié)镀裤,數(shù)據(jù)包的SEQ為1200,服務(wù)器為了確認(rèn)這一點(diǎn)缴饭,向客戶(hù)端發(fā)送ACK為1300消息暑劝。
此時(shí)ACK號(hào)為1300而非1200,也不是1301颗搂,原因在于ACK號(hào)的增量為傳遞的數(shù)據(jù)字節(jié)數(shù)担猛,假設(shè)每次ACK號(hào)不加傳輸?shù)淖止?jié),這樣雖然可以確認(rèn)數(shù)據(jù)包的傳輸丢氢,但無(wú)法確認(rèn)100字節(jié)全部正確傳遞還是丟失了一部分傅联,比如只傳遞了80個(gè)字節(jié),按公式計(jì)算傳遞ACK消息ACK號(hào) = SEQ號(hào) + 傳遞的字節(jié)數(shù)疚察。
網(wǎng)上和書(shū)上很多說(shuō)法說(shuō)這里ACK值的計(jì)算應(yīng)該是ACK號(hào) = SEQ號(hào) + 傳遞的字節(jié)數(shù) + 1蒸走,實(shí)際這種說(shuō)法是不準(zhǔn)確的,在連接的三次握手和斷開(kāi)的4次握手里面貌嫡,每次傳輸數(shù)據(jù)長(zhǎng)度為0(這些數(shù)據(jù)報(bào)只有報(bào)頭)比驻,回復(fù)確認(rèn)都是ACK號(hào) = 接收到數(shù)據(jù)報(bào)的SEQ號(hào) + 1,但是除了這特別的情況外(連接完成衅枫,正常通信過(guò)程中)嫁艇,每個(gè)數(shù)據(jù)報(bào)的SEQ都是這段TCP數(shù)據(jù)首個(gè)字節(jié)的序號(hào),按上面的圖來(lái)說(shuō)弦撩,第一次傳輸時(shí)SEQ = 1200步咪,其實(shí)序列號(hào)1200也是該段數(shù)據(jù)第一個(gè)字節(jié)的序號(hào),那么第二個(gè)字節(jié)的序號(hào)就是1201益楼,傳輸了100個(gè)字節(jié)猾漫,該數(shù)據(jù)段的最后一個(gè)字節(jié)的序號(hào)應(yīng)該是1299点晴,下次需要傳輸?shù)臄?shù)據(jù)要從1300開(kāi)始,所以服務(wù)器回復(fù)的確認(rèn)號(hào)是1300悯周,而不是1301粒督,這里需要注意下。
客戶(hù)端在規(guī)定時(shí)間沒(méi)有收到服務(wù)器的確認(rèn)消息禽翼,則認(rèn)為丟失屠橄,然后重傳

2. 斷開(kāi)時(shí)的四次握手

對(duì)于TCP的斷開(kāi)也是非常優(yōu)雅的,如下圖

TCP斷開(kāi)四次握手.png

大概流程簡(jiǎn)單翻譯:
客戶(hù)端:“我希望斷開(kāi)連接”
服務(wù)器:“哦闰挡,是嗎锐墙?請(qǐng)稍等”

服務(wù)器:“我也準(zhǔn)備就緒,可以斷開(kāi)連接”
客戶(hù)端:“好的长酗,謝謝合作”

如上圖所示溪北,數(shù)據(jù)包內(nèi)的FIN表示斷開(kāi)連接,也就是說(shuō)雙方各發(fā)送1次FIN消息后斷開(kāi)連接夺脾,此過(guò)程經(jīng)理4個(gè)階段之拨,因此又稱(chēng)四次握手,SEQ和ACK前面已經(jīng)做過(guò)解釋?zhuān)适÷浴?/p>

注意:
服務(wù)器收到客戶(hù)端連FIN報(bào)文段后就立即發(fā)送確認(rèn)咧叭,然后就進(jìn)入close-wait狀態(tài)蚀乔,此時(shí)TCP服務(wù)器進(jìn)程就通知高層應(yīng)用進(jìn)程,此時(shí)是“半關(guān)閉”狀態(tài)佳簸。即客戶(hù)端不可以發(fā)送數(shù)據(jù)到服務(wù)器乙墙,但是服務(wù)器可以發(fā)送數(shù)據(jù)給客戶(hù)端。
此時(shí)生均,若服務(wù)器沒(méi)有數(shù)據(jù)報(bào)要發(fā)送給客戶(hù)端了,其應(yīng)用進(jìn)程就通知TCP釋放連接腥刹,然后發(fā)送給客戶(hù)端FIN報(bào)文段马胧,并等待確認(rèn)。
客戶(hù)端發(fā)送確認(rèn)后衔峰,進(jìn)入time-wait佩脊,注意,此時(shí)TCP連接還沒(méi)有釋放掉垫卤,然后經(jīng)過(guò)時(shí)間等待計(jì)時(shí)器設(shè)置的2MSL后威彰,客戶(hù)端才進(jìn)入到close狀態(tài)。
為什么要等待呢?
①穴肘、為了保證客戶(hù)端發(fā)送的最后一個(gè)ACK報(bào)文段能夠到達(dá)服務(wù)器歇盼。即最后這個(gè)確認(rèn)報(bào)文段很有可能丟失,那么服務(wù)器會(huì)超時(shí)重傳评抚,然后客戶(hù)端再一次確認(rèn)豹缀,同時(shí)啟動(dòng)2MSL計(jì)時(shí)器氛悬,如此下去眉枕。如果沒(méi)有等待時(shí)間,發(fā)送完確認(rèn)報(bào)文段就立即釋放連接的話(huà),服務(wù)器就無(wú)法重傳了(連接已被釋放谎僻,任何數(shù)據(jù)都不能出傳了),因而也就收不到確認(rèn)绍刮,就無(wú)法按照步驟進(jìn)入CLOSE狀態(tài)赦役,即必須收到確認(rèn)才能close,流程看下圖

TCP斷開(kāi)Wait-Time.png

②妇汗、防止“已失效的連接請(qǐng)求報(bào)文段”出現(xiàn)在連接中帘不。經(jīng)過(guò)2MSL,那些在這個(gè)連接持續(xù)的時(shí)間內(nèi)铛纬,產(chǎn)生的所有報(bào)文段就可以都從網(wǎng)絡(luò)中消失厌均。即在這個(gè)連接釋放的過(guò)程中會(huì)有一些無(wú)效的報(bào)文段滯留在樓閣結(jié)點(diǎn),但是呢告唆,經(jīng)過(guò)2MSL這些無(wú)效報(bào)文段就肯定可以發(fā)送到目的地棺弊,不會(huì)滯留在網(wǎng)絡(luò)中。這樣的話(huà)擒悬,在下一個(gè)連接中就不會(huì)出現(xiàn)上一個(gè)連接遺留下來(lái)的請(qǐng)求報(bào)文段了模她。
可以看出:服務(wù)器結(jié)束TCP連接的時(shí)間比客戶(hù)端早一點(diǎn),因?yàn)榉?wù)器收到確認(rèn)就斷開(kāi)連接了懂牧,而客戶(hù)端還得等待Time-Wait侈净,雖然這個(gè)Time-Wait看似重要,但是在實(shí)際開(kāi)發(fā)中并不那么討人喜歡僧凤,后面的一片文章里面有介紹怎么去掉這個(gè)Time-Wait的等待(套接字(Socket)編程(三) 套接字可選項(xiàng))畜侦。

上面經(jīng)過(guò)四次握手?jǐn)嚅_(kāi)的屬于正常斷開(kāi),經(jīng)過(guò)四次握手雙方都知道連接斷開(kāi)了躯保,但是平時(shí)通信的過(guò)程中有各種原因會(huì)造成異常斷開(kāi)旋膳,比方說(shuō)服務(wù)器斷電或者客戶(hù)端斷電...一般的TCP通信中有兩種方式來(lái)解決這個(gè)異常:①、自己在應(yīng)用層定時(shí)發(fā)送心跳包來(lái)判斷連接是否正常途事,此方法比較通用验懊,靈活可控,但改變了現(xiàn)有的協(xié)議尸变;②义图、使用TCP的keepalive機(jī)制,TCP協(xié)議自帶的闭倮茫活功能碱工,使用起來(lái)簡(jiǎn)單,減少了應(yīng)用層代碼的復(fù)雜度, 推測(cè)也會(huì)更節(jié)省流量痛垛,因?yàn)橐话銇?lái)說(shuō)應(yīng)用層的數(shù)據(jù)傳輸?shù)絽f(xié)議層時(shí)都會(huì)被加上額外的包頭包尾草慧,由TCP協(xié)議提供的檢活,其發(fā)的探測(cè)包匙头,理論上實(shí)現(xiàn)的會(huì)更精妙(用更少的字節(jié)完成更多的目標(biāo))漫谷,耗費(fèi)更少的流量;具體請(qǐng)看后面文章實(shí)現(xiàn)蹂析,這里先不做深聊

二舔示、TCP套接字中的I/O緩沖區(qū)

如當(dāng)前所述,TCP套接字收發(fā)無(wú)邊界电抚,服務(wù)器端調(diào)用1次send函數(shù)傳輸40個(gè)字節(jié)惕稻,客戶(hù)端也能通過(guò)4次調(diào)用recv函數(shù)每次讀10個(gè)字節(jié),那么這個(gè)時(shí)候問(wèn)題就來(lái)了蝙叛,服務(wù)器一次發(fā)送了40個(gè)字節(jié)的數(shù)據(jù)俺祠,而客戶(hù)端則可以緩慢分批讀取,客戶(hù)端讀取了10個(gè)字節(jié)后剩余的30個(gè)字節(jié)的數(shù)據(jù)在什么地方呢借帘?
實(shí)際上調(diào)用send函數(shù)不是立即發(fā)送數(shù)據(jù)蜘渣,調(diào)用recv函數(shù)也并非立馬接收數(shù)據(jù),更精確的講肺然,send函數(shù)調(diào)用瞬間蔫缸,將數(shù)據(jù)移至輸出緩沖區(qū),recv函數(shù)調(diào)用瞬間际起,從輸入緩沖區(qū)讀取數(shù)據(jù)拾碌,入下圖所示

TCP套接字的I/O緩沖區(qū).png

如圖所示,調(diào)用send函數(shù)將數(shù)據(jù)移至緩沖區(qū)街望,在適當(dāng)?shù)臅r(shí)候(不管是分批傳送還是一次性傳送)傳向?qū)Ψ降妮斎刖彌_區(qū)校翔,這個(gè)時(shí)對(duì)方將調(diào)用recv函數(shù)從自己的輸入緩沖隊(duì)列里面讀取對(duì)方發(fā)送過(guò)來(lái)的數(shù)據(jù)。

特性

  • I/O緩沖區(qū)在每個(gè)套接字中單獨(dú)存在
  • I/O緩沖區(qū)在創(chuàng)建套接字時(shí)自動(dòng)生成
  • 即使關(guān)閉套接字灾前,也可以繼續(xù)傳送輸入緩沖區(qū)里面的數(shù)據(jù)
  • 關(guān)閉套接字將丟失輸入緩沖區(qū)里面的數(shù)據(jù)

三展融、UDP套接字中的I/O緩沖區(qū)

前面說(shuō)過(guò)TCP數(shù)據(jù)傳輸中不存在邊界,這表示數(shù)據(jù)傳輸過(guò)程中調(diào)用I/O函數(shù)的次數(shù)不具有任何意義豫柬,相反UDP是具有數(shù)據(jù)邊界的協(xié)議,傳輸中調(diào)用I/O函數(shù)的次數(shù)非常重要扑浸,因?yàn)檩斎牒瘮?shù)的調(diào)用次數(shù)應(yīng)和輸出函數(shù)的調(diào)用次數(shù)完全一致烧给,這樣才能保證接收全部已發(fā)送數(shù)據(jù),例如調(diào)用了3次輸出函數(shù)發(fā)送數(shù)據(jù)喝噪,就必須調(diào)用3次輸入函數(shù)才能完成接收础嫡,下面通過(guò)簡(jiǎn)單的例子來(lái)印證下

發(fā)送UDP數(shù)據(jù)端代碼

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <errno.h>

int sendPacket(void);

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        
        int send_result = sendPacket();
        if (send_result == 0) printf("開(kāi)啟發(fā)送失敗\n");
    }
    return 0;
}

#pragma mark ---廣播
int sendPacket()
{
    printf("請(qǐng)輸入U(xiǎn)DP數(shù)據(jù)傳送IP地址:");
    char ip[INET_ADDRSTRLEN];
    scanf("%s",ip);
    
    int send_sock;
    send_sock = socket(AF_INET, SOCK_DGRAM, 0);
    if (send_sock < 0) return 0;
    
    struct sockaddr_in addr;
    addr.sin_len = sizeof(struct sockaddr_in);
    addr.sin_family = AF_INET;
    addr.sin_port = htons(2001);
    inet_aton(ip, &addr.sin_addr);
    
    printf("開(kāi)始連續(xù)三次發(fā)送數(shù)據(jù)\n");
    char msg1[] = "Hi";
    char msg2[] = "Hello";
    char msg3[] = "Nice to meet you";
    sendto(send_sock, msg1, sizeof(msg1), 0, (struct sockaddr*)&addr, addr.sin_len);
    sendto(send_sock, msg2, sizeof(msg2), 0, (struct sockaddr*)&addr, addr.sin_len);
    sendto(send_sock, msg3, sizeof(msg3), 0, (struct sockaddr*)&addr, addr.sin_len);
    
    printf("關(guān)閉UDP套接字\n");
    close(send_sock);
    
    return 1;
}

接收UDP數(shù)據(jù)端代碼

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <errno.h>

int recvPacket(void);

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        
        int recv_result = recvPacket();
        if (recv_result == 0) printf("開(kāi)啟接收失敗\n");
    }
    return 0;
}

#pragma mark ---UDP數(shù)據(jù)接收端
int recvPacket()
{
    int recv_sock;
    recv_sock = socket(AF_INET, SOCK_DGRAM, 0);
    if (recv_sock < 0) return 0;
    
    struct sockaddr_in addr,recv_addr;
    socklen_t len = sizeof(struct sockaddr_in);
    
    addr.sin_family = AF_INET;
    addr.sin_len = sizeof(struct sockaddr_in);
    addr.sin_port = htons(2001);
    addr.sin_addr.s_addr = htonl(INADDR_ANY);
    
    if (bind(recv_sock, (struct sockaddr*)&addr, addr.sin_len) < 0) {
        printf("%s\n",strerror(errno));
        return 0;
    }
    
    printf("開(kāi)始接收UDP數(shù)據(jù)\n");
    char recv_buffer[512];
    for (int i = 0; i<3; i++) {
        
        sleep(5);
        
        memset(recv_buffer, 0, 512);
        ssize_t recv_len = recvfrom(recv_sock,
                                    recv_buffer,
                                    sizeof(recv_buffer),
                                    0,
                                    (struct sockaddr*)&recv_addr,
                                    &len);
        if (recv_len <= 0) {
            printf("接收數(shù)據(jù)失敗:%s\n",strerror(errno));
            break;
        }
        printf("recv: %s\n",recv_buffer);
    }
    printf("關(guān)閉UDP套接字\n");
    close(recv_sock);
    return 1;
}

UDP發(fā)送端打印

/**
 *  請(qǐng)輸入U(xiǎn)DP數(shù)據(jù)傳送IP地址:10.22.70.99
 *  開(kāi)始連續(xù)三次發(fā)送數(shù)據(jù)
 *  關(guān)閉UDP套接字
 */

UDP接收端打印

/**
 *  開(kāi)始接收UDP數(shù)據(jù)
 *  recv: Hi
 *  recv: Hello
 *  recv: Nice to meet you
 *  關(guān)閉UDP套接字
 */

結(jié)論
從上面的例子可以看出,發(fā)送端連續(xù)發(fā)送了三個(gè)UDP數(shù)據(jù)包,而接收端輪循了三次去接收榴鼎,每次接收到都等待5s再進(jìn)行下次數(shù)據(jù)讀取伯诬,也就是說(shuō)接收端在讀第一次數(shù)據(jù)的時(shí)候,其實(shí)輸入緩沖區(qū)里面已經(jīng)有三組數(shù)據(jù)巫财,但是他只讀取了最前面的一組盗似,這正好說(shuō)明了發(fā)送端和接收端在傳輸數(shù)據(jù)的過(guò)程中調(diào)用I/O數(shù)據(jù)次數(shù)要一致才能將全部數(shù)據(jù)讀取完

解釋
UDP套接字傳輸?shù)臄?shù)據(jù)包又稱(chēng)數(shù)據(jù)報(bào),實(shí)際上數(shù)據(jù)報(bào)也屬于數(shù)據(jù)包的一種平项,只是與TCP包不同赫舒,其本身可以成為一個(gè)完整數(shù)據(jù),這與UDP數(shù)據(jù)傳輸特性有關(guān)闽瓢,UDP中存在數(shù)據(jù)邊界接癌,1個(gè)數(shù)據(jù)包即可以成為1個(gè)完整數(shù)據(jù),因此稱(chēng)為數(shù)據(jù)報(bào)扣讼。

四缺猛、UDP套接字的連接(connected)和非連接(unconnected)

TCP套接字傳輸數(shù)據(jù)需要注冊(cè)目的地址的IP和端口號(hào)信息,而UDP中則無(wú)需注冊(cè)椭符,于是通過(guò)sendto()函數(shù)發(fā)發(fā)送數(shù)據(jù)的流程大概如下
① 向UDP套接字中注冊(cè)目標(biāo)IP和端口號(hào)
② 傳輸數(shù)據(jù)
③ 刪除套接字中注冊(cè)的IP和端口號(hào)
每次調(diào)用sendto()函數(shù)荔燎,每次都重復(fù)上面步驟變更目標(biāo)地址,因此可以利用同一個(gè)UDP套接字向不同的地址發(fā)送數(shù)據(jù)艰山,這種未注冊(cè)目標(biāo)地址信息的套接字稱(chēng)為未連接套接字湖雹,相反注冊(cè)過(guò)地址的套接字稱(chēng)為連接套接字,顯然UDP默認(rèn)套接字為未連接套接字曙搬。
在平時(shí)開(kāi)發(fā)中有這么一種情況摔吏,需要向同一個(gè)地址連續(xù)發(fā)送UDP數(shù)據(jù),比方說(shuō)前面的《TFTP服務(wù)器和TFTP客戶(hù)端》纵装,需要向同一個(gè)客戶(hù)端地址連續(xù)發(fā)送數(shù)據(jù)包和接收該地址發(fā)送過(guò)來(lái)的確認(rèn)包征讲,這個(gè)時(shí)候如果還用無(wú)連接模式發(fā)送數(shù)據(jù),上述的第一個(gè)階段和第三個(gè)階段占整個(gè)通信過(guò)程近1/3的時(shí)間橡娄。

創(chuàng)建已連接UDP套接字
創(chuàng)建已連接UDP套接字诗箍,跟前面創(chuàng)建未連接UDP套接字步驟一樣,只不過(guò)多調(diào)用了connect函數(shù)挽唉,創(chuàng)建步驟如下:
int sock = socket(AF_INET, SOCK_DGRAM, 0);
addr.sin_family = AF_INET;
addr.sin_port = ....
addr.sin_addr.s_addr = ...
connect(sock, (struct sockaddr*)&addr, sizeof(addr));
針對(duì)UDP套接字調(diào)用connect()函數(shù)并不意味著要與對(duì)方套接字連接滤祖,只是向UDP套接字注冊(cè)目標(biāo)IP和端口信息,之后就與TCP套接字一樣瓶籽,每次調(diào)用sendto()函數(shù)時(shí)只需傳輸數(shù)據(jù)匠童,因?yàn)橐呀?jīng)指定了接收對(duì)象,所以不僅可以使用sendto()塑顺、recvfrom函數(shù)汤求,還可以使用send()俏险、recv()函數(shù)進(jìn)行數(shù)據(jù)傳送和讀取。

五扬绪、尾聲

接下來(lái)的文章會(huì)繼續(xù)更新有關(guān)套接字的詳解竖独,這是我起初學(xué)習(xí)的一個(gè)流程,希望也對(duì)大家有幫助!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末挤牛,一起剝皮案震驚了整個(gè)濱河市莹痢,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌赊颠,老刑警劉巖格二,帶你破解...
    沈念sama閱讀 221,695評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異竣蹦,居然都是意外死亡顶猜,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,569評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門(mén)痘括,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)长窄,“玉大人,你說(shuō)我怎么就攤上這事纲菌∧尤眨” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 168,130評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵翰舌,是天一觀(guān)的道長(zhǎng)嚣潜。 經(jīng)常有香客問(wèn)我,道長(zhǎng)椅贱,這世上最難降的妖魔是什么懂算? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,648評(píng)論 1 297
  • 正文 為了忘掉前任,我火速辦了婚禮庇麦,結(jié)果婚禮上计技,老公的妹妹穿的比我還像新娘。我一直安慰自己山橄,他們只是感情好垮媒,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,655評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著航棱,像睡著了一般睡雇。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上饮醇,一...
    開(kāi)封第一講書(shū)人閱讀 52,268評(píng)論 1 309
  • 那天入桂,我揣著相機(jī)與錄音,去河邊找鬼驳阎。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的呵晚。 我是一名探鬼主播蜘腌,決...
    沈念sama閱讀 40,835評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼饵隙!你這毒婦竟也來(lái)了撮珠?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,740評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤金矛,失蹤者是張志新(化名)和其女友劉穎芯急,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體驶俊,經(jīng)...
    沈念sama閱讀 46,286評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡娶耍,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,375評(píng)論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了饼酿。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片榕酒。...
    茶點(diǎn)故事閱讀 40,505評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖故俐,靈堂內(nèi)的尸體忽然破棺而出想鹰,到底是詐尸還是另有隱情,我是刑警寧澤药版,帶...
    沈念sama閱讀 36,185評(píng)論 5 350
  • 正文 年R本政府宣布辑舷,位于F島的核電站,受9級(jí)特大地震影響槽片,放射性物質(zhì)發(fā)生泄漏何缓。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,873評(píng)論 3 333
  • 文/蒙蒙 一筐乳、第九天 我趴在偏房一處隱蔽的房頂上張望歌殃。 院中可真熱鬧,春花似錦蝙云、人聲如沸氓皱。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,357評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)波材。三九已至,卻和暖如春身隐,著一層夾襖步出監(jiān)牢的瞬間廷区,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,466評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工贾铝, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留隙轻,地道東北人埠帕。 一個(gè)月前我還...
    沈念sama閱讀 48,921評(píng)論 3 376
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像玖绿,于是被迫代替她去往敵國(guó)和親敛瓷。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,515評(píng)論 2 359