C#編程socket編程原理_第1頁(yè)
C#編程socket編程原理_第2頁(yè)
C#編程socket編程原理_第3頁(yè)
C#編程socket編程原理_第4頁(yè)
已閱讀5頁(yè),還剩63頁(yè)未讀 繼續(xù)免費(fèi)閱讀

下載本文檔

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

文檔簡(jiǎn)介

socket編程原理socket編程原理!問題的引入U(xiǎn)NIX系統(tǒng)的I/O命令集,是從Maltics和早期系統(tǒng)中的命令演變出來的,其模式為打開ー讀/寫ー關(guān)閉(open-write-read-elose)。在ー個(gè)用戶進(jìn)程進(jìn)行I/O操作時(shí),它首先調(diào)用“打開”獲得對(duì)指定文件或設(shè)備的使用權(quán),并返回稱為文件描述符的整型數(shù),以描述用戶在打開的文件或設(shè)備上進(jìn)行I/O操作的進(jìn)程。然后這個(gè)用戶進(jìn)程多次調(diào)用“讀/寫’‘以傳輸數(shù)據(jù)。當(dāng)所有的傳輸操作完成后,用戶進(jìn)程關(guān)閉調(diào)用,通知操作系統(tǒng)已經(jīng)完成了對(duì)某對(duì)象的使用。TCP/IP協(xié)議被集成到UNIX內(nèi)核中時(shí),相當(dāng)于在UNIX系統(tǒng)引入了一種新型的I/O操作。UNIX用戶進(jìn)程與網(wǎng)絡(luò)協(xié)議的交互作用比用戶進(jìn)程與傳統(tǒng)的I/O設(shè)備相互作用復(fù)雜得多。首先,進(jìn)行網(wǎng)絡(luò)操作的兩個(gè)進(jìn)程鑰紀(jì)紀(jì)同機(jī)器上,如何建立它們之間的聯(lián)系?其次,網(wǎng)絡(luò)協(xié)議存在多種,如何建立一種通用機(jī)制以支持多種協(xié)議?這些都是網(wǎng)絡(luò)應(yīng)用編程界面所要解決的問題。在UNIX系統(tǒng)中,網(wǎng)絡(luò)應(yīng)用編程界面有兩類:UNIXBSD的套接字(soeket)和UNIXSystemV的TLI。由于Sun公司采用了支持TCP/IP的UNIXBSD操作系統(tǒng),使TCP/IP的應(yīng)用有更大的發(fā)展,其網(wǎng)絡(luò)應(yīng)用編程界面一套接字(socket)在網(wǎng)絡(luò)軟件中被廣泛應(yīng)用,至今已引進(jìn)微機(jī)操作系統(tǒng)DOS和Windows系統(tǒng)中,成為開發(fā)網(wǎng)絡(luò)應(yīng)用軟件的強(qiáng)有力工具,本章將要詳細(xì)討論這個(gè)問題。2套接字編程基本概念鑰紀(jì)紀(jì)始使用套接字編程之前,首先必須建立以下概念。2.1網(wǎng)間進(jìn)程通信進(jìn)程通信的概念最初來源于單機(jī)系統(tǒng)。由于每個(gè)進(jìn)程都在自己的地址范圍內(nèi)運(yùn)行,為保證兩個(gè)相互通信的進(jìn)程之間既互不干擾又協(xié)調(diào)一致工作,操作系統(tǒng)為進(jìn)程通信提供了相應(yīng)設(shè)施,如UNIXBSD中的管道(pipe)、命名管道(namedpipe)和軟中斷信號(hào)(signal),UNIXsystemV的消息(message)、共享存儲(chǔ)區(qū)(sharedmemory)和信號(hào)量(semaphore)等,但都僅限于用在本機(jī)進(jìn)程之間通信。網(wǎng)間進(jìn)程通信要解決的是不同主機(jī)進(jìn)程間的相互通信問題(可把同機(jī)進(jìn)程通信看作是其中的特例)。為此,首先要解決的是網(wǎng)間進(jìn)程標(biāo)識(shí)問題。同一主機(jī)上,不同進(jìn)程可用進(jìn)程號(hào)(processID)唯一標(biāo)識(shí)。但在網(wǎng)絡(luò)環(huán)境下,各主機(jī)獨(dú)立分配的進(jìn)程號(hào)不能唯一標(biāo)識(shí)該進(jìn)程。例如,主機(jī)A賦于某進(jìn)程號(hào)5,在B機(jī)中也可以存在5號(hào)進(jìn)程,因此,“5號(hào)進(jìn)程”這句話就沒有意義了。其次,操作系統(tǒng)支持的網(wǎng)絡(luò)協(xié)議眾多,不同協(xié)議的工作方式不同,地址格式也不同。因此,網(wǎng)間進(jìn)程通信還要解決多重協(xié)議的識(shí)別問題。為了解決上述問題,TCP/IP協(xié)議引入了下列幾個(gè)概念。端口網(wǎng)絡(luò)中可以被命名和尋址的通信端口,是操作系統(tǒng)可分配的ー種資源。按照OSI七層協(xié)議的描述,傳輸層與網(wǎng)絡(luò)層在功能上的最大區(qū)別是傳輸層提供進(jìn)程通信能力。從這個(gè)意義上講,網(wǎng)絡(luò)通信的最終地址就不僅僅是主機(jī)地址了,還包括可以描述進(jìn)程的某種標(biāo)識(shí)符。為此,TCP/IP協(xié)議提出了協(xié)議端口(protocolport?簡(jiǎn)稱端ロ)的概念,用于標(biāo)識(shí)通信的進(jìn)程。端口是ー種抽象的軟件結(jié)構(gòu)(包括一些數(shù)據(jù)結(jié)構(gòu)和I/O緩沖區(qū))。應(yīng)用程序(即進(jìn)程)通過系統(tǒng)調(diào)用與某端口建立連接(binding)后,傳輸層傳給該端口的數(shù)據(jù)都被相應(yīng)進(jìn)程所接收,相應(yīng)進(jìn)程發(fā)給傳輸層的數(shù)據(jù)都通過該端口輸出。在TCP/IP協(xié)議的實(shí)現(xiàn)中,端靠紀(jì)紀(jì)作類似于一般的I/O操作,進(jìn)程獲取ー個(gè)端口,相當(dāng)于獲取本地唯一的I/O文件,可以用一般的讀寫原語(yǔ)訪問之。類似于文件描述符,每個(gè)端口都擁有一個(gè)叫端口號(hào)(portnumber)的整數(shù)型標(biāo)識(shí)符,用于區(qū)別不同端口。由于TCP/IP傳輸層的兩個(gè)協(xié)議TCP和UDP是完全獨(dú)立的兩個(gè)軟件模塊,因此各自的端ロ號(hào)也相互獨(dú)立,如TCP有一個(gè)255號(hào)端口,UDP也可以有一個(gè)255號(hào)端口,二者并不沖突。端口號(hào)的分配是一個(gè)重要問題。有兩種基本分配方式:第一種叫全局分配,這是ー種集中控制方式,由一個(gè)公認(rèn)的中央機(jī)構(gòu)根據(jù)用戶需要進(jìn)行統(tǒng)ー分配,并將結(jié)果公布于眾。第二種是本地分配,又稱動(dòng)態(tài)連接,即進(jìn)程需要訪問傳輸層服務(wù)時(shí),向本地操作系統(tǒng)提出申請(qǐng),操作系統(tǒng)返回ー個(gè)本地唯一的端口號(hào),進(jìn)程再通過合適的系統(tǒng)調(diào)用將自己與該端口號(hào)聯(lián)系起來(綁扎)。TCP/IP端口號(hào)的分配中綜合了上述兩種方式。TCP/IP將端口號(hào)分為兩部分,少量的作為保留端口,以全局方式分配給服務(wù)進(jìn)程。因此,每ー個(gè)標(biāo)準(zhǔn)服務(wù)器都擁有一個(gè)全局公認(rèn)的端口(即周知口,well-knownport),即使鑰紀(jì)紀(jì)同機(jī)器上,其端口號(hào)也相同。剩余的為自由端口,以本地方式進(jìn)行分配。TCP和UDP均規(guī)定,小于256的端口號(hào)才能作保留端口。網(wǎng)絡(luò)通信中通信的兩個(gè)進(jìn)程分別鑰紀(jì)紀(jì)同的機(jī)器上。在互連網(wǎng)絡(luò)中,兩臺(tái)機(jī)器可能位涌紀(jì)紀(jì)同的網(wǎng)絡(luò),這些網(wǎng)絡(luò)通過網(wǎng)絡(luò)互連設(shè)備(網(wǎng)關(guān),網(wǎng)橋,路由器等)連接。因此需要三級(jí)尋址:.某ー主機(jī)可與多個(gè)網(wǎng)絡(luò)相連,必須指定一特定網(wǎng)絡(luò)地址;.網(wǎng)絡(luò)上每一臺(tái)主機(jī)應(yīng)有其唯一的地址;.每一主機(jī)上的每ー進(jìn)程應(yīng)有在該主機(jī)上的唯一標(biāo)識(shí)符。通常主機(jī)地址由網(wǎng)絡(luò)ID和主機(jī)ID組成,在TCP/IP協(xié)議中用32位整數(shù)值表示;TCP和UDP均使用16位端口號(hào)標(biāo)識(shí)用戶進(jìn)程。網(wǎng)絡(luò)字節(jié)順序不同的計(jì)算機(jī)存放多字節(jié)值的順序不同,有的機(jī)器在起始地址存放低位字節(jié)(低價(jià)先存),有的存高位字節(jié)(高價(jià)先存)。為保證數(shù)據(jù)的正確性,在網(wǎng)絡(luò)協(xié)議中須指定網(wǎng)絡(luò)字節(jié)順序。TCP/IP協(xié)議使用!6位整數(shù)和32位整數(shù)的高價(jià)先存格式,它們均含在協(xié)議頭文件中。連接兩個(gè)進(jìn)程間的通信鏈路稱為連接。連接在目紀(jì)紀(jì)表現(xiàn)為ー些緩沖區(qū)和一組協(xié)議機(jī)制,在外部表現(xiàn)出比無連接高的可靠性。半相關(guān)綜上所述,網(wǎng)絡(luò)中用ー個(gè)三元組可以在全局唯一標(biāo)志一個(gè)進(jìn)程:(協(xié)議,本地地址,本地端口號(hào))這樣ー個(gè)三元組,叫做ー個(gè)半相關(guān)(half-association),它指定連接的每半部分。全相關(guān)ー個(gè)完整的網(wǎng)間進(jìn)程通信需要由兩個(gè)進(jìn)程組成,并且只能使用同ー種高層協(xié)議。也就是說,不可能通信的一端用TCP協(xié)議,而另一端用UDP協(xié)議。因此ー個(gè)完整的網(wǎng)間通信需要一個(gè)五元組來標(biāo)識(shí):(協(xié)議,本地地址,本地端口號(hào),遠(yuǎn)地地址,遠(yuǎn)地端口號(hào))這樣ー個(gè)五元組,叫做ー個(gè)相關(guān)(association),即兩個(gè)協(xié)議相同的半相關(guān)才能組合成一個(gè)合適的相關(guān),或完全指定組成一連接。服務(wù)方式在網(wǎng)絡(luò)分層結(jié)構(gòu)中,各層之間是嚴(yán)格單向依賴的,各層次的分エ和協(xié)作集中體現(xiàn)在相量紀(jì)紀(jì)之間的界面上。“服務(wù)”是描述相量紀(jì)紀(jì)之間關(guān)系的抽象概念,即網(wǎng)絡(luò)中各層向緊鄰上層提供的一組操作。下層是服務(wù)提供者,上層是請(qǐng)求服務(wù)的用戶。服務(wù)的表現(xiàn)形式是原語(yǔ)(primitive),如系統(tǒng)調(diào)用或庫(kù)函數(shù)。系統(tǒng)調(diào)用是操作系統(tǒng)內(nèi)核向網(wǎng)絡(luò)應(yīng)用程序或高層協(xié)議提供的服務(wù)原語(yǔ)。網(wǎng)絡(luò)中的n層總要向n+1層提供比n-1層更完備的服務(wù),否則n層就沒有存在的價(jià)值。在OSI的術(shù)語(yǔ)中,網(wǎng)絡(luò)層及其以下各層又稱為通信子網(wǎng),只提供點(diǎn)到點(diǎn)通信,沒有程序或進(jìn)程的概念。而傳輸層實(shí)現(xiàn)的是“端到端”通信,引進(jìn)網(wǎng)間進(jìn)程通信概念,同時(shí)也要解決差錯(cuò)控制,流量控制,數(shù)據(jù)排序(報(bào)文排序),連接管理等問題,為此提供不同的服務(wù)方式:面向連接(虛電路)或無連接面向連接服務(wù)是電話系統(tǒng)服務(wù)模式的抽象,即每一次完整的數(shù)據(jù)傳輸都要經(jīng)過建立連接,使用連接,終止連接的過程。在數(shù)據(jù)傳輸過程中,各數(shù)據(jù)分組不攜帶目的地址,而使用連接號(hào)(connectID)O本質(zhì)上,連接是ー個(gè)管道,收發(fā)數(shù)據(jù)不但順序一致,而且內(nèi)容相同。TCP協(xié)議提供面向連接的虛電路。無連接服務(wù)是郵政系統(tǒng)服務(wù)的抽象,每個(gè)分組都攜帶完整的目的地址,各分組在系統(tǒng)中獨(dú)立傳送。無連接服務(wù)不能保證分組的先后順序,不進(jìn)行分組出錯(cuò)的恢復(fù)與重傳,不保證傳輸?shù)目煽啃?。UDP協(xié)議提供無連接的數(shù)據(jù)報(bào)服務(wù)。ド面給出這兩種服務(wù)的類型及應(yīng)用中的例子:服務(wù)類型服務(wù)例子面向連接可靠的報(bào)文流可靠的字節(jié)流不可靠的連接文件傳輸(FTP)遠(yuǎn)程登錄(Telnet)數(shù)字話音無連接不可靠的數(shù)據(jù)報(bào)有確認(rèn)的數(shù)據(jù)報(bào)請(qǐng)求一應(yīng)答電子郵件(E-mail)電子郵件中的掛號(hào)信網(wǎng)絡(luò)數(shù)據(jù)庫(kù)查詢順序在網(wǎng)絡(luò)傳輸中,兩個(gè)連續(xù)報(bào)文在端一端通信中可能經(jīng)過不同路徑,這樣到達(dá)目的地時(shí)的順序可能會(huì)與發(fā)送時(shí)不同?!绊樞颉笔侵附邮諗?shù)據(jù)順序與發(fā)送數(shù)據(jù)順序相同。TCP協(xié)議提供這項(xiàng)服務(wù)。差錯(cuò)控制保證應(yīng)用程序接收的數(shù)據(jù)無差錯(cuò)的ー種機(jī)制。檢查差錯(cuò)的方法一般是采用檢驗(yàn)“檢查和(Checksum)”的方法。而保證傳送無差錯(cuò)的方法是雙方采用確認(rèn)應(yīng)答技術(shù)。TCP協(xié)議提供這項(xiàng)服務(wù)。在數(shù)據(jù)傳輸過程中控制數(shù)據(jù)傳輸速率的ー種機(jī)制,以保證數(shù)據(jù)不被丟失。TCP協(xié)議提供這項(xiàng)服務(wù)。字節(jié)流字節(jié)流方式指的是僅把傳輸中的報(bào)文看作是ー個(gè)字節(jié)序列,不提供數(shù)據(jù)流的任何邊界。TCP協(xié)議提供字節(jié)流服務(wù)。報(bào)文接收方要保存發(fā)送方的報(bào)文邊界。UDP協(xié)議提供報(bào)文服務(wù)。全雙工/半雙エ端一端間數(shù)據(jù)同時(shí)以兩個(gè)方向/ー個(gè)方向傳送。緩存/帶外數(shù)據(jù)在字節(jié)流服務(wù)中,由于沒有報(bào)文邊界,用戶進(jìn)程在某ー時(shí)刻可以讀或?qū)懭我鈹?shù)量的字節(jié)。為保證傳輸正確或采用有流控制的協(xié)議時(shí),都要進(jìn)行緩存。但對(duì)某些特殊的需求,如交互式應(yīng)用程序,又會(huì)要求取消這種緩存。在數(shù)據(jù)傳送過程中,希望不通過常規(guī)傳輸方式傳送給用戶以便及時(shí)處理的某ー類信息,如UNIX系統(tǒng)的中斷鍵(Delete或Control-c)>終端流控制符(Control-sControl-q)?稱為帶外數(shù)據(jù)。邏輯上看,好象用戶進(jìn)程使用了一個(gè)獨(dú)立的通道傳輸這些數(shù)據(jù)。該通道與每對(duì)連接的流相聯(lián)系。由于BerkeleySoftwareDistribution中對(duì)帶外數(shù)據(jù)的實(shí)現(xiàn)與RFC1122中規(guī)定的HostAgreement不一致,為了將互操作中的問題減到最小,應(yīng)用程序編寫者除非與現(xiàn)有服務(wù)互操作時(shí)要求帶外數(shù)據(jù)外,最好不使用它。客戶/服務(wù)器模式在TCP/IP網(wǎng)絡(luò)應(yīng)用中,通信的兩個(gè)進(jìn)程間相互作用的主要模式是客戶/服務(wù)器模式(Client/Servermodel),即客戶向服務(wù)器發(fā)出服務(wù)請(qǐng)求,服務(wù)器接收到請(qǐng)求后,提供相應(yīng)的服務(wù)。客戶/服務(wù)器模式的建立基于以下兩點(diǎn):首先,建立網(wǎng)絡(luò)的起因是網(wǎng)絡(luò)中軟硬件資源、運(yùn)算能力和信息不均等,需要共享,從而造就擁有眾多資源的主機(jī)提供服務(wù),資源較少的客戶請(qǐng)求服務(wù)這ー非對(duì)等作用。其次,網(wǎng)間進(jìn)程通信完全是異步的,相互通信的進(jìn)程間既不存在父子關(guān)系,又不共享內(nèi)存緩沖區(qū),因此需要一種機(jī)制為希望通信的進(jìn)程間建立聯(lián)系,為二者的數(shù)據(jù)交換提供同步,這就是基涌紀(jì)紀(jì)戶/服務(wù)器模式的TCP/IP??蛻?服務(wù)器模式鑰紀(jì)紀(jì)作過程中采取的是主動(dòng)請(qǐng)求方式:首先服務(wù)器方要先啟動(dòng),并根據(jù)請(qǐng)求提供相應(yīng)服務(wù):.打開一通信通道并告知本地主機(jī),它愿意在某一公認(rèn)地址上(周知口,如FTP為21)接收客戶請(qǐng)求;.等待客戶請(qǐng)求到達(dá)該端口;.接收到重復(fù)服務(wù)請(qǐng)求,處理該請(qǐng)求并發(fā)送應(yīng)答信號(hào)。接收到并發(fā)服務(wù)請(qǐng)求,要激活一新進(jìn)程來處理這個(gè)客戶請(qǐng)求(如UNIX系統(tǒng)中用fork、exec)〇新進(jìn)程處理此客戶請(qǐng)求,并不需要對(duì)其它請(qǐng)求作出應(yīng)答。服務(wù)完成后,關(guān)閉此新進(jìn)程與客戶的通信鏈路,并終止。.返回第二步,等待另ー客戶請(qǐng)求。.關(guān)閉服務(wù)器客戶方:.打開一通信通道,并連接到服務(wù)器所在主機(jī)的特定端口;.向服務(wù)器發(fā)服務(wù)請(qǐng)求報(bào)文,等待并接收應(yīng)答;繼續(xù)提出請(qǐng)求..….請(qǐng)求結(jié)束后關(guān)閉通信通道并終止。從上面所描述過程可知:.客戶與服務(wù)器進(jìn)程的作用是非對(duì)稱的,因此編碼不同。.服務(wù)進(jìn)程一般是先涌紀(jì)紀(jì)戶請(qǐng)求而啟動(dòng)的。只要系統(tǒng)運(yùn)行,該服務(wù)進(jìn)程一直存在,直到正常或強(qiáng)迫終止。2.4套接字類型TCP/IP的socket提供下列三種類型套接字。流式套接字(SOCKSTREAM)提供了一個(gè)面向連接、可靠的數(shù)據(jù)傳輸服務(wù),數(shù)據(jù)無差錯(cuò)、無重復(fù)地發(fā)送,且按發(fā)送順序接收。內(nèi)設(shè)流量控制,避免數(shù)據(jù)流超限;數(shù)據(jù)被看作是字節(jié)流,無長(zhǎng)度限制。文件傳送協(xié)議(FTP)即使用流式套接字。數(shù)據(jù)報(bào)式套接字(SOCK_DGRAM)提供了一個(gè)無連接服務(wù)。數(shù)據(jù)包以獨(dú)立包形式被發(fā)送,不提供無錯(cuò)保證,數(shù)據(jù)可能丟失或重復(fù),并且接收順序混亂。網(wǎng)絡(luò)文件系統(tǒng)(NFS)使用數(shù)據(jù)報(bào)式套接字。原始式套接字(SOCKRAW)該接口允許對(duì)較低層協(xié)議,如IP、ICMP直接訪問。常用于檢驗(yàn)新的協(xié)議實(shí)現(xiàn)或訪問現(xiàn)有服務(wù)中配置的新設(shè)備。3基本套接字系統(tǒng)調(diào)用為了更好地說明套接字編程原理,下面給出幾個(gè)基本套接字系統(tǒng)調(diào)用說明。.!創(chuàng)建套接字socket()應(yīng)用程序在使用套接字前,首先必須擁有一個(gè)套接字,系統(tǒng)調(diào)用socket。向應(yīng)用程序提供創(chuàng)建套接字的手段,其調(diào)用格式如下:SOCKETPASCALFARsocket(intaf,inttype,intprotocol);該調(diào)用要接收三個(gè)參數(shù):af、type、protocol。參數(shù)af指定通信發(fā)生的區(qū)域,UNIX系統(tǒng)支持的地址族有:AFJJNIX、AF」NET、AF_NS等,而DOS、WINDOWS中僅支持AFINET,它是網(wǎng)際網(wǎng)區(qū)域。因此,地址族與協(xié)議族相同。參數(shù)type描述要建立的套接字的類型。參數(shù)protocol說明該套接字使用的特定協(xié)議,如果調(diào)用者不希望特別指定使用的協(xié)議,則置為0,使用默認(rèn)的連接模式。根據(jù)這三個(gè)參數(shù)建立一個(gè)套接字,并將相應(yīng)的資源分配給它,同時(shí)返回一個(gè)整型套接字號(hào)。因此,socket。系統(tǒng)調(diào)用實(shí)際上指定了相關(guān)五元組中的“協(xié)議”這ー元。有關(guān)socket。的詳細(xì)描述參看5.2.23o指定本地地址一bind()當(dāng)ー個(gè)套接字用socket。創(chuàng)建后,存在ー個(gè)名字空間(地址族),但它沒有被命名。bind。將套接字地址(包括本地主機(jī)地址和本地端口地址)與所創(chuàng)建的套接字號(hào)聯(lián)系起來,即將名字賦予套接字,以指定本地半相關(guān)。其調(diào)用格式如下:intPASCALFARbind(SOCKETs,conststructsockaddrFAR*name,intnamelen);參數(shù)s是由socket。調(diào)用返回的并且未作連接的套接字描述符(套接字號(hào))。參數(shù)name是賦給套接字s的本地地址(名字),其長(zhǎng)度可變,結(jié)構(gòu)隨通信域的不同而不同。namelen表明了name的長(zhǎng)度。如果沒有錯(cuò)誤發(fā)生,bind。返回0。否則返回值SOCKET_ERRORo地址在建立套接字通信過程中起著重要作用,作為一個(gè)網(wǎng)絡(luò)應(yīng)用程序設(shè)計(jì)者對(duì)套接字地址結(jié)構(gòu)必須有明確認(rèn)識(shí)。例如,UNIXBSD有一組描述套接字地址的數(shù)據(jù)結(jié)構(gòu),其中使用TCP/IP協(xié)議的地址結(jié)構(gòu)為:structsockaddr_in{shortsinfamily;/*AF_INET*/ushortsin_port;/*16位端口號(hào),網(wǎng)絡(luò)字節(jié)順序?/structinaddrsinaddr;/*32位IP地址,網(wǎng)絡(luò)字節(jié)順序?/charsin_zero[8];/?保留?/}有關(guān)bind()的詳細(xì)描述參看5.2.2〇建立套接字連接 connect。與accept。這兩個(gè)系統(tǒng)調(diào)用用于完成一個(gè)完整相關(guān)的建立,其中connect。用于建立連接。無連接的套接字進(jìn)程也可以調(diào)用connect。,但這時(shí)在進(jìn)程之間沒有實(shí)際的報(bào)文交換,調(diào)用將從本地操作系統(tǒng)直接返回。這樣做的優(yōu)點(diǎn)是程序員不必為每ー數(shù)據(jù)指定目的地址,而且如果收到的ー個(gè)數(shù)據(jù)報(bào),其目的端口未與任何套接字建立“連接”,便能判斷該端靠紀(jì)紀(jì)可操作。而accept。用于使服務(wù)器等待來自某客戶進(jìn)程的實(shí)際連接。connect。的調(diào)用格式如下:intPASCALFARconnect(SOCKETs,conststructsockaddrFAR*name,intnamelen);參數(shù)s是欲建立連接的本地套接字描述符。參數(shù)name指出說明對(duì)方套接字地址結(jié)構(gòu)的指針。對(duì)方套接字地址長(zhǎng)度由namelen說明。如果沒有錯(cuò)誤發(fā)生,connect。返回〇。否則返回值SOCKETERRORo在面向連接的協(xié)議中,該調(diào)用導(dǎo)致本地系統(tǒng)和外部系統(tǒng)之間連接實(shí)際建立。由于地址族總被包含在套接字地址結(jié)構(gòu)的前兩個(gè)字節(jié)中,并通過socket。調(diào)用與某個(gè)協(xié)議族相關(guān)。因此bind。和connect。無須協(xié)議作為參數(shù)。有關(guān)connect。的詳細(xì)描述參看5.2.4〇accept。的調(diào)用格式如下:SOCKETPASCALFARaccept(SOCKETs,structsockaddrFAR*addr,intFAR*addrlen);參數(shù)s為本地套接字描述符,在用做accept。調(diào)用的參數(shù)前應(yīng)該先調(diào)用過listen。。addr指向客戶方套接字地址結(jié)構(gòu)的指針,用來接收連接實(shí)體的地址。addr的確切格式由套接字創(chuàng)建時(shí)建立的地址族決定。addrlen為客戶方套接字地址的長(zhǎng)度(字節(jié)數(shù))。如果沒有錯(cuò)誤發(fā)生,accept。返回ー個(gè)SOCKET類型的值,表示接收到的套接字的描述符。否則返回值INVALID_SOCKET。acc印t。用于面向連接服務(wù)器。參數(shù)addr和addrlen存放客戶方的地址信息。調(diào)用前,參數(shù)addr指向ー個(gè)初始值為空的地址結(jié)構(gòu),而addrlen的初始值為0;調(diào)用accept。后,服務(wù)器等待從編號(hào)為s的套接字上接受客戶連接請(qǐng)求,而連接請(qǐng)求是由客戶方的connect。調(diào)用發(fā)出的。當(dāng)有連接請(qǐng)求到達(dá)時(shí),accept。調(diào)用將請(qǐng)求連接隊(duì)列上的第一個(gè)客戶方套接字地址及長(zhǎng)度放入addr和addrlen,并創(chuàng)建一個(gè)與s有相同特性的新套接字號(hào)。新的套接字可用于處理服務(wù)器并發(fā)請(qǐng)求。有關(guān)accept。的詳細(xì)描述參看5.2.1〇四個(gè)套接字系統(tǒng)調(diào)用,socket。>bind。、connect。>acceptQ?可以完成一個(gè)完全五元相關(guān)的建立。socket。指定五元組中的協(xié)議元,它的用法與是否為客戶或服務(wù)器、是否面向連接無關(guān)。bind。指定五元組中的本地二元,即本地主機(jī)地址和端口號(hào),其用法與是否面向連接有關(guān):在服務(wù)器方,無論是否面向連接,均要調(diào)用bind。;鑰紀(jì)紀(jì)戶方,若采用面向連接,則可以不調(diào)用bind。,面通過connect。自動(dòng)完成。若采用無連接,客戶方必須使用bind()以獲得一個(gè)唯一的地址。以上討論僅對(duì)客戶/服務(wù)器模式而言,實(shí)際上套接字的使用是非常靈活的,唯一需遵循的原則是進(jìn)程通信之前,必須建立完整的相關(guān)。監(jiān)聽連接 listen。此調(diào)用用于面向連接服務(wù)器,表明它愿意接收連接。listen。需在aec印t。之前調(diào)用,其調(diào)用格式如下:intPASCALFARlisten(SOCKETs,intbacklog);參數(shù)S標(biāo)識(shí)ー個(gè)本地已建立、尚未連接的套接字號(hào),服務(wù)器愿意從它上面接收請(qǐng)求。backlog表示請(qǐng)求連接隊(duì)列的最大長(zhǎng)度,用于限制排隊(duì)請(qǐng)求的個(gè)數(shù),目前允許的最大值為5。如果沒有錯(cuò)誤發(fā)生,listen。返回〇。否則它返回SOCKET_ERROR。listen。在執(zhí)行調(diào)用過程中可為沒有調(diào)用過bind。的套接字s完成所必須的連接,并建立長(zhǎng)度為backlog的請(qǐng)求連接隊(duì)列。調(diào)用listen。是服務(wù)器接收ー個(gè)連接請(qǐng)求的四個(gè)步驟中的第三步。它在調(diào)用socket。分配ー個(gè)流套接字,且調(diào)用bind。給s賦于ー個(gè)名字之后調(diào)用,而且一定要在accept。之前調(diào)用。有關(guān)listen。的詳細(xì)描述參看5.2.13o2.3節(jié)中提到鑰紀(jì)紀(jì)戶/服務(wù)器模式中,有兩種類型的服務(wù):重復(fù)服務(wù)和并發(fā)服務(wù)。accept。調(diào)用為實(shí)現(xiàn)并發(fā)服務(wù)提供了極大方便,因?yàn)樗祷匾粋€(gè)新的套接字號(hào),其典型結(jié)構(gòu)為:intinitsockid,newsockid;if((initsockid=socket(....))<0)error("can'tcreatesocket");if(bind(initsockid,....)<0)error(4tbinderror");if(listen(initsockid,5)<0)error("listenerror");for(;{newsockid=accept(initsockid,...)/?阻塞?/if(newsockid<0)error,'accepterror");if(fbrk()=0){/?子進(jìn)程?/closesocket(initsockid);exit(O);closesocket(newsockid);/?父進(jìn)程?/這段程序執(zhí)行的結(jié)果是newsockid與客戶的套接字建立相關(guān),子進(jìn)程啟動(dòng)后,關(guān)閉繼承下來的主服務(wù)器的initsockid,并利用新的newsockid與客戶通信。主服務(wù)器的initsockid可繼續(xù)等待新的客戶連接請(qǐng)求。由于在Unix等搶先多任務(wù)系統(tǒng)中,在系統(tǒng)調(diào)度下,多個(gè)進(jìn)程可以同時(shí)進(jìn)行。因此,使用并發(fā)服務(wù)器可以使服務(wù)器進(jìn)程在同一時(shí)間可以有多個(gè)子進(jìn)程和不同的客戶程序連接、通信。鑰紀(jì)紀(jì)戶程序看來,服務(wù)器可以同時(shí)并發(fā)地處理多個(gè)客戶的請(qǐng)求,這就是并發(fā)服務(wù)器名稱的來由。面向連接服務(wù)器也可以是重復(fù)服務(wù)器,其結(jié)構(gòu)如下:intinitsockid,newsockid;if((initsockid=socket(....))<0)error(4tcan,tcreatesocket");if(bind(initsoekid,....)<0)error("binderror");if(listen(initsockid,5)<0)error(4tlistenerror");for(;{newsockid=accept(initsockid,...)/?阻塞?/if(newsockid<0)error(44accepterror44);closesocket(newsockid);重復(fù)服務(wù)器在ー個(gè)時(shí)間只能和一個(gè)客戶程序建立連接,它對(duì)多個(gè)客戶程序的處理是采用循環(huán)的方式重復(fù)進(jìn)行,因此叫重復(fù)服務(wù)器。并發(fā)服務(wù)器和重復(fù)服務(wù)器各有利弊:并發(fā)服務(wù)器可以改善客戶程序的響應(yīng)速度,但它增加了系統(tǒng)調(diào)度的開銷;重復(fù)服務(wù)器正好與其相反,因此用戶在決定是使用并發(fā)服務(wù)器還是重復(fù)服務(wù)器時(shí),要根據(jù)應(yīng)用的實(shí)際情考網(wǎng)考網(wǎng)來定。數(shù)據(jù)傳輸 send。與recv()當(dāng)ー個(gè)連接建立以后,就可以傳輸數(shù)據(jù)了。常用的系統(tǒng)調(diào)用有send。和recv()osend()調(diào)用用于鑰紀(jì)紀(jì)數(shù)s指定的已連接的數(shù)據(jù)報(bào)或流套接字上發(fā)送輸出數(shù)據(jù),格式如下:intPASCALFARsend(SOCKETs,constcharFAR*buf,intlen,intflags);參數(shù)s為已連接的本地套接字描述符。buf指向存有發(fā)送數(shù)據(jù)的緩沖區(qū)的指針,其長(zhǎng)度由len指定。flags指定傳輸控制方式,如是否發(fā)送帶外數(shù)據(jù)等。如果沒有錯(cuò)誤發(fā)生,send。返回總共發(fā)送的字節(jié)數(shù)。否則它返回SOCKETERRORo有關(guān)send。的詳細(xì)描述參看5.2.19〇recv()調(diào)用用于鑰紀(jì)紀(jì)數(shù)s指定的已連接的數(shù)據(jù)報(bào)或流套接字上接收輸入數(shù)據(jù),格式如下:intPASCALFARrecv(SOCKETs,charFAR*buf,intlen,intflags);參數(shù)s為已連接的套接字描述符。buf指向接收輸入數(shù)據(jù)緩沖區(qū)的指針,其長(zhǎng)度由len指定。flags指定傳輸控制方式,如是否接收帶外數(shù)據(jù)等。如果沒有錯(cuò)誤發(fā)生,recv()返回總共接收的字節(jié)數(shù)。如果連接被關(guān)閉,返回〇〇否則它返回SOCKET_ERRORo有關(guān)recv()的詳細(xì)描述參看5.2.16〇輸入/輸出多路復(fù)用select()select。調(diào)用用來檢測(cè)ー個(gè)或多個(gè)套接字的狀態(tài)。對(duì)每ー個(gè)套接字來說,這個(gè)調(diào)用可以請(qǐng)求讀、寫或錯(cuò)誤狀態(tài)方面的信息。請(qǐng)求給定狀態(tài)的套接字集合由一個(gè)fd_set結(jié)構(gòu)指示。在返回時(shí),此結(jié)構(gòu)被更新,以反映那些滿足特定條件的套接字的子集,同時(shí),select。調(diào)用返回滿足條件的套接字的數(shù)目,其調(diào)用格式如下:intPASCALFARselect(intnfds,fd_setFAR*readfds,fd_setFAR*writefds,fd_setFAR*exceptfds,conststructtimevalFAR*timeout);參數(shù)nfds指明被檢查的套接字描述符的值域,此變量一般被忽略。參數(shù)readfds指向要做讀檢測(cè)的套接字描述符集合的指針,調(diào)用者希望從中讀取數(shù)據(jù)。參數(shù)writefds指向要做寫檢測(cè)的套接字描述符集合的指針。exceptfds指向要檢測(cè)是否出錯(cuò)的套接字描述符集合的指針。timeout指向select。函數(shù)等待的最大時(shí)間,如果設(shè)為NULL則為阻塞操作。select。返回包含在fd_set結(jié)構(gòu)中已準(zhǔn)備好的套接字描述符的總數(shù)目,或者是發(fā)生錯(cuò)誤則返回SOCKETERRORo有關(guān)select。的詳細(xì)描述參看5.2.18〇關(guān)閉套接字 closesocket()ck)sesocket()關(guān)閉套接字s,并釋放分配給該套接字的資源;如果s涉及ー個(gè)打開的TCP連接,則該連接被釋放。closesocket。的調(diào)用格式如下:BOOLPASCALFARclosesocket(SOCKETs);參數(shù)s待關(guān)閉的套接字描述符。如果沒有錯(cuò)誤發(fā)生,closesocket()返回〇。否則返回值SOCKETERROR〇有關(guān)closesocket。的詳細(xì)描述參看5.2.3〇典型套接字調(diào)用過程舉例如前所述,TCP/IP協(xié)議的應(yīng)用一般采用客戶/服務(wù)器模式,因此在實(shí)際應(yīng)用中,必須有客戶和服務(wù)器兩個(gè)進(jìn)程,并且首先啟動(dòng)服務(wù)器,其系統(tǒng)調(diào)用時(shí)序圖如下。面向連接的協(xié)議(如TCP)的套接字系統(tǒng)調(diào)用如圖2.1所示:服務(wù)器必須首先啟動(dòng),直到它執(zhí)行完accept。調(diào)用,進(jìn)入等待狀態(tài)后,方能接收客戶請(qǐng)求。假如客戶在此前啟動(dòng),則connect()將返回出錯(cuò)代碼,連接不成功。圖2.1面向連接的套接字系統(tǒng)調(diào)用時(shí)序圖無連接協(xié)議的套接字調(diào)用如圖2.2所示:圖2.2無連接協(xié)議的套接字調(diào)用時(shí)序圖無連接服務(wù)器也必須先啟動(dòng),否則客戶請(qǐng)求傳不到服務(wù)進(jìn)程。無連接客戶不調(diào)用connect。。因此在數(shù)據(jù)發(fā)送之前,客戶與服務(wù)器之間尚未建立完全相關(guān),但各自通過socket。和bind。建立了半相關(guān)。發(fā)送數(shù)據(jù)時(shí),發(fā)送方除指定本地套接字號(hào)外,還需指定接收方套接字號(hào),從而在數(shù)據(jù)收發(fā)過程中動(dòng)態(tài)地建立了全相關(guān)。實(shí)例本實(shí)例使用面向連接協(xié)議的客戶/服務(wù)器模式,其流程如圖2.3所示:圖2.3面向連接的應(yīng)用程序流程圖服務(wù)器方程序:/*FileName:streams.c*/#include#include#defineTRUE1/?這個(gè)程序建立一個(gè)套接字,然后開始無限循環(huán);每當(dāng)它通過循環(huán)接收到ー個(gè)連接,則打印出ー個(gè)信息。當(dāng)連接斷開,或接收到終止信息,則此連接結(jié)束,程序再接收一個(gè)新的連接。命令行的格式是:streams*/main()intsock,length;structsockaddrinserver;structsockaddrtcpaddr;intmsgsock;charbuf[1024];intrval,len;/?建立套接字?/sock=socket(AF_INET,SOCKSTREAM,0);if(sock<0){perror(“〇peningstreamsocket");exit(l);/*使用任意端口命名套接字?/server.sin_family=AF_INET;server.sin_port=INADDRANY;if(bind(sock,(structsockaddr*)&server,sizeof(server))<0){perror(4tbindingstreamsocket");exit(1);/?找出指定的端口號(hào)并打印出來?/length=size。ヨserver);if(getsockname(sock,(structsockaddr*)&server,&length)<0){perror("gettingsocketname");exit(1);}printR“socketport#%d\n",ntohs(server.sin_port));/?開始接收連接?/listen(sock,5);len=sizeo心tructsockaddr);do{msgsock=accept(sock,(structsockaddr*)&tcpaddr,(int*)&len);if(msgsock==-1)perror("accept");elsedo{memset(buf,0,sizeofifbuf));if((rval=recv(msgsock,buf,1024))<0)perror("readingstreammessage,,);if(rval==0)printR“endingconnection\n");elseprintf("ー〉;%s\n”,buf);}while(rval!=0);closesocket(msgsock);}while(TRUE);/?因?yàn)檫@個(gè)程序已經(jīng)有了一個(gè)無限循環(huán),所以套接字“sock”從來不顯式關(guān)閉。然而,當(dāng)進(jìn)程被殺死或正常終止時(shí),所有套接字都將自動(dòng)地被關(guān)閉。*/exit(O);客戶方程序:/*FileName:streamc.c*/#include#defineDATA"halfaleague,halfaleague…”/?這個(gè)程序建立套接字,然后與命令行給出的套接字連接;連接結(jié)束時(shí),在連接上發(fā)送ー個(gè)消息,然后關(guān)閉套接字。命令行的格式是:streamc主機(jī)名端口號(hào)端口號(hào)要與服務(wù)器程序的端口號(hào)相同?/main(argc,argv)intargc;char*argv[];(intsock;structsockaddrinserver;structhostent*hp,*gethostbyname();charbuf[1024];/?建立套接字?/sock=socket(AF_INET,SOCKSTREAM,0);if(sock<0){perror("openingstreamsocket");exit(1);}/?使用命令行中指定的名字連接套接字?/server.sin_family=AF_INET;hp=gethostbyname(argv[1]);if(hp==0){fprintffstderr,t4%s:unknownhost\n",argv[1]);exit(2);memcpy((char*)&server.sin_addr, (char*)hp->;h_addr,hp->;h_length);sever.sin_port=htons(atoi(argv[2]));if(connect(sock,(structsockaddr*)&server,sizeoRserver))<0){perror("eonneetingstreamsoeket");exit(3);if(send(sock,DATA,sizeof(DATA))<0)perror("sendingonstreamsocket");closesocket(sock);exit(0);2.5ー個(gè)通用的實(shí)例程序在上一節(jié)中,我們介紹了一個(gè)簡(jiǎn)單的socket程序?qū)嵗?。從這個(gè)例子我們可以看出,使用socket編程幾乎有一個(gè)模式,即所有的程序幾乎毫無例外地按相同的順序調(diào)用相同的函數(shù)。因此我們可以設(shè)想,設(shè)計(jì)一個(gè)中間層,它向上提供幾個(gè)簡(jiǎn)單的函數(shù),程序只要調(diào)用這幾個(gè)函數(shù)就可以實(shí)現(xiàn)普通情考網(wǎng)考網(wǎng)下的數(shù)據(jù)傳輸,程序設(shè)計(jì)者不必太多地關(guān)心socket程序設(shè)計(jì)的細(xì)節(jié)。本節(jié)我們將介紹ー個(gè)通用的網(wǎng)絡(luò)程序接口,它向上層提供幾個(gè)簡(jiǎn)單的函數(shù),程序設(shè)計(jì)者只要使用這幾個(gè)函數(shù)就可以完成絕大多數(shù)情考網(wǎng)考網(wǎng)下的網(wǎng)絡(luò)數(shù)據(jù)傳輸。這些函數(shù)將socket編程和上層隔離開來,它使用面向連接的流式套接字,采用非阻塞的工作機(jī)制,程序只要調(diào)用這些函數(shù)查詢網(wǎng)絡(luò)消息并作出相應(yīng)的響應(yīng)即可。這些函數(shù)包括:1InitSocketsStruct:初始化socket結(jié)構(gòu),獲取服務(wù)端口號(hào)。客戶程序使用。InitPassiveSock:初始化socket結(jié)構(gòu),獲取服務(wù)端口號(hào),建立主套接字。服務(wù)器程序使用。CloseMainSock:關(guān)閉主套接字。服務(wù)器程序使用。ICreateConnection:建立連接??蛻舫绦蚴褂?。1AcceptConnection:接收連接。服務(wù)器程序使用。1CloseConnection:關(guān)閉連接。IQuerySocketsMsg:查詢套接字消息。1SendPacket:發(fā)送數(shù)據(jù)。1RecvPacket:接收數(shù)據(jù)。頭文件/*FileName:tcpsock.h*//?頭文件包括socket程序經(jīng)常用到的系統(tǒng)頭文件(本例中給出的是SCOUnix下的頭文件,其它版本的Unix的頭文件可能略有不同),并定義了我們自己的兩個(gè)數(shù)據(jù)結(jié)構(gòu)及其實(shí)例變量,以及我們提供的函數(shù)說明。*/#include#include#include#include#include#include#include#include#include#include#include#include#includetypedefstructSocketsMsg{/?套接字消息結(jié)構(gòu)*/intAcceptNum;/*指示是否有外來連接等待接收*/intReadNum;/?有外來數(shù)據(jù)等待讀取的連接數(shù)?/intReadQueue[32];/?有外來數(shù)據(jù)等待讀取的連接隊(duì)列?/intWriteNum;/?可以發(fā)送數(shù)據(jù)的連接數(shù)?/intWriteQueue[32];/*可以發(fā)送數(shù)據(jù)的連接隊(duì)列?/intExc印tNum;/?有例外的連接數(shù)?/intExceptQueue[32];/?有例外的連接隊(duì)列?/}SocketsMsg;typedefstructSockets{/?套接字結(jié)構(gòu)?/intDaemonSock;/?主套接字?/intSockNum;/*數(shù)據(jù)套接字?jǐn)?shù)目?/intSockets[64];/*數(shù)據(jù)套接字?jǐn)?shù)組?/fdsetreadfds,writefds,exceptfds;/?要被檢測(cè)的可讀、可寫、例外的套接字集合?/intPort;/*端口號(hào)*/}Sockets;SocketsMysock;/?全局變量?/SocketsMsgSockMsg;intInitSocketsStruct(char*servicename);intInitPassiveSock(char*servicename);voidCloseMainSock();intCreateConnection(structinaddr*sin_addr);intAcceptConnection(structinaddr*IPaddr);intCloseConnection(intSockno);intQuerySocketsMsgO;intSendPacket(intSockno,void*buf,intlen);intRecvPacket(intSockno,void*buf,intsize);函數(shù)源文件/*FileName:tcpsock.c*//?本文件給出九個(gè)函數(shù)的源代碼,其中部分地方給出中文注釋*/#include"tcpsock.h"intInitSocketsStruct(char*servicename)/*InitializeSocketsstructure.Ifsucceedthenreturn1,elsereturnerrorcode(<0)*//?此函數(shù)用于只需要主動(dòng)套接字的客戶程序,它用來獲取服務(wù)信息。服務(wù)的定義在/etc/services文件中?/(structservent*servrec;structsockaddrinserv_addr;if((servrec=getservbyname(servicename,"tcp"))=NULL){retum(-l);bzero((char*)&Mysock,sizeof(Sockets));Mysock.Port=servrec->;s_port;/*ServicePortinNetworkByteOrder*/retum(l);intInitPassiveSock(char*servicename)/*InitializePassiveSocket.Ifsucceedthenreturn1,elsereturnerrorcode(<0)*//?此函數(shù)用于需要被動(dòng)套接字的服務(wù)器程序,它除了獲取服務(wù)信息外,還建立ー個(gè)被動(dòng)套接字。*/intmainsock,flag=1;structservent*servrec;structsockaddrinservaddr;if((servrec=getservbyname(servicename,"tcp"))=NULL){retum(-l);bzero((char*)&Mysock,sizeof(Sockets));Mysock.Port=servrec->;s_port;/*ServicePortinNetworkByteOrder*/if((mainsock=socket(AF_INET,SOCKSTREAM,0))<0){retum(-2);}bzero((char*)&serv_addr,sizeoffservaddr));servaddr.sinfamily=AFINET;servaddr.sinaddr.saddr=htonl(INADDRANY);/*任意網(wǎng)絡(luò)接ロ*/serv_addr.sin_port=servrec->;s_port;if(bind(mainsock,(structsockaddr*)&serv_addr,sizeo心ervaddr))<0){close(mainsock);retum(-3);if(listen(mainsock,5)==-1){/?將主動(dòng)套接字變?yōu)楸粍?dòng)套接字,準(zhǔn)備好接收連接?/close(mainsock);retum(-4);)/*SetthissocketasaNon-blockingsocket.*/if(ioctl(mainsock,FIONBIO,&flag)=-1){close(mainsock);retum(-5);Mysock.DaemonSock=mainsock;FD_SET(mainsock,&Mysock.readfds);/?申明對(duì)主套接字‘‘可讀"感興趣?/FD_SET(mainsock,&Mysock.exceptfds);/?申明對(duì)主套接字上例外事件感興趣?/retum(l);}voidCloseMainSock()/?關(guān)閉主套接字,并清除對(duì)它上面事件的申明。在程序結(jié)束前關(guān)閉主套接字是ー個(gè)好習(xí)慣?/close(Mysock.DaemonSock);FD_CLR(Mysock.DaemonSock,&Mysock.readfds);FD_CLR(Mysock.DaemonSock,&Mysock.exceptfds);intCreateConnection(structinaddr*sin_addr)/*CreateaConnectiontoremotehostwhichIPaddressisinsinaddr.Param:sinaddrindicatestheIPaddressinNetworkByteOrder.ifsucceedreturnthesocketnumberwhichindicatesthisconnection,elsereturnerrorcode(<0)*/structsockaddrinserver;/*serveraddress*/inttmpsock,flag=1,i;if((tmpsock=socket(AF_INET,SOCKSTREAM,0))<0)retum(-l);server.sinfamily=AFINET;server.sin_port=Mysock.Port;server,sinaddr.saddr=sin_addr->;s_addr;/*SetthissocketasaNon-blockingsocket.*/if(ioctl(tmpsock,FIONBIO,&flag)=-1){close(tmpsock);retum(-2);/*Connecttotheserver.*/if(connect(tmpsock,(structsockaddr*)&server,sizeofifserver))<0)(if((ermo!=EWOULDBLOCK)&&(ermo!=EINPROGRESS)){/?如果錯(cuò)誤代碼是EWOULDBLOCK和EINPROGRESS?則不用關(guān)閉套接字,因?yàn)橄到y(tǒng)將在之后繼續(xù)為套接字建立連接,連接是否建立成功可用select。函數(shù)來檢測(cè)套接字是否“可寫”來確定。*/close(tmpsock);retum(-3);/*Connecterror.*/FD_SET(tmpsock,&Mysock.readfds);FD_SET(tmpsock,&Mysock.writefds);FD_SET(tmpsock,&Mysock.exceptfds);i=0;while(Mysock.Sockets!=0)i++;/*lookforablanksocketsposition*/if(i>;=64){close(tmpsock);retum(-4):/*toomanyconnections*/Mysock.Sockets=tmpsock;Mysock.SockNum++;retum(i);LintAcceptConnection(structinaddr*IPaddr)/*Acceptaconnection.Ifsucceed,returnthedatasocketsnumber,elsereturn-1.*/Lintnewsock,len,flag=l,i;structsockaddrinaddr;len=sizeoffaddr);bzero((char*)&addr,len);if((newsock=accept(Mvsock.DaemonSock,&addr,&len))=-1)retum(-l):/*Accepterror.*//*SetthissocketasaNon-blockingsocket.*/ioctl(newsock,FIONBIO,&flag);FDSET(newsock,&Mysock.readfds):FDSET(newsock,&Mysock.writefds);FDSET(newsock,&Mysock.exceDtfds):/*ReturnIPaddressintheParameter.*/IPaddr->;saddr=addr.sinaddr.saddr;i=0;while(Mysoek.Soekets!=0)i++;/*lookforablanksoeketsposition*/if(i>:=64)]elose(newsoek):retum(-4);/*toomanyconnections*/Mysock.Sockets=newsock:Mysock.SockNum++;return⑴;J-intCloseConnection(intSockno)/*CloseaconnectionindicatedbySockno.*/Lintretcode;if((Sockno>:=64)||(Sockno<0)||(Mysock.SocketsJSockno]=OILreturn(0);retcode=close(Mysock.SocketsrSocknol);FDCLR(Mysock.SocketsrSockno1,&Mysock.readfds);FDCLR(Mysock.Sockets|~Sockno],&Mysock.writefds):FDCLRfMysock.SocketsrSocknol,&Mysock.exceptf(js);Mysock.SocketsrSocknol=0;Mysock.SockNum—;retum(retcode);intQuerySocketsMsg。/*OuerySocketsMessage.Ifsucceedreturnmessagenumber,elsereturn-1.ThemessageinformationstoredinstructSockMsg.*/Lfdsetrfds,wfds,efds;intretcode,i;structtimevalTimeOut;rfds=Mysock.readfds;wfds=Mysock.writefds:efds=Mysock.exceptfds:TimeOut.tvsec=〇:/?立即返回,不阻塞。*/TimeOut.tvusee=0;bzero((char*)&SockMsg,sizeolSockMsg)):if((retcode=select(64,&rfids,&wfds,&efds,&TimeOut))==0)return(〇):if(FDISSET(Mysock.DaemonSock,&rfds))SockMsg.AcceptNum=1;/*someclientcallserver.*/for(i=0:i<64;i++)/*Datainmessage*/丄if((Mysock.Sockets>;0)&&(FDISSET(Mysock.Sockets,&rfds)))SockMsg.ReadQueue|'SockMsg.ReadNum++]=匕for(i=0:i<64;i++)/*Dataoutreadymessage*/Lif((Mysock.Sockets>:0)&&(FDISSET(Mysock.Sockets,&wfds)))SockMsg.WriteQueue「SockMsg.WriteNum++]=i:Lif(FDISSET(Mysock.DaemonSock,&efds))SockMsg.AcceptNum=-1:/*serversocketerror.*/for(i=0;i<64;i++)/*Errormessage*/if((Mysock.Sockets>;0)&&(FDISSET(Mysock.Sockets,&efds)))SockMsg.ExceptQueue「SockMsg.ExceptNum++]=i:J-retum(retcode):LintSendPacket(intSockno,void*bu£intlen)/*Sendapacket.Ifsucceedreturnthenumberofsenddata,elsereturn-1*/Lintactlen;if((Sockno>:=64)||(Sockno<0)||(Mysock.SocketslSockno]=mrreturn(0);if((actlen=send(Mysock.Sockets「Sockno],buf,len,〇))<〇)retum(-l):retum(actlen):LintRecvPacket(intSockno,void*buf,intsize)/*Receiveapacket.Ifsucceedreturnthenumberofreceivedata,elseiftheconnectionisshutdownbypeerthenreturn〇,otherwisereturn0-errno*/Lintactlen;if((Sockno>;=64)||(Sockno<0)||(Mysock.SocketsJSockno]=m),return(0):if((actlen=recv(Mysock.Sockets|~Sockno],buf,size,〇))<0)retum(O-ermo):retum(actlen);/*actlen是接收的數(shù)據(jù)長(zhǎng)度,如果為零,指示連接被對(duì)方關(guān)閉。ッL2.5.3簡(jiǎn)單服務(wù)器程序示例/*FileName:

溫馨提示

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

評(píng)論

0/150

提交評(píng)論