网络编程01-TCP协议(详)
目錄
一、網(wǎng)絡(luò)編程學(xué)習(xí)大綱
二、回顧系統(tǒng)編程中進(jìn)程的通信方式
1、 管道
2、信號(hào)
3、IPC對(duì)象
三、網(wǎng)絡(luò)編程(套接字編程)
1、特點(diǎn)
2、協(xié)議
3、歷史
四、網(wǎng)絡(luò)應(yīng)用程序設(shè)計(jì)模式
1、C/S模式
2、B/S模式
3、優(yōu)缺點(diǎn)
五、計(jì)算機(jī)網(wǎng)絡(luò)體系結(jié)構(gòu)模型
1、概念
2、作用
3、分類
4、OSI參考模型(七層模型)
5、TCP/IP協(xié)議模型(四層)
六、TCP/IP網(wǎng)絡(luò)模型通信過(guò)程
1、兩臺(tái)計(jì)算機(jī)通過(guò)TCP/IP協(xié)議通訊的過(guò)程
2、數(shù)據(jù)包封裝
3、以太網(wǎng)幀格式
4、ARP數(shù)據(jù)報(bào)格式
5、IP段格式
6、TCP數(shù)據(jù)報(bào)格式
七、傳輸層協(xié)議
TCP協(xié)議(打電話)
UDP協(xié)議(信)
八、網(wǎng)絡(luò)編程中幾個(gè)重要概念
1、socket --> 插座,套接字 插座的種類繁多,就像很多個(gè)協(xié)議一樣,必須提前設(shè)置好協(xié)議。
?2、IP地址
3、端口號(hào)(16位)
4、字節(jié)序
九、TCP通信
1、通信過(guò)程
2、客戶端API
1、建立套接字
2、發(fā)起連接 -撥打電話
3、發(fā)送數(shù)據(jù)-------write send
4、關(guān)閉文件--close
服務(wù)器端API:
1、綁定 bind
2、設(shè)置鈴聲
3、等待客戶端的連接 --accept
4、接收數(shù)據(jù) recv
練習(xí)3: 修改代碼,實(shí)現(xiàn)TCP客戶端與TCP服務(wù)器互相可以收跟發(fā)。
練習(xí)4: 寫一個(gè)回射服務(wù)器,這個(gè)服務(wù)器功能就是客戶端發(fā)送什么數(shù)據(jù)給服務(wù)器,服務(wù)器都會(huì)將數(shù)據(jù)回發(fā)到客戶端。
一、網(wǎng)絡(luò)編程學(xué)習(xí)大綱
1、計(jì)算機(jī)網(wǎng)絡(luò)體系結(jié)構(gòu)模型、網(wǎng)絡(luò)編程專業(yè)術(shù)語(yǔ)(socket/IP/端口號(hào))、通信時(shí)序圖 2、傳輸層協(xié)議: TCP協(xié)議/UDP協(xié)議。 3、多進(jìn)程并發(fā)服務(wù)器、多線程并發(fā)服務(wù)器 4、網(wǎng)絡(luò)編程IO模型:阻塞IO/非阻塞IO/多路復(fù)用/信號(hào)驅(qū)動(dòng) 5、超時(shí)接收數(shù)據(jù)方法二、回顧系統(tǒng)編程中進(jìn)程的通信方式
1、 管道
無(wú)名管道(只能作用親緣關(guān)系) -- pipe() 有名管道(任意兩個(gè)進(jìn)程) --- mkfifo()2、信號(hào)
發(fā)送信號(hào) -- kill() 捕捉信號(hào) -- signal()3、IPC對(duì)象
消息隊(duì)列---接收特征類型的數(shù)據(jù) -- ftok() msgget() msgsnd() msgrcv() msgctl() 共享內(nèi)存 --> 雙方進(jìn)程可以同時(shí)對(duì)一片內(nèi)存進(jìn)行讀寫 shmget() shmat() shmdt() shmctl() 信號(hào)量 --> 不屬于通信方式,只是一種互斥的量 semget() semop() semctl() 特點(diǎn): 只能在同一臺(tái)主機(jī)上內(nèi)部通信,不能跨平臺(tái)。三、網(wǎng)絡(luò)編程(套接字編程)
1、特點(diǎn)
既可以在同一臺(tái)主機(jī)上內(nèi)部通信,也可以在不同主機(jī)之間通信。 自己的ubuntu ----自己的ubuntu 自己的ubuntu -----同一個(gè)局域網(wǎng)內(nèi)除了自己之外任意一臺(tái)主機(jī) 總結(jié)一下: 網(wǎng)絡(luò)通信前提: 只要你在某個(gè)局域網(wǎng)內(nèi),就可以與局域網(wǎng)任意一臺(tái)主機(jī)通信。2、協(xié)議
1)概念應(yīng)用的角度出發(fā),在不同的主機(jī)之間通信,雙方都必須遵循的同一種規(guī)則。協(xié)議可理解為“規(guī)則”, 是數(shù)據(jù)傳輸和數(shù)據(jù)的解釋的規(guī)則。 假設(shè),A、B雙方欲傳輸文件。 規(guī)定: 第一次,傳輸文件名,接收方接收到文件名,應(yīng)答OK給傳輸方; 第二次,發(fā)送文件的尺寸,接收方接收到該數(shù)據(jù)再次應(yīng)答一個(gè)OK; 第三次,傳輸文件內(nèi)容。同樣,接收方接收數(shù)據(jù)完成后應(yīng)答OK表示文件內(nèi)容接收成功。 由此,無(wú)論A、B之間傳遞何種文件,都是通過(guò)三次數(shù)據(jù)傳輸來(lái)完成。A、B之間形成了一個(gè)最簡(jiǎn)單的數(shù) 據(jù)傳輸規(guī)則。雙方都按此規(guī)則發(fā)送、接收數(shù)據(jù)。A、B之間達(dá)成的這個(gè)相互遵守的規(guī)則即為協(xié)議。 這種僅在A、B之間被遵守的協(xié)議稱之為原始協(xié)議。當(dāng)此協(xié)議被更多的人采用,不斷的增加、改進(jìn)、維 護(hù)、完善。最終形成一個(gè)穩(wěn)定的、完整的文件傳輸協(xié)議,被廣泛應(yīng)用于各種文件傳輸過(guò)程中。該協(xié)議 就成為一個(gè)標(biāo)準(zhǔn)協(xié)議。最早的ftp協(xié)議就是由此衍生而來(lái)。 TCP協(xié)議注重?cái)?shù)據(jù)的傳輸。http協(xié)議著重于數(shù)據(jù)的解釋。 2)常見(jiàn)的協(xié)議 傳輸層 常見(jiàn)協(xié)議有TCP/UDP協(xié)議。 應(yīng)用層 常見(jiàn)的協(xié)議有HTTP協(xié)議,FTP協(xié)議。 網(wǎng)絡(luò)層 常見(jiàn)協(xié)議有IP協(xié)議、ICMP協(xié)議、IGMP協(xié)議。 網(wǎng)絡(luò)接口層 常見(jiàn)協(xié)議有ARP協(xié)議、RARP協(xié)議。 TCP傳輸控制協(xié)議(Transmission Control Protocol)是一種面向連接的、可靠的、基于字節(jié)流的傳輸層通信協(xié)議。 UDP用戶數(shù)據(jù)報(bào)協(xié)議(User Datagram Protocol)是OSI參考模型中一種無(wú)連接的傳輸層協(xié)議,提供面向事務(wù)的簡(jiǎn)單不可靠信息傳送服務(wù)。 HTTP超文本傳輸協(xié)議(Hyper Text Transfer Protocol)是互聯(lián)網(wǎng)上應(yīng)用最為廣泛的一種網(wǎng)絡(luò)協(xié)議。 FTP文件傳輸協(xié)議(File Transfer Protocol) IP協(xié)議是因特網(wǎng)互聯(lián)協(xié)議(Internet Protocol) ICMP協(xié)議是Internet控制報(bào)文協(xié)議(Internet Control Message Protocol)它是TCP/IP協(xié)議族的一 個(gè)子協(xié)議, 用于在IP主機(jī)、路由器之間傳遞控制消息。 IGMP協(xié)議是 Internet 組管理協(xié)議(Internet Group Management Protocol),是因特網(wǎng)協(xié)議家族中的一個(gè)組 播協(xié)議。該協(xié)議運(yùn)行在主機(jī)和組播路由器之間。 ARP協(xié)議是正向地址解析協(xié)議(Address Resolution Protocol),通過(guò)已知的IP,尋找對(duì)應(yīng)主機(jī)的MAC地址。 RARP是反向地址轉(zhuǎn)換協(xié)議,通過(guò)MAC地址確定IP地址。3、歷史
ARPAnet(阿帕網(wǎng)): 1)歷史: 1958年美國(guó)總統(tǒng)艾森豪威爾向美國(guó)國(guó)會(huì)提出建立DARPA (Defense Advanced Research Project Agency),即國(guó)防部高級(jí)研究計(jì)劃署,簡(jiǎn)稱ARPA。1968年6月DARPA提出“資源共享計(jì)算機(jī)網(wǎng)絡(luò)” (Resource Sharing Computer Networks),目的在于讓DARPA的所有電腦互連起來(lái),這個(gè)網(wǎng)絡(luò)就叫做ARPAnet。 2)使用的協(xié)議:網(wǎng)絡(luò)控制協(xié)議(Network Control Protoco,NCP)3) 缺點(diǎn):不能互聯(lián)不同類型的計(jì)算機(jī) 和 不同類型的操作系統(tǒng),同時(shí)也沒(méi)有糾錯(cuò)功能 Internet(因特網(wǎng)): 1)由于ARPAnet網(wǎng)絡(luò)的局限性,引入了TCP/IP協(xié)議。TCP/IP是Internet上所有網(wǎng)絡(luò)和主機(jī)之間進(jìn)行交 流的標(biāo)準(zhǔn)連接協(xié)議。通常所說(shuō)的TCP/IP協(xié)議實(shí)際上包含了大量的協(xié)議和應(yīng)用,且由多個(gè)獨(dú)立定義的協(xié)議組合在一起,因此,更確切地說(shuō),應(yīng)該稱之為TCP/IP協(xié)議簇。 TCP/IP協(xié)議: 傳輸控制協(xié)議/因特網(wǎng)互聯(lián)協(xié)議 TCP協(xié)議: 用于檢測(cè)網(wǎng)絡(luò)中傳輸差錯(cuò) IP協(xié)議: 負(fù)責(zé)不同的網(wǎng)絡(luò)之間的通信 通俗講: TCP負(fù)責(zé)發(fā)送傳輸問(wèn)題,一旦有問(wèn)題發(fā)出信號(hào),要求重新傳輸,直到數(shù)據(jù)安全到達(dá)對(duì)方為止。 IP給每一臺(tái)聯(lián)網(wǎng)設(shè)備規(guī)定一個(gè)地址四、網(wǎng)絡(luò)應(yīng)用程序設(shè)計(jì)模式
1、C/S模式
傳統(tǒng)的網(wǎng)絡(luò)應(yīng)用設(shè)計(jì)模式,客戶機(jī)(client)/服務(wù)器(server)模式。需要在通訊兩端各自部署客戶機(jī)和服務(wù)器來(lái)完成數(shù)據(jù)通信。2、B/S模式
瀏覽器(browser)/服務(wù)器(server)模式。只需在一端部署服務(wù)器,而另外一端使用每臺(tái)PC都默認(rèn)配置的瀏覽器即可完成數(shù)據(jù)的傳輸。3、優(yōu)缺點(diǎn)
對(duì)于C/S模式來(lái)說(shuō),其優(yōu)點(diǎn)明顯。客戶端位于目標(biāo)主機(jī)上可以保證性能,將數(shù)據(jù)緩存至客戶端本地,從而提高數(shù)據(jù)傳輸效率。且,一般來(lái)說(shuō)客戶端和服務(wù)器程序由一個(gè)開(kāi)發(fā)團(tuán)隊(duì)創(chuàng)作,所以他們之間所采用 的協(xié)議相對(duì)靈活。可以在標(biāo)準(zhǔn)協(xié)議的基礎(chǔ)上根據(jù)需求裁剪及定制。例如,騰訊公司所采用的通信協(xié) 議,即為ftp協(xié)議的修改剪裁版。 因此,傳統(tǒng)的網(wǎng)絡(luò)應(yīng)用程序及較大型的網(wǎng)絡(luò)應(yīng)用程序都首選C/S模式進(jìn)行開(kāi)發(fā)。如,知名的網(wǎng)絡(luò)游戲魔 獸世界。3D畫面,數(shù)據(jù)量龐大,使用C/S模式可以提前在本地進(jìn)行大量數(shù)據(jù)的緩存處理,從而提高觀感。 C/S模式的缺點(diǎn)也較突出。由于客戶端和服務(wù)器都需要有一個(gè)開(kāi)發(fā)團(tuán)隊(duì)來(lái)完成開(kāi)發(fā)。工作量將成倍提 升,開(kāi)發(fā)周期較長(zhǎng)。另外,從用戶角度出發(fā),需要將客戶端安插至用戶主機(jī)上,對(duì)用戶主機(jī)的安全性構(gòu)成威脅。這也是很多用戶不愿使用C/S模式應(yīng)用程序的重要原因。 B/S模式相比C/S模式而言,由于它沒(méi)有獨(dú)立的客戶端,使用標(biāo)準(zhǔn)瀏覽器作為客戶端,其工作開(kāi)發(fā)量較小。只需開(kāi)發(fā)服務(wù)器端即可。另外由于其采用瀏覽器顯示數(shù)據(jù),因此移植性非常好,不受平臺(tái)限制。如早期的偷菜游戲,在各個(gè)平臺(tái)上都可以完美運(yùn)行。 B/S模式的缺點(diǎn)也較明顯。由于使用第三方瀏覽器,因此網(wǎng)絡(luò)應(yīng)用支持受限。另外,沒(méi)有客戶端放到對(duì)方主機(jī)上,緩存數(shù)據(jù)不盡如人意,從而傳輸數(shù)據(jù)量受到限制。應(yīng)用的觀感大打折扣。第三,必須與瀏覽器一樣,采用標(biāo)準(zhǔn)http協(xié)議進(jìn)行通信,協(xié)議選擇不靈活。因此在開(kāi)發(fā)過(guò)程中,模式的選擇由上述各自的特點(diǎn)決定。根據(jù)實(shí)際需求選擇應(yīng)用程序設(shè)計(jì)模式。五、計(jì)算機(jī)網(wǎng)絡(luò)體系結(jié)構(gòu)模型
1、概念
指的是主機(jī)內(nèi)部集成的結(jié)構(gòu)和每層協(xié)議的集合。每臺(tái)主機(jī)本身就存在一個(gè)相同的網(wǎng)絡(luò)體系結(jié)構(gòu)。2、作用
封裝數(shù)據(jù)和解析數(shù)據(jù)。3、分類
OSI(Open System Interconnection開(kāi)放系統(tǒng)互聯(lián))參考模型 TCP/IP參考模型4、OSI參考模型(七層模型)
1.物理層:主要定義物理設(shè)備標(biāo)準(zhǔn),如網(wǎng)線的接口類型、光纖的接口類型、各種傳輸介質(zhì)的傳輸速率 等。它的主要作用是傳輸比特流(就是由1、0轉(zhuǎn)化為電流強(qiáng)弱來(lái)進(jìn)行傳輸,到達(dá)目的地后再轉(zhuǎn)化為1、0,也就是我們常說(shuō)的數(shù)模轉(zhuǎn)換與模數(shù)轉(zhuǎn)換)。這一層的數(shù)據(jù)叫做比特。 2.數(shù)據(jù)鏈路層:定義了如何讓格式化數(shù)據(jù)以幀為單位進(jìn)行傳輸,以及如何讓控制對(duì)物理介質(zhì)的訪問(wèn)。這 一層通常還提供錯(cuò)誤檢測(cè)和糾正,以確保數(shù)據(jù)的可靠傳輸。如:串口通信中使用到的115200、8、N、1 3.網(wǎng)絡(luò)層:在位于不同地理位置的網(wǎng)絡(luò)中的兩個(gè)主機(jī)系統(tǒng)之間提供連接和路徑選擇。Internet的發(fā)展使 得從世界各站點(diǎn)訪問(wèn)信息的用戶數(shù)大大增加,而網(wǎng)絡(luò)層正是管理這種連接的層。 4.傳輸層:定義了一些傳輸數(shù)據(jù)的協(xié)議和端口號(hào)(WWW端口80等),如:TCP(傳輸控制協(xié)議,傳輸 效率低,可靠性強(qiáng),用于傳輸可靠性要求高,數(shù)據(jù)量大的數(shù)據(jù)),UDP(用戶數(shù)據(jù)報(bào)協(xié)議,與TCP特性恰恰相反,用于傳輸可靠性要求不高,數(shù)據(jù)量小的數(shù)據(jù),如QQ聊天數(shù)據(jù)就是通過(guò)這種方式傳輸?shù)?#xff09;。 主要是將從下層接收的數(shù)據(jù)進(jìn)行分段和傳輸,到達(dá)目的地址后再進(jìn)行重組。常常把這一層數(shù)據(jù)叫做段。 5.會(huì)話層:通過(guò)傳輸層(端口號(hào):傳輸端口與接收端口)建立數(shù)據(jù)傳輸?shù)耐贰V饕谀愕南到y(tǒng)之間發(fā)起會(huì)話或者接受會(huì)話請(qǐng)求(設(shè)備之間需要互相認(rèn)識(shí)可以是IP也可以是MAC或者是主機(jī)名)。 6.表示層:可確保一個(gè)系統(tǒng)的應(yīng)用層所發(fā)送的信息可以被另一個(gè)系統(tǒng)的應(yīng)用層讀取。例如,PC程序與另一臺(tái)計(jì)算機(jī)進(jìn)行通信,其中一臺(tái)計(jì)算機(jī)使用擴(kuò)展二一十進(jìn)制交換碼(EBCDIC),而另一臺(tái)則使用美國(guó)信息交換標(biāo)準(zhǔn)碼(ASCII)來(lái)表示相同的字符。如有必要,表示層會(huì)通過(guò)使用一種通格式來(lái)實(shí)現(xiàn)多種數(shù)據(jù)格式之間的轉(zhuǎn)換。 7.應(yīng)用層:是最靠近用戶的OSI層。這一層為用戶的應(yīng)用程序(例如電子郵件、文件傳輸和終端仿真)提供網(wǎng)絡(luò)服務(wù)。 口訣:物數(shù)網(wǎng)傳會(huì)表應(yīng) 注意:OSI模型非常臃腫,處理數(shù)據(jù)效率非常低,這個(gè)模型已經(jīng)被TCP/IP協(xié)議模型所取代5、TCP/IP協(xié)議模型(四層)
TCP/IP網(wǎng)絡(luò)協(xié)議棧分為應(yīng)用層(Application)、傳輸層(Transport)、網(wǎng)絡(luò)層(Network)和鏈路 層(Link)四層。如下圖所示: 一般在應(yīng)用開(kāi)發(fā)過(guò)程中,討論最多的是TCP/IP模型。鏈路層 也可以稱之為 網(wǎng)絡(luò)接口層六、TCP/IP網(wǎng)絡(luò)模型通信過(guò)程
1、兩臺(tái)計(jì)算機(jī)通過(guò)TCP/IP協(xié)議通訊的過(guò)程
2、數(shù)據(jù)包封裝
傳輸層及其以下的機(jī)制由內(nèi)核提供,應(yīng)用層由用戶進(jìn)程提供(后面將介紹如何使用socket API編寫應(yīng)用程序),應(yīng)用程序?qū)νㄓ崝?shù)據(jù)的含義進(jìn)行解釋,而傳輸層及其以下處理通訊的細(xì)節(jié),將數(shù)據(jù)從一臺(tái)計(jì)算機(jī)通過(guò)一定的路徑發(fā)送到另一臺(tái)計(jì)算機(jī)。應(yīng)用層數(shù)據(jù)通過(guò)協(xié)議棧發(fā)到網(wǎng)絡(luò)上時(shí),每層協(xié)議都要加上一個(gè)數(shù)據(jù)首部(header),稱為封裝(Encapsulation),如下圖所示: 不同的協(xié)議層對(duì)數(shù)據(jù)包有不同的稱謂,在傳輸層叫做段(segment),在網(wǎng)絡(luò)層叫做數(shù)據(jù)報(bào) (datagram),在鏈路層叫做幀(frame)。數(shù)據(jù)封裝成幀后發(fā)到傳輸介質(zhì)上,到達(dá)目的主機(jī)后每層 協(xié)議再剝掉相應(yīng)的首部,最后將應(yīng)用層數(shù)據(jù)交給應(yīng)用程序處理。3、以太網(wǎng)幀格式
1、以太網(wǎng)的幀格式如下所示: 其中的源地址和目的地址是指網(wǎng)卡的硬件地址(也叫MAC地址),長(zhǎng)度是48位(6個(gè)字節(jié)),是在網(wǎng)卡出廠時(shí)固化的。可在shell中使用ifconfig命令查看,“HWaddr 00:15:F2:14:9E:3F”部分就是硬件地 址。協(xié)議字段有三種值,分別對(duì)應(yīng)IP、ARP、RARP。幀尾是CRC校驗(yàn)碼。以太網(wǎng)幀中的數(shù)據(jù)長(zhǎng)度規(guī)定最小46字節(jié),最大1500字節(jié),ARP和RARP數(shù)據(jù)包的長(zhǎng)度不夠46字節(jié),要在后面補(bǔ)填充位。最大值1500稱為以太網(wǎng)的最大傳輸單元(MTU),不同的網(wǎng)絡(luò)類型有不同的MTU,如果一個(gè)數(shù)據(jù)包從以太網(wǎng)路由到撥號(hào)鏈路上,數(shù)據(jù)包長(zhǎng)度大于撥號(hào)鏈路的MTU,則需要對(duì)數(shù)據(jù)包進(jìn)行分片 (fragmentation)。ifconfig命令輸出中也有“MTU:1500”。注意,MTU這個(gè)概念指數(shù)據(jù)幀中有效 載荷的最大長(zhǎng)度,不包括幀頭長(zhǎng)度。4、ARP數(shù)據(jù)報(bào)格式
在網(wǎng)絡(luò)通訊時(shí),源主機(jī)的應(yīng)用程序知道目的主機(jī)的IP地址和端口號(hào),卻不知道目的主機(jī)的硬件地址,而數(shù)據(jù)包首先是被網(wǎng)卡接收到再去處理上層協(xié)議的,如果接收到的數(shù)據(jù)包的硬件地址與本機(jī)不符,則直接丟棄。因此在通訊前必須獲得目的主機(jī)的硬件地址。ARP協(xié)議就起到這個(gè)作用。源主機(jī)發(fā)出ARP請(qǐng)求,詢問(wèn)“IP地址是192.168.0.1的主機(jī)的硬件地址是多少”,并將這個(gè)請(qǐng)求廣播到本地網(wǎng)段(以太網(wǎng)幀首部的硬件地址填FF:FF:FF:FF:FF:FF表示廣播),目的主機(jī)接收到廣播的ARP請(qǐng)求,發(fā)現(xiàn)其中的IP地址與本機(jī)相符,則發(fā)送一個(gè)ARP應(yīng)答數(shù)據(jù)包給源主機(jī),將自己的硬件地址填寫在應(yīng)答包中。 每臺(tái)主機(jī)都維護(hù)一個(gè)ARP緩存表,可以用arp -a命令查看。緩存表中的表項(xiàng)有過(guò)期時(shí)間(一般為20分鐘),如果20分鐘內(nèi)沒(méi)有再次使用某個(gè)表項(xiàng),則該表項(xiàng)失效,下次還要發(fā)ARP請(qǐng)求來(lái)獲得目的主機(jī)的硬件地址。 ARP數(shù)據(jù)報(bào)的格式如下所示: 源MAC地址、目的MAC地址在以太網(wǎng)首部和ARP請(qǐng)求中各出現(xiàn)一次,對(duì)于鏈路層為以太網(wǎng)的情況是多余的,但如果鏈路層是其它類型的網(wǎng)絡(luò)則有可能是必要的。硬件類型指鏈路層網(wǎng)絡(luò)類型,1為以太網(wǎng),協(xié)議類型指要轉(zhuǎn)換的地址類型,0x0800為IP地址,后面兩個(gè)地址長(zhǎng)度對(duì)于以太網(wǎng)地址和IP地址分別為6和4(字節(jié)),op字段為1表示ARP請(qǐng)求,op字段為2表示ARP應(yīng)答。5、IP段格式
IP數(shù)據(jù)報(bào)的首部長(zhǎng)度和數(shù)據(jù)長(zhǎng)度都是可變長(zhǎng)的,但總是4字節(jié)的整數(shù)倍。對(duì)于IPv4,4位版本字段是4。4位首部長(zhǎng)度的數(shù)值是以4字節(jié)為單位的,最小值為5,也就是說(shuō)首部長(zhǎng)度最小是4x5=20字節(jié),也就是不帶任何選項(xiàng)的IP首部,4位能表示的最大值是15,也就是說(shuō)首部長(zhǎng)度最大是60字節(jié)。8位TOS字段有3個(gè)位用來(lái)指定IP數(shù)據(jù)報(bào)的優(yōu)先級(jí)(目前已經(jīng)廢棄不用),還有4個(gè)位表示可選的服務(wù)類型(最小延遲、最大?吐量、最大可靠性、最小成本),還有一個(gè)位總是0。總長(zhǎng)度是整個(gè)數(shù)據(jù)報(bào)(包括IP首部和IP層payload)的字節(jié)數(shù)。每傳一個(gè)IP數(shù)據(jù)報(bào),16位的標(biāo)識(shí)加1,可用于分片和重新組裝數(shù)據(jù)報(bào)。3位標(biāo)志和13位片偏移用于分片。TTL(Time to live)是這樣用的:源主機(jī)為數(shù)據(jù)包設(shè)定一個(gè)生存時(shí)間,比如64,每過(guò)一個(gè)路由器就把該值減1,如果減到0就表示路由已經(jīng)太長(zhǎng)了仍然找不到目的主機(jī)的網(wǎng)絡(luò),就丟棄該包,因此這個(gè)生存時(shí)間的單位不是秒,而是跳(hop)。協(xié)議字段指示上層協(xié)議是TCP、UDP、ICMP還是IGMP。然后是校驗(yàn)和,只校驗(yàn)IP首部,數(shù)據(jù)的校驗(yàn)由更高層協(xié)議負(fù)責(zé)。IPv4的IP地址長(zhǎng)度為32位。 思考題:如果源主機(jī)和目的主機(jī)不在同一網(wǎng)段,ARP請(qǐng)求的廣播幀無(wú)法穿過(guò)路由器,源主機(jī)如何與目的主機(jī)通信? 參考:https://blog.csdn.net/weixin_43166958/article/details/865035066、TCP數(shù)據(jù)報(bào)格式
有源端口號(hào)和目的端口號(hào),通訊的雙方由IP地址和端口號(hào)標(biāo)識(shí)。32位序號(hào)、32位確認(rèn)序號(hào)、窗口大小。4位首部長(zhǎng)度和IP協(xié)議頭類似,表示TCP協(xié)議頭的長(zhǎng)度,以4字節(jié)為單位,因此TCP協(xié)議頭最長(zhǎng)可以是4x15=60字節(jié),如果沒(méi)有選項(xiàng)字段,TCP協(xié)議頭最短20字節(jié)。URG、ACK、PSH、RST、SYN、FIN是六個(gè)控制位。16位檢驗(yàn)和將TCP協(xié)議頭和數(shù)據(jù)都計(jì)算在內(nèi)。七、傳輸層協(xié)議
TCP協(xié)議(打電話)
1、概念: 用來(lái)檢測(cè)網(wǎng)絡(luò)傳輸中差錯(cuò)的傳輸控制協(xié)議 transmission control protocol。 是一種面向連接的傳 輸層協(xié)議,它能提供高可靠性通信(即數(shù)據(jù)無(wú)誤、數(shù)據(jù)無(wú)丟失、數(shù)據(jù)無(wú)失序、數(shù)據(jù)無(wú)重復(fù)到達(dá)的通信) 2、適用場(chǎng)合: 1)、對(duì)傳輸質(zhì)量要求較高,以及傳輸大量數(shù)據(jù)的通信 2)、在需要可靠數(shù)據(jù)傳輸?shù)膱?chǎng)合,通常使用TCP協(xié)議 3)、QQ等即時(shí)通訊軟件的用戶登錄賬戶管理相關(guān)的功能,通常采用TCP協(xié)議UDP協(xié)議(信)
1、概念 UDP(User Datagram Protocol)用戶數(shù)據(jù)報(bào)協(xié)議。是不可靠的無(wú)連接的協(xié)議。在數(shù)據(jù)發(fā)送前, 因?yàn)椴恍枰M(jìn)行連接,所以可以進(jìn)行高效率的數(shù)據(jù)傳輸。 2、適用場(chǎng)合: 1)發(fā)送小尺寸數(shù)據(jù) 2)適用于廣播/組播式通信 3) QQ等即時(shí)通訊軟件的點(diǎn)對(duì)點(diǎn)文本通訊以及音視頻通訊 常采用 UDP協(xié)議 4)網(wǎng)絡(luò)多媒體服務(wù)中通常采用UDP方式進(jìn)行實(shí)時(shí)數(shù)據(jù)傳輸八、網(wǎng)絡(luò)編程中幾個(gè)重要概念
1、socket --> 插座,套接字 插座的種類繁多,就像很多個(gè)協(xié)議一樣,必須提前設(shè)置好協(xié)議。
1)概念 Socket本身有“插座”的意思,在Linux環(huán)境下,用于表示進(jìn)程間網(wǎng)絡(luò)通信的特殊文件類型。本質(zhì)為內(nèi) 核借助緩沖區(qū)形成的偽文件。既然是文件,那么理所當(dāng)然的,我們可以使用文件描述符引用套接字。與管道類似的,Linux系統(tǒng)將其封裝成文件的目的是為了統(tǒng)一接口,使得讀寫套接字和讀寫文件的操作一致。區(qū)別是管道主要應(yīng)用于本地進(jìn)程間通信,而套接字多應(yīng)用于網(wǎng)絡(luò)進(jìn)程間數(shù)據(jù)的傳遞。套接字的內(nèi)核實(shí)現(xiàn)較為復(fù)雜,不宜在學(xué)習(xí)初期深入學(xué)習(xí)。 在TCP/IP協(xié)議中,“IP地址+TCP或UDP端口號(hào)”唯一標(biāo)識(shí)網(wǎng)絡(luò)通訊中的一個(gè)進(jìn)程。“IP地址+端口 號(hào)”就對(duì)應(yīng)一個(gè)socket。欲建立連接的兩個(gè)進(jìn)程各自有一個(gè)socket來(lái)標(biāo)識(shí),那么這兩個(gè)socket組成的 socket pair就唯一標(biāo)識(shí)一個(gè)連接。因此可以用Socket來(lái)描述網(wǎng)絡(luò)連接的一對(duì)一關(guān)系。 2)套接字通信原理如下圖所示: 在網(wǎng)絡(luò)通信中,套接字一定是成對(duì)出現(xiàn)的。一端的發(fā)送緩沖區(qū)對(duì)應(yīng)對(duì)端的接收緩沖區(qū)。我們使用同一個(gè)文件描述符指向發(fā)送緩沖區(qū)和接收緩沖區(qū)。 TCP/IP協(xié)議最早在BSD UNIX上實(shí)現(xiàn),為TCP/IP協(xié)議設(shè)計(jì)的應(yīng)用層編程接口稱為socket API。 3)特點(diǎn) 是一套編程的函數(shù)接口,目的是 建立套接字 無(wú)論是TCP協(xié)議,還是UDP協(xié)議,都必須使用socket socket在網(wǎng)絡(luò)模型中處于應(yīng)用層與傳輸層之間 4)例子 使用TCP協(xié)議,那么socket建立出來(lái)的套接字就是TCP套接字 1 套接字: int sockfd = socket(TCP協(xié)議); --> 得到TCP協(xié)議套接字 2 普通文件: int fd = open(普通文件); --> fd就是文件描述符?2、IP地址
1)概念 用來(lái)標(biāo)識(shí)網(wǎng)絡(luò)中不同的主機(jī)。通信必須要具有一個(gè)IP地址 2)分類 IPV4地址---32位 IPV6地址 ---128位 3)如何表示 常常以點(diǎn)分制"192.168.8.108" 4)數(shù)據(jù)包中都必須要包含目的IP地址,源IP地址。路由器依賴于此信息作為數(shù)據(jù)選擇路由3、端口號(hào)(16位)
標(biāo)識(shí)同一臺(tái)主機(jī)內(nèi) 不同的應(yīng)用程序。 網(wǎng)絡(luò) 1主機(jī)1 -----------------------------------主機(jī)2qq qq 微信 微信 ......Jack.c ------------> Rose.c IP地址 192.168.8.100 192.168.8.108 ---> 雙方必須在相同的局域網(wǎng)端口號(hào) 50001 50001 ---> 雙方都必須使用相同的端口號(hào) 端口號(hào)占用: 1)系統(tǒng)占用端口號(hào): 1 - 1023 (用戶不能再次使用端口號(hào)進(jìn)行通信) 2)可用端口: 1024 - 65535 端口號(hào)選擇錯(cuò)誤 --> 連接錯(cuò)誤4、字節(jié)序
1)概念 一個(gè)多字節(jié)存儲(chǔ)單位的低地址存儲(chǔ)數(shù)據(jù)的高有效位 還是 低有效 位,說(shuō)白了,也就是數(shù)據(jù)在計(jì)算機(jī) 內(nèi)存中以什么樣的方式存儲(chǔ) 2)分類 小端字節(jié)序:數(shù)據(jù)的低有效位 存儲(chǔ) 在 內(nèi)存中的 低地址 大端字節(jié)序:數(shù)據(jù)的低有效位 存儲(chǔ) 在 內(nèi)存中的 高地址 3)為了避免不同類別主機(jī)之間在數(shù)據(jù)交換時(shí)由于對(duì)于字節(jié)序的不同而導(dǎo)致的差錯(cuò),引入了網(wǎng)絡(luò)字節(jié)序。也就是統(tǒng)一規(guī)定所有主機(jī)通過(guò)網(wǎng)絡(luò)發(fā)送數(shù)據(jù)包時(shí)轉(zhuǎn)為大端序,也就是網(wǎng)絡(luò)字節(jié)序。 內(nèi)存地址: 低地址 -------------------------> 高地址 "hello"x86平臺(tái) -----> ARM小端字節(jié)序: 低字節(jié)存放在低地址 1 2 3 4大端字節(jié)序: 高字節(jié)存放在低地址 4 3 2 1本地字節(jié)序: 取決于主機(jī)本身 x86是小端 ARM是大端網(wǎng)絡(luò)字節(jié)序: 一定是大端字節(jié)序高 低 例子: 192.168.1.120x86: 021.1.861.291 ---> 網(wǎng)絡(luò): 192.168.1.120 ARM: 192.168.1.120 ---> 網(wǎng)絡(luò): 192.168.1.120 總結(jié)一下: 無(wú)論本身是大端還是小端,都需要把自身的字節(jié)序轉(zhuǎn)換為網(wǎng)絡(luò)字節(jié)序,即大端,才能在網(wǎng)絡(luò)中傳輸數(shù)據(jù)。九、TCP通信
1、通信過(guò)程
2、客戶端API
1、建立套接字
1 #include <sys/types.h> /* See NOTES */ 2 #include <sys/socket.h> 3 int socket(int domain, int type, int protocol); 函數(shù)作用 建立套接字,返回套接字文件描述符 函數(shù)參數(shù) ????????domain : 你要選擇 哪一種 地址族 ????????????????????????PF_INET / AF_INET Ipv4 網(wǎng)絡(luò)協(xié)議 ????????????????????????PF_INET6/AF_INET6 Ipv6 網(wǎng)絡(luò)協(xié)議 ????????type: 你 要選擇 哪一種 協(xié)議 ????????????????????????SOCK_STREAM 選擇TCP ----流式 套接字 ????????????????????????SOCK_DGRAM 選擇UDP ----數(shù)據(jù)報(bào)套接字 ????????protocol : 傳0表示使用默認(rèn)協(xié)議 返回值 ????????成功返回 套接字文件描述符 sockfd ????????失敗返回 -12、發(fā)起連接 -撥打電話
1 #include <sys/types.h> /* See NOTES */ 2 #include <sys/socket.h> 3 int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen); 函數(shù)參數(shù) ????????sockfd --> 套接字文件描述符 ????????addr ---對(duì)方的IP地址 和 端口號(hào) ????????addrlen ---地址的長(zhǎng)度 sizeof(struct sockaddr_in ) 1 使用 man 7 ip 可以查看以下結(jié)構(gòu)體 2 struct sockaddr ---舊的結(jié)構(gòu)體 3 { 4 unsigned short int sa_family; ---IP地址 端口號(hào) 、 協(xié)議 5 unsigned char sa_data[14]; 6 }; 7 IPV4結(jié)構(gòu)體 8 struct sockaddr_in 9 { 10 short int sin_family; /地址族 IPV4 IPV6/ 11 unsigned short int sin_port; /端口號(hào)/ 12 struct in_addr sin_addr; /IP地址/ 13 }; 14 struct in_addr { 15 in_addr_t s_addr; /in_addr_t為 32位的unsigned int,該無(wú)符號(hào)整數(shù)采用大端字節(jié)序。/ 16 }; 比如:初始化 IP地址 和端口號(hào) --IPV4 1 struct sockaddr_in serverAddr; 2 serverAddr.sin_family = PF_INET ; 3 serverAddr.sin_port = htons (5000) ; // host to network short 4 serverAddr.sin_addr.s_addr = inet_addr("192.168.1.120"); //主機(jī) IP地址 ---》網(wǎng)絡(luò)IP地 址 inet_addr5 connect(sockfd, (struct sockaddr * )&serverAddr, sizeof(struct sockaddr_in) ); 擴(kuò)展函數(shù): 1 #include <arpa/inet.h> 2 uint16_t htons(uint16_t hostshort); // 將主機(jī)端口號(hào) 轉(zhuǎn)成 網(wǎng)絡(luò)端口號(hào) 3 uint16_t ntohs(uint16_t netshort); // 將網(wǎng)絡(luò)端口號(hào) 轉(zhuǎn)成 主機(jī)端口號(hào) 4 5 #include <sys/socket.h> 6 #include <netinet/in.h> 7 #include <arpa/inet.h> 8 in_addr_t inet_addr(const char *cp); //將主機(jī)IP 轉(zhuǎn)成 網(wǎng)絡(luò)IP 9 char *inet_ntoa(struct in_addr in); //將網(wǎng)絡(luò)IP 轉(zhuǎn)成 主機(jī)IP 參數(shù): ????????cp ---》主機(jī)IP 返回值 : ????????返回 網(wǎng)絡(luò)IP3、發(fā)送數(shù)據(jù)-------write send
1 #include <sys/types.h> 2 #include <sys/socket.h> 3 ssize_t send(int sockfd, const void *buf, size_t len, int flags); 作用 ????????用于網(wǎng)絡(luò)中發(fā)送數(shù)據(jù) 參數(shù) ????????sockfd: 套接字文件描述符 ????????buf : 你要發(fā)送的數(shù)據(jù) ????????len : 你要發(fā)送數(shù)據(jù)的大小 ,以 字節(jié)為單位 ????????flags : 一般默認(rèn)為 0 返回值 ????????成功返回發(fā)送的字節(jié)數(shù) ????????失敗返回 -14、關(guān)閉文件--close
服務(wù)器端API:
1、綁定 bind
1 #include <sys/types.h> /* See NOTES */ 2 #include <sys/socket.h> 3 int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen); 作用: ????????綁定自己的IP地址 和 端口號(hào) 參數(shù) ????????sockfd : 套接字文件描述符 ????????addr : 自己的IP地址和端口號(hào) ????????addrlen : 地址的大小長(zhǎng)度 返回值2、設(shè)置鈴聲
1 #include <sys/types.h> /* See NOTES */ 2 #include <sys/socket.h> 3 int listen(int sockfd, int backlog); 作用 ????????創(chuàng)建 一個(gè) 未連接 隊(duì)列 ,同時(shí)最多連接的客戶端 總數(shù) backlog 參數(shù) ????????sockfd : 套接字文件描述符 ????????backlog :同時(shí) 最多支持連接上來(lái)的客戶端總數(shù) 返回值 ????????成功返回 0 ????????失敗返回 -1 說(shuō)明 典型的服務(wù)器程序可以同時(shí)服務(wù)于多個(gè)客戶端,當(dāng)有客戶端發(fā)起連接時(shí),服務(wù)器調(diào)用的accept()返 回并接受這個(gè)連接,如果有大量的客戶端發(fā)起連接而服務(wù)器來(lái)不及處理,尚未accept的客戶端就處于連接等待狀態(tài),listen()聲明sockfd處于監(jiān)聽(tīng)狀態(tài),并且最多允許有backlog個(gè)客戶端處于連接待狀態(tài),如果接收到更多的連接請(qǐng)求就忽略。 1 查看系統(tǒng)默認(rèn)backlog 2 gec@ubuntu:/mnt/hgfs/2$ cat /proc/sys/net/ipv4/tcp_max_syn_backlog 3 1283、等待客戶端的連接 --accept
1 #include <sys/types.h> /* See NOTES */ 2 #include <sys/socket.h> 3 int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen); 作用 ????????等待客戶端的連接 參數(shù) ????????sockfd: 套接字文件描述符 ????????addr : 連接上來(lái)的客戶端的IP地址和端口號(hào) ????????addrlen : 長(zhǎng)度 //如果客戶端連接上來(lái)了,可以獲取到連接上來(lái)的客戶端的IP地址和端口號(hào) 返回值: ????????成功返回 已連接的套接字文件描述符4、接收數(shù)據(jù) recv
1 #include <sys/types.h> 2 #include <sys/socket.h> 3 ssize_t recv(int sockfd, void *buf, size_t len, int flags); 參數(shù) ????????sockfd: 已連接的套接字文件描述符 connectfd ????????buf : 接收到的數(shù)據(jù)存儲(chǔ)到這里 ????????len : 接收數(shù)據(jù)的大小 ????????flags : 一般設(shè)置為 0 返回值: ????????成功返回 接收到的字節(jié)數(shù) ????????失敗返回 -1 ????????返回 0 表示 客戶端斷開(kāi)連接 服務(wù)器代碼: 1 #include<stdio.h> 2 #include <sys/types.h> /* See NOTES */ 3 #include <sys/socket.h> 4 #include <sys/socket.h> 5 #include <arpa/inet.h>6 #include <unistd.h> 7 #include<stdlib.h> 8 9 10 #define SERVER_ADDR "192.168.63.25" //服務(wù)器的IP地址 11 #define SERVER_PORT 20000 //port 服務(wù)器的端口號(hào) 12 13 14 int main() 15 { 16 int ret; 17 //1、買手機(jī)(建立套接字) 18 int socketFd = socket(AF_INET, SOCK_STREAM, 0); 19 if(socketFd == -1){ 20 perror("socket error"); 21 exit(0); 22 } 23 //2、綁定自己的電話號(hào)碼(綁定自己的IP地址和端口號(hào)) 24 //定義一個(gè)IPV4結(jié)構(gòu)體變量,存儲(chǔ)IP地址和端口號(hào) 25 struct sockaddr_in serverAddr; 26 serverAddr.sin_family = AF_INET ;//IPv4 27 serverAddr.sin_port = htons(SERVER_PORT);//16端口號(hào) --本地端口號(hào)--網(wǎng)絡(luò)端口號(hào) 28 serverAddr.sin_addr.s_addr = inet_addr(SERVER_ADDR); //32IP地址 --本地IP--網(wǎng)絡(luò)IP 29 30 ret = bind(socketFd, (struct sockaddr*)&serverAddr, sizeof(struct sockaddr_in)); 31 if(ret == -1){ 32 perror("bind error"); 33 exit(0); 34 } 35 //3、設(shè)置鈴聲(設(shè)置監(jiān)聽(tīng)) 36 ret = listen(socketFd, 20); //20表示同時(shí)連接上來(lái)的客戶端的最大數(shù)量 37 if(ret == -1){ 38 perror("listen error"); 39 exit(0); 40 } 41 printf("阻塞等待新的客戶端連接......\n"); 42 //4、坐等電話(阻塞等待客戶端的連接) 43 int newClientFd = accept(socketFd, NULL,NULL); 44 if(ret == -1){45 perror("accept error"); 46 exit(0); 47 } 48 //注意 accept函數(shù)的返回值 表示 新的客戶端的文件描述符 ,后面與客戶端通信 必須使用該文件描述 符 49 printf("有新的客戶端連接上來(lái)....\n"); 50 51 //5、聊天 接收 數(shù)據(jù) 52 char buf[1024]={0}; 53 read(newClientFd,buf,sizeof(buf)); 54 55 printf("buf:%s\n",buf); 56 57 58 //6、關(guān)閉 斷開(kāi)連接 59 close(socketFd); 60 close(newClientFd); 61 62 return 0; 63 } 客戶端代碼: 1 #include<stdio.h> 2 #include <sys/socket.h> 3 #include <sys/types.h> /* See NOTES */ 4 #include <netinet/in.h> 5 #include <arpa/inet.h> 6 #include <string.h> 7 #include <unistd.h> 8 9 #define OWNADDR "192.168.112.109" //我自己電腦的ip地址 10 #define OWNPORT 10000 //我自己電腦的該程序的端口號(hào) 11 12 #define SERVERADDR "192.168.112.109" //對(duì)方的 服務(wù)器的IP地址 13 #define SERVERPORT 11111 //對(duì)方的 服務(wù)器的端口號(hào) 14 15 int main() 16 { 17 //1、買手機(jī)(建立套接字)18 int socketfd = socket(AF_INET, SOCK_STREAM, 0); 19 if(socketfd == -1) 20 { 21 printf("沒(méi)錢了....,失敗\n"); 22 return -1; 23 } 24 //2、綁定自己的電話號(hào)碼(綁定自己的IP地址 和端口號(hào)) 25 //定義一個(gè)IPV4結(jié)構(gòu)體變量,初始化自己的IP地址和端口號(hào) 26 struct sockaddr_in ownAddr; 27 ownAddr.sin_family = AF_INET;/*地址族 IPV4*/ 28 ownAddr.sin_port = htons(OWNPORT); //htons 將本地端口號(hào)轉(zhuǎn)為網(wǎng)絡(luò)端口號(hào) 29 ownAddr.sin_addr.s_addr = inet_addr(OWNADDR); //將本地IP地址轉(zhuǎn)為網(wǎng)絡(luò)IP地址 30 31 bind(socketfd, (struct sockaddr *)&ownAddr,sizeof(struct sockaddr_in)); 32 33 //3、開(kāi)始打電話(發(fā)起連接) 34 struct sockaddr_in serverAddr; 35 serverAddr.sin_family = AF_INET;/*地址族 IPV4*/ 36 serverAddr.sin_port = htons(SERVERPORT); //htons 將本地端口號(hào)轉(zhuǎn)為網(wǎng)絡(luò)端口號(hào) 37 serverAddr.sin_addr.s_addr = inet_addr(SERVERADDR); //將本地IP地址轉(zhuǎn)為網(wǎng)絡(luò)IP地址 38 39 connect(socketfd,(struct sockaddr *)&serverAddr,sizeof(struct sockaddr_in)); 40 41 //4、聊天 42 while(1) 43 { 44 printf("data:"); 45 char buf[1024]={0}; 46 scanf("%s",buf); 47 //發(fā)送數(shù)據(jù) 48 send(socketfd, buf, strlen(buf), 0); 49 } 50 //5、關(guān)閉 51 close(socketfd); 52 53 return 0; 54 } 練習(xí)2: 自己ubuntu與自己的開(kāi)發(fā)板通信。自己的ubuntu與同桌的ubuntu通信。 自己的開(kāi)發(fā)板與同桌的ubuntu通信。 自己的開(kāi)發(fā)板與同桌的開(kāi)發(fā)板的通信。練習(xí)3: 修改代碼,實(shí)現(xiàn)TCP客戶端與TCP服務(wù)器互相可以收跟發(fā)。
服務(wù)器 #include <stdio.h> #include <unistd.h> #include <string.h> #include <pthread.h>#include <sys/types.h> /* See NOTES */ #include <sys/socket.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h>//服務(wù)器的IP地址 #define SERVER_IP "192.168.11.2" #define SERVER_PORT 60000//用來(lái)給客戶端發(fā)送數(shù)據(jù) void *send_buf(void *arg) {//設(shè)置自己為分離屬性pthread_detach(pthread_self());int ret;char buf[1024] = {0};while(1){scanf("%s",buf); //給客戶端發(fā)送數(shù)據(jù)send(*(int *)arg,buf,strlen(buf),0); }}int main(int argc,char **argv) {//建立套接字int socket_fd;//AF_INET-->ipv4 SOCK_STREAM-->tcpsocket_fd = socket(AF_INET,SOCK_STREAM,0);if(socket_fd < 0){perror("socket fail");return -1;} //重復(fù)綁定本機(jī)IP可能會(huì)出現(xiàn)失敗,這個(gè)時(shí)候要設(shè)置端口號(hào)復(fù)用。(筆記2)int optval = 1;setsockopt(socket_fd, SOL_SOCKET, SO_REUSEADDR,&optval, sizeof(optval));//填充本機(jī)IP地址和端口struct sockaddr_in server_addr;server_addr.sin_family = AF_INET;server_addr.sin_port = htons(SERVER_PORT);//本機(jī)端口轉(zhuǎn)化為網(wǎng)絡(luò)端口server_addr.sin_addr.s_addr = inet_addr(SERVER_IP);//將本機(jī)IP轉(zhuǎn)換為網(wǎng)絡(luò)IP//綁定本機(jī)IP和端口int ret;ret = bind(socket_fd,(struct sockaddr *)&server_addr,sizeof(server_addr));if(ret < 0){perror("bind fail");return -1;} printf("bind success [%s %d]\n",SERVER_IP,SERVER_PORT);//監(jiān)聽(tīng)ret = listen(socket_fd,20);if(ret < 0){perror("listen fail");return -1;} //等待客戶端的連接int socket_client;//客戶端的套接字struct sockaddr_in client_addr;//用來(lái)接受客戶端的ip地址和端口socklen_t addrlen = sizeof(client_addr);//socket_client = accept(socket_fd,NULL,NULL); //如果不解析客戶端的地址和端口可以設(shè)置地址參數(shù)為NULLsocket_client = accept(socket_fd,(struct sockaddr *)&client_addr,&addrlen);if(socket_client < 0){perror("accept fail");return -1; }//解析客戶端的ip和端口號(hào)char *ip = inet_ntoa(client_addr.sin_addr);//將網(wǎng)絡(luò)ip轉(zhuǎn)換為本機(jī)ipint port = ntohs(client_addr.sin_port);//將網(wǎng)絡(luò)端口號(hào)轉(zhuǎn)換為本機(jī)端口號(hào)printf("new client [ip:%s port:%d]\n",ip,port);//server_addr.sin_addr.s_addr//單獨(dú)開(kāi)一個(gè)線程用來(lái)給客戶端發(fā)送數(shù)據(jù)pthread_t tid;ret = pthread_create(&tid,NULL,send_buf,&socket_client);//傳參客戶端的socket套接字if(ret != 0){perror("pthread_create fail");return -1;}//接受客戶端發(fā)送的數(shù)據(jù)(接受的是客戶端的套接字)char buf[1024] = {0};while(1){ memset(buf,0,sizeof(buf));ret = recv(socket_client,buf,sizeof(buf),0);//ret = read(socket_client,buf,sizeof(buf));//和recv函數(shù)的功能一樣if(ret == 0){printf("client down\n"); //客戶端掉線break;}printf("[ip:%s port:%d] buf:%s ret:%d \n",ip,port,buf,ret);}//關(guān)閉套接字close(socket_fd);return 0; }客戶端
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <pthread.h>/*socket*/ #include <sys/types.h> /* See NOTES */ #include <sys/socket.h> /*ip*/ #include <netinet/in.h> //man 3 inet_addr #include <arpa/inet.h>//服務(wù)器的IP地址 #define SERVER_IP "192.168.11.2" #define SERVER_PORT 60000//用來(lái)單獨(dú)接收的線程 void *recv_buf(void *arg) {//設(shè)置自己為分離屬性pthread_detach(pthread_self());int ret;char buf[1024] = {0};while(1){ bzero(buf,1024);//接受服務(wù)器回射的數(shù)據(jù)ret = recv(*(int *)arg,buf,sizeof(buf),0);if(ret == 0){printf("server down\n"); //服務(wù)器掉線break;} printf("recv buf:%s ret:%d \n",buf,ret); } }int main(int argc,char **argv) {//ret用來(lái)判斷返回值int ret;//建立套接字--socketint socket_fd;//AF_INET-->ipv4 SOCK_STREAM-->tcpsocket_fd = socket(AF_INET,SOCK_STREAM,0);if(socket_fd < 0){perror("socket fail");return -1;}#if 0//綁定本機(jī)ip和端口(可以綁定自己的端口,如果不綁定系統(tǒng)隨機(jī)分配)struct sockaddr_in my_addr;my_addr.sin_family = AF_INET; //ipv4my_addr.sin_port = htons(60001);//host to net(本機(jī)端口號(hào)轉(zhuǎn)網(wǎng)絡(luò)端口號(hào))my_addr.sin_addr.s_addr = inet_addr("192.168.11.2");//將本機(jī)IP轉(zhuǎn)換為網(wǎng)絡(luò)IP bind(socket_fd,(struct sockaddr *)&my_addr,sizeof(my_addr));#endif//創(chuàng)建一個(gè)線程用來(lái)接受服務(wù)器發(fā)送過(guò)來(lái)的數(shù)據(jù)pthread_t tid;ret = pthread_create(&tid,NULL,recv_buf,&socket_fd);if(ret != 0){perror("pthread_create fail");return -1;}//填充IP地址(服務(wù)器)--新結(jié)構(gòu)體struct sockaddr_in server_addr;server_addr.sin_family = AF_INET; //ipv4server_addr.sin_port = htons(SERVER_PORT);//host to net(本機(jī)端口號(hào)轉(zhuǎn)網(wǎng)絡(luò)端口號(hào))server_addr.sin_addr.s_addr = inet_addr(SERVER_IP);//將本機(jī)IP轉(zhuǎn)換為網(wǎng)絡(luò)IP//連接服務(wù)器ret = connect(socket_fd,(struct sockaddr *)&server_addr,sizeof(server_addr));if(ret < 0){printf("connect fail\n");return -1;}//給服務(wù)器發(fā)送數(shù)據(jù)char buf[1024] = {0};while(1){ memset(buf,0,sizeof(buf));scanf("%s",buf);ret = send(socket_fd,buf,strlen(buf),0); //如果服務(wù)器掉線,客戶端繼續(xù)發(fā)送,就會(huì)導(dǎo)致進(jìn)程退出//ret = write(socket_fd,buf,strlen(buf));printf("send success ret:%d\n",ret);}//關(guān)閉套接字close(socket_fd);return 0; }練習(xí)4: 寫一個(gè)回射服務(wù)器,這個(gè)服務(wù)器功能就是客戶端發(fā)送什么數(shù)據(jù)給服務(wù)器,服務(wù)器都會(huì)將數(shù)據(jù)回發(fā)到客戶端。
服務(wù)器
#include <stdio.h> #include <unistd.h> #include <string.h> #include <sys/types.h> /* See NOTES */ #include <sys/socket.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h>//服務(wù)器的IP地址 #define SERVER_IP "192.168.11.2" #define SERVER_PORT 60000int main(int argc,char **argv) {//建立套接字int socket_fd;//AF_INET-->ipv4 SOCK_STREAM-->tcpsocket_fd = socket(AF_INET,SOCK_STREAM,0);if(socket_fd < 0){perror("socket fail");return -1;} //重復(fù)綁定本機(jī)IP可能會(huì)出現(xiàn)失敗,這個(gè)時(shí)候要設(shè)置端口號(hào)復(fù)用。int optval = 1;setsockopt(socket_fd, SOL_SOCKET, SO_REUSEADDR,&optval, sizeof(optval));//填充本機(jī)IP地址和端口struct sockaddr_in server_addr;server_addr.sin_family = AF_INET;server_addr.sin_port = htons(SERVER_PORT);//本機(jī)端口轉(zhuǎn)化為網(wǎng)絡(luò)端口server_addr.sin_addr.s_addr = inet_addr(SERVER_IP);//將本機(jī)IP轉(zhuǎn)換為網(wǎng)絡(luò)IP//綁定本機(jī)IP和端口int ret;ret = bind(socket_fd,(struct sockaddr *)&server_addr,sizeof(server_addr));if(ret < 0){perror("bind fail");return -1;} printf("bind success [%s %d]\n",SERVER_IP,SERVER_PORT);//監(jiān)聽(tīng)ret = listen(socket_fd,20);if(ret < 0){perror("listen fail");return -1;} //等待客戶端的連接int socket_client;//客戶端的套接字struct sockaddr_in client_addr;//用來(lái)接受客戶端的ip地址和端口socklen_t addrlen = sizeof(client_addr);socket_client = accept(socket_fd,(struct sockaddr *)&client_addr,&addrlen);if(socket_client < 0){perror("accept fail");return -1; }//解析客戶端的ip和端口號(hào)char *ip = inet_ntoa(client_addr.sin_addr);//將網(wǎng)絡(luò)ip轉(zhuǎn)換為本機(jī)ipint port = ntohs(client_addr.sin_port);//將網(wǎng)絡(luò)端口號(hào)轉(zhuǎn)換為本機(jī)端口號(hào)printf("[ip:%s port:%d]\n",ip,port);//接受客戶端發(fā)送的數(shù)據(jù)(接受的是客戶端的套接字)char buf[1024] = {0};while(1){ memset(buf,0,sizeof(buf));ret = recv(socket_client,buf,sizeof(buf),0);//ret = read(socket_client,buf,sizeof(buf));//和recv函數(shù)的功能一樣if(ret == 0){printf("client down\n");break;}printf("[ip:%s port:%d] buf:%s ret:%d \n",ip,port,buf,ret);//將接收到的數(shù)據(jù)回射給客戶端send(socket_client,buf,strlen(buf),0); }//關(guān)閉套接字close(socket_fd);return 0; }客戶端
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> /*socket*/ #include <sys/types.h> /* See NOTES */ #include <sys/socket.h> /*ip*/ #include <netinet/in.h> //man 3 inet_addr #include <arpa/inet.h>//服務(wù)器的IP地址 #define SERVER_IP "192.168.11.2" #define SERVER_PORT 60000int main(int argc,char **argv) {//建立套接字--socketint socket_fd;//AF_INET-->ipv4 SOCK_STREAM-->tcpsocket_fd = socket(AF_INET,SOCK_STREAM,0);if(socket_fd < 0){perror("socket fail");return -1;}#if 0//綁定本機(jī)ip和端口(可以綁定自己的端口,如果不綁定系統(tǒng)隨機(jī)分配)struct sockaddr_in my_addr;my_addr.sin_family = AF_INET; //ipv4my_addr.sin_port = htons(60001);//host to net(本機(jī)端口號(hào)轉(zhuǎn)網(wǎng)絡(luò)端口號(hào))my_addr.sin_addr.s_addr = inet_addr("192.168.11.2");//將本機(jī)IP轉(zhuǎn)換為網(wǎng)絡(luò)IP bind(socket_fd,(struct sockaddr *)&my_addr,sizeof(my_addr));#endif//填充IP地址(服務(wù)器)--新結(jié)構(gòu)體struct sockaddr_in server_addr;server_addr.sin_family = AF_INET; //ipv4server_addr.sin_port = htons(SERVER_PORT);//host to net(本機(jī)端口號(hào)轉(zhuǎn)網(wǎng)絡(luò)端口號(hào))server_addr.sin_addr.s_addr = inet_addr(SERVER_IP);//將本機(jī)IP轉(zhuǎn)換為網(wǎng)絡(luò)IP//連接服務(wù)器int ret;ret = connect(socket_fd,(struct sockaddr *)&server_addr,sizeof(server_addr));if(ret < 0){printf("connect fail\n");return -1;}//給服務(wù)器發(fā)送數(shù)據(jù)char buf[1024] = {0};while(1){ memset(buf,0,sizeof(buf));scanf("%s",buf);ret = send(socket_fd,buf,strlen(buf),0); //ret = write(socket_fd,buf,strlen(buf));//printf("send success ret:%d\n",ret);//接受服務(wù)器回射的數(shù)據(jù)ret = recv(socket_fd,buf,sizeof(buf),0);printf("recv buf:%s ret:%d \n",buf,ret);}//關(guān)閉套接字close(socket_fd);return 0; }總結(jié)
以上是生活随笔為你收集整理的网络编程01-TCP协议(详)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 2017年工作学习计划(20170120
- 下一篇: HTML+CSS+JS大作业:服装购物网