linux網(wǎng)絡(luò)編程-基礎(chǔ)socket高并發(fā)服務(wù)器_第1頁
linux網(wǎng)絡(luò)編程-基礎(chǔ)socket高并發(fā)服務(wù)器_第2頁
linux網(wǎng)絡(luò)編程-基礎(chǔ)socket高并發(fā)服務(wù)器_第3頁
linux網(wǎng)絡(luò)編程-基礎(chǔ)socket高并發(fā)服務(wù)器_第4頁
linux網(wǎng)絡(luò)編程-基礎(chǔ)socket高并發(fā)服務(wù)器_第5頁
已閱讀5頁,還剩115頁未讀, 繼續(xù)免費閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認領(lǐng)

文檔簡介

C++網(wǎng)絡(luò)協(xié)議的概假設(shè),A、B雙方欲傳輸文件。規(guī)第一次,傳輸文件名,接收方接收到文件名,應(yīng)答OK給傳輸方;第二次發(fā)送文件的尺寸接收方接收到該數(shù)據(jù)再次應(yīng)答一個OK;第三次,傳輸文件內(nèi)容。同樣,接收方接收數(shù)據(jù)完成后應(yīng)答OK表示文件內(nèi)容接收成功成。A、B之間形成了一個最簡單的數(shù)據(jù)傳輸規(guī)則。雙方都按此規(guī)則發(fā)送、接收數(shù)據(jù)。A、B之間達成的這個相互遵守的規(guī)則即為協(xié)議。這種僅在AB之間被遵守的協(xié)議稱之為原始協(xié)議當此協(xié)議被、 成為一個標準協(xié)議。最早的ftp協(xié)議就是由此衍生而來。、TCP協(xié)議注重數(shù)據(jù)的傳輸。http協(xié)議著重于數(shù)據(jù)的解傳輸層常見協(xié)議有TCP/UDP協(xié)議應(yīng)用層常見的協(xié)議有HTTP協(xié)議,F(xiàn)TP網(wǎng)絡(luò)層常見協(xié)議有IP協(xié)議、ICMP協(xié)議、IGMP協(xié)議。網(wǎng)絡(luò)接口層常見協(xié)議有ARP協(xié)議、RARP協(xié)議。TCP傳輸控制協(xié)議(TransmissionControlProtocol)是一種面向連UDP用戶數(shù)據(jù)報協(xié)議(UserDatagramProtocol)是OSI參考模型中HTTP超文本傳輸協(xié)議(HyperTextTransferProtocol)是互聯(lián)網(wǎng)上IP協(xié)議是因特網(wǎng)互聯(lián)協(xié)議(InternetICMP協(xié)議是Internet控制報文協(xié)議(InternetControlMessageProtocol)它是TCP/IP協(xié)議族的一個子協(xié)議,用于在IP主機、路由之間傳遞控制消息IGMP協(xié)議是Internet組管理協(xié)議(InternetGroupManagementProtocolProtocol已知的IP,尋找對應(yīng)主機的MAC地址。RARP是反向地址轉(zhuǎn)換協(xié)議,通過MAC地址確定IP地址網(wǎng)絡(luò)應(yīng)用程序設(shè)計模C/SB/S端使用每臺PC都默認配置的瀏覽器即可完成數(shù)據(jù)的傳輸。對于C/S模式來說,其優(yōu)點明顯??蛻舳宋挥谀繕酥鳈C上可以保般來說客戶端和服務(wù)器程序由一個開發(fā)團隊創(chuàng)作所以他們之間所例如,騰訊公司所采用的通信協(xié)議,即為ftp協(xié)議的修改剪裁版。因此,傳統(tǒng)的網(wǎng)絡(luò)應(yīng)用程序及較大型的網(wǎng)絡(luò)應(yīng)用程序都首選C/S模式進行開發(fā)。如,知名的魔獸世界。3D畫面,數(shù)據(jù)量龐C/S模式的缺點也較突出由于客戶端和服務(wù)器都需要有一個開發(fā)成。這也是很多用戶不愿使用C/S模式應(yīng)用程序的重要原因。B/S模式相比C/S模式而言,由于它沒有獨立的客戶端,使用標準B/S支持受限緩存數(shù)據(jù)不盡如人意,tp協(xié)議進行通信,協(xié)議選擇不靈活。分層模OSIOSI如何讓控制對物理介質(zhì)的。這一層通常還提供錯誤檢測和糾正,以確保數(shù)據(jù)的可靠傳輸。如:串口通信中使用到的115200、網(wǎng)絡(luò)層連接和路徑選擇。Internet的發(fā)展使得從世界各站點 WWW端口80等性要求高,數(shù)據(jù)量大的數(shù)據(jù),UDP(用戶數(shù)據(jù)報協(xié)議,與TCP特聊天數(shù)據(jù)就是通過這種方式傳輸?shù)摹V饕菍南聦咏邮盏臄?shù)會話層:通過傳輸層(端:傳輸端口與接收端口)建立數(shù)據(jù)傳輸間需要互相認識可以是IP也可以是MAC或者是主機名。。統(tǒng)的應(yīng)用層。例如,PC程序與另一臺計算機進行通信,其中信息交換標準碼(ASCII)來表示相同的字符必要,表示。TCP/IP(Appliction(ransport一般在應(yīng)用開發(fā)過程中,討論最多的是TCP/IP模型通信過兩臺計算機通過TCP/IP協(xié)議通訊的過程如下所、幀同(即從網(wǎng)線上檢測到什么信號算作新幀的開始)檢(如果檢測到就自動重發(fā)、數(shù)據(jù)差錯校驗等工作。交換機是工作在,、網(wǎng)絡(luò)層的IP協(xié)議是構(gòu)成Internet的基礎(chǔ)Internet上的主機通過IPInter-徑轉(zhuǎn)發(fā)數(shù)據(jù)包,數(shù)據(jù)包從Internet上的源主機到目的主機往往要將進來的數(shù)據(jù)包拆掉網(wǎng)絡(luò)層和鏈路層兩層首部并重新封裝IP協(xié)議不(這里的“端”指源主機和目的主機。傳輸層可選擇TCPUDP協(xié)TCP是一種面向連接的可靠的協(xié)議有點像打雙方拿起電證聽得到,并且是按說話的順序聽到的,說完話斷開連接。也就是說TCP傳輸?shù)碾p方需要首先建立連接之后由TCP協(xié)議保證數(shù)據(jù)收UDP是無連接的傳輸協(xié)議,不保證可靠性,有點像寄信,信寫好件寄送順序。使用UDP協(xié)議的應(yīng)用程序需要自己完成丟包重發(fā)、消是IP、ARP還是RARP協(xié)議的數(shù)據(jù)報,然后交給相應(yīng)的協(xié)議處理。假如是IP數(shù)據(jù)報,IP協(xié)議再根據(jù)IP首部中的“上層協(xié)議”字段確定該數(shù)據(jù)報的有效載荷是TCP、UDP、ICMP還是IGMP,然后交給相應(yīng)的協(xié)議處理。假如是TCP段或UDP段,TCP或UDP協(xié)議再根據(jù)TCP首部或UDP首部的“端 戶進程。IP地址是標識網(wǎng)絡(luò)中不同主機的地址,而端 主機上標識不同進程的地址,IP地址和端 雖然IP、ARPRARP數(shù)據(jù)報都需要以太網(wǎng)驅(qū)動程序來封裝成幀,但是從功能上劃分,ARP和RARP屬于鏈路層,IP屬于網(wǎng)絡(luò)層。雖然ICMP、IGMP、TCP、UDP的數(shù)據(jù)都需要IP協(xié)議來封裝成數(shù)據(jù)報,但是從功能上劃分,ICMP、IGMP與IP同屬于網(wǎng)絡(luò)層,TCP和UDP屬于傳輸層協(xié)議格面將介紹如何使用socketAPI編寫應(yīng)用程序,應(yīng)用程序?qū)νㄓ崝?shù)據(jù)發(fā)到網(wǎng)絡(luò)上時每層協(xié)議都要加上一個數(shù)據(jù)首(header稱為封(Encapsulation,如下圖所示(segment(datagram(frame部,最后將應(yīng)用層數(shù)據(jù)交給應(yīng)用程序處理以太網(wǎng)的幀格式如下所其中的源地址和目的地址是指網(wǎng)卡的硬件地址(也叫MAC地址,長度是48位,是在網(wǎng)卡出廠時 的??稍趕 HWaddr協(xié)議字段有三種值,分別對應(yīng)IP、ARP、RARP。幀尾是CRC校驗碼以太網(wǎng)幀中的數(shù)據(jù)長度規(guī)定最小46字節(jié),最大1500字節(jié),ARP和RARP數(shù)據(jù)包的長度不夠46字節(jié),要在后面補填充位。最大值(MTU,的MTU,如果一個數(shù)據(jù)包從以太網(wǎng)路由到撥號鏈 (fagmentation“MTU:1500ARP在網(wǎng)絡(luò)通訊時,源主機的應(yīng)用程序知道目的主機的IP地址和端口起到這個作用。源主機發(fā)出ARP請求,詢問“IP地址是幀首部的硬件地址填FF:FF:FF:FF:FF:FF表示廣播目的主機接收到廣播的ARP請求,發(fā)現(xiàn)其中的IP地址與本機相符,則發(fā)送一個ARP應(yīng)每臺主機都一個ARP緩存表,可以用arp-a命令查看。緩存表中的表項有過期時間(一般為20分鐘,如果20分鐘內(nèi)沒有再次使用某個表項則該表項失效下次還要發(fā)ARP請求來獲得目的主機的硬件地址。想,為什么表項要有過期時間而不是一直有效?ARP數(shù)據(jù)報的格式如下所示ARPMAC地址、目的MAC地址在以太網(wǎng)首部和ARP請求中各出現(xiàn)網(wǎng),協(xié)議類型指要轉(zhuǎn)換的地址類型,0x0800為IP地址,后面兩個地址長度對于以太網(wǎng)地址和IP地址分別為64(字節(jié),op字段為1表示ARP請求,op字段為2表示ARP應(yīng)答??匆粋€具體的例子:以太網(wǎng)首部(14字節(jié)0000:ffffffffffff00055d6158a80806ARP幀(28字節(jié))0000:000010:08000604000100055d6158a8c0a8000020:000000000000c0a800填充位(18字節(jié)0020:007731d2500030:fd7841d300000000000000以太網(wǎng)首部:目的主機采用廣播地址,源主機的MAC地址是00:05:5d:61:58:a8,上層協(xié)議類型0x0806表示ARPARP幀:硬件類型0x0001表示以太網(wǎng),協(xié)議類型0x0800表示4,op為0x0001表示請求目的主機的MAC地址,源主機MAC地址00:05:5d:61:58:a8,源主機IP地址為c0a80037(5目的主機MAC地址全0待填寫,目的主機IP地址為c0a80002(由于以太網(wǎng)規(guī)定最小數(shù)據(jù)長度為46字節(jié),ARP幀長度只有28節(jié),因此有18字節(jié)填充位,填充位的內(nèi)容沒有定義,與具體實現(xiàn)相0000:00055d6158a800055da1b84008ARP0000:000010:08000604000200055da1b840c0a8000020:00055d6158a8c0a800填充0020:007731d2500030:fd7841d300000000000000以太網(wǎng)首部:目的主機的MAC地址是00:05:5d:61:58:a8,源主機的MAC地址是00:05:5d:a1:b8:40,上層協(xié)議類型0x0806表示ARP。ARP幀:硬件類型0x0001表示以太網(wǎng),協(xié)議類型0x0800表示4,op為0x0002表示應(yīng)答,源主機MAC地址為00:05:5d:a1:b8:40,源主機IP地址為c0a80002(,目的主機MAC地址為00:05:5d:61:58:a8,目的主機IP地址為c0a80037(5思考題:如果源主機和目的主機不在同一網(wǎng)段,ARP請求的廣播IPIPIP數(shù)據(jù)報的首部長度和數(shù)據(jù)長度都是可變長的,但總是4字節(jié)的整數(shù)倍對于IPv4,4位版本字段是44位首部長度的數(shù)值是以4字節(jié)為單位的最小值5,也就是說首部長度最小是4x5=20字節(jié)也就是不帶任何選項的IP首部,4位能表示的最大值是15,也就是說首部長度最大是60字節(jié)。8TOS字段有3個位用來指定IP數(shù)據(jù)的優(yōu)先(目前已經(jīng)廢棄不用還有4個位表示可選的服務(wù)類(最小延遲、最大?吐量、最大可靠性、最小成本,還有一個位總是0??傞L度是整個數(shù)據(jù)報(包括IP首部和IP層payload)的字節(jié)數(shù)。每傳一個IP數(shù)據(jù)報,16位的標識加1,可用于分片和重新組裝數(shù)據(jù)報。3位標志和13位片偏移用于分片。TTL(Timetolive)是這樣用的:源主機為數(shù)據(jù)包設(shè)定一個生存時間比如64,每過一個路由器就把該值減1,如果減到0就表示路由已經(jīng)太長了仍然找不到目的主機的網(wǎng)絡(luò),(hop字段指示上層協(xié)議是TCP、UDP、ICMP還是IGMP。然后是校驗和,只校驗IP首部,數(shù)據(jù)的校驗由更協(xié)議負責。IPv4的IP地址長度為32位。 前面講了以太網(wǎng)幀中的最小數(shù)據(jù)長度為46字節(jié)不足字節(jié)的要用填充字節(jié)補上,那么如何界定這46字節(jié)里前多少個字節(jié)是IP、ARP或RARP數(shù)據(jù)報而后面是填充字節(jié)?UDPUDP下面分析一幀基于UDPTFTP協(xié)議幀。0000:00055d67d0b100055d6158a808IP首0000:450010:005393250000801125ecc0a80037c00020:00UDP首0020:05d40045003facTFTP協(xié)0020:00010040:'i'00'b''l''k''s''i''z''e'00'5''1''2'000050:'m''e''o''u''t'00'1''0'00't''s''i''z''e'0000:05:5d:61:58:a8地址是00:05:5d:67:d0:b1,上層協(xié)議類型0x0800IPIP首部:每一個字節(jié)0x45包含4位版本號和4位首部長度,版本號為4IPv4,首部長度為5,說明IP首部不帶有選項字段。服務(wù)類型為0,沒有使用服務(wù)。16位總長度字段(包括IP首部和IP層payload的長度)0x005383字節(jié),加上以太網(wǎng)首部14字節(jié)可知整個幀長度是97字節(jié)。IP報標識是0x9325,標志字段和片偏移字段設(shè)置為0x0000,就是DF=0允許分片,MF=0此數(shù)據(jù)報沒有分片,沒有分片偏移。TTL是0x80,也就是128。上層協(xié)議0x11表UDP協(xié)議。IP首部校驗和為0x25ec,源主機IP是c0a800(5,01( 0x05d4(1492)是客戶端的 ,目的0x0045(69)是TFTP服務(wù)的well-known 0x003f63字節(jié),包括UDP首部和UDPpay-load的長度。UDP首部和UDP層payload的校驗和為0xac40。TFTP是基于文本的協(xié)議各字段之間用字節(jié)0分隔開頭的00表示請求一個文件,接下來的各字段是timeouttsize一般的網(wǎng)絡(luò)通信都是像TFTP協(xié)議這樣通信的雙方分別是客戶端幀而服務(wù)器地等待、接收和應(yīng)答請求??蛻舳说腎P地址和端唯一標識了該主機上的TFTP客戶端進程,服務(wù)器的IP地址和端唯一標識了該主機上的TFTP服務(wù)進程,由于客戶端是主動發(fā)起請求的一方它必須知道服務(wù)器的IP地址和TFTP服務(wù)進程的端 所以,一些常見的網(wǎng)絡(luò)協(xié)議有默認的服務(wù)器端口,例如HTTP服務(wù)默認TCP協(xié)議的80端口,F(xiàn)TP服務(wù)默認TCP協(xié)議的21端口,TFTP服務(wù)默認UDP協(xié)議的69端口(如上例所示。在使用客戶端程序時,必須指定服務(wù)器的主機名或IP地址,如果不明確指定端則采用默認端口,請讀者查閱ftp、tftp等程序的manpage了解如何指定端口號。/etc/services中列出了所有well-known的服務(wù)端口和對應(yīng)的傳輸其中有些服務(wù)既可以用TCP也可以用UDP,為了清晰,IANA規(guī)定這樣的服務(wù)采用相同的TCP或UDP默認端而另外一些TCP和UDP的相同端卻對應(yīng)不同的服務(wù)。很多服務(wù)有well-known的端,然而客戶端程序的端卻不必是well-known的,往往是每次運行客戶端程序時由系統(tǒng)自動分配一個空閑的端,用完就釋放掉,稱為ephemeral的端,想想前面提過,UDP協(xié)議不面向連接也不保證傳輸?shù)目煽啃园l(fā)送端的UDP協(xié)議層把應(yīng)用層傳來的數(shù)據(jù)封裝成段交給IP協(xié)接收端的UDP協(xié)議層把收到的數(shù)據(jù)根據(jù)端交給相應(yīng)的應(yīng)通常接收端的UDP協(xié)議層將收到的數(shù)據(jù)放在一個固定大小的緩沖因此,使用UDP協(xié)議的應(yīng)用程序必須考慮到這些可能的問題并實現(xiàn)適當?shù)慕鉀Q方案,例如等待應(yīng)答、超時重發(fā)、為數(shù)據(jù)包、流量控制等。一般使用UDP協(xié)議的應(yīng)用程序?qū)崿F(xiàn)都比較簡單,只是發(fā)送一些對可靠性要求不高的消息而不發(fā)送大量的數(shù)據(jù)例如基于UDPftpTCPFTP協(xié)議適用于各種文件的傳輸。TCP協(xié)議又是如何用面向TCPTCP與UDP協(xié)議一樣也有源端和目的端,通訊的雙方由IP地址和端標識。32位序號、32位確認序號、窗口大小稍后詳細解釋。4位首部長度和IP協(xié)議頭類似,表示TCP協(xié)議頭的長度4字節(jié)為單位因此TCP協(xié)議頭最長可以是4x15=60字節(jié)如果沒有選項字段,TCP協(xié)議頭最短20字節(jié)。URG、ACK、PSH、RST、SYN、FIN是六個控制位,本節(jié)稍后將解釋SYN、ACK、FIN、RST四個位,其它位的解釋從略。16位檢驗和將TCP協(xié)議頭和數(shù)TCP協(xié)TCP下圖是一次TCP通訊的時序圖。TCP連接建立斷開。包含大家熟TCP 為1-10,各段中的主要信息在箭頭上標出,例如段2的箭頭上標著SYN,8000(0),ACK1001,,表示該段中的SYN位置1,320,ACK1,32位確認序號是1001,帶有一個mss( umSegmentSize,最大報文長度)選項值為1024。建立連接(三次握手)的過程客戶端發(fā)送一個帶SYN標志的TCP報文到服務(wù)器。這是三握手過程中的段1客戶端發(fā)出段1,SYN位表示連接請求。序號是1000,這這個序號要加1,這樣在接收端可以根據(jù)序號排出數(shù)據(jù)包的正確順序,也可以發(fā)現(xiàn)丟包的情況,另外,規(guī)定SYN位和FIN位也要占一個序號,這次雖然沒發(fā)數(shù)據(jù),但是由于發(fā)了SYN位,因此下次再發(fā)送應(yīng)該用序號1001。mss表示最大段長度,就必須在IP層分片,為了避免這種情況,客戶端服務(wù)器端回應(yīng)客戶端,是三次握手中的第2個報文段,同時帶ACK標志和SYN標志。它表示對剛才客戶端SYN的回應(yīng);同時又發(fā)送SYN給客戶端,詢問客戶端是否準備好進行數(shù)據(jù)通訊。服務(wù)器發(fā)出段2,也帶有SYN位,同時置ACK位表示確認,確認序號是1001,表示“我接收到序號1000及其以前所有的段,請你下次發(fā)送序號為1001的段,也就是應(yīng)答了 時最大尺寸為1024??蛻舯仨氃俅位貞?yīng)服務(wù)器端一個ACK報文,這是報文段3??蛻舳税l(fā)出段3,對服務(wù)器的連接請求進行應(yīng)答,確認序號是8001。在這個過程中,客戶端和服務(wù)器分別給對方發(fā)了為“握手(three-way-handshake。在建立連接的同在TCP通訊中,如果一方收到另一方發(fā)來的段,讀出其中的目的 含RST位的段給另一方。例如,服務(wù)器并沒有任何進程使用8080端 net客戶端去連接它,服務(wù)器收到客戶端發(fā)來的SYN段就會應(yīng)答一個RST段,客戶端的 net程序收到RST段后報告錯誤Connectionrefused:$net00net:Unabletoconnecttoremotehost:Connection數(shù)據(jù)傳輸?shù)倪^程客戶端發(fā)出段4,包含從序號1001開始的20個字節(jié)數(shù)據(jù)服務(wù)器發(fā)出段5,確認序號為1021,對序號為1001-1020的據(jù)表示確認收到,同時請求發(fā)送序號1021開始的數(shù)據(jù),服器在應(yīng)答的同時也向客戶端發(fā)送從序號8001開始的10個字節(jié)數(shù)據(jù),這稱為piggyback??蛻舳税l(fā)出段6,對服務(wù)器發(fā)來的序號為8001-8010的數(shù)據(jù)示確認收到,請求發(fā)送序號8011開始的數(shù)據(jù)在數(shù)據(jù)傳輸過程中,ACK和確認序號是非常重要的,應(yīng)用程序交給TCP協(xié)議發(fā)送的數(shù)據(jù)會暫存在TCP層的發(fā)送緩沖區(qū)中,發(fā)出數(shù)據(jù)包給對方之后,只有收到對方應(yīng)答的ACK段才知道該數(shù)據(jù)包確實發(fā)了數(shù)據(jù)包或者丟失了對方發(fā)回的ACK段,經(jīng)過等待超時后TCP協(xié)議關(guān)閉連接(四次握手)的過程由于TCP連接是全雙工的,因此每個方向都必須單獨進行關(guān)閉。這原則是當一方完成它的數(shù)據(jù)發(fā)送任務(wù)后就能發(fā)送一個FIN來終止這個方向的連接。收到一個FIN只意味著這一方向上沒有數(shù)據(jù)流動,一個TCP連接在收到一個FIN后仍能發(fā)送數(shù)據(jù)。首先進行關(guān)閉 客戶端發(fā)出段7,F(xiàn)IN位表示關(guān)閉連接的請求服務(wù)器發(fā)出段8,應(yīng)答客戶端的關(guān)閉連接請求服務(wù)器發(fā)出段9,其中也包含F(xiàn)IN位,向客戶端發(fā)送關(guān)閉連接客戶端發(fā)出段10,應(yīng)答服務(wù)器的關(guān)閉連接請求建立連接的過程 握手,而關(guān)閉連接通常需要4個段,服滑動窗口(TCP流量控制介紹UDP時我們描述了這樣的問題如果發(fā)送端發(fā)送的速度較快,就會丟失數(shù)據(jù)。TCP協(xié)議通過“滑動窗口(SlidingWindow”機制解,發(fā)送端發(fā)起連接最大段尺寸是1460,初始序號是0,窗口大4K始序號是8000,窗口大小是6K。發(fā)送端應(yīng)答,握手結(jié)束。,發(fā)送端發(fā)出段4-9,每個段帶1K的數(shù)據(jù),發(fā)送端根據(jù)窗口大小知接收端的應(yīng)用程序提走2K數(shù)據(jù),接收緩沖區(qū)又有了2K空閑,接10接收端的應(yīng)用程序又提走2K數(shù)據(jù),接收緩沖區(qū)有4K空閑,接收端發(fā)出段11,重新窗口大小為4K。138192,同時窗口大小為2K。接收端的應(yīng)用程序提走2K數(shù)據(jù),接收端重新窗口大小為4K重新窗口大小為6K。接收端的應(yīng)用程序在提走全部數(shù)據(jù)后,決定關(guān)閉連接,發(fā)出段包含F(xiàn)IN位,發(fā)送端應(yīng)答,連接完全關(guān)閉從這個例子還可以看出,發(fā)送端是一K一K地發(fā)送數(shù)據(jù),而接收端的應(yīng)用程序可以兩K兩K地提走數(shù)據(jù),當然也有可能一次提走3K或6K數(shù)據(jù),或者一次只提走幾個字節(jié)的數(shù)據(jù)。也就是說,應(yīng)用程序(stream些數(shù)據(jù)可能被拆成很多數(shù)據(jù)包來發(fā)送但是一個數(shù)據(jù)包有多少字節(jié)應(yīng)用程序是不可見的,因此TCP協(xié)議是面向流的協(xié)議。而UDP是面向消息的協(xié)議,每個UDP段都是一條消息,應(yīng)用程序必須以消息為TCP這個圖N多人都知道它排除和定位網(wǎng)絡(luò)或系統(tǒng)故障時大有幫助但是怎樣牢牢地將這 刻在腦中呢?那么你就一定要對這的每一個狀態(tài)及轉(zhuǎn)換的過程有深刻的認識不能只停留在一知半解之中。下面對這的1種狀態(tài)詳細解析一下,以便加強 !不過在這之前,先回顧一下P建立連接的三次握手過程,以及關(guān)閉連接的四次握手過程。CLOSED:表示初始狀態(tài)

TCPLISTEN:該狀態(tài)表示服務(wù)器端的某個SOCKET處于 SYN_SENT:這個狀態(tài)與SYN_RCVD遙相呼應(yīng),當客戶端SOCKET執(zhí)行CONNECT連接時,它首先發(fā)送SYN報文,隨即進入到了SYN_SENT狀態(tài),并等待服務(wù)端的發(fā)送三次握手中的第2個報文。SYN_SENT狀態(tài)表示客戶端已發(fā)送SYN報文SYN_RCVD:該狀態(tài)表示接收到SYN報文,在正常情況下,這個狀態(tài)是服務(wù)器端的SOCKET在建立TCP連接時的三次握手會話過程中的一個中間狀態(tài),很短暫。此種狀態(tài)時,當收到客戶端的ACK報文后,會進入到ESTABLISHED狀態(tài)。ESTABLISHED:表示連接已經(jīng)建立 FIN_WAIT_1FIN_WAIT_2狀態(tài)的真正含義都是表示等待對方的FIN報文。區(qū)別是:FIN_WAIT_1狀態(tài)是當socket在ESTABLISHED狀態(tài)時,想主動關(guān)閉連接,向?qū)Ψ桨l(fā)送了FIN報文,此時該socket進入到FIN_WAIT_1FIN_WAIT_2狀態(tài)是當對方回應(yīng)ACK后,該socket進入到FIN_WAIT_2狀態(tài),正常情況下,對方應(yīng)馬上回應(yīng)ACK報文,所以FIN_WAIT_1狀態(tài)一般較難見到,而FIN_WAIT_2狀態(tài)可用netstat看FIN_WAIT_2:主動關(guān)閉的一方,發(fā)出FINACK以后進入該狀態(tài)。稱之為半連接或半關(guān)閉狀態(tài)。該狀態(tài)下的socket只能接收數(shù)TIME_WAIT:表示收到了對方的FIN報文,并發(fā)送出了ACK報文,等2MSL后即可回到CLOSED可用狀態(tài)。如果FIN_WAIT_1狀態(tài)下,收到對方同時帶FIN標志和ACK標志的報文時,可以直接進入到CLOSING:這種狀態(tài)較特殊,屬于一種較罕見的狀態(tài)。正常情況下,當你發(fā)送FIN報文后,按理來說是應(yīng)該先收到(或同時收到)對方的ACK報文,再收到對方的FIN報文。但是CLOSING狀態(tài)表示你發(fā)送FIN報文后,并沒有收到對方的ACK報文,反而卻也收到了對方的FIN報文。什么情況下會出現(xiàn)此種情況呢?如果雙方幾乎在同時close一個SOCKET的話,那么就出現(xiàn)了雙方同時發(fā)送FIN報文的情況,也即會出現(xiàn)CLOSING狀態(tài),表示雙方都正在關(guān)閉SOCKET連接CLOSE_WAIT:此種狀態(tài)表示在等待關(guān)閉。當對方關(guān)閉一個SOCKET后發(fā)送FIN報文給自己,系統(tǒng)會回應(yīng)一個ACK報文給對方,此時則進入到CLOSE_WAIT狀態(tài)。接下來呢,察看是否還有數(shù)據(jù)發(fā)送給對方,如果沒有可以closeSOCKET,發(fā)送FIN報文給對方,即關(guān)閉連接。所以在CLOSE_WAIT狀態(tài)下,需要關(guān)閉連接。LAST_ACK:該狀態(tài)是關(guān)閉一方在發(fā)送FIN報文后,最后等待對方的ACK報文。當收到ACK報文后,即可以進入到CLOSED可用狀當 中A發(fā)送FIN請求關(guān)閉,B端回應(yīng)ACK后(A端進,B #includeintshutdown(intsockfd,inthow);sockfd:需要關(guān)閉的socket的描述符 允許為shutdown操作選擇以下幾種方式關(guān)閉sockfd上的讀功能,此選項將不允許sockfd進行讀#includeintshutdown(intsockfd,inthow);sockfd:需要關(guān)閉的socket的描述符 允許為shutdown操作選擇以下幾種方式關(guān)閉sockfd上的讀功能,此選項將不允許sockfd進行讀操作該套接字不再接受數(shù)據(jù),任何當前在套接字接受緩沖區(qū)的數(shù)據(jù)將被無聲的丟然后以SHUT_WR關(guān)閉sockfd的寫功能,此選項將不允許sockfd進行寫操作。進程不能在關(guān)閉sockfd的讀寫功能。相當于調(diào)用shutdown兩次:首先是以使用close中止接但它只是減少描述符的計數(shù)并不直接關(guān)閉連接,只有當描述符的計數(shù)為0時才關(guān)閉連接。注意如果有多個進程共個套接字,close每被調(diào)用一次,計數(shù)在多進程中如果一個進程調(diào)用了shutdown(sfd,SHUT_RDWR)2MSL( umSegmentLifetime)TIME_WAIT狀態(tài)的存在有兩個,讓4次握手關(guān)閉流程更加可靠;4次握手的最后一個ACK是是由主動關(guān)閉方發(fā)送出去的若這個ACK丟失 一個FIN過來。若主動關(guān)閉方能夠保持一個2MSL的TIME_WAIT狀態(tài),則有更大的機會讓丟失的ACK被再次發(fā)送出去。,防止lostduplicate對后續(xù)新建正常的傳輸造成破壞lostuplicate在實際的網(wǎng)絡(luò)中非常常見,經(jīng)常是由于路由器產(chǎn)生故障,路徑無法收斂,導(dǎo)致一個packet在路由器A,B,C之間做類似死循環(huán)的跳轉(zhuǎn)。IP頭部有個TTL,限制了一個包在網(wǎng)絡(luò)中的最大跳數(shù),因此這個包有兩種命運,要么最后TTL變?yōu)?,在網(wǎng)絡(luò)中;要么TTL在變?yōu)?之前路由器路徑收斂它憑借剩余的TTL跳數(shù)終于到達目的但非??上У氖荰CP通過超時重傳機制在早些時候發(fā)送了注定被TCP協(xié)議棧拋棄。另外一個概念叫做incarnationconnection,指跟上次的socketpair一摸一樣的新連接,叫做incarnationofpreviousconnection。lostconnectionTCP是流式的所有包到達的順序是不一致的依靠序列號由協(xié)議棧做順序的拼接;假設(shè)一個incarnationconnection這時收到的seq=1000,來了一個lostduplicate為seq=1000,len=1000,則TCP認為這個lostduplicate合法,并存放入了receivebuffer,導(dǎo)致傳輸出現(xiàn)錯誤。通過一個2MSLTIME_WAIT狀態(tài),確保所有的lostduplicate都該狀態(tài)為什么設(shè)計在主動關(guān)閉這一方發(fā)最后ACK的是主動關(guān)閉一方只要有一方保持TIME_WAIT狀態(tài)就能起到避免incarnationconnection在2MSL內(nèi)的重新建立,不需要兩方都有。如何正確對待2MSLRFC要求socketpair在處于TIME_WAIT時,不能再起一個incarnationconnection。但絕大部分TCP實現(xiàn),強加了更為嚴格的限制。在2MSL等待期間,socket中使用的本地端口在默認情況下不能若A1234和B0:6666建立了連接,A主關(guān)閉,那么在A端只要port1234,無論對方的portip是什么,都不允許再起服務(wù)。這甚至比RFC限制更為嚴格,RFC僅僅是要求socketpair不一致,而實現(xiàn)當中只要這個port處于TIME_WAIT,就不是臨時端口但對于打開方一般是server就悲劇了因為server一般是熟知端口比如http,一般端口是80,不可能允許這個服務(wù)在2MSL內(nèi)不能起來解決方案是給服務(wù)器的socket設(shè)置SO_REUSEADDR選項,這樣的話就算熟知端口處于TIME_WAIT狀態(tài),在這個端口上依舊可以將服務(wù)啟動。當然,雖然有了SO_REUSEADDR選項,但socktpair這個限制依舊存在。比如上面的例子,A通過SO_REUSEADDR選項依舊在1234端口上起了,但這時我們?nèi)羰菑腂通過6666端口去連它,TCP協(xié)議會告訴我們連接失敗,原因為Addressalreadyinuse.RFC793中規(guī)定MSL2分鐘,實際應(yīng)用中常用的是30秒,1分鐘和2分鐘等。有關(guān)因特網(wǎng)相關(guān)資訊,以及UNIX和因特網(wǎng)社群的軟件文件。程序設(shè)計中的問serverclient馬上再運行server,運行結(jié)果:$$binderror:Addressalreadyin這是因為,雖然server的應(yīng)用程序終止了,但TCP協(xié)議層的連接并沒有完全斷開,因此不能再次同樣的server端口。我們用netstat命令查看一下:$$netstat-apn|grep10 0 socketclientclient收到FIN后處于CLOSE_WAIT狀態(tài),但是client并沒有終止,也沒有關(guān)閉socket描述符,因此不會發(fā)FINserver,因此server的TCP連接處于FIN_WAIT2狀態(tài)?,F(xiàn)在用Ctrl-C把client也終止掉,再觀察現(xiàn)象$$netstat-apn|grep00 $binderror:Addressalreadyinclient終止時自動關(guān)閉socket描述符server的TCP連接收到client發(fā)的FIN段后處于TIME_WAIT狀態(tài)。TCP協(xié)議規(guī)定,主動關(guān)閉連接的一方要處于TIME_WAIT狀態(tài),等待兩個MSL(umsegmentlifetime)的時間后才能回到CLOSED狀態(tài),因為我們先Ctrl-C終止了server,所以server是主動關(guān)閉連接的一方TIME_WAIT期間仍然不能再次同樣的server端口。MSL在RFC1122中規(guī)定為兩分鐘,但是各操作系統(tǒng)的實現(xiàn)不同,在Linux上一般經(jīng)過半分鐘后就可以再次啟動server了。至于為什么要規(guī)定TIME_WAIT的時間,可參考UNP2.7節(jié)。端口復(fù)在server的TCP連接沒有完全斷開之前不允許重新是不合理有完全斷開,而我們重新的是lis-tenf(:6666雖然是用同一個端口,但IP地址不同,connfd對應(yīng)的是與某個客戶端通訊的一個具體的IP地址listenfd對應(yīng)的是wildcardaddress。解決這個問題的方法是使用setsockopt設(shè)置socket描述符的選項SO_REUSEADDR為1,表示允許創(chuàng)建端相同但IP地址不同的多個socket描述符。intintopt=setsockopt(listenfd,SOL_SOCKET,SO_REUSEADDR,&opt,有關(guān)setsockopt可以設(shè)置的其它選項請參考UNP7章TCP心跳檢測機在TCP網(wǎng)絡(luò)通信中,經(jīng)常會出現(xiàn)客戶端和服務(wù)器之間的斷這個是最常用的簡單方法。在接收和發(fā)送數(shù)據(jù)時個人設(shè)計一個守護進程(線程)Bet/服務(wù)器收到該小包后,立刻返回相應(yīng)的包即可檢測對方是否實時。UNIX網(wǎng)絡(luò)編程不推薦使用SO_KEEPALIVE來做心跳檢測,還是在TCP屬SO_KEEPALIVE保持連接檢測對方主機是否,避免(服務(wù)器)阻塞于TCP連接的輸入設(shè)置該選項后,如果2小時內(nèi)在此套接口的任一方向都沒有,TCP就自動給對方發(fā)一個保持存活探測分節(jié)(keepaliveprobe)。這是一個對方必須響應(yīng)的TCP分節(jié).它會導(dǎo)致以下三種情況對方接收一切正常以期望的ACK響應(yīng)2小時后,TCP將發(fā)出另一個探測分節(jié)對方已且已重新啟動以RST響應(yīng)。套接口的待處理錯誤被置為ECONNRESET,套接口本身則被關(guān)閉對方無任何響應(yīng)源自berkeley的TCP發(fā)送另外8個探測分節(jié)相隔75秒一個,試圖得到一個響應(yīng)。在發(fā)出第一個探測分節(jié)11分鐘15秒后若仍無響應(yīng)就放棄。套接口的待處理錯誤被置為ETIMEOUT,套接口本身則被關(guān)閉。如ICMP錯誤是“hostunreachable(主機不可達)為EHOSTUNREACH。的時候,我們可以設(shè)置SO_KEEPALIVE屬性使得我們在2小時以后發(fā)現(xiàn)對方的TCP連接是否依然存在。keepAlive=1;keepAlive=1; 的配置參數(shù),另外一種就是SOL_TCP字段的TCP_KEEPIDLE, T三個選項。Thetcp_keepidleparameterspecifiestheintervalofinactivitythatthatrequeststhem.tcp_keepidledefaultsto14400(twohours)./*開始首次KeepAlive探測前的TCP空閉時間Thetcp_keepintvlparameterspecifiestheintervalbetweenthenineretriesthatareattemptedifaKEEPALIVEtransmissionisnotacknowledged.tcp_keepntvldefaultsto150(75seconds)./*兩次KeepAlive探測間的時間間隔 toptionspecifiestheumnumberofkeepaliveprobestobesent.Thevalueof Tisanintegervaluebetween1andn,wherensthevalueofthe tparameter./*判定斷開前的KeepAlive探測次數(shù)intkeepIdle=1000;intintkeepIdle=1000;intkeepInterval=10;intkeepCount=SO_KEEPALIVE設(shè)置空閑2小時才發(fā)送一個“保持存活探測分套接口!關(guān)聯(lián)了完成端口的socket可能會忽略掉該套接字選項。網(wǎng)絡(luò)名詞術(shù)語解路由路由(名詞路由(動詞某個路由節(jié)點為數(shù)據(jù)包選擇投遞方向的選路過程傳統(tǒng)地,路由器工作于OSI七層協(xié)議中的第三層,其主要任務(wù)目的地址若找到了目的地址就在數(shù)據(jù)包的幀格前添加下一個MAC地址,同時IP數(shù)據(jù)包頭的TTL(TimeToLive)域也開始減數(shù),并重便被傳送到輸出鏈。功能之一,該功能可以幫助具有很多節(jié)點站的網(wǎng)絡(luò)來尋址信息,路由和交換之間的主要區(qū)別就是交換發(fā)生在OSI參考模型第二,而路由和交換在移動信息的過程中需使用不同的控制信息,所以兩者路由表(Routing 總線型傳輸方式的局域網(wǎng)以太網(wǎng)交換機的結(jié)構(gòu)是每個端口都直多對端口,使每一對相互通信的主機都能像獨占通信那樣,進行無地傳輸數(shù)據(jù)。以太網(wǎng)交換機工作于I(即數(shù)據(jù)鏈路層,是一種基于M(MediaAccessto,介質(zhì)控制)地址識別、完成以太網(wǎng)數(shù)據(jù)幀轉(zhuǎn)發(fā)的網(wǎng)絡(luò)設(shè)備。hub的端口服務(wù),所以集線器又叫多口中繼器。與集線器的上聯(lián)設(shè)(交換機路由器或服務(wù)器等進行通信Hub準的共享式設(shè)備。其次是Hub只與它的上聯(lián)設(shè)備(如上層Hub、交換過上聯(lián)設(shè)備再將信息廣播到所有端口上。由此可見,即使是在同一Hub的不同兩個端口之間進行通信,都必須要經(jīng)過兩步操作:第一步是將信息上傳到上聯(lián)設(shè)備第二步是上聯(lián)設(shè)備再將該信息廣播到所有端口上duple(Half-duplex(半雙工)在通道中同時只能沿著一個方向傳輸數(shù)DNSDNS是系統(tǒng)( NameSystem)的縮寫,是因特網(wǎng)的一項服務(wù),它作為可以將和IP地址相互的一個分布式數(shù)據(jù)庫,能夠使人更方便的互聯(lián)網(wǎng),而不用去記住能夠被機器直接的IP地址串。它是由解析器以及服務(wù)器組成的。服務(wù)器是指保存有該網(wǎng)絡(luò)中所有主機的和對應(yīng)IP地址,并具有將轉(zhuǎn)換為IP地址局域網(wǎng)localareanetwork,一種覆蓋一座或幾座大樓、一個校園或者一使用專門鋪設(shè)的傳輸介質(zhì)進行聯(lián)網(wǎng),數(shù)據(jù)傳輸速率高通信延遲時間短,可靠性較局域網(wǎng)可以支持多種傳輸介廣域網(wǎng)wideareanetwork,一種用來實現(xiàn)不同地區(qū)的局域網(wǎng)或城域網(wǎng)的互連可提供不同地區(qū)城市和國家之間的計算機通信 計算機網(wǎng) 適應(yīng)大容量與突發(fā)性通信的要求適應(yīng)綜合業(yè)務(wù)服務(wù)的要求開放的設(shè)備接口與規(guī)范化的協(xié)議完善的通信服務(wù)與網(wǎng)絡(luò)管理邏輯意義上的端口,一般是指TCP/IP協(xié)議中的端口, 的圍從0到65535,比如用于瀏覽網(wǎng)頁服務(wù)的80端口,用于FTP服務(wù)的21端口等等。 小于256的定義為常用端口服務(wù)器一般都是通過常用 大多數(shù)TCP/IP實現(xiàn)給臨時端 分配1024—5000之間的端口號。大于5000的端 我們應(yīng)該在自定義端口避免使用well-known的端8021MTU:通信術(shù)語最大傳輸單( UnitMTU)是指一種通信協(xié)議的某一層上面所能通過的最大數(shù)據(jù)包大?。?以下是一些協(xié)議的FDDI協(xié)議:4352字以太網(wǎng)(Ethernet)協(xié)議:1500字PPPoE(ADSL)協(xié)議:1492字Up/Modem:576Point-to-Point:4470字常見網(wǎng)絡(luò)知識面試TCP如何建TCP如何通TCP如何關(guān)什么是滑動窗什么是半關(guān)局域網(wǎng)內(nèi)兩臺機器如何利用TCP/IP通internet上兩臺主機如何進行通如何在internet上識別唯一一個進答:通過“IP地址+ ”來區(qū)分不同的服為什么說TCP是可靠 ,UDP不可路由器和交換機的區(qū)點到點,端到Socket編套接字概既然是文件,那么理所當然的,我們可以使用文件描述符套接字。與管道類似的,Linux系統(tǒng)將其封裝成文件的目的是為了統(tǒng)一套接字的內(nèi)核實現(xiàn)較為復(fù)雜,不宜在學(xué)習(xí)初期深入學(xué)習(xí)在TCP/IP協(xié)議中IP地址+TCPUDP端”唯一標識網(wǎng)絡(luò)通訊中的一個進程IP地址+端”就對應(yīng)一個socket。欲建立連接的兩個進程各自有一個socket來標識,那么這兩個socket組成的socketpair就唯一標識接。因此可以用Socket來描述網(wǎng)絡(luò)連接套接字通信原理如下圖所TCP/IP協(xié)議最早在BSDUNIX上實現(xiàn),為TCP/IP協(xié)議設(shè)計的應(yīng)用層API協(xié)議的函數(shù)接口,最后介紹UDP協(xié)議和UNIX Socket的函數(shù)預(yù)備知我們已經(jīng)知道,內(nèi)存中的多字節(jié)數(shù)據(jù)相對于內(nèi)存地址有大端和小流的地址呢?發(fā)送主機通常將發(fā)送緩沖區(qū)中的數(shù)據(jù)按內(nèi)存地址從低TCP/IP協(xié)議規(guī)定,網(wǎng)絡(luò)數(shù)據(jù)流應(yīng)采用大端字節(jié)序,即低地址高字節(jié)。例如上一節(jié)的UDP段格式,地址0-116位的源端這個端是1000(0x3e8,則地址0是0x03,地址1是0xe8,也就是先發(fā)0x03,再發(fā)0xe816位在發(fā)送主機的緩沖區(qū)中也應(yīng)該是低地址存0x03,高地址存0xe8。但是,如果發(fā)送主機是小端字節(jié)序小端字節(jié)序的,接到16位的源端也要做字節(jié)序的轉(zhuǎn)換。如果主機是大端字節(jié)序的,發(fā)送和接收都不需要做轉(zhuǎn)換。同理,32位的IP為使網(wǎng)絡(luò)程序具有可移植性,使同樣的C網(wǎng)絡(luò)字節(jié)序和主機字節(jié)序的轉(zhuǎn)換。#include#includeuint32_thtonl(uint32_thostlong);uint16_thtons(uint16_thostshort); ohl(uint32_tnetlong); ohs(uint16_tnetshort);hhost,nnetwork,l表示32位長整數(shù),s16位短IP早期#include<netinet/in.h>#include<arpa/inet.h>intinet_aton(constchar*cp,structin_addr*inp);in_addr_tinet_addr(constchar*cp);只能處理IPv4的ip地址注意參數(shù)是struct現(xiàn)在#include#includeintinet_pton(intaf,constchar*src,voidconstchar*inet_ntop(intaf,constvoid*src,char*dst,socklen_t支持IPv4和其中inet_ptoninet_ntop不僅可以轉(zhuǎn)換IPv4in_addr以轉(zhuǎn)換IPv6的in6_addr。因此函數(shù)接口是void*addrptrsockaddrstrcutsockaddr很多網(wǎng)絡(luò)編程函數(shù)誕生早于IPv4協(xié)議,那時候都使用的是sockaddr結(jié)構(gòu)體,為了向前兼容,現(xiàn)在sockaddr成還是sockaddr_in6,由地址族確定,然后函數(shù)再強制類型轉(zhuǎn)化為sockaddrstructsockaddr{sa_family_tstructsockaddr{sa_family_tsa_family;charsa_data[14];/*addressfamily,AF_xxx/*14bytesofprotocoladdress使用sudogrep-r"structsockaddr_in{" /usr命令可查看到struct 結(jié)構(gòu)體的定義。一般其默認的位置structstructsockaddr_inkernel_sa_family_tbe16structin_addr/*Padtosizeof`structsockaddr'./*Addressfamily /*Portnumber/*Internetaddress端IP地unsignedunsignedcharpad[SOCK_SIZE-sizeof(shortint)sizeof(unsignedshortint)-sizeof(structstructin_addrbe32/*Internetaddress.structsockaddr_in6unsignedshortintbe16be32sin6_flowinfo;structin6_addrsin6_addr;u32/*AF_INET6/*Transportlayerport#/*IPv6flowinformation/*IPv6address/*scopeid(newinRFC2553)structstructin6_addr{union{u8be16be32}#defines6_addr #defines6_addr16in6_u.u6_addr16#defines6_addr32 #defineUNIX_PATH_MAX108structsockaddr_un{kernel_sa_family_tsun_family; /*AF_UNIX*/charsun_path[UNIX_PATH_MAX];/*pathname*/Pv4和IPv6的地址格式定義在netinet/in.h中,IPv4地址用sockaddr_in結(jié)構(gòu)體表示,包括16位端和32位IP地址,IPv6地址用sockaddr_in6結(jié)構(gòu)體表示,包括16位端、128位IP地址和一些控制字段。UNIXSocket的地址格式定義在sys/un.h中,sock-addr_un結(jié)構(gòu)體表示各種socket地址結(jié)構(gòu)體的開頭都是相同的16位表示整個結(jié)構(gòu)體的長(并不是所有UNIX的實現(xiàn)都有長度字段Linux就沒有16位表示地址類型IPv4IPv6和UnixSocket的地址類型分別定義為常數(shù)AF_INET、AF_INET6、AF_UNIX。這樣,只要取得某種sockaddr結(jié)構(gòu)體的首地址,不需要知體是哪種類型的sockaddr結(jié)構(gòu)體,就可以根據(jù)地址類型字段確定結(jié)構(gòu)體中的內(nèi)容。因此,socketAPI可以接受各種類型的sockaddr參數(shù)應(yīng)該設(shè)計成void*類型以便接受各種類型的指針,但是sockAPI的實現(xiàn)早于ANSIC標準化,那時還沒有void*類型,因此這些函數(shù)的參數(shù)都用structsockaddr*類型表示,在傳遞參數(shù)之前要強制類型轉(zhuǎn)structstructsockaddr_inbind(listen_fd,(structsockaddr*)&servaddr,sizeof(servaddr));initializeservaddr*/網(wǎng)絡(luò)套接字函socketsocket#include#include<sys/types.h>/*SeeNOTES*/#include<sys/socket.h>int ,inttype,int:AF_INET這是大多數(shù)用來產(chǎn)生socket的協(xié)議,使用TCP或UDP來傳輸,用IPv4的地AF_INET6與上面類似,不過是來用IPv6的地的socket類型,這個socket是使用TCP來進行傳輸。SOCK_DGRAM這個協(xié)議是無連接的、固定長度的傳輸調(diào)用。該協(xié)議是不可靠的,使用UDP來進 SOCK_RAWsockettraceroute使用該協(xié)議,這個socket類型使用ICMP、成功:返回指向新創(chuàng)建的socket的文件描述符,失?。悍祷?1,設(shè)置,socket()打開一個網(wǎng)絡(luò)通訊端口,如果成功的話,就像open()一樣網(wǎng)絡(luò)上收發(fā)數(shù)據(jù)如果socket()調(diào)用出錯則返回-1對于IPv4參數(shù)指定為AF_INETTCP協(xié)議type參數(shù)指定為SOCK_STREAM,表示面向流的傳輸協(xié)議。如果是UDP協(xié)議,則type參數(shù)指定為SOCK_DGRAM,表示面向數(shù)據(jù)報的傳輸協(xié)議。protocol參數(shù)的介紹從略,指定為0即可。,bind#include#include<sys/types.h>/*SeeNOTES*/#include<sys/socket.h>intbind(intsockfd,conststructsockaddr*addr,socklen_taddrlen);socket文件描述構(gòu)造出IP地址加0,失敗返回-1,設(shè)置因此服務(wù)器需要調(diào)用bind綁定一個固定的網(wǎng)絡(luò)地址和端bind()的作用是將參數(shù)sockfdaddr綁定在一起,使sockfd這個用于網(wǎng)絡(luò)通訊的文件描述符addr所描述的地址和端。前面受多種協(xié)議的sockaddr結(jié)構(gòu)體而它們的長度各不相同所以需要第三個參數(shù)addrlen指定結(jié)構(gòu)體的長度。如:structsockaddr_inservaddr;bzero(&servaddr,structsockaddr_inservaddr;bzero(&servaddr,sizeof(servaddr));servaddr.sin_family=AF_INET;servaddr.sin_addr.s_addr=servaddr.sin_port=多個網(wǎng)卡,每個網(wǎng)卡也可能綁定多個IP地址,這樣設(shè)置可以在所有的IP地址上,直到與某個客戶端建立了連接時才確定下來到底用哪個IP地址,端為6666。listen#include#include<sys/types.h>/*SeeNOTES*/#include<sys/socket.h>intlisten(intsockfd,intbacklog);socket文件描述排隊建立3次握手隊列和剛剛建立3次握手隊列 數(shù)查看系統(tǒng)默認catcat連接時,服務(wù)器調(diào)用的accept()返回并接受這個連接,如果有大量的客戶端發(fā)起連接而服務(wù)器來不及處理,尚未accept的客戶端就處于連接等待狀態(tài),listen()sockfd處于狀態(tài),并且最多允許有backlog個客戶端處于連接待狀態(tài),如果接收到的連接請求就忽略。listen()成功返回0,失敗返回-1。accept#include#include#include/*SeeNOTESintaccept(intsockfd,structsockaddr*addr,socklen_t*addrlen);socket文件描述傳出參數(shù),返 客戶端地址信息,含IP地址和傳入傳出參數(shù)(值-結(jié)果),傳入sizeof(addr)大小,函數(shù)返回時返回真正接收到地址結(jié)構(gòu)體的小成功返回一個新的socket文件描述符,用于和客戶端通信,失敗返回-1,設(shè)置握手完成后服務(wù)器調(diào)用accept()接受連接如果服務(wù)器調(diào)用上來。addr是一個傳出參數(shù),accept()返回時傳出客戶端的地址和端argument,入的是調(diào)用者提供的緩沖區(qū)addr的長度以避免緩沖區(qū)溢出問題,傳緩沖區(qū)。如果給addr參數(shù)傳NULL,表示不關(guān)心客戶端的地址。我們的服務(wù)器程序結(jié)構(gòu)是這樣的whilewhile(1)cliaddr_len=connfd=accept(listenfd,(structsockaddr*)&cliaddr,&cliaddr_len);n=read(connfd,buf,MAXLINE);}整個是一個while死循環(huán),每次循環(huán)處理一個客戶端連接。由于accept()的參數(shù)listenfd是先前的文件描述符,而accept()的返回值是另外一個文件描述符connfd,之后與客戶端之間就通過這個connfd通訊,最后關(guān)閉connfd斷開連接,而不關(guān)閉listenfd,再次回到循環(huán)開頭listenfd仍然用作accept的參數(shù)accept()成功返回一個文connect#include#include#include/*SeeNOTESintconnect(intsockfd,conststructsockaddr*addr,socklen_taddrlen);socket文件描述傳入?yún)?shù),指定服務(wù)器端地址信息,含IP地址和傳入?yún)?shù),傳入sizeof(addr)大小成功返回0,失敗返回-1,設(shè)置客戶端需要調(diào)用connect()連接服務(wù)器,connect和bind的參數(shù)形式一致區(qū)別在于bind的參數(shù)是自己的地址connect的參數(shù)是對方的地址。connect()成功返回0,出錯返回-1。C/S模型-下圖是基于TCP協(xié)議的客戶端/服務(wù)器程序的一般流程TCP服務(wù)器調(diào)用socket()、bind()、listen()完成初始化后,調(diào)用accept()阻塞等待,處于端口的狀態(tài),客戶端調(diào)用socket()初始化后,調(diào)用connect()發(fā)出SYN段并阻塞等待服務(wù)器應(yīng)答務(wù)器應(yīng)答一個SYN-器收到后從accept()返回。數(shù)據(jù)傳輸?shù)倪^程建立連接后,TCP協(xié)議提供全雙工的通信服務(wù)但是一般的客 一問一答的方式因此服務(wù)器從accept()返回后立刻調(diào)用read(),socket就像讀管道一樣,如果沒有數(shù)據(jù)到達就阻塞等待,這時客戶端調(diào)用write()發(fā)送請求給服務(wù)器,服務(wù)器收到后從read()返回,對客戶端的請求進行處理,在此期間客戶端調(diào)用read()阻塞等待服務(wù)器的應(yīng)答,服務(wù)器調(diào)用write()將處理結(jié)果發(fā)回給客戶端,再次調(diào)用read()阻塞等待下一條請求,客戶端收到后從read()返回,發(fā)送下一條請求,如果客戶端沒有的請求了,就調(diào)用close()關(guān)閉連接,就像寫端關(guān)閉的管道一樣,服務(wù)器的read()返回0,這樣服務(wù)器就知道客戶在學(xué)習(xí)socketAPI時要注意應(yīng)用程序和TCP協(xié)議層是如何交互的:應(yīng)用程序調(diào)用某個socket函數(shù)時TCP協(xié)議層完成什么動作,比如調(diào)用connect()會發(fā)出SYN段應(yīng)用程序如何知道TCP協(xié)議層的狀態(tài)變化,比如從某個阻塞的socket函數(shù)返回就表明TCP協(xié)議收到了某些段,再比如read()返回0就表明收到了FIN段server.c的作用是從客戶端讀字符,然后將每個字符轉(zhuǎn)換為大寫#include<stdio.h>#include<stdlib.h>#include<string.h>#include<unistd.h>#include#include<stdio.h>#include<stdlib.h>#include<string.h>#include<unistd.h>#include<sys/socket.h>#include<netinet/in.h>#include<arpa/inet.h>#defineMAXLINE#defineSERV_PORTint{structsockaddr_inservaddr,cliaddr;socklen_tcliaddr_len;intlistenfd,connfd;charbuf[MAXLINE];charstr[INET_ADDRSTRLEN];inti,n;listenfd=socket(AF_INET,SOCK_STREAM,bzero(&servaddr,sizeof(servaddr));servaddr.sin_family=AF_INET;servaddr.sin_addr.s_addr=htonl(INADDR_ANY);servaddr.sin_port=htons(SERV_PORT);bind(listenfd,(structsockaddr*)&servaddr,sizeof(servaddr));listen(listenfd,20);printf("Acceptingconnections...\n");while(1){cliaddr_len=connfd=accept(listenfd,(structsockaddr*)&cliaddr,&cliaddr_len);n=read(connfd,buf,MAXLINE);printf("receivedfrom%satPORT%d\n",inet_ntop(AF_INET,&cliaddr.sin_addr,str,sizeof(str)),for(i=0;i<n;buf[i]=write(connfd,write(connfd,buf,n);}return}client.c的作用是從命令行參數(shù)中獲得一個字符串發(fā)給服務(wù)器,然#include<stdio.h>#include<stdlib.h>#include<string.h>#include<unistd.h>#include#include<stdio.h>#include<stdlib.h>#include<string.h>#include<unistd.h>#include<sys/socket.h>#include#defineMAXLINE#defineSERV_PORTintmain(intargc,char{structsockaddr_inservaddr;charbuf[MAXLINE];intsockfd,n;char*str;if(argc!=2)fputs("usage:./clientmessage\n",stderr);}str=sockfd=socket(AF_INET,SOCK_STREAM,bzero(&servaddr,sizeof(servaddr));servaddr.sin_family=AF_INET;inet_pton(AF_INET,"",&servaddr.sin_addr);servaddr.sin_port=htons(SERV_PORT);connect(sockfd,(structsockaddr*)&servaddr,write(sockfd,write(sockfd,str,n=read(sockfd,buf,MAXLINE);printf("Responsefromserver:\n");write(STDOUT_FILENO,buf,n);return}由于客戶端不需要固定的端因此不必調(diào)用bind(),客戶端的端由內(nèi)核自動分配。注意,客戶端不是不允許調(diào)用bind(),只是沒有必要調(diào)用bind()固定一個端服務(wù)器也不是必須調(diào)用bind(),但如果服務(wù)器不調(diào)用bind(),內(nèi)核會自動給服務(wù)器分配端口,每客戶端和服務(wù)器啟動后可以使用netstat命令查看情況出錯處理封裝函為使錯誤處理的代碼不影響主程序的可讀性,我們把與socket相#include<stdlib.h>#include<errno.h>#include#include<stdlib.h>#include<errno.h>#include<sys/socket.h>voidperr_exit(constchar{}intAccept(intfd,structsockaddr*sa,socklen_t{intn;if((n=accept(fd,sa,salenptr))<0)if((errno==ECONNABORTED)||(errno==EINTR))gotoagain;perr_exit("accept}return}intBind(intfd,conststructsockaddr*sa,socklen_t{intif((n=bind(fd,sa,salen))<0)perr_exit("binderror");return}intConnect(intfd,conststructsockaddr*sa,socklen_t{intif((n=connect(fd,sa,salen))<0)perr_exit("connecterror");return}intListen(intfd,int{intif((n=listen(fd,backlog))<0)perr_exit("listenerror");return}intSocket(intfamily,inttype,int{intif((n=socket(family,type,protocol))<0)perr_exit("socketerror");return}ssize_tRead(intfd,void*ptr,size_t{ssize_tn;if((n=read(fd,ptr,nbytes))==-1){if(errno==EINTR)gotoagain;return-}return}ssize_tWrite(intfd,constvoid*ptr,size_t{ssize_tn;if((n=write(fd,ptr,nbytes))==-1){if(errno==EINTR)gotoagain;return-}return}intClose(int{intif((n=close(fd))==-1)perr_exit("closeerror");return}ssize_tReadn(intfd,void*vptr,size_t{size_tnleft;ssize_tnread;char*ptr;ptr=nleft=while(nleft>0)if((nread=read(fd,ptr,nleft))<0){if(errno==EINTR)nread=0;return-}elseif(nread==0)nleft-=nread;ptr+=nread;}returnn-}ssize_tWriten(intfd,constvoid*vptr,size_t{size_tnleft;ssize_tnwritten;constchar*ptr;ptr=vptr;nleft=n;while(nleft>0)if((nwritten=write(fd,ptr,nleft))<=0){if(nwritten<0&&errno==EINTR)nwritten=0;return-}nleft-=nwritten;ptr+=nwritten;}return}staticssize_tmy_read(intfd,char{staticint staticchar*read_ptr;staticcharread_buf[100];ifif(ift<=0)t=read(fd,read_buf,sizeof(read_buf)))<0)if(errno==EINTR)gotoagain;return-}elseif( t==0)return0;read_ptr=}t--*ptr=*read_ptr++;return1;}ssize_tReadline(intfd,void*vptr,size_t{ssize_tn,rc;charc,*ptr;ptr=vptr;for(n=1;n<maxlen;n++)if((rc=my_read(fd,&c))==1)*ptr++=if(c=='\n')}elseif(rc==0)*ptr=0;returnn-1;}return-}*ptr=0;return}#ifndef#ifndefWRAP_H_#definevoidperr_exit(constcharintAccept(intfd,structsockaddr*sa,socklen_t*salenptr);intBind(intfd,conststructsockaddr*sa,socklen_tsalen);intConnect(intfd,conststructsockaddr*sa,socklen_tsalen);intListen(intfd,intbacklog);intSocket(intfamily,inttype,intprotocol);ssize_tRead(intfd,void*ptr,size_tnbytes);ssize_tintSocket(intfamily,inttype,intprotocol);ssize_tRead(intfd,void*ptr,size_tnbytes);ssize_tWrite(intfd,constvoid*ptr,size_tnbytes);intClose(intfd);ssize_tReadn(intfd,void*vptr,size_tn);ssize_tWriten(intfd,constvoid*vptr,size_tn);ssize_tmy_read(intfd,char*ptr);ssize_tReadline(intfd,void*vptr,size_t高并發(fā)服務(wù)多進程并發(fā)服務(wù)使用多進程并發(fā)服務(wù)器時要考慮以下幾點父進程最大文件描述個數(shù)(父進程中需要close關(guān)閉返回的新文件描述符系統(tǒng)內(nèi)創(chuàng)建進程個數(shù)(與內(nèi)存大小相關(guān)進程創(chuàng)建過多是否降低整體服務(wù)性能(進程調(diào)度/*server.c*/#include<stdio.h>#include/*server.c*/#include<stdio.h>#include<string.h>#include<netinet/in.h>#include<arpa/inet.h>#include<signal.h>#include<sys/wait.h>#include<sys/types.h>#include"wrap.h"#defineMAXLINE#defineSERV_PORTvoiddo_sigchild(int{while(waitpid(0,NULL,WNOHANG)>;}int{structsockaddr_inservaddr,cliaddr;socklen_tcliaddr_len;intlistenfd,connfd;charbuf[MAXLINE];charstr[INET_ADDRSTRLEN];inti,n;pid_tstructsigactionnewact;newact.sa_handler=do_sigchild;newact.sa_flags=0;sigaction(SIGCHLD,&newact,NULL);listenfd=Socket(AF_INET,SOCK_STREAM,bzero(&servaddr,sizeof(servaddr));servaddr.sin_family=AF_INET;servaddr.sin_addr.s_addr=htonl(INADDR_ANY);servaddr.sin_portservaddr.sin_port=Bind(listenfd,(structsockaddr*)&serv

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責。
  • 6. 下載文件中如有侵權(quán)或不適當內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論