柚子快報(bào)激活碼778899分享:Linux系統(tǒng)編程——網(wǎng)絡(luò)編程
柚子快報(bào)激活碼778899分享:Linux系統(tǒng)編程——網(wǎng)絡(luò)編程
文章目錄
一、網(wǎng)絡(luò)編程概述1.網(wǎng)絡(luò)通信即Socket編程介紹(1)網(wǎng)絡(luò)通信① 網(wǎng)絡(luò)通信模式② OSI七層網(wǎng)絡(luò)模型(2)Socket編程2.網(wǎng)絡(luò)編程三要素(1)協(xié)議①TCP傳輸協(xié)議②UDP傳輸協(xié)議③TCP/UDP的區(qū)別(2)IP地址(3)端口號(hào)3.字節(jié)序(1)字節(jié)系相關(guān)概念(2)有關(guān)字節(jié)序轉(zhuǎn)換的函數(shù)①主機(jī)字節(jié)序轉(zhuǎn)換網(wǎng)絡(luò)字節(jié)序函數(shù)②將網(wǎng)絡(luò)字節(jié)序轉(zhuǎn)換主機(jī)字節(jié)序函數(shù)③點(diǎn)分十進(jìn)制IP地址轉(zhuǎn)換為二進(jìn)制IP地址函數(shù)④ 二進(jìn)制IP地址轉(zhuǎn)換成點(diǎn)分十進(jìn)制的IP地址的函數(shù)
二、socket套接字相關(guān)函數(shù)1.socket 編程流程
(1)TCP通信流程(2)UDP通信的過程2.socket 相關(guān)函數(shù)(1)socket()函數(shù)(創(chuàng)建套建字)(2)bind()函數(shù)(綁定套建字)(3)listen()函數(shù)(監(jiān)聽被綁定的端口)(4)accept()函數(shù)(接收連接請(qǐng)求)(5)connect()函數(shù)(發(fā)送連接請(qǐng)求)(6)send()、recv()(TCP發(fā)送、接收信息)(7)sendto()、recvfrom()函數(shù)(UDP發(fā)送、接收消息)3.示例代碼
一、網(wǎng)絡(luò)編程概述
1.網(wǎng)絡(luò)通信即Socket編程介紹
(1)網(wǎng)絡(luò)通信
① 網(wǎng)絡(luò)通信模式
大部分的網(wǎng)絡(luò)應(yīng)用系統(tǒng)可以分為兩個(gè)部分:客戶(Client)和服務(wù)器(Server),而網(wǎng)絡(luò)服務(wù)程序架構(gòu)有兩種一種是CS模式,一種是BS模式。 CS即Client/Server(客戶機(jī)/服務(wù)器)結(jié)構(gòu):C/S結(jié)構(gòu)在技術(shù)上很成熟,它的主要特點(diǎn)是交互性強(qiáng)、具有安全的存取模式、網(wǎng)絡(luò)通信量低、響應(yīng)速度快、利于處理大量數(shù)據(jù)。但是該結(jié)構(gòu)的程序是針對(duì)性開發(fā),變更不夠靈活,維護(hù)和管理的難度較大。并且,由于該結(jié)構(gòu)的每臺(tái)客戶機(jī)都需要安裝相應(yīng)的客戶端程序,分布功能弱且兼容性差,不能實(shí)現(xiàn)快速部署安裝和配置,因此缺少通用性,具有較大的局限性。QQ就是CS模式,需要安裝客戶端軟件才能訪問服務(wù)器。 BS即Browser/Server(瀏覽器/服務(wù)器)結(jié)構(gòu):就是只安裝維護(hù)一個(gè)服務(wù)器(Server),而客戶端采用**瀏覽器(Browse)**運(yùn)行軟件。B/S結(jié)構(gòu)應(yīng)用程序相對(duì)于傳統(tǒng)的C/S結(jié)構(gòu)應(yīng)用程序是一個(gè)非常大的進(jìn)步。 B/S結(jié)構(gòu)的主要特點(diǎn)是分布性強(qiáng)、維護(hù)方便、開發(fā)簡(jiǎn)單且共享性強(qiáng)、總體擁有成本低。但數(shù)據(jù)安全性問題、對(duì)服務(wù)器要求過高、數(shù)據(jù)傳輸速度慢、軟件的個(gè)性化特點(diǎn)明顯降低,這些缺點(diǎn)是有目共睹的,難以實(shí)現(xiàn)傳統(tǒng)模式下的特殊功能要求。例如通過瀏覽器進(jìn)行大量的數(shù)據(jù)輸入或進(jìn)行報(bào)表的應(yīng)答、專用性打印輸出都比較困難和不便。此外,實(shí)現(xiàn)復(fù)雜的應(yīng)用構(gòu)造有較大的困難。那像網(wǎng)站、學(xué)校教務(wù)系統(tǒng)等這些通過瀏覽器訪問的模式叫 BS模式。
② OSI七層網(wǎng)絡(luò)模型
OSI(Open System Interconnection 開放系統(tǒng)互聯(lián))七層網(wǎng)絡(luò)模型是ISO(International Organization forStandardization,國(guó)際標(biāo)準(zhǔn)化組織)提出的一個(gè)參考模型,是一個(gè)把網(wǎng)絡(luò)通信在邏輯上的定義,也可以理解成為定義了通用的網(wǎng)絡(luò)通信規(guī)范。而我們的數(shù)據(jù)在網(wǎng)絡(luò)中傳輸?shù)倪^程,實(shí)際上就是如下圖的封裝和解封裝的過程,發(fā)送方通過各種封裝處理,把數(shù)據(jù) 轉(zhuǎn)換成比特流的形式,比特流在信號(hào)傳輸?shù)挠布浇橹袀鬏敚邮辗皆侔驯忍亓鬟M(jìn)行解封裝處理。 1.物理層 規(guī)定了如何為網(wǎng)絡(luò)通信實(shí)現(xiàn)最底層的物理連接,以及物理設(shè)備的機(jī)械、電氣、功能和過程特性。如:如何使用電纜和接頭的類型、用來傳送信號(hào)的電壓等。需要注意的是,網(wǎng)絡(luò)通信過程中所需的物理媒介(網(wǎng)線、線纜等),其實(shí)并不屬于物理層,因?yàn)槲锢韺訉?shí)際上是一種規(guī)定,規(guī)定這些物理媒介設(shè)備在連接網(wǎng)絡(luò)時(shí)的各種規(guī)格、參數(shù)以及工作方式。但是同時(shí),雙絞線、線纜等物理媒介又是物理層的實(shí)現(xiàn)。 2.數(shù)據(jù)鏈路層 規(guī)定了如何進(jìn)行物理地址尋址、如何在物理線路上進(jìn)行數(shù)據(jù)(幀frame)的可靠傳遞以及流量控制。數(shù)據(jù)鏈路層協(xié)議有SLIP協(xié)議、CSLIP協(xié)議、PPP協(xié)議等。交換機(jī),對(duì)幀解碼并根據(jù)幀中包含的信息把數(shù)據(jù)發(fā)送到正確的接收方,所以交換機(jī)是工作在數(shù)據(jù)鏈路層的。 3.網(wǎng)絡(luò)層 規(guī)定了通過哪些網(wǎng)絡(luò)節(jié)點(diǎn)、什么樣的網(wǎng)絡(luò)路徑來將數(shù)據(jù)(數(shù)據(jù)包)從發(fā)送方發(fā)送到接收方。在網(wǎng)絡(luò)層中,確定了從節(jié)點(diǎn)A發(fā)數(shù)據(jù)到節(jié)點(diǎn)B的網(wǎng)絡(luò)路徑,經(jīng)過哪些節(jié)點(diǎn)。網(wǎng)絡(luò)層既可以建立LAN通信系統(tǒng),更主要的是可以在WAN網(wǎng)絡(luò)系統(tǒng)中建立通信,這是因?yàn)樗凶约旱穆酚傻刂方Y(jié)構(gòu),通過路由協(xié)議(又稱可路由協(xié)議)進(jìn)行網(wǎng)絡(luò)通信的路由工作。 4.傳輸層 負(fù)責(zé)總體的數(shù)據(jù)傳輸和數(shù)據(jù)控制,提供端到端的交換數(shù)據(jù)的機(jī)制。傳輸層對(duì)數(shù)據(jù)(段)進(jìn)行分割和重組,并且進(jìn)行流量控制和根據(jù)接收方的接收數(shù)據(jù)能力確定適當(dāng)?shù)膫鬏斔俾?。例如以太網(wǎng)無(wú)法處理大于1500字節(jié)的數(shù)據(jù)包,傳輸層將數(shù)據(jù)分割成數(shù)據(jù)片段,并對(duì)小數(shù)據(jù)片段進(jìn)行序列編號(hào)。接收方的傳輸層將根據(jù)序列編號(hào)對(duì)數(shù)據(jù)進(jìn)行重組。傳輸層協(xié)議有TCP協(xié)議、UDP協(xié)議等。 5.會(huì)話層 在網(wǎng)絡(luò)中的兩個(gè)節(jié)點(diǎn)之間建立、維持和終止通信。 6.表示層 在應(yīng)用程序和網(wǎng)絡(luò)之間對(duì)數(shù)據(jù)進(jìn)行格式化,使之能夠被另一方理解。即發(fā)送方的表示層將應(yīng)用程序數(shù)據(jù)的抽象語(yǔ)法轉(zhuǎn)換成網(wǎng)絡(luò)適用于OSI網(wǎng)絡(luò)傳輸?shù)膫魉驼Z(yǔ)法,接收方則相反。除此之外,表示層還可對(duì)數(shù)據(jù)進(jìn)行加密與解密。 7.應(yīng)用層 最頂層的OSI層,為應(yīng)用程序提供網(wǎng)絡(luò)服務(wù)。如為電子郵件、文件傳輸功能提供協(xié)議支持。應(yīng)用層協(xié)議有HTTP協(xié)議、FTP協(xié) 議、SMTP協(xié)議等。
(2)Socket編程
在UNIX、Linux系統(tǒng)中,為了統(tǒng)一對(duì)各種硬件的操作,簡(jiǎn)化接口,不同的硬件設(shè)備也都被看成一個(gè)文件。對(duì)這些文件的操作,等同于對(duì)磁盤上普通文件的操作。 為了表示和區(qū)分已經(jīng)打開的文件,UNIX/Linux會(huì)為每個(gè)文件分配一個(gè)ID,這個(gè)文件就是一個(gè)整數(shù),被稱為文件描述符 例如: 通常用 0 來表示標(biāo)準(zhǔn)輸入文件(stdin),它對(duì)應(yīng)的硬件設(shè)備就是鍵盤; 通常用 1 來表示標(biāo)準(zhǔn)輸出文件(stdout),它對(duì)應(yīng)的硬件設(shè)備就是顯示器。 網(wǎng)絡(luò)連接也是一個(gè)文件,它也有文件描述符 我們可以通過 socket() 函數(shù)來創(chuàng)建一個(gè)網(wǎng)絡(luò)連接,或者說打開一個(gè)網(wǎng)絡(luò)文件,socket() 的返回值就是文件描述符(注意在windows下的socket返回的叫文件句柄,并不是叫文件描述符)。有了文件描述符,我們就可以使用普通的文件操作函數(shù)來傳輸數(shù)據(jù)了,例如: 用 read() 讀取從遠(yuǎn)程計(jì)算機(jī)傳來的數(shù)據(jù); 用 write() 向遠(yuǎn)程計(jì)算機(jī)寫入數(shù)據(jù)。
2.網(wǎng)絡(luò)編程三要素
(1)協(xié)議
協(xié)議計(jì)算機(jī)網(wǎng)絡(luò)通信必須遵守的規(guī)則 上面已經(jīng)介紹了OSI七層網(wǎng)絡(luò)模型中,各個(gè)層之間都有相對(duì)應(yīng)的協(xié)議,這里我們主要關(guān)注的是傳輸層的協(xié)議:TCP/UDP協(xié)議
①TCP傳輸協(xié)議
TCP:傳輸控制協(xié)議 (Transmission Control Protocol)。TCP協(xié)議是面向連接的通信協(xié)議,即傳輸數(shù)據(jù)之前,在發(fā)送端和接收端建立邏輯連接,然后再傳輸數(shù)據(jù),它提供了兩臺(tái)計(jì)算機(jī)之間可靠無(wú)差錯(cuò)的數(shù)據(jù)傳輸。 TCP建立連接的三次握手:: TCP協(xié)議中,在發(fā)送數(shù)據(jù)的準(zhǔn)備階段,客戶端與服務(wù)器之間的三次交互,以保證連接的可靠。
(1)Client首先向Server發(fā)送連接請(qǐng)求報(bào)文段,同步自己的seq(x),Client進(jìn)入SYN_SENT狀態(tài)。
(2)Server收到Client的連接請(qǐng)求報(bào)文段,返回給Client自己的seq(y)以及ack(x+1),Server進(jìn)入SYN_REVD狀態(tài)。
(3)Client收到Server的返回確認(rèn),再次向服務(wù)器發(fā)送確認(rèn)報(bào)文段ack(y+1),這個(gè)報(bào)文段已經(jīng)可以攜帶數(shù)據(jù)了。Client進(jìn)入ESTABLISHED狀態(tài)。
(4)Server再次收到Client的確認(rèn)信息后,進(jìn)入ESTABLISHED狀態(tài)。
TCP斷開連接的四次握手:
(1)Client向Server發(fā)送斷開連接請(qǐng)求的報(bào)文段,seq=m(m為Client最后一次向Server發(fā)送報(bào)文段的最后一個(gè)字節(jié)序號(hào)加1),Client進(jìn)入FIN-WAIT-1狀態(tài)。
(2)Server收到斷開報(bào)文段后,向Client發(fā)送確認(rèn)報(bào)文段,seq=n(n為Server最后一次向Client發(fā)送報(bào)文段的最后一個(gè)字節(jié)序號(hào)加1),ack=m+1,Server進(jìn)入CLOSE-WAIT狀態(tài)。此時(shí)這個(gè)TCP連接處于半開半閉狀態(tài),Server發(fā)送數(shù)據(jù)的話,Client仍然可以接收到。
(3)Server向Client發(fā)送斷開確認(rèn)報(bào)文段,seq=u(u為半開半閉狀態(tài)下Server最后一次向Client發(fā)送報(bào)文段的最后一個(gè)字節(jié)序號(hào)加1),ack=m+1,Server進(jìn)入LAST-ACK狀態(tài)。
(4)Client收到Server的斷開確認(rèn)報(bào)文段后,向Server發(fā)送確認(rèn)斷開報(bào)文,seq=m+1,ack=u+1,Client進(jìn)入TIME-WAIT狀態(tài)。
(5)Server收到Client的確認(rèn)斷開報(bào)文,進(jìn)入CLOSED狀態(tài),斷開了TCP連接。
(6)Client在TIME-WAIT狀態(tài)等待一段時(shí)間(時(shí)間為2*MSL((Maximum Segment Life)),確認(rèn)Client向Server發(fā)送的最后一次斷開確認(rèn)到達(dá)(如果沒有到達(dá),Server會(huì)重發(fā)步驟(3)中的斷開確認(rèn)報(bào)文段給Client,告訴Client你的最后一次確認(rèn)斷開沒有收到)。如果Client在TIME-WAIT過程中沒有再次收到Server的報(bào)文段,就進(jìn)入CLOSES狀態(tài)。TCP連接至此斷開
TCP:服務(wù)端和客戶端的通信流程:
②UDP傳輸協(xié)議
用戶數(shù)據(jù)報(bào)協(xié)議(User Datagram Protocol)。UDP協(xié)議是一個(gè)面向無(wú)連接的協(xié)議。傳輸數(shù)據(jù)時(shí),不需要建立連接,不管對(duì)方端服務(wù)是否啟動(dòng),直接將數(shù)據(jù)、數(shù)據(jù)源和目的地都封裝在數(shù)據(jù)包中,直接發(fā)送。每個(gè)數(shù)據(jù)包的大小限制在64k以內(nèi)。它是不可靠協(xié)議,因?yàn)闊o(wú)連接,所以傳輸速度快,但是容易丟失數(shù)據(jù)。日常應(yīng)用中,例如視頻會(huì)議、QQ聊天等。
UDP:服務(wù)端和客戶端的通信流程:
③TCP/UDP的區(qū)別
首先標(biāo)準(zhǔn)套接字分為TCP和UDP協(xié)議兩種不同type的工作流程,TCP網(wǎng)絡(luò)編程相對(duì)于UDP來說相對(duì)復(fù)雜,因?yàn)門CP是面向連接的服務(wù),其中包括三次握手建立連接的過程,而UDP則是無(wú)連接的服務(wù)。
(1)TCP面向連接(如打電話要先撥號(hào)建立連接),而UDP是無(wú)連接的,即發(fā)送數(shù)據(jù)之前不需要建立連接。 (2)TCP提供可靠的服務(wù),也就是說,通過TCP連接傳送的數(shù)據(jù),無(wú)差錯(cuò),不丟失,不重復(fù),且按序到達(dá);而UDP則是盡最大努力進(jìn)行交付,即不保證可靠交付。 (3)TCP面向字節(jié)流,實(shí)際上是TCP把數(shù)據(jù)看成一連串無(wú)結(jié)構(gòu)的字節(jié)流;而UDP是面向報(bào)文的,UDP沒有擁塞控制,因此網(wǎng)絡(luò)出現(xiàn)擁塞不會(huì)使源主機(jī)的發(fā)送速率降低(這樣對(duì)實(shí)時(shí)應(yīng)用很有用,如,IP電話,實(shí)時(shí)視頻會(huì)議等)。 (4)每一條的TCP只能是點(diǎn)到點(diǎn)的,UDP支持一對(duì)一,一對(duì)多,多對(duì)一和多對(duì)多的交互通信 (5)TCP首部開銷20字節(jié);UDP首部開銷小,只有8個(gè)字節(jié)。 (6)TCP的邏輯通信的信道是全雙工的可靠信道,而UDP則是不可靠信道。
(2)IP地址
IP地址:指互聯(lián)網(wǎng)協(xié)議地址(Internet Protocol Address),俗稱IP。IP地址用來給一個(gè)網(wǎng)絡(luò)中的計(jì)算機(jī)設(shè)備做唯一的編號(hào)。IP地址為32(IPV4)或者128位(IPV6),每一個(gè)數(shù)據(jù)包都必須攜帶目的地址IP和源IP地址,路由器依靠此信息為數(shù)據(jù)包選擇最優(yōu)路由(路線)。(IP地址就有點(diǎn)像是幾棟樓中的樓的號(hào)碼)
(3)端口號(hào)
網(wǎng)絡(luò)的通信,本質(zhì)上是兩個(gè)進(jìn)程(應(yīng)用程序)的通信。每臺(tái)計(jì)算機(jī)都有很多的進(jìn)程,那么在網(wǎng)絡(luò)通信時(shí),如何區(qū)分這些進(jìn)程呢?
如果說IP地址可以唯一標(biāo)識(shí)網(wǎng)絡(luò)中的設(shè)備,那么端口號(hào)就可以唯一標(biāo)識(shí)設(shè)備中的進(jìn)程(應(yīng)用程序)了。(注意TCP和UDP的端口號(hào)是相互獨(dú)立的) 實(shí)際上通過"IP地址+端口號(hào)"來區(qū)分不同的服務(wù)的 端口提供了一種訪問通道,服務(wù)器一般都是通過知名的端口來識(shí)別的。例如,對(duì)于每個(gè)TCP/IP的實(shí)現(xiàn)來說,F(xiàn)TP服務(wù)的端口號(hào)是21,每個(gè)Telnet服務(wù)器的TCP端口號(hào)都是23,每個(gè)TFTP(簡(jiǎn)單文件傳送協(xié)議)服務(wù)器的UDP端口號(hào)都是69。
端口號(hào),用兩個(gè)字節(jié)表示的整數(shù),它的取值范圍是065535。其中0~1023之間的端口號(hào)用于一些知名的網(wǎng)絡(luò)服務(wù)和應(yīng)用,普通的應(yīng)用程序需要使用1024以上的端口號(hào)。如果端口號(hào)被另外一個(gè)服務(wù)或應(yīng)用所占用,會(huì)導(dǎo)致當(dāng)前程序啟動(dòng)失敗。(如果IP地址是相當(dāng)于一棟樓的樓號(hào)的話,那么端口號(hào)就相當(dāng)于是這棟樓里面的房間的房號(hào))
利用 協(xié)議 + IP地址 + 端口號(hào) 三元組合,就可以標(biāo)識(shí)網(wǎng)絡(luò)中的進(jìn)程了,那么進(jìn)程間的通信就可以利用這個(gè)標(biāo)識(shí)與其它進(jìn)程進(jìn)行交互。
3.字節(jié)序
(1)字節(jié)系相關(guān)概念
字節(jié)序:是指多字節(jié)數(shù)據(jù)的存儲(chǔ)順序,在設(shè)計(jì)計(jì)算機(jī)系統(tǒng)的時(shí)候,有兩種處理內(nèi)存中數(shù)據(jù)的方法:即大端格式、小端格式。 小端格式(Little-Endian):將低位字節(jié)數(shù)據(jù)存儲(chǔ)在低地址。 大端格式(Big-Endian):將高位字節(jié)數(shù)據(jù)存儲(chǔ)在低地址。
例如: 0x12345678 在不同機(jī)器中的存儲(chǔ)是不同的,如下所示。 為何使用字節(jié)序
(1)計(jì)算機(jī)電路先處理低位字節(jié),效率比較高。因?yàn)橛?jì)算就是從低位開始的,所以計(jì)算機(jī)內(nèi)部很多都是小端字節(jié)序。
(2)格式規(guī)范是為人類編寫的,大端字節(jié)序更符合人類習(xí)慣。
網(wǎng)絡(luò)字節(jié)序的定義:將收到的第一個(gè)字節(jié)的數(shù)據(jù)當(dāng)做高位來看待,這就要求發(fā)送端的發(fā)送的第一個(gè)字節(jié)應(yīng)該是高位。而在發(fā)送端發(fā)送數(shù)據(jù)時(shí),發(fā)送的第一個(gè)字節(jié)是該數(shù)字在內(nèi)存中起始地址對(duì)應(yīng)的字節(jié)。可見多字節(jié)數(shù)值在發(fā)送前,在內(nèi)存中數(shù)值應(yīng)該以大端法存放。
所以,網(wǎng)絡(luò)協(xié)議指定了通訊字節(jié)序:大端。只有在多字節(jié)數(shù)據(jù)處理時(shí)才需要考慮字節(jié)序,運(yùn)行在同一臺(tái)計(jì)算機(jī)上的進(jìn)程相互通信時(shí),一般不用考慮字節(jié)序,異構(gòu)計(jì)算機(jī)之間進(jìn)行通訊時(shí),需要將自己的字節(jié)序轉(zhuǎn)換為網(wǎng)絡(luò)字節(jié)序。
(2)有關(guān)字節(jié)序轉(zhuǎn)換的函數(shù)
以下所有接口的頭文件都是: #include
①主機(jī)字節(jié)序轉(zhuǎn)換網(wǎng)絡(luò)字節(jié)序函數(shù)
函數(shù)原型:
uint16_t htons(uint16_t hostshort);//將16位主機(jī)字節(jié)序數(shù)據(jù)轉(zhuǎn)換成網(wǎng)絡(luò)字節(jié)序數(shù)據(jù)
uint32_t htonl(uint32_t hostlong);//將32位主機(jī)字節(jié)序數(shù)據(jù)轉(zhuǎn)換成網(wǎng)絡(luò)字節(jié)序數(shù)據(jù)
函數(shù)功能: htons將16位主機(jī)字節(jié)序數(shù)據(jù)轉(zhuǎn)換成網(wǎng)絡(luò)字節(jié)序數(shù)據(jù) htonl將32位主機(jī)字節(jié)序數(shù)據(jù)轉(zhuǎn)換成網(wǎng)絡(luò)字節(jié)序數(shù)據(jù)參數(shù)說明: hostshort:需要轉(zhuǎn)換的16位主機(jī)字節(jié)序數(shù)據(jù),uint16_t:unsigned short int hostlong:需要轉(zhuǎn)換的32位主機(jī)字節(jié)序數(shù)據(jù),uint32_t:32位無(wú)符號(hào)整型返回值: 成功返回網(wǎng)絡(luò)字節(jié)序的值
②將網(wǎng)絡(luò)字節(jié)序轉(zhuǎn)換主機(jī)字節(jié)序函數(shù)
函數(shù)原型:
uint16_t ntohs(uint16_t netshort);//將16位網(wǎng)絡(luò)字節(jié)序數(shù)據(jù)轉(zhuǎn)換成主機(jī)字節(jié)序數(shù)據(jù)
uint32_t ntohl(uint32_t netlong);//將32位網(wǎng)絡(luò)字節(jié)序數(shù)據(jù)轉(zhuǎn)換成主機(jī)字節(jié)序數(shù)據(jù)
函數(shù)功能: ntohs將16位網(wǎng)絡(luò)字節(jié)序數(shù)據(jù)轉(zhuǎn)換成主機(jī)字節(jié)序數(shù)據(jù) ntohl將32位網(wǎng)絡(luò)字節(jié)序數(shù)據(jù)轉(zhuǎn)換成主機(jī)字節(jié)序數(shù)據(jù) 參數(shù)說明: netshort:需要轉(zhuǎn)換的16位網(wǎng)絡(luò)字節(jié)序數(shù)據(jù);uint16_t:unsigned short int netlong:需要轉(zhuǎn)換的32位網(wǎng)絡(luò)字節(jié)序數(shù)據(jù);uint32_t:unsigned int 返回值: 成功:返回主機(jī)字節(jié)序的值
③點(diǎn)分十進(jìn)制IP地址轉(zhuǎn)換為二進(jìn)制IP地址函數(shù)
inet_aton函數(shù)函數(shù)原型
int inet_aton(const char *cp, struct in_addr *inp);
函數(shù)說明 將點(diǎn)分十進(jìn)制字符串轉(zhuǎn)換成32位無(wú)符號(hào)整數(shù),只適用于IPV4地址 參數(shù): cp:將被轉(zhuǎn)換的點(diǎn)分十進(jìn)制IP地址 inp:保存被轉(zhuǎn)換成二進(jìn)制的IP地址 返回值: 如果地址合法,則返回非0值,反之返回0值 inet_pton函數(shù) 函數(shù)原型:
int inet_pton(int af, const char *src, void *dst);
函數(shù)說明: 該函數(shù)將字符串src轉(zhuǎn)換為af地址類型協(xié)議簇的網(wǎng)絡(luò)地址,并存儲(chǔ)到dst中。 對(duì)于af參數(shù),必須為AF_INET或AF_INET6,適用于IPV4和IPV6地址參數(shù)說明: af:AF_INET或AF_INET6 src:將被轉(zhuǎn)換的點(diǎn)分十進(jìn)制的IP地址 dst:保存被轉(zhuǎn)換的二進(jìn)制IP地址返回值 轉(zhuǎn)換成功則返回1,對(duì)于指定的地址類型協(xié)議簇,如果不是一個(gè)有效的網(wǎng)絡(luò)地址, 將轉(zhuǎn)換失敗,返回 0,如果指定的地址類型協(xié)議簇不合法,將返回-1并,并且errno設(shè)置為EAFNOSUPPORT
④ 二進(jìn)制IP地址轉(zhuǎn)換成點(diǎn)分十進(jìn)制的IP地址的函數(shù)
inet_ntoa函數(shù)函數(shù)原型:
char *inet_ntoa(struct in_addr in);
函數(shù)說明: inet_ntoa()用來將參數(shù)in所指的大端網(wǎng)絡(luò)字節(jié)序二進(jìn)制的數(shù)字轉(zhuǎn)換成ipv4點(diǎn)分十進(jìn)制字符串網(wǎng)絡(luò)地址,然后將指向此網(wǎng)絡(luò)地址字符串的指針返回。成功則返回字符串指針,失敗則返回NULL。 參數(shù): in:將被轉(zhuǎn)換的二進(jìn)制IP地址(即32位無(wú)符號(hào)整數(shù)) 返回值: 成功:返回點(diǎn)分十進(jìn)制的IP地址 失敗:返回NULL inet_ntop函數(shù)
const char *inet_ntop(int af, const void *src, char *dst, socklen_t size);
函數(shù)說明: 將二進(jìn)制的IP地址轉(zhuǎn)換成點(diǎn)分十進(jìn)制的字符串(即點(diǎn)分十進(jìn)制的IP地址) 該函數(shù)將地址類型協(xié)議簇為af的網(wǎng)絡(luò)地址src轉(zhuǎn)換為字符串,并將其存儲(chǔ)到dst中,其中dst不能是空指針。 調(diào)用者在參數(shù)size中指定可使用的緩沖字節(jié)數(shù)。 inet_ntop拓展自inet_ntoa來支持多種地址類型協(xié)議簇,inet_ntoa現(xiàn)在已經(jīng)被棄用。參數(shù): af:AF_INET或AF_INET6 src:二進(jìn)制的IP地址 dst:保存被轉(zhuǎn)換成點(diǎn)分十進(jìn)制的IP地址 size:指定可使用的緩沖字節(jié)數(shù)返回值: inet_ntop執(zhí)行成功,返回一個(gè)指向dst的非空指針,如果執(zhí)行失敗,將返回NULL,并且errno設(shè)置為相應(yīng)的錯(cuò)誤類型。
二、socket套接字相關(guān)函數(shù)
1.socket 編程流程
(1)TCP通信流程
服務(wù)器 1.創(chuàng)建套接字(socket) 2.將socket與IP地址和端口綁定(bind) 3.監(jiān)聽被綁定的端口(listen) 4.接收連接請(qǐng)求(accept) 5.從socket中讀取客戶端發(fā)送來的信息(read) 6.向socket中寫入信息(write) 7.關(guān)閉socket(close) 客戶端 1.創(chuàng)建套接字(socket) 2.連接指定計(jì)算機(jī)的端口(connect) 3.向socket中寫入信息(write) 4.從socket中讀取服務(wù)端發(fā)送過來的消息(read) 5.關(guān)閉socket(close)
(2)UDP通信的過程
服務(wù)器: 1.使用函數(shù)socket(),生成套接字文件描述符; 2.通過struct sockaddr_in 結(jié)構(gòu)設(shè)置服務(wù)器地址和監(jiān)聽端口; 3.使用bind() 函數(shù)綁定監(jiān)聽端口,將套接字文件描述符和地址類型變量(struct sockaddr_in )進(jìn)行綁定; 4.接收客戶端的數(shù)據(jù),使用recvfrom() 函數(shù)接收客戶端的網(wǎng)絡(luò)數(shù)據(jù); 5.向客戶端發(fā)送數(shù)據(jù),使用sendto() 函數(shù)向服務(wù)器主機(jī)發(fā)送數(shù)據(jù); 6.關(guān)閉套接字,使用close() 函數(shù)釋放資源;
客戶端:
1.使用socket(),生成套接字文件描述符;
2.通過struct sockaddr_in 結(jié)構(gòu)設(shè)置服務(wù)器地址和監(jiān)聽端口;
3.向服務(wù)器發(fā)送數(shù)據(jù),sendto() ;
4.接收服務(wù)器的數(shù)據(jù),recvfrom() ;
5.關(guān)閉套接字,close() ;
2.socket 相關(guān)函數(shù)
(1)socket()函數(shù)(創(chuàng)建套建字)
函數(shù)原型:
#include
#include
int socket(int domain, int type, int protocol);
函數(shù)說明: 用于創(chuàng)建套接字,同時(shí)指定協(xié)議和類型 參數(shù): domain:指明所使用的協(xié)議族,通常為AF_INET,表示互聯(lián)網(wǎng)協(xié)議族(TCP/IP協(xié)議族); AF_INET IPv4因特網(wǎng)域
AF_INET6 IPv6因特網(wǎng)域
AF_UNIX Unix域
AF_ROUTE 路由套接字
AF_KEY 密鑰套接字
AF_UNSPEC 未指定
type:參數(shù)指定socket的類型: SOCK_STREAM: 流式套接字提供可靠的,面向連接的通信流,它使用TCP協(xié)議,從而保證了數(shù)據(jù)傳輸?shù)恼_性和順序性 SOCK_DGRAM: 數(shù)據(jù)報(bào)套接字定義了一種無(wú)連接的服,數(shù)據(jù)通過相互獨(dú)立的報(bào)文進(jìn)行傳輸,是無(wú)序的,并且不保證是可靠,無(wú)差錯(cuò)的。它使用數(shù)據(jù)報(bào)協(xié)議UDP SOCK_RAW: 允許程序使用底層協(xié)議,原始套接字允許對(duì)底層協(xié)議如IP或ICMP進(jìn)行直接訪問,功能強(qiáng)大但使用較為不便,主要用于一些協(xié)議的開發(fā)。
protocol:通常賦值為“0” 0選擇type類型對(duì)應(yīng)的默認(rèn)協(xié)議 IPPROTO_TCP TCP傳輸協(xié)議 IPPROTO_UDP UDP傳輸協(xié)議 IPPROTO_SCTP SCTP傳輸協(xié)議 IPPROTO_TIPC TIPC傳輸協(xié)議
返回值:成功返回非負(fù)套接字描述符,失敗返回-1
(2)bind()函數(shù)(綁定套建字)
函數(shù)原型:
int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
函數(shù)說明: 用于綁定IP地址和端口號(hào)到socket 參數(shù): sockfd:是一個(gè)socket描述符 addr:是一個(gè)指向包含有本機(jī)IP地址及端口號(hào)等信息的sockaddr類型的指針,指向要綁定給sockfd的協(xié)議地址結(jié)構(gòu),這個(gè)地址結(jié)構(gòu)根據(jù)地址創(chuàng)建socket時(shí)的地址協(xié)議族的不同而不同。 說明:
sockaddr在頭文件`#include
sockaddr的缺陷是:sa_data把目標(biāo)地址和端口信息混在一起了,如下:
struct sockaddr {
sa_family_t sin_family;//地址族
char sa_data[14]; //14字節(jié),包含套接字中的目標(biāo)地址和端口信息
};
sockaddr_in在頭文件#include
該結(jié)構(gòu)體解決了sockaddr的缺陷,把port和addr 分開儲(chǔ)存在兩個(gè)變量中,如下:
struct sockaddr_in {
__kernel_sa_family_t sin_family; /* 地址族Address family */
__be16 sin_port; /* 16位端口號(hào)Port number */
struct in_addr sin_addr; /* 網(wǎng)絡(luò)地址Internet address */
};
/* Internet address. */
struct in_addr {
__be32 s_addr;//32位網(wǎng)絡(luò)地址
};
上述兩者結(jié)構(gòu)體長(zhǎng)度一樣,都是16個(gè)字節(jié),即占用的內(nèi)存大小是一致的,因此可以互相轉(zhuǎn)化。二者是并列結(jié)構(gòu),指向sockaddr_in結(jié)構(gòu)的指針也可以指向sockaddr。 一般先把sockaddr_in變量賦值后,強(qiáng)制類型轉(zhuǎn)換后傳入用sockaddr做參數(shù)的函數(shù):sockaddr_in用于socket定義和賦值;sockaddr用于函數(shù)參數(shù)。
addrlen:地址的長(zhǎng)度,一般用sizeof(struct sockaddr_in)表示
返回值:成功返回0,失敗返回-1
(3)listen()函數(shù)(監(jiān)聽被綁定的端口)
函數(shù)原型:
int listen(int sockfd, int backlog);
函數(shù)說明: 功能: 設(shè)置能處理的最大連接數(shù),listen并未開始接受連線,只是設(shè)置了socket的listen模式,listen函數(shù)只用于服務(wù)器端,服務(wù)器進(jìn)程不知道要與誰(shuí)進(jìn)行連接,因此,它不會(huì)主動(dòng)的要求與某個(gè)進(jìn)程連接,只是一直監(jiān)聽是否有其他客戶進(jìn)程與之連接,然后響應(yīng)該連接請(qǐng)求,并對(duì)它做出處理,一個(gè)服務(wù)進(jìn)程可以同時(shí)處理多個(gè)客戶進(jìn)程的連接,主要就連個(gè)功能:將一個(gè)未連接的套接字轉(zhuǎn)換為一個(gè)被動(dòng)套接字(監(jiān)聽),規(guī)定內(nèi)核為相應(yīng)套接字排隊(duì)的最大連接數(shù)。 內(nèi)核為任何一個(gè)給定監(jiān)聽套接字維護(hù)兩個(gè)隊(duì)列: 未完成連接隊(duì)列,每個(gè)這樣的SYN報(bào)文段對(duì)應(yīng)其中一項(xiàng):已由某個(gè)客戶端發(fā)出并到達(dá)服務(wù)器,而服務(wù)器正在等待完成相應(yīng)的TCP三次握手過程,這些套接字處于SYN_REVD狀態(tài) 已完成連接隊(duì)列,每個(gè)已完成TCP三次握手過程的客戶端對(duì)應(yīng)其中一項(xiàng),這些套接字處于ESTABLISHED狀態(tài);參數(shù): sockfd:socket系統(tǒng)調(diào)用返回的服務(wù)端socket描述符 backlog:指定在請(qǐng)求隊(duì)列中允許的最大的請(qǐng)求數(shù),大多數(shù)系統(tǒng)默認(rèn)為5 返回值:成功返回0,失敗返回-1
(4)accept()函數(shù)(接收連接請(qǐng)求)
函數(shù)原型:
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
函數(shù)說明: accept函數(shù)由TCP服務(wù)器調(diào)用,用于從已完成連接隊(duì)列對(duì)頭返回下一個(gè)已完成連接,如果已完成連接隊(duì)列為空,那么進(jìn)程被投入睡眠。 參數(shù): sockfd:是socket系統(tǒng)調(diào)用返回的服務(wù)器端socket描述符 addr:用來返回已連接的對(duì)端(客戶端)的協(xié)議地址 addrlen:客戶端地址長(zhǎng)度,注意需要取地址 返回值: 該函數(shù)的返回值是一個(gè)新的套接字的描述符,返回值是表示已連接的套接字描述符,而第一個(gè)參數(shù)是服務(wù)器監(jiān)聽套接字描述符,一個(gè)服務(wù)器通常僅僅創(chuàng)建一個(gè)監(jiān)聽套接字,它在該服務(wù)器的生命周期內(nèi)一直存在。內(nèi)核為每個(gè)由服務(wù)器進(jìn)程接受的客戶連接創(chuàng)建一個(gè)已連接套接字(表示TCP三次握手已完成),當(dāng)服務(wù)器完成對(duì)某個(gè)給定客戶的服務(wù)時(shí),相應(yīng)的已連接套接字就會(huì)被關(guān)閉。
(5)connect()函數(shù)(發(fā)送連接請(qǐng)求)
函數(shù)原型:
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
函數(shù)說明: 該函數(shù)用于綁定之后的client端(客戶端),與服務(wù)器建立連接 參數(shù): sockfd:創(chuàng)建的socket描述符 addr:服務(wù)端的ip地址和端口號(hào)的地址結(jié)構(gòu)指針 addrlen:地址的長(zhǎng)度,通常被設(shè)置為sizeof(struct sockaddr) 返回值:成功返回0,遇到錯(cuò)誤時(shí)返回-1,并且errno中包含相應(yīng)的錯(cuò)誤碼
(6)send()、recv()(TCP發(fā)送、接收信息)
send函數(shù)原型:
ssize_t send(int sockfd, const void *buf, size_t len, int flags);
函數(shù)說明: 函數(shù)只能對(duì)處于連接狀態(tài)的套接字進(jìn)行使用,參數(shù)sockfd為已建立好連接的套接字描述符 參數(shù): sockfd:為已建立好連接的套接字描述符即accept函數(shù)的返回值 buf:要發(fā)送的內(nèi)容 len:發(fā)送內(nèi)容的長(zhǎng)度 flags:設(shè)置為MSG_DONTWAITMSG 時(shí) 表示非阻塞,設(shè)置為0時(shí) 功能和write一樣 返回值:成功返回實(shí)際發(fā)送的字節(jié)數(shù),失?。悍祷?-1 recv函數(shù)原型:
ssize_t recv(int sockfd, const void *buf, size_t len, int flags);
函數(shù)說明:接收套接字中的數(shù)據(jù) 參數(shù): sockfd:在哪個(gè)套接字接 buf:存放要接收的數(shù)據(jù)的首地址 len:要接收的數(shù)據(jù)的字節(jié) flags:設(shè)置為MSG_DONTWAITMSG 時(shí) 表示非阻塞,設(shè)置為0時(shí) 功能和read一樣 返回值:成功返回實(shí)際發(fā)送的字節(jié)數(shù),失?。悍祷?-1
(7)sendto()、recvfrom()函數(shù)(UDP發(fā)送、接收消息)
sendto()函數(shù)原型:
int sendto(int s, const void *buf, int len, unsigned int flags, const struct sockaddr *to, int tolen);
函數(shù)說明:UDP發(fā)送消息 參數(shù)說明: s: socket描述符; buf:UDP數(shù)據(jù)報(bào)緩存區(qū)(包含待發(fā)送數(shù)據(jù)); len: UDP數(shù)據(jù)報(bào)的長(zhǎng)度; flags:調(diào)用方式標(biāo)志位(一般設(shè)置為0),設(shè)置為MSG_DONTWAITMSG 時(shí) 表示非阻塞 to: 指向接收數(shù)據(jù)的主機(jī)地址信息的結(jié)構(gòu)體(sockaddr_in需類型轉(zhuǎn)換);之前介紹bind函數(shù)時(shí)已經(jīng)介紹。 tolen:to所指結(jié)構(gòu)體的長(zhǎng)度; 返回值:成功則返回實(shí)際傳送出去的字符數(shù),失敗返回-1,錯(cuò)誤原因會(huì)存于errno 中。 recvfrom()函數(shù)原型:
int recvfrom(int s, void *buf, int len, unsigned int flags,struct sockaddr *from, int *fromlen);
函數(shù)說明:UDP接收消息參數(shù): s: socket描述符; buf: UDP數(shù)據(jù)報(bào)緩存區(qū)(包含所接收的數(shù)據(jù)); len: 緩沖區(qū)長(zhǎng)度。 flags: 調(diào)用操作方式(一般設(shè)置為0),設(shè)置為MSG_DONTWAITMSG 時(shí) 表示非阻塞 from: 指向發(fā)送數(shù)據(jù)的客戶端地址信息的結(jié)構(gòu)體(sockaddr_in需類型轉(zhuǎn)換); fromlen:指針,指向from結(jié)構(gòu)體長(zhǎng)度值。返回值:成功則返回實(shí)際接收到的字符數(shù),失敗返回-1,錯(cuò)誤原因會(huì)存于errno 中。
3.示例代碼
實(shí)現(xiàn)客戶端,服務(wù)器通信服務(wù)器端
#include
#include
#include
#include
#include
#include
#include
#include
int main()
{
int s_fd;//socket 返回的套接字,服務(wù)器端
int c_fd;//accept函數(shù)返回的客戶端的套接字
int ret;
int c_len;//客戶端結(jié)構(gòu)體的大小
int readSize;
int quit_flag = 0;
pid_t pid;
char readBuf[128] = {'\0'};//存放讀取的客戶端內(nèi)容
char writeBuf[128] = {'\0'};//存放發(fā)往客戶端的內(nèi)容
char ipBuf[32] = {'\0'};
struct sockaddr_in s_addr;//設(shè)置本機(jī)ip地址及端口的結(jié)構(gòu)體
struct sockaddr_in c_addr;//接收的客戶端的ip地址及端口的結(jié)構(gòu)體
//1.創(chuàng)建套接字(socket)
s_fd = socket(AF_INET,SOCK_STREAM, 0);
if(s_fd<0){
perror("creat socket fail");
return -1;
}
s_addr.sin_family = AF_INET;
s_addr.sin_port = htons(8989);//綁定端口號(hào),并將端口號(hào)變?yōu)榫W(wǎng)絡(luò)字節(jié)序
ret = inet_aton("192.168.109.137",&s_addr.sin_addr);//綁定本機(jī)IP地址,并將其轉(zhuǎn)換為二進(jìn)制IP地址
if(ret == 0){
perror("inet_aton fail");
return -1;
}
//2.將socket與IP地址和端口綁定(bind)
ret = bind(s_fd,(struct sockaddr*)&s_addr,sizeof(struct sockaddr_in));//注意將struct sockaddr_in*強(qiáng)制轉(zhuǎn)換為struct sockaddr*
if(ret < 0){
perror("bind fail");
return -1;
}
//3.監(jiān)聽被綁定的端口(listen)
listen(s_fd,5);
c_len = sizeof(struct sockaddr_in);
while(1){
//4.接收連接請(qǐng)求(accept)
c_fd = accept(s_fd, (struct sockaddr*)&c_addr, &c_len);
if(c_fd < 0){
perror("accept error");
}else{
memset(ipBuf,'\0',32);
strcpy(ipBuf,inet_ntoa(c_addr.sin_addr));
printf("get connect:%s\n",ipBuf);//將客戶度鏈接的地址轉(zhuǎn)換為十進(jìn)制打印
pid = fork();
//創(chuàng)建子進(jìn)程對(duì)接每個(gè)客戶端
if(pid == 0)
{
pid_t pid;
pid = fork();
if(pid > 0){
while(1){
printf("message to the IP :%s:",ipBuf);
memset(writeBuf,'\0',128);
scanf("%s",writeBuf);
write(c_fd,writeBuf,strlen(writeBuf));
}
}else if(pid == 0){
while(1){
//從socket中讀取客戶端發(fā)送來的信息(read)進(jìn)程
memset(readBuf,'\0',sizeof(readBuf));
readSize = read(c_fd,readBuf,128);
if(readSize < 0){
perror("read fail");
}else if(readSize == 0){
printf("client quits\n");
break;
}else{
printf("IP %s :%s\n",inet_ntoa(c_addr.sin_addr),readBuf);
}
}
//發(fā)送信息到客戶端
}
}
}
}
//7.關(guān)閉socket(close)
close(c_fd);
close(s_fd);
return 0;
}
客戶端
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
int main(int argc,char *argv[])
{
int c_fd;//客戶端套接字
int ret;
int readSize;
pid_t pid;
char readBuf[128] = {'\0'};//存放讀取的服務(wù)器內(nèi)容
char writeBuf[128] = {'\0'};//存放發(fā)往服務(wù)器的內(nèi)容
struct sockaddr_in addr;//想要連接的目標(biāo)地址
if(argc < 3){
printf("the param is not good! Please in put ip and port\n");
exit(-1);
}
//1.創(chuàng)建套接字(socket)
c_fd = socket(AF_INET,SOCK_STREAM,0);
if(c_fd<0){
perror("creat socket fail");
return -1;
}
addr.sin_family = AF_INET;
addr.sin_port = htons(atoi(argv[2]));//設(shè)置目標(biāo)端口
inet_aton(argv[1],&addr.sin_addr);//設(shè)置目標(biāo)IP地址
//2.連接指定計(jì)算機(jī)的端口(connect)
ret = connect(c_fd,(struct sockaddr*)&addr,sizeof( struct sockaddr_in));
if(ret<0 ){
perror("connect error");
return -1;
}
pid = fork();
if(pid > 0){
while(1)
{
//3.從socket中讀取服務(wù)端發(fā)送過來的消息(read)
memset(readBuf,0,sizeof(readBuf));
readSize = read(c_fd,readBuf,128);
if(readSize == -1)
{
perror("read");
}
printf("get %d from the sever:%s\n",readSize,readBuf);
}
}else if(pid ==0){
while(1){
//4.向服務(wù)區(qū)中發(fā)送數(shù)據(jù)
printf("Please input:");
memset(writeBuf,'\0',128);
scanf("%s",writeBuf);
write(c_fd,writeBuf,strlen(writeBuf));
if(strstr(writeBuf,"quit")!=NULL){
kill(getppid(),9);
kill(getpid(),9);
}
}
}
return 0;
}
運(yùn)行結(jié)果 服務(wù)器
客戶端: 服務(wù)器:
柚子快報(bào)激活碼778899分享:Linux系統(tǒng)編程——網(wǎng)絡(luò)編程
好文推薦
本文內(nèi)容根據(jù)網(wǎng)絡(luò)資料整理,出于傳遞更多信息之目的,不代表金鑰匙跨境贊同其觀點(diǎn)和立場(chǎng)。
轉(zhuǎn)載請(qǐng)注明,如有侵權(quán),聯(lián)系刪除。