Socket之TCP相關(guān)

客戶端socket簡單示例(以下都是針對TCP協(xié)議)

#define BUF_SIZE 100

int socket = socket(AF_INET,SOCK_STREAm,IPPROTO_TCP);
struct sockaddr_in sockAddr;
sockAddr.sn_famil = AF_INET;
sockAddr.sn_addr.s_addr = inet_addr(127.0.0.1);//處理網(wǎng)絡(luò)字節(jié)序憔晒,大小端差異
sockAddr.sn_port = htons(1234);

connect(socket,(struct sockAddr *)&sockAddr, sizeof(sockAddr) );
char buffer[BUF_SIZE];
read(socket,buffer,sizeof(BUF_SIZE)-1);

close(socket);

服務(wù)端sockt簡單示例

AF_INET:代表ipv4地址苍苞;SOCK_STREAM:數(shù)據(jù)傳輸方式颤专,代表面向連接的數(shù)據(jù)傳輸方法销斟;IPPROTO_TCP:指定協(xié)議
int socket = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
struct sockAddr_in serv_addr;
memset(&serv_addr,0,sizeof(serv_addr));//清理內(nèi)存
sockAddr.sin_famil = AF_INET;
sockAddr.sin_addr.s_addr = inet_addr(127.0.0.1);
sockAddr.sin_port = htons(1234);

bind(socket,(struct sockAddr_in*)sockAddr,sizeof(sockAddr));//綁定socket描述符,把組合體特定地址分配給socket踢关。
listen(socket,20);//socket是對應(yīng)描述符咐鹤,20 是可以排隊的最大連接數(shù)
sickle_t  clnt_addr_size = sizeof(clnt_addr);
int clinSocket =   accpect(socket, (struct sockaddr*)&serv_addr,clnt_addr_size);//獲取客戶端的socket
char str[] = "你好,客戶端";
write(socket, str,sizeof(str));
close(clinSocket);
close(socket);

客戶端流程

  1. 客戶端創(chuàng)建socket侨核,先調(diào)socket函數(shù)創(chuàng)建個socket返回socket描述符草穆,它存在于協(xié)議族空間中沒有具體地址。
  2. 聲明結(jié)構(gòu)體sockAddr_in,賦值地址族或稱協(xié)議域搓译,IP地址悲柱,端口,用來識別主機(jī)中的進(jìn)程些己。
  3. conect連接服務(wù)器(這里開始三次握手連接)豌鸡;
  4. 然后可以使用下面函數(shù)進(jìn)行發(fā)送接收數(shù)據(jù)了
  • read()/write()
  • recv()/send()
  • readv()/writev()
  • recvmsg()/sendmsg()
  • recvfrom()/sendto()
  1. close() 使用后可以關(guān)閉數(shù)據(jù)了(這里開始四次握手?jǐn)嚅_連接)。

服務(wù)端流程

  1. 客戶端創(chuàng)建socket段标,先調(diào)socket函數(shù)創(chuàng)建個socket返回socket描述符涯冠,它存在于協(xié)議族空間中沒有具體地址。
  2. 聲明結(jié)構(gòu)體sockAddr_in,賦值地址族或稱協(xié)議域逼庞,IP地址蛇更,端口,用來識別主機(jī)中的進(jìn)程。
  3. 調(diào)用bind()函數(shù)把一個地址族中的特定地址賦給socket派任,如:把AF_INET砸逊,SOCK_STREAM,IPPROTO_TCP組合賦值給socket掌逛。如果沒有調(diào)用過bind()這個函數(shù)师逸,客戶端調(diào)用connect() 或者服務(wù)端調(diào)用listen()監(jiān)聽函數(shù),系統(tǒng)會自動分配一個端口號和自身的IP地址組合豆混,用于提供服務(wù)篓像,而客戶端就不用指定,這也是服務(wù)端代碼要調(diào)bind()函數(shù)而客戶端不用的原因皿伺。(客戶端不用bind函數(shù))遗淳。
  4. 調(diào)用listen(),開始監(jiān)聽上面創(chuàng)造的socket。
  5. 當(dāng)socket處于listen狀態(tài),調(diào)用accept()程序就會進(jìn)入阻塞狀態(tài)心傀,直到接受到客戶端的connect()請求。accept() 會返回一個和客戶端有關(guān)的socket描述字符拆讯,然后可以對這個字符進(jìn)行各種讀寫操作脂男,便是向客戶端發(fā)送數(shù)據(jù)。
    6.使用完成后便可以close關(guān)閉自己創(chuàng)建的socket和accept返回的socket

注:close是將socket從內(nèi)存中清楚种呐,還有種方式shutdown宰翅,用來斷開連接,但是不會清除socket爽室,函數(shù)原型int shutdown(int sock, int howto),howto參數(shù)表示斷開方式汁讼。在linux下有以下幾個方式。

  • SHUT_RD:斷開輸入流阔墩。套接字無法接收數(shù)據(jù)(即使輸入緩沖區(qū)收到數(shù)據(jù)也被抹去)嘿架,無法調(diào)用輸入相關(guān)函數(shù)。其他平臺稍有不同啸箫。
  • SHUT_WR:斷開輸出流耸彪。套接字無法發(fā)送數(shù)據(jù),但如果輸出緩沖區(qū)中還有未傳輸?shù)臄?shù)據(jù)忘苛,則將傳遞到目標(biāo)主機(jī)蝉娜。
  • SHUT_RDWR:同時斷開 I/O 流。相當(dāng)于分兩次調(diào)用 shutdown()扎唾,其中一次以
  • SHUT_RD 為參數(shù)召川,另一次以 SHUT_WR 為參數(shù)

三次握手建立連接

首先得有個服務(wù)端處于監(jiān)聽socket的狀態(tài),accept阻塞著線程等待客戶端的請求胸遇。
下圖是網(wǎng)上找的TCP數(shù)據(jù)報結(jié)構(gòu)荧呐,握手時的數(shù)據(jù)包格式


TCP數(shù)據(jù)報結(jié)構(gòu)

三次握手中,需要對TCP頭部的這幾個概念理解

  1. seq表示包的序號。
  2. ack來確認(rèn)對方發(fā)送的某個序號seq收到了坛增,采用ack = seq + 1的方式
  3. URG获雕,ACK,PSH收捣,PST届案,SYN,F(xiàn)IN這6個bit表示標(biāo)志罢艾,SYN表示是同步連接數(shù)據(jù)包楣颠,F(xiàn)IN是否斷連數(shù)據(jù)包。所以四次斷連發(fā)送的就是FIN包咐蚯。

第一次:客戶端調(diào)用connect()向目標(biāo)socket發(fā)起連接請求童漩,這一步客戶端socket會組織一個數(shù)據(jù)包比如:ACK標(biāo)志位不設(shè)置,設(shè)置SYN標(biāo)志位(Synchronous同步的意思春锋,同步建立連接)標(biāo)志位 =1矫膨,seq = x,進(jìn)入SYN-SEND(同步已發(fā)送)狀態(tài)期奔,發(fā)送給服務(wù)端侧馅,此時connect()函數(shù)會進(jìn)入阻塞狀態(tài),等待服務(wù)端的返回呐萌。


第一次握手?jǐn)?shù)據(jù)包

第二次:服務(wù)端接受到客戶端的請求和數(shù)據(jù)包后馁痴,組織數(shù)據(jù)包,SYN置1肺孤,ACK標(biāo)志位(Acknowledge確認(rèn)的意思)置1罗晕, 同時生成一個確認(rèn)序號ack= x+1 ,同時隨機(jī)選擇seq = y進(jìn)入SYN-RCVD(同步已發(fā)送)狀態(tài)赠堵。


第二次握手?jǐn)?shù)據(jù)包

第三次:客戶端接受到了服務(wù)端的確認(rèn)連接請求數(shù)據(jù)包小渊,此時connect會返回,進(jìn)入連接狀茫叭,組織數(shù)據(jù)包:SYN標(biāo)志位不設(shè)置粤铭,ACK置1,確認(rèn)號ack = y+1杂靶,seq= x + 1(我第二次發(fā)給你包)梆惯,再次發(fā)送給服務(wù)端,表示已經(jīng)知道你已經(jīng)收到我想連接的請求并且可以連接了吗垮。此時服務(wù)端收到數(shù)據(jù)后垛吗,進(jìn)入已建立連接的狀態(tài),accept()函數(shù)會返回一個socket 描述符烁登,即怯屉,我們在服務(wù)端進(jìn)行讀寫操作的socket蔚舀,這樣連接就完成了。


第三次握手TCP數(shù)據(jù)包
三次握手連接示意圖

注:

seq的作用是標(biāo)識數(shù)據(jù)包序號锨络,ack的作用是確認(rèn)對方發(fā)送相應(yīng)seq序號的數(shù)據(jù)包是否收到赌躺,把客戶端發(fā)的seq加上1賦值給ack,然后返回給客戶端表示已經(jīng)收到你的連接請求羡儿,同時自己根據(jù)ack的值礼患,再次組織下一個按序號應(yīng)該發(fā)送的包,給seq賦值掠归,發(fā)送給客戶端缅叠,客戶端收到數(shù)據(jù)后,查看ack是否是自己第一次發(fā)送數(shù)據(jù)包里的seq加上1的值虏冻,來確認(rèn)服務(wù)端有沒有收到自己的連接請求肤粱,然后客戶端重新組織數(shù)據(jù)包,把服務(wù)端數(shù)據(jù)包里的seq加上1 賦值給自己的ack確認(rèn)號厨相,發(fā)送給服務(wù)端领曼,表示我已經(jīng)知道你收到我發(fā)送連接的請求了。
這個過程中若是收到服務(wù)的數(shù)據(jù)包ack并不是上次自己發(fā)送seq加上1蛮穿,則會認(rèn)為服務(wù)端沒有收到數(shù)據(jù)悯森,會重新組織數(shù)據(jù)再次發(fā)送,直到接到服務(wù)端包含正確的seq數(shù)據(jù)包或者等待數(shù)據(jù)包時間超時失效绪撵。

四次握手?jǐn)嚅_連接

  1. 客戶端調(diào)用close()函數(shù)后,向服務(wù)端發(fā)送FIN數(shù)據(jù)包(設(shè)置了FIN標(biāo)志位為1)祝蝠,客戶端進(jìn)入第一等待階段音诈。

  2. 服務(wù)器收到數(shù)據(jù)包后,檢測到設(shè)置FIN標(biāo)志位绎狭,知道要斷開連接了细溅,向客戶端發(fā)送一個確認(rèn)包,服務(wù)端進(jìn)入關(guān)閉等待階段儡嘶。并不是立即關(guān)閉喇聊,只是先向客戶端發(fā)送確認(rèn)包,告訴客戶端蹦狂,我知道要斷開了誓篱,準(zhǔn)備些事情。此時凯楔,客戶端收到確認(rèn)包后窜骄,等待服務(wù)器準(zhǔn)備完畢再次給自己發(fā)送數(shù)據(jù)包,這時客戶端進(jìn)入第二等待階段摆屯。

  3. 過一段時間后邻遏,服務(wù)器準(zhǔn)備完成,可以斷開連接了,于時再次發(fā)送FIN包准验,告訴客戶端赎线,準(zhǔn)備好了,可以斷開連接了糊饱,這時服務(wù)端進(jìn)入一個LAST_ACK的狀態(tài)(猜測時只能接收一個ACK包的狀態(tài))垂寥。

  4. 客戶端收到服務(wù)端的FIN數(shù)據(jù)包后,再次向服務(wù)端發(fā)送ACK包济似,告訴服務(wù)端矫废,斷開吧,就進(jìn)入了TIME_WAIT狀態(tài)(注意砰蠢,此時客戶端并不是立即斷開連接蓖扑,而是會等待一會,因為會存在數(shù)據(jù)包丟失的情況台舱,若是服務(wù)端沒有收到自己最后發(fā)送的數(shù)據(jù)包律杠,則服務(wù)端還會向客戶端發(fā)送FIN_2數(shù)據(jù)包,等待時間竞惋,與數(shù)據(jù)包在網(wǎng)絡(luò)中的有效時限有關(guān))柜去。此時,服務(wù)端接受到數(shù)據(jù)后會關(guān)閉Socket進(jìn)入Close狀態(tài)拆宛∩ど荩客戶端等了一段時間后仍沒有接到FIN_2的數(shù)據(jù)包,就會認(rèn)為浑厚,服務(wù)端已經(jīng)接到自己最后一次發(fā)送的數(shù)據(jù)包股耽,這時客戶端才會進(jìn)入CLOSE的狀態(tài)。


    四次斷連圖

socket緩沖區(qū)以及阻塞模式

socket緩沖區(qū)

每個socket創(chuàng)建后钳幅,都會分配兩個緩沖區(qū)物蝙,輸入緩沖區(qū)和輸出緩沖區(qū)。
write()和send() 并不是立即向網(wǎng)絡(luò)中傳輸數(shù)據(jù)敢艰,而是先將數(shù)據(jù)寫入緩沖區(qū)中诬乞,由TCP協(xié)議將數(shù)據(jù)發(fā)送到目標(biāo)機(jī)器,一旦數(shù)據(jù)寫入緩沖區(qū)钠导,函數(shù)就可以成功返回震嫉,不管有沒有到達(dá)目標(biāo)機(jī)器,也不管他們何時被發(fā)送到網(wǎng)絡(luò)牡属,這些都是TCP負(fù)責(zé)的事情责掏。
這些I/O緩沖區(qū)特性如下:

  • I/O緩沖區(qū)在每個TCP套接字中單獨(dú)存在
  • I/O緩沖區(qū)在創(chuàng)建套接字時會自動生成
  • 即使關(guān)閉套接字也會繼續(xù)傳送輸出緩沖區(qū)中遺留的數(shù)據(jù)。
  • 關(guān)閉套接字將丟失輸入緩沖區(qū)中的數(shù)據(jù)


    四次握手?jǐn)噙B示意圖.jpg

阻塞模式

對于TCP套接字湃望,當(dāng)使用write()/send()發(fā)送數(shù)據(jù)時:

  1. 首先會檢查緩沖區(qū)换衬,若緩沖區(qū)的剩余空間小雨要發(fā)送的數(shù)據(jù)時痰驱,write()/send()進(jìn)入阻塞模式,將會等待數(shù)據(jù)發(fā)送到目標(biāo)機(jī)器瞳浦,騰出足夠空間担映,才會喚醒write()/send()函數(shù)繼續(xù)寫入數(shù)據(jù)。
  2. 如果TCP協(xié)議正在向網(wǎng)絡(luò)發(fā)送數(shù)據(jù)叫潦,那么輸出緩沖區(qū)將會被鎖定蝇完,不允許寫入,write/send()函數(shù)寫入數(shù)據(jù)矗蕊。
  3. 如果要寫入的數(shù)據(jù)大于緩沖區(qū)的最大長度短蜕,那么將會分批寫入。
  4. 直到所有數(shù)據(jù)被寫入緩沖區(qū) write()/send() 才能返回傻咖。
    當(dāng)使用read和recv讀取數(shù)據(jù)時朋魔。
  5. 手寫會檢查緩沖區(qū),如果緩沖區(qū)中有數(shù)據(jù)卿操,那么就讀取警检,否則函數(shù)會被阻塞,直到網(wǎng)絡(luò)上有數(shù)據(jù)到來害淤。
  6. 如果要讀取的數(shù)據(jù)長度小于緩沖區(qū)中的數(shù)據(jù)長度扇雕,那么就不能不一次性將緩沖區(qū)中的所有數(shù)據(jù)讀出,剩余數(shù)據(jù)將不斷積壓窥摄,直到read()/recv()函數(shù)才會返回镶奉,否則就一直被阻塞。
  7. 直到讀取到數(shù)據(jù)后 read()/recv()才會返回崭放,否則就一直被阻塞哨苛。

所謂阻塞,就是上一步動作沒有完成莹菱,下一步動作將暫停,直到上一步動作完成后才能繼續(xù)吱瘩,以保持同步性道伟。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市使碾,隨后出現(xiàn)的幾起案子蜜徽,更是在濱河造成了極大的恐慌,老刑警劉巖票摇,帶你破解...
    沈念sama閱讀 218,607評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件拘鞋,死亡現(xiàn)場離奇詭異,居然都是意外死亡矢门,警方通過查閱死者的電腦和手機(jī)盆色,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,239評論 3 395
  • 文/潘曉璐 我一進(jìn)店門灰蛙,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人隔躲,你說我怎么就攤上這事摩梧。” “怎么了宣旱?”我有些...
    開封第一講書人閱讀 164,960評論 0 355
  • 文/不壞的土叔 我叫張陵仅父,是天一觀的道長。 經(jīng)常有香客問我浑吟,道長笙纤,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,750評論 1 294
  • 正文 為了忘掉前任组力,我火速辦了婚禮省容,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘忿项。我一直安慰自己蓉冈,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,764評論 6 392
  • 文/花漫 我一把揭開白布轩触。 她就那樣靜靜地躺著寞酿,像睡著了一般。 火紅的嫁衣襯著肌膚如雪脱柱。 梳的紋絲不亂的頭發(fā)上伐弹,一...
    開封第一講書人閱讀 51,604評論 1 305
  • 那天,我揣著相機(jī)與錄音榨为,去河邊找鬼惨好。 笑死,一個胖子當(dāng)著我的面吹牛随闺,可吹牛的內(nèi)容都是我干的日川。 我是一名探鬼主播,決...
    沈念sama閱讀 40,347評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼矩乐,長吁一口氣:“原來是場噩夢啊……” “哼龄句!你這毒婦竟也來了漏策?” 一聲冷哼從身側(cè)響起部凑,我...
    開封第一講書人閱讀 39,253評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎勒叠,沒想到半個月后欧漱,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體职抡,經(jīng)...
    沈念sama閱讀 45,702評論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,893評論 3 336
  • 正文 我和宋清朗相戀三年误甚,在試婚紗的時候發(fā)現(xiàn)自己被綠了缚甩。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片谱净。...
    茶點(diǎn)故事閱讀 40,015評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖蹄胰,靈堂內(nèi)的尸體忽然破棺而出岳遥,到底是詐尸還是另有隱情,我是刑警寧澤裕寨,帶...
    沈念sama閱讀 35,734評論 5 346
  • 正文 年R本政府宣布浩蓉,位于F島的核電站,受9級特大地震影響宾袜,放射性物質(zhì)發(fā)生泄漏捻艳。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,352評論 3 330
  • 文/蒙蒙 一庆猫、第九天 我趴在偏房一處隱蔽的房頂上張望认轨。 院中可真熱鬧,春花似錦月培、人聲如沸嘁字。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,934評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽纪蜒。三九已至,卻和暖如春此叠,著一層夾襖步出監(jiān)牢的瞬間纯续,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,052評論 1 270
  • 我被黑心中介騙來泰國打工灭袁, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留猬错,地道東北人。 一個月前我還...
    沈念sama閱讀 48,216評論 3 371
  • 正文 我出身青樓茸歧,卻偏偏與公主長得像倦炒,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子软瞎,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,969評論 2 355