版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡介
網(wǎng)絡(luò)編程第1章網(wǎng)絡(luò)編程基礎(chǔ)
本章介紹網(wǎng)絡(luò)編程相關(guān)的基本概念,包括進(jìn)程通信的概念因特網(wǎng)中網(wǎng)間進(jìn)程的標(biāo)識(shí)方法網(wǎng)絡(luò)協(xié)議的特征TCP/IP中用戶數(shù)據(jù)報(bào)協(xié)議UDP和傳輸控制協(xié)議TCP的特點(diǎn)目前的網(wǎng)絡(luò)編程現(xiàn)狀網(wǎng)絡(luò)應(yīng)用程序的客戶/服務(wù)器交互模式
1.1網(wǎng)絡(luò)編程相關(guān)的基本概念
1.1.1網(wǎng)絡(luò)編程與進(jìn)程通信1.進(jìn)程與線程的基本概念
進(jìn)程是處于運(yùn)行過程中的程序?qū)嵗?,是操作系統(tǒng)調(diào)度和分配資源的基本單位。一個(gè)進(jìn)程實(shí)體由程序代碼、數(shù)據(jù)和進(jìn)程控制塊三部分構(gòu)成。各種計(jì)算機(jī)應(yīng)用程序在運(yùn)行時(shí),都以進(jìn)程的形式存在。網(wǎng)絡(luò)應(yīng)用程序也不例外。Windows系統(tǒng)不但支持多進(jìn)程,還支持多線程。當(dāng)創(chuàng)建一個(gè)進(jìn)程時(shí),系統(tǒng)會(huì)自動(dòng)創(chuàng)建它的第一個(gè)線程,稱為主線程。然后,該線程可以創(chuàng)建其他的線程,而這些線程又能創(chuàng)建更多的線程。
圖1.1單CPU分時(shí)地運(yùn)行進(jìn)程中的各個(gè)線程2.網(wǎng)絡(luò)應(yīng)用進(jìn)程在網(wǎng)絡(luò)體系結(jié)構(gòu)中的位置
從計(jì)算機(jī)網(wǎng)絡(luò)體系結(jié)構(gòu)的角度來看,網(wǎng)絡(luò)應(yīng)用進(jìn)程處于網(wǎng)絡(luò)層次結(jié)構(gòu)的最上層。從功能上,可以將網(wǎng)絡(luò)應(yīng)用程序分為兩部分,一部分是專門負(fù)責(zé)網(wǎng)絡(luò)通信的模塊,它們與網(wǎng)絡(luò)協(xié)議棧相連接,借助網(wǎng)絡(luò)協(xié)議棧提供的服務(wù)完成網(wǎng)絡(luò)上數(shù)據(jù)信息的交換。另一部分是面向用戶或者作其他處理的模塊,它們接收用戶的命令,或者對(duì)借助網(wǎng)絡(luò)傳輸過來的數(shù)據(jù)進(jìn)行加工,這兩部分模塊相互配合,來實(shí)現(xiàn)網(wǎng)絡(luò)應(yīng)用程序的功能。
圖1.2網(wǎng)絡(luò)應(yīng)用程序在網(wǎng)絡(luò)體系結(jié)構(gòu)中的位置
網(wǎng)絡(luò)應(yīng)用程序最終要實(shí)現(xiàn)網(wǎng)絡(luò)資源的共享,共享的基礎(chǔ)就是必須能夠通過網(wǎng)絡(luò)輕松地傳遞各種信息。網(wǎng)絡(luò)編程首先要解決網(wǎng)間進(jìn)程通信的問題。然后才能在通信的基礎(chǔ)上開發(fā)各種應(yīng)用功能。3.實(shí)現(xiàn)網(wǎng)間進(jìn)程通信必須解決的問題網(wǎng)間進(jìn)程通信是指網(wǎng)絡(luò)中不同主機(jī)中的應(yīng)用進(jìn)程之間的相互通信問題,網(wǎng)間進(jìn)程通信必須解決以下問題:(1)網(wǎng)間進(jìn)程的標(biāo)識(shí)問題(2)如何與網(wǎng)絡(luò)協(xié)議棧連接的問題(3)多重協(xié)議的識(shí)別問題(4)不同的通信服務(wù)的問題1.傳輸層在網(wǎng)絡(luò)通信中的地位圖1.3說明了基于TCP/IP協(xié)議棧的進(jìn)程之間的通信的情況。
圖1.3網(wǎng)絡(luò)應(yīng)用程序在網(wǎng)絡(luò)體系結(jié)構(gòu)中的位置
1.1.2因特網(wǎng)中網(wǎng)間進(jìn)程的標(biāo)識(shí)
按照OSI七層協(xié)議的描述,傳輸層與網(wǎng)絡(luò)層在功能上的最大區(qū)別是傳輸層提供進(jìn)程通信的能力。TCP/IP協(xié)議提出了傳輸層協(xié)議端口(protocolport,簡稱端口)的概念,成功地解決了通信進(jìn)程的標(biāo)識(shí)問題。傳輸層是計(jì)算機(jī)網(wǎng)絡(luò)中,通信主機(jī)內(nèi)部進(jìn)行獨(dú)立操作的第一層,是支持端到端的進(jìn)程通信的關(guān)鍵的一層。2.端口的概念
端口是TCP/IP協(xié)議族中,應(yīng)用層進(jìn)程與傳輸層協(xié)議實(shí)體間的通信接口類似于文件描述符,每個(gè)端口都擁有一個(gè)叫作端口號(hào)(portnumber)的整數(shù)型標(biāo)識(shí)符
從實(shí)現(xiàn)的角度講,端口是一種抽象的軟件機(jī)制,包括一些數(shù)據(jù)結(jié)構(gòu)和I/O緩沖區(qū)。
圖1.4UDP與TCP的報(bào)文格式3.端口號(hào)的分配機(jī)制
TCP/IP協(xié)議采用了全局分配(靜態(tài)分配)和本地分配(動(dòng)態(tài)分配)相結(jié)合的分配方法。對(duì)于TCP,或者UDP,將它們的全部65535個(gè)端口號(hào)分為保留端口號(hào)和自由端口號(hào)兩部分。保留端口的范圍是0—1023,又稱為眾所周知的端口或熟知端口(well-knownport),只占少數(shù),采用全局分配或集中控制的方式,由一個(gè)公認(rèn)的中央機(jī)構(gòu)根據(jù)需要進(jìn)行統(tǒng)一分配,靜態(tài)地分配給因特網(wǎng)上著名的眾所周知的服務(wù)器進(jìn)程,并將結(jié)果公布于眾。
表1.1一些典型的應(yīng)用層協(xié)議分配到的保留端口
TCP的保留端口UDP的保留端口FTP21DNS53HTTP80TFTP69SMTP25SNMP161POP3110……
其余的端口號(hào),1024-65535,稱為自由端口號(hào),采用本地分配,又稱為動(dòng)態(tài)分配的方法。
總之,TCP或UDP端口的分配規(guī)則是:端口0:不使用,或者作為特殊的使用;端口1-255:保留給特定的服務(wù),TCP和UDP均規(guī)定,小于256的端口號(hào)才能分配給網(wǎng)上著名的服務(wù);端口256-1023:保留給其他的服務(wù),如路由;端口1024-4999:可以用作任意客戶的端口;端口5000-65535:可以用作用戶的服務(wù)器端口。
我們可以描述一下,在這樣的端口分配機(jī)制下,客戶進(jìn)程C與服務(wù)器進(jìn)程S第一次通信的情景。圖1.5表示了客戶與服務(wù)器第一次通信的情況。圖1.5客戶與服務(wù)器的第一次通信4.進(jìn)程的網(wǎng)絡(luò)地址的概念在因特網(wǎng)絡(luò)中,用一個(gè)三元組可以在全局中唯一地標(biāo)識(shí)一個(gè)應(yīng)用層進(jìn)程:應(yīng)用層進(jìn)程地址=(傳輸層協(xié)議,主機(jī)的IP地址,傳輸層的端口號(hào))這樣一個(gè)三元組,叫做一個(gè)半相關(guān)(half-association),它標(biāo)識(shí)了因特網(wǎng)中,進(jìn)程間通信的一個(gè)端點(diǎn),也把它稱為進(jìn)程的網(wǎng)絡(luò)地址。
5.網(wǎng)絡(luò)中進(jìn)程通信的標(biāo)識(shí)一個(gè)完整的網(wǎng)間通信需要一個(gè)五元組在全局中唯一地來標(biāo)識(shí):(傳輸層協(xié)議,本地機(jī)IP地址,本地機(jī)傳輸層端口,遠(yuǎn)地機(jī)IP地址,遠(yuǎn)地機(jī)傳輸層端口)
這個(gè)五元組稱為一個(gè)全相關(guān)(association)。即兩個(gè)協(xié)議相同的半相關(guān)才能組合成一個(gè)合適的全相關(guān),或完全指定一對(duì)網(wǎng)間通信的進(jìn)程。
1.面向消息的協(xié)議與基于流的協(xié)議(1)面向消息的協(xié)議面向消息的協(xié)議以消息為單位在網(wǎng)上傳送數(shù)據(jù),在發(fā)送端,消息一條一條地發(fā)送,在接收端,也只能一條一條地接收,每一條消息是獨(dú)立的,消息之間存在著邊界。
1.1.3網(wǎng)絡(luò)協(xié)議的特征
圖1.6保護(hù)消息邊界的數(shù)據(jù)報(bào)傳輸服務(wù)
(2)基于流的協(xié)議基于流的協(xié)議不保護(hù)消息邊界,將數(shù)據(jù)當(dāng)作字節(jié)流連續(xù)地傳輸,不管實(shí)際消息邊界是否存在。圖1.7無消息邊界的流傳輸服務(wù)2.面向連接的服務(wù)和無連接的服務(wù)
一個(gè)協(xié)議可以提供面向連接的服務(wù),或者提供無連接的服務(wù)。面向連接服務(wù)是電話系統(tǒng)服務(wù)模式的抽象,即每一次完整的數(shù)據(jù)傳輸都要經(jīng)過建立連接,使用連接,終止連接的過程。無連接服務(wù)是郵政系統(tǒng)服務(wù)的抽象,每個(gè)分組都攜帶完整的目的地址,各分組在系統(tǒng)中獨(dú)立傳送。
3.可靠性和次序性
可靠性保證了發(fā)送端發(fā)出的每個(gè)字節(jié)都能到達(dá)既定的接收端,不出錯(cuò),不丟失,不重復(fù),保證數(shù)據(jù)的完整性,稱為保證投遞。次序性是指對(duì)數(shù)據(jù)到達(dá)接收端的順序進(jìn)行處理。保護(hù)次序性的協(xié)議保證接收端收到數(shù)據(jù)的順序就是數(shù)據(jù)的發(fā)送順序,稱為按序遞交。
1.1.4高效的用戶數(shù)據(jù)報(bào)協(xié)議UDP
傳輸層的用戶數(shù)據(jù)報(bào)協(xié)議(UserDatagramProtocol,UDP)是一種盡力傳送的無連接的不保障可靠的傳輸服務(wù),是一種保護(hù)消息邊界的數(shù)據(jù)的傳輸。
1.1.5可靠的傳輸控制協(xié)議TCP1.可靠性是很多應(yīng)用的基礎(chǔ)
2.TCP為應(yīng)用層提供的服務(wù)
傳輸控制協(xié)議(TransmissionControlProtocol,TCP)應(yīng)用層進(jìn)程提供一個(gè)面向連接的、端到端的、完全可靠的(無差錯(cuò)、無丟失、無重復(fù)或失序)全雙工的流傳輸服務(wù)。
3.TCP利用IP數(shù)據(jù)報(bào)實(shí)現(xiàn)了端對(duì)端的傳輸服務(wù)
TCP被稱作一種端對(duì)端(endtoend)協(xié)議,這是因?yàn)樗峁┮粋€(gè)直接從一臺(tái)計(jì)算機(jī)上的應(yīng)用進(jìn)程到另一遠(yuǎn)程計(jì)算機(jī)上的應(yīng)用進(jìn)程的連接。應(yīng)用進(jìn)程能請(qǐng)求TCP構(gòu)造一個(gè)連接,通過這個(gè)連接發(fā)送和接收數(shù)據(jù),以及關(guān)閉連接。由TCP提供的連接叫做虛連接(virtualconnection),虛連接是由軟件實(shí)現(xiàn)的。事實(shí)上,底層的因特網(wǎng)系統(tǒng)并不對(duì)連接提供硬件或軟件支持,只是兩臺(tái)機(jī)器上的TCP軟件模塊通過交換消息來實(shí)現(xiàn)連接的幻象。
圖1.8TCP是一個(gè)端到端的傳輸協(xié)議
4.三次握手為確保連接的建立和終止都是可靠的,TCP使用三次握手(3-wayhandshake)的方式來建立連接,圖1.9TCP的三次握手過程
1.2.1基于TCP/IP協(xié)議棧的網(wǎng)絡(luò)編程基于TCP/IP協(xié)議棧的網(wǎng)絡(luò)編程是最基本的網(wǎng)絡(luò)編程方式,主要是使用各種編程語言,利用操作系統(tǒng)提供的套接字網(wǎng)絡(luò)編程接口,直接開發(fā)各種網(wǎng)絡(luò)應(yīng)用程序。本書主要講解這種網(wǎng)絡(luò)編程的相關(guān)技術(shù)。1.2三類網(wǎng)絡(luò)編程
1.2.2基于WWW應(yīng)用的網(wǎng)絡(luò)編程
WWW應(yīng)用是因特網(wǎng)上最廣泛的應(yīng)用,稱為萬維網(wǎng)基于WWW應(yīng)用的網(wǎng)絡(luò)編程技術(shù),包括所見即所得的網(wǎng)頁制作工具,和動(dòng)態(tài)服務(wù)器頁面的制作技術(shù)。
1.2.3基于.NET框架的WebServices網(wǎng)絡(luò)編程1.關(guān)于.NET平臺(tái)微軟公司在2000年7月公布的.NET平臺(tái)是一個(gè)全新的開發(fā)框架,集成了微軟二十世紀(jì)九十年代后期的許多技術(shù),(1).NET平臺(tái)有四組產(chǎn)品①開發(fā)工具:②專用服務(wù)器③Web服務(wù)。④設(shè)備:(2)MS.NET順應(yīng)了軟件工業(yè)的趨勢,包括以下幾個(gè)方面:①分布式計(jì)算:②組件化:③企業(yè)級(jí)別的服務(wù):④WEB范型轉(zhuǎn)移:(3).NET平臺(tái)由三層軟件構(gòu)成①頂層是全新的開發(fā)工具VS.NET,②中間層包括三部分:.NET服務(wù)器、.NET服務(wù)構(gòu)件和.NET框架。③底層是WINDOWS操作系統(tǒng)。(4).NET框架的設(shè)計(jì)要支持的目標(biāo)①簡化組件的使用:②實(shí)現(xiàn)語言的集成:③支持Internet的互操作:④簡化軟件的開發(fā):⑤簡化組件的部署:⑥提高可靠性:⑦提高安全性:
2.關(guān)于Web服務(wù)什么是Web服務(wù)?Web服務(wù)是松散耦合的可復(fù)用的軟件模塊,在Internet上發(fā)布后,能通過標(biāo)準(zhǔn)的Internet協(xié)議在程序中訪問,具有以下的特點(diǎn):(1)可復(fù)用:(2)松散耦合:(3)封裝了離散:(4)Web服務(wù)可以在程序中訪問(5)Web服務(wù)在Internet上發(fā)布
本節(jié)著重于因特網(wǎng)上的高級(jí)服務(wù),以及提供這些服務(wù)的應(yīng)用軟件。討論網(wǎng)絡(luò)應(yīng)用軟件的客戶/服務(wù)器交互模式,并說明網(wǎng)絡(luò)協(xié)議操作的方式為什么需要這種模式。這是構(gòu)筑所有網(wǎng)絡(luò)應(yīng)用的基礎(chǔ)。1.3客戶/服務(wù)器交互模式
1.3.1網(wǎng)絡(luò)應(yīng)用軟件的地位和功能
因特網(wǎng)僅僅提供一個(gè)通用的通信構(gòu)架,它只負(fù)責(zé)傳送信息,而對(duì)于信息傳過去干什么用,利用因特網(wǎng)究竟提供什么服務(wù),由哪些計(jì)算機(jī)來運(yùn)行這些服務(wù),如何確定服務(wù)的存在,如何使用這些服務(wù)等等問題,都要由應(yīng)用軟件和用戶解決。
網(wǎng)絡(luò)應(yīng)用進(jìn)程通信時(shí),普遍采用客戶/服務(wù)器交互模式(client-serverparadigmofinteraction),簡稱C/S模式。這是因特網(wǎng)上應(yīng)用程序最常用的通信模式。
1.3.2客戶/服務(wù)器模式
C/S模式過程中服務(wù)器處于被動(dòng)服務(wù)的地位。首先服務(wù)器方要先啟動(dòng),并根據(jù)客戶請(qǐng)求提供相應(yīng)服務(wù),服務(wù)器的工作過程是:(1)打開一通信通道,并告知服務(wù)器所在的主機(jī),它愿意在某一公認(rèn)的地址上(熟知知端口,如FTP為21)接收客戶請(qǐng)求。(2)等待客戶的請(qǐng)求到達(dá)該端口。(3)服務(wù)器接收到服務(wù)請(qǐng)求,處理該請(qǐng)求并發(fā)送應(yīng)答信號(hào)。為了能并發(fā)地接收多個(gè)客戶的服務(wù)請(qǐng)求,要激活一個(gè)新進(jìn)程或新線程來處理這個(gè)客戶請(qǐng)求(如UNIX系統(tǒng)中用fork、exec)。服務(wù)完成后,關(guān)閉此新進(jìn)程與客戶的通信鏈路,并終止。(4)返回第二步,等待并處理另一客戶請(qǐng)求。(5)在特定的情況下,關(guān)閉服務(wù)器。
客戶方采取的是主動(dòng)請(qǐng)求方式,其工作過程是:(1)打開一通信通道,并連接到服務(wù)器所在主機(jī)的特定監(jiān)聽端口。(2)向服務(wù)器發(fā)送請(qǐng)求報(bào)文,等待并接收應(yīng)答;繼續(xù)提出請(qǐng)求,與服務(wù)器的會(huì)話按照應(yīng)用協(xié)議進(jìn)行。(3)請(qǐng)求結(jié)束后,關(guān)閉通信通道并終止。表1.2一些著名的網(wǎng)絡(luò)應(yīng)用網(wǎng)絡(luò)應(yīng)用客戶端軟件服務(wù)器軟件應(yīng)用層協(xié)議電子郵件foxmail電子郵件服務(wù)器SMTP、Pop3文件傳輸cutFTP文件傳輸服務(wù)器FTPWWW瀏覽IE瀏覽器IIS服務(wù)器HTTP
客戶軟件和服務(wù)器軟件通常還具有以下一些主要特點(diǎn):
1.客戶軟件(1)在進(jìn)行網(wǎng)絡(luò)通信時(shí)臨時(shí)成為客戶,但它也可在本地進(jìn)行其他的計(jì)算。(2)被用戶調(diào)用,只為一個(gè)會(huì)話運(yùn)行。在打算通信時(shí)主動(dòng)向遠(yuǎn)地服務(wù)器發(fā)起通信。1.3.3客戶與服務(wù)器的特性
(3)能訪問所需的多種服務(wù),但在某一時(shí)刻只能與一個(gè)遠(yuǎn)程服務(wù)器進(jìn)行主動(dòng)通信。(4)主動(dòng)地啟動(dòng)與服務(wù)器的通信。(5)在用戶的計(jì)算機(jī)上運(yùn)行,不需要特殊的硬件和很復(fù)雜的操作系統(tǒng)。
2.服務(wù)器軟件(1)是一種專門用來提供某種服務(wù)的程序,可同時(shí)處理多個(gè)遠(yuǎn)地客戶的請(qǐng)求。(2)當(dāng)系統(tǒng)啟動(dòng)時(shí)即自動(dòng)調(diào)用,并且連續(xù)運(yùn)行著,不斷地為多個(gè)會(huì)話服務(wù)。(3)接受來自任何客戶的通信請(qǐng)求,但只提供一種服務(wù)。(4)被動(dòng)地等待并接受來自多個(gè)遠(yuǎn)端客戶的通信請(qǐng)求。(5)在共享計(jì)算機(jī)上運(yùn)行,一般需要強(qiáng)大的硬件和高級(jí)的操作系統(tǒng)支持。(1)客戶和服務(wù)器都是軟件進(jìn)程,C/S模式是網(wǎng)絡(luò)上通過進(jìn)程通信建立分布式應(yīng)用的常用模型。(2)非對(duì)稱性:服務(wù)器通過網(wǎng)絡(luò)提供服務(wù),客戶通過網(wǎng)絡(luò)使用服務(wù),這種不對(duì)稱性體現(xiàn)在軟件結(jié)構(gòu)和工作過程上。3.基于因特網(wǎng)的C/S模式的應(yīng)用程序的特點(diǎn)
(3)對(duì)等性:客戶和服務(wù)器必有一套共識(shí)的約定,必與以某種應(yīng)用層協(xié)議相聯(lián),并且協(xié)議必須在通信的兩端實(shí)現(xiàn)。比如瀏覽器和3W服務(wù)器就都基于HTTP超文本傳輸協(xié)議。(4)服務(wù)器的被動(dòng)性:服務(wù)器必須先行啟動(dòng),時(shí)刻監(jiān)聽,日夜值守,及時(shí)服務(wù),只要有客戶請(qǐng)求,就立即處理并響應(yīng),回傳信息。但決不主動(dòng)提供服務(wù)。(5)客戶機(jī)的主動(dòng)性:客戶機(jī)可以隨時(shí)提出請(qǐng)求,通過網(wǎng)絡(luò)得到服務(wù),也可以關(guān)機(jī)走人,一次請(qǐng)求與服務(wù)的過程是由客戶機(jī)首先激發(fā)的。(6)一對(duì)多:一個(gè)服務(wù)器可以為多個(gè)客戶機(jī)服務(wù),客戶機(jī)也可以打開多個(gè)窗口,連接多個(gè)服務(wù)器。(7)分布性與共享性:資源在服務(wù)器端組織與存儲(chǔ),通過網(wǎng)絡(luò)分散在多個(gè)客戶端使用。1.服務(wù)器程序與服務(wù)器類計(jì)算機(jī)服務(wù)器(server)這個(gè)術(shù)語來指那些運(yùn)行著的服務(wù)程序。服務(wù)器類計(jì)算機(jī)(server-classcomputer)這一術(shù)語來稱呼那些運(yùn)行服務(wù)器軟件的強(qiáng)大的計(jì)算機(jī)。1.3.4容易混淆的術(shù)語
2.客戶與用戶“客戶”(client)和服務(wù)器都指的是應(yīng)用進(jìn)程,即計(jì)算機(jī)軟件?!坝脩簟?user)指的是使用計(jì)算機(jī)的人。
圖1.10說明了這些概念的區(qū)別。
圖1.10用戶、客戶、服務(wù)器、服務(wù)器類計(jì)算機(jī)
客戶與服務(wù)器的通信過程一般是這樣的:(1)通信之前,服務(wù)器應(yīng)先行啟動(dòng),并通知它的下層協(xié)議棧做好接收客戶請(qǐng)求的準(zhǔn)備,然后被動(dòng)地等待客戶的通信請(qǐng)求,稱服務(wù)器處于監(jiān)聽狀態(tài)。1.3.5客戶與服務(wù)器的通信過程
(2)一般是先由客戶向服務(wù)器發(fā)送請(qǐng)求,服務(wù)器向客戶返回應(yīng)答??蛻綦S時(shí)可以主動(dòng)啟動(dòng)通信,向服務(wù)器發(fā)出連接請(qǐng)求,服務(wù)器接收這個(gè)請(qǐng)求,建立了二者的通信關(guān)系。(3)客戶與服務(wù)器的通信關(guān)系一旦建立,客戶和服務(wù)器都可發(fā)送和接收信息。信息在客戶與服務(wù)器之間可以沿任一方向或兩個(gè)方向傳遞。在某些情況下,客戶向服務(wù)器發(fā)送一系列請(qǐng)求,服務(wù)器相應(yīng)地返回一系列應(yīng)答。
客戶與服務(wù)器作為兩個(gè)軟件實(shí)體,它們之間的通信是虛擬的,是概念上的,實(shí)際的通信要借助下層的網(wǎng)絡(luò)協(xié)議棧來進(jìn)行。1.3.6網(wǎng)絡(luò)協(xié)議與C/S模式的關(guān)系
1.3.7錯(cuò)綜復(fù)雜的客戶/服務(wù)器交互
在C/S模式中,存在著三種一個(gè)與多個(gè)的關(guān)系:
(1)一個(gè)服務(wù)器同時(shí)為多個(gè)客戶服務(wù):(2)一個(gè)用戶的計(jì)算機(jī)上同時(shí)運(yùn)行多個(gè)連接不同服務(wù)器的客戶(3)一個(gè)服務(wù)器類的計(jì)算機(jī)同時(shí)運(yùn)行多個(gè)服務(wù)器:圖1.11一臺(tái)計(jì)算機(jī)中的多個(gè)服務(wù)器被多個(gè)計(jì)算機(jī)的客戶訪問
并發(fā)性是客戶/服務(wù)器交互模式的基礎(chǔ),并發(fā)允許多個(gè)客戶獲得同一種服務(wù),而不必等待服務(wù)器完成對(duì)上一個(gè)請(qǐng)求的處理。這樣才能很好地同時(shí)為多個(gè)客戶提供服務(wù)。
圖1.12服務(wù)器創(chuàng)建多個(gè)線程來為多個(gè)客戶服務(wù)
1.3.8服務(wù)器如何同時(shí)為多個(gè)客戶服務(wù)
在一臺(tái)服務(wù)器類的計(jì)算機(jī)中可以并發(fā)地運(yùn)行多個(gè)服務(wù)器進(jìn)程。它們都要借助協(xié)議棧來交換信息,協(xié)議棧就是多個(gè)服務(wù)器進(jìn)程傳輸數(shù)據(jù)的公用通道,這有了一個(gè)問題,既然在一個(gè)服務(wù)器類計(jì)算機(jī)中運(yùn)行著多個(gè)服務(wù)器,如何能讓客戶無二義性地指明所希望的服務(wù)?
1.3.9標(biāo)識(shí)一個(gè)特定服務(wù)圖1.13沙漏計(jì)時(shí)器形狀的TCP/IP協(xié)議族
2.1.1問題的提出站在應(yīng)用程序?qū)崿F(xiàn)的角度,應(yīng)用程序如何方便地使用協(xié)議棧軟件進(jìn)行通信呢?如果能在應(yīng)用程序與協(xié)議棧軟件之間提供一個(gè)軟件接口,就可以方便客戶與服務(wù)器軟件的編程。2.1UNIX套接字網(wǎng)絡(luò)編程接口的產(chǎn)生與發(fā)展
套接字應(yīng)用程序編程接口是網(wǎng)絡(luò)應(yīng)用程序通過網(wǎng)絡(luò)協(xié)議棧進(jìn)行通信時(shí)所使用的接口,即應(yīng)用程序與協(xié)議棧軟件之間的接口,簡稱套接字編程接口(SocketAPI)。它定義了應(yīng)用程序與協(xié)議棧軟件進(jìn)行交互時(shí)可以使用的一組操作,決定了應(yīng)用程序使用協(xié)議棧的方式、應(yīng)用程序所能實(shí)現(xiàn)的功能、以及開發(fā)具有這些功能的程序的難度。
加州大學(xué)伯克利(Berkley)分校開發(fā)并推廣了一個(gè)包括TCP/IP互聯(lián)協(xié)議的UNIX,稱為BSDUNIX(BerkeleySoftwareDistributionUNIX)操作系統(tǒng),套接字編程接口是這個(gè)操作系統(tǒng)的一個(gè)部分。后來的許多操作系統(tǒng)并沒有另外搞一套其它的編程接口,而是選擇了對(duì)于套接字編程接口的支持。由于這個(gè)套接字規(guī)范最早是由Berkeley大學(xué)開發(fā)的,一般將它稱為BerkeleySockets規(guī)范。2.1.2套接字編程接口的起源與應(yīng)用
要想實(shí)現(xiàn)套接字編程接口,可以采用兩種實(shí)現(xiàn)方式,一種是在操作系統(tǒng)的內(nèi)核中增加相應(yīng)的軟件來實(shí)現(xiàn),一種是通過開發(fā)操作系統(tǒng)之外的函數(shù)庫來實(shí)現(xiàn)。
2.1.3套接字編程接口的兩種實(shí)現(xiàn)方式
UNIX操作系統(tǒng)對(duì)文件和所有其它的輸入/輸出設(shè)備采用一種統(tǒng)一的的操作模式,就是“打開-讀-寫-關(guān)閉”(open-read-write-close)的I/O模式。當(dāng)TCP/IP協(xié)議被集成到UNIX內(nèi)核中的時(shí)候,相當(dāng)于在UNIX系統(tǒng)中引入了一種新型的I/O操作,就是應(yīng)用程序通過網(wǎng)絡(luò)協(xié)議棧來交換數(shù)據(jù)。
2.1.4套接字通信與UNIX操作系統(tǒng)的輸入/輸出
在UNIX系統(tǒng)的實(shí)現(xiàn)中,套接字是完全與其他I/O集成在一起的。操作系統(tǒng)和應(yīng)用程序都將套接字編程接口也看作一種輸入/輸出機(jī)制。但是,用戶進(jìn)程與網(wǎng)絡(luò)協(xié)議的交互作用實(shí)際要比用戶進(jìn)程與傳統(tǒng)的I/O設(shè)備相互作用要復(fù)雜得多。
其次,使用套接字的應(yīng)用程序必須說明許多細(xì)節(jié)。僅僅提供open、read、write、close四個(gè)過程遠(yuǎn)遠(yuǎn)不夠。為避免單個(gè)套接字函數(shù)參數(shù)過多,套接字編程接口的設(shè)計(jì)者定義了多個(gè)函數(shù)。2.2套接字編程的基本概念
套接口是對(duì)網(wǎng)絡(luò)中不同主機(jī)上應(yīng)用進(jìn)程之間進(jìn)行雙向通信的端點(diǎn)的抽象,一個(gè)套接口就是網(wǎng)絡(luò)上進(jìn)程通信的一端,提供了應(yīng)用層進(jìn)程利用網(wǎng)絡(luò)協(xié)議棧交換數(shù)據(jù)的機(jī)制。
圖2.1電插座與電話插座的作用2.2.1什么是套接字(SOCKET)
我們應(yīng)當(dāng)從多個(gè)層面來理解套接字這個(gè)概念的內(nèi)涵。從套接字所處的地位來講,套接字上聯(lián)應(yīng)用進(jìn)程,下聯(lián)網(wǎng)絡(luò)協(xié)議棧,是應(yīng)用程序通過網(wǎng)絡(luò)協(xié)議棧進(jìn)行通信的接口,是應(yīng)用程序與網(wǎng)絡(luò)協(xié)議棧進(jìn)行交互的接口。圖2.2應(yīng)用進(jìn)程、套接口、網(wǎng)絡(luò)協(xié)議棧及操作系統(tǒng)的關(guān)系
從實(shí)現(xiàn)的角度來講,非常復(fù)雜。套接字是一個(gè)復(fù)雜的軟件機(jī)構(gòu),包含了一定的數(shù)據(jù)結(jié)構(gòu),包含許多選項(xiàng),由操作系統(tǒng)內(nèi)核管理。從使用的角度來講,非常簡單。對(duì)于套接字的操作形成了一種網(wǎng)絡(luò)應(yīng)用程序的編程接口(API)。本書把這一套操作套接字的編程接口函數(shù)稱作套接字編程接口,套接字是它的操作對(duì)象。總之,套接字是網(wǎng)絡(luò)通信的基石。
2.2.2套接字的特點(diǎn)1.通信域
接字存在于通信域中,通信域是為了處理一般的進(jìn)程通過套接字通信而引入的一種抽象概念,套接字通常只和同一域中的套接字交換數(shù)據(jù)。如果數(shù)據(jù)交換要穿越域的邊界,就一定要執(zhí)行某種解釋程序?,F(xiàn)在,僅僅針對(duì)Internet域,并且使用Internet協(xié)議族(即TCP/IP協(xié)議族)來通信。2.套接字具有三種類型每一個(gè)正被使用的套接字都有它確定的類型,只有相同類型的套接字才能相互通信。(1)數(shù)據(jù)報(bào)套接字(DatagramSOCKET)數(shù)據(jù)報(bào)套接字提供無連接的不保證可靠的獨(dú)立的數(shù)據(jù)報(bào)傳輸服務(wù)。在Internet通信域中,數(shù)據(jù)報(bào)套接字使用UDP數(shù)據(jù)報(bào)協(xié)議形成的進(jìn)程間通路,具有UDP協(xié)議為上層所提供的服務(wù)的所有特點(diǎn)。圖2.3在Internet通信域中,數(shù)據(jù)報(bào)套接字基于UDP協(xié)議
(2)流式套接字(StreamSOCKET)流式套接字提供雙向的、有序的、無重復(fù)的、無記錄邊界的可靠的數(shù)據(jù)流傳輸服務(wù)。在Internet通信域中,流式套接字使用TCP協(xié)議形成的進(jìn)程間通路,具有TCP協(xié)議為上層所提供的服務(wù)的所有特點(diǎn),在使用流式套接字傳輸數(shù)據(jù)之前,必須在數(shù)據(jù)的發(fā)送端和接收端之間建立連接,如圖2.4所示。
圖2.4在Internet通信域中,流式套接字基于TCP協(xié)議(3)原始式套接字(RAWSOCKET)
原始式套接字允許對(duì)較低層次的協(xié)議,如IP、ICMP直接訪問,用于檢驗(yàn)新的協(xié)議的實(shí)現(xiàn)。3.套接字由應(yīng)用層的通信進(jìn)程創(chuàng)建,并為其服務(wù)就是說,每一個(gè)套接字都有一個(gè)相關(guān)的應(yīng)用進(jìn)程,操作該套接字的代碼是該進(jìn)程的組成部分。4.使用確定的IP地址和傳輸層端口號(hào)
往往在生成套接字的描述符后,要將套接字與計(jì)算機(jī)上的特定的IP地址和傳輸層端口號(hào)相關(guān)聯(lián),這個(gè)過程稱為綁定。一個(gè)套接口要使用一個(gè)確定的三元組網(wǎng)絡(luò)地址信息,才能使它在網(wǎng)絡(luò)中唯一地被標(biāo)識(shí)。(1)不管是采用對(duì)等模式或者客戶機(jī)/服務(wù)器模式,通信雙方的應(yīng)用程序都需要開發(fā)。(2)雙方所交換數(shù)據(jù)的結(jié)構(gòu)和交換數(shù)據(jù)的順序有特定的要求,不符合現(xiàn)在成熟的應(yīng)用層協(xié)議,甚至需要自己去開發(fā)應(yīng)用層協(xié)議,自己設(shè)計(jì)最適合的數(shù)據(jù)結(jié)構(gòu)和信息交換規(guī)程。
2.2.3套接字的應(yīng)用場合2.2.4套接字使用的數(shù)據(jù)類型和相關(guān)的問題1.三種表示套接字地址的結(jié)構(gòu)在套接字編程接口中,專門定義了三種結(jié)構(gòu)型的數(shù)據(jù)類型,用來存儲(chǔ)協(xié)議相關(guān)的網(wǎng)絡(luò)地址,在套接字編程接口的函數(shù)調(diào)用中要用到它們。(1)sockaddr結(jié)構(gòu),針對(duì)各種通信域的套接字,存儲(chǔ)它們的地址信息。structsockaddr{unsignedshortsa_family;//地址家族charsa_data;//協(xié)議地址}
(2)sockaddr_in結(jié)構(gòu),專門針對(duì)Internet通信域,存儲(chǔ)套接字相關(guān)的網(wǎng)絡(luò)地址信息,例如IP地址,傳輸層端口號(hào)等信息。structsockaddr_in{shortintsin_family;//地址家族unsignedshortintsin_port;//端口號(hào)structin_addrsin_addr;//IP地址unsignedcharsin_zero[8];//全為0}
(3)in_addr結(jié)構(gòu),專門用來存儲(chǔ)IP地址。Structin_addr{Unsignedlongs_addrl;}(4)這些數(shù)據(jù)結(jié)構(gòu)的一般用法:首先,定義一個(gè)Sockaddr_in的結(jié)構(gòu)實(shí)例,并將它清零。比如:structsockaddr_inmyad;memset(&myad,0,sizeof(structsockaddr_in));
然后,為這個(gè)結(jié)構(gòu)賦值,比如:myad.sin_family=AF_INET;myad.sin_port=htons(8080);myad.sin_addr.s_addr=htonl(INADDR-ANY);第三步:在函數(shù)調(diào)用中使用時(shí),將這個(gè)結(jié)構(gòu)強(qiáng)制轉(zhuǎn)換為sockaddr類型。如:accept(listenfd,(sockaddr*)(&myad),&addrlen);
2.本機(jī)字節(jié)順序和網(wǎng)絡(luò)字節(jié)順序在具體計(jì)算機(jī)中的多字節(jié)數(shù)據(jù)的存儲(chǔ)順序,稱為本機(jī)字節(jié)順序。多字節(jié)數(shù)據(jù)在網(wǎng)絡(luò)協(xié)議報(bào)頭中的存儲(chǔ)順序,稱為網(wǎng)絡(luò)字節(jié)順序。
網(wǎng)絡(luò)應(yīng)用程序要在不同的計(jì)算機(jī)中運(yùn)行,本機(jī)字節(jié)順序是不同的,但是,網(wǎng)絡(luò)字節(jié)順序是一定的。所以,應(yīng)用程序在編程的時(shí)候,在把IP地址和端口號(hào)裝入套接字的時(shí)候,應(yīng)當(dāng)把它們從本機(jī)字節(jié)順序轉(zhuǎn)換為網(wǎng)絡(luò)字節(jié)順序;相反,在本機(jī)輸出時(shí),應(yīng)將它們從網(wǎng)絡(luò)字節(jié)順序轉(zhuǎn)換為本機(jī)字節(jié)順序。
套接字編程接口特為解決這個(gè)問題設(shè)置了四個(gè)函數(shù):htons()短整數(shù)本機(jī)順序轉(zhuǎn)換為網(wǎng)絡(luò)順序,用于端口號(hào)。htonl()長整數(shù)本機(jī)順序轉(zhuǎn)換為網(wǎng)絡(luò)順序,用于IP地址。ntohs()短整數(shù)網(wǎng)絡(luò)順序轉(zhuǎn)換為本機(jī)順序,用于端口號(hào)。ntohl()長整數(shù)網(wǎng)絡(luò)順序轉(zhuǎn)化為本機(jī)順序,用于IP地址。這四個(gè)函數(shù)將被轉(zhuǎn)換的數(shù)值作為函數(shù)的參數(shù),函數(shù)返回值是轉(zhuǎn)換后的結(jié)果。
3.點(diǎn)分十進(jìn)制的IP地址的轉(zhuǎn)換在因特網(wǎng)中,IP地址常常用點(diǎn)分十進(jìn)制的表示方法,但在套接字中,IP地址是無符號(hào)的長整型數(shù),套接字編程接口設(shè)置了兩個(gè)函數(shù),專門用于兩種形式的IP地址的轉(zhuǎn)換。(1)inet-addr函數(shù)
unsignedlonginet-addr(constchar*cp)入口參數(shù)cp:點(diǎn)分十進(jìn)制形式的IP地址。返回值:網(wǎng)絡(luò)字節(jié)順序的IP地址,是無符號(hào)的長整數(shù),(2)inet_ntoa函數(shù)char*inet_ntoa(structin_addrin)入口參數(shù)in:包含長整型IP地址的in_addr結(jié)構(gòu)變量,返回值:指向點(diǎn)分十進(jìn)制IP地址的字符串的指針。
通常,我們使用域名來標(biāo)識(shí)站點(diǎn),可以將文字型的主機(jī)域名直接轉(zhuǎn)換成IP地址,structhostent*gethodtbyname(constchar*name);入口參數(shù):是站點(diǎn)的主機(jī)域名字符串,返回值:是指向hostent結(jié)構(gòu)的指針,hostent結(jié)構(gòu)包含主機(jī)名,主機(jī)別名數(shù)組,返回地址的類型(一般是AF-INET),地址長度的字節(jié)數(shù),已符合網(wǎng)絡(luò)字節(jié)順序的主機(jī)網(wǎng)絡(luò)地址等。
4.域名服務(wù)
2.3面向連接的套接字編程2.3.1套接字的工作過程2.3.2UNIX套接字編程接口的系統(tǒng)調(diào)用1.創(chuàng)建套接字SOCKET()SOCKET過程創(chuàng)建一個(gè)套接字并返回一個(gè)整型描述符:intSOCKET(intProtofamily,intType,intProtocol);
2.綁定套接字到指定的地址BIND()
intBIND(intSockfd,structsockaddr*My_addr,intAddrlen);
3.啟動(dòng)監(jiān)聽Listen()
intLISTEN(intSockfd,intQueuesize);舉例:LISTEN(Sockfe,10);
圖2.6監(jiān)聽套接字使用緩沖區(qū)接納多個(gè)客戶端的連接請(qǐng)求
4.接收連接請(qǐng)求ACCEPT()intACCEPT(intSockfd,structsockaddr*Addr,int*addrlen);舉例:intclientfd;//定義響應(yīng)套接字描述符變量intaddrler=sizeof(sockaddr);//獲得套接字地址結(jié)構(gòu)長度。structsockaddr_incltsockaddr;//定義用于返回客戶端地址的結(jié)構(gòu)。clientfd=ACCEPT(listenfd,(sockaddr*)(&cltsockaddr),&addrlen);//接收客戶連接請(qǐng)求
5.請(qǐng)求建立連接CONNECT()intCONNECT(intSockfd,structsockaddr*Service_addr,intAddrlen);舉例:if(CONNECT(sockfd,(structsockaddr*)(&serv_addr),sizeof(structsockaddr))<0){報(bào)錯(cuò),并退出}
6.讀/寫套接字READ()和WRITE()intREAD(intsockfd,void*buffer,intlen);intWRITE(intsockfd,void*buffer,intlen)
7.向套接字發(fā)送SEND()和從套接字接收RECV()
intSEND(intsockfd,char*buf,intlen,intflags);intRECV(intsockfd,char*buf,intlen,intflags);
8.關(guān)閉套接字CLOSE()
intCLOSE(intsockfd);2.3.3面向連接的套接字編程實(shí)例1.實(shí)例的功能服務(wù)器對(duì)來訪的客戶計(jì)數(shù),并向客戶報(bào)告這個(gè)計(jì)數(shù)值??蛻艚⑴c服務(wù)器的一個(gè)連接并等待它的輸出。每當(dāng)連接請(qǐng)求到達(dá)時(shí),服務(wù)器生成一個(gè)可打印的ASCII串信息,將它在連接上發(fā)回,然后關(guān)閉連接。客戶顯示收到的信息,然后退出。
例如,對(duì)于服務(wù)器接收的第10次客戶連接請(qǐng)求,該客戶將收到并打印如下信息:Thisserverhasbeencontacted10times.2.實(shí)例程序的命令行參數(shù)實(shí)例是UNIX環(huán)境下的C程序,客戶和服務(wù)器程序在編譯后,均以命令行的方式執(zhí)行。服務(wù)器程序執(zhí)行時(shí)可以帶一個(gè)命令行參數(shù),是用來接受請(qǐng)求的監(jiān)聽套接字的協(xié)議端口號(hào)。這個(gè)參數(shù)是可選的。如果不指定端口號(hào),代碼將使用程序內(nèi)定的缺省端口號(hào)5188。
客戶程序執(zhí)行時(shí)可以帶兩個(gè)命令行參數(shù):一個(gè)是服務(wù)器所在計(jì)算機(jī)的主機(jī)名,另一個(gè)是服務(wù)器監(jiān)聽的協(xié)議端口號(hào)。這兩個(gè)參數(shù)都是可選的。如果沒有指定協(xié)議端口號(hào),客戶使用程序內(nèi)定的缺省值5188。如果一個(gè)參數(shù)也沒有,客戶使用缺省端口和主機(jī)名localhost,localhost是映射到客戶所運(yùn)行的計(jì)算機(jī)的一個(gè)別名。允許客戶與本地機(jī)上的服務(wù)器通信,對(duì)調(diào)試是很有用的。3.客戶程序代碼/**程序:client.c*目的:創(chuàng)建一個(gè)套接字,通過網(wǎng)絡(luò)連接一個(gè)服務(wù)器,并打印來自服務(wù)器的信息*語法:client[host[port]]*host-運(yùn)行服務(wù)器的計(jì)算機(jī)的名字*port-服務(wù)器監(jiān)聽套接字所用協(xié)議端口號(hào)*注意:兩個(gè)參數(shù)都是可選的。如果未指定主機(jī)名,客戶使用localhost;如果未指定端口號(hào),*客戶將使用PROTOPORT中給定的缺省協(xié)議端口號(hào)**/
#include<sys/types.h>#include<sys/socket.h>/*UNIX下,套接字的相關(guān)包含文件。*/#include<netinet/in.h>#include<arpa/inet.h>#include<netdb.h>#include<stdio.h>#include<string.h>
#definePROTOPORT5188/*缺省協(xié)議端口號(hào)*/externinterrno;charlocalhost=“l(fā)ocalhost”;/*缺省主機(jī)名*/
main(argc,argv)intargc;char*argv[];{structhostent*ptrh;/*指向主機(jī)列表中一個(gè)條目的指針*/structsockaddr_inservaddr;/*存放服務(wù)器端網(wǎng)絡(luò)地址的結(jié)構(gòu)*/int sockfd;/*客戶端的套接字描述符*/int port;/*服務(wù)器端套接字協(xié)議端口號(hào)*/char*host;/*服務(wù)器主機(jī)名指針*/intn;/*讀取的字符數(shù)*/charbuf[1000];/*緩沖區(qū),接收服務(wù)器發(fā)來的數(shù)據(jù)*/
memset((char*)&servaddr,0,sizeof(servaddr));/*清空sockaddr結(jié)構(gòu)*/servaddr.sin_family=AF_INET;/*設(shè)置為因特網(wǎng)協(xié)議族*/
/*檢查命令行參數(shù),如果有,就抽取端口號(hào)。否則使用內(nèi)定的缺省值*/if(argc>2){port=atoi(argv[2]);/*如果指定了協(xié)議端口,就轉(zhuǎn)換成整數(shù)*/}else{port=PROTOPORT;/*否則,使用缺省端口號(hào)*/}if(port>0)/*如果端口號(hào)是合法的數(shù)值,就將它裝入網(wǎng)絡(luò)地址結(jié)構(gòu)*/
servaddr.sin_port=htons((u_short)port);else{/*否則,打印錯(cuò)誤信息并退出*/fprintf(stderr,”badportnumber%s\n”,argv[2]);exit(1);}
/*檢查主機(jī)參數(shù)并指定主機(jī)名*/if(argc>1){host=argv[1];/*如果指定了主機(jī)名參數(shù),就使用它*/}else{host=localhost;/*否則,使用缺省值*/}
/*將主機(jī)名轉(zhuǎn)換成相應(yīng)的IP地址并復(fù)制到servaddr結(jié)構(gòu)中*/ptrh=gethostbyname(host);/*從服務(wù)器主機(jī)名得到相應(yīng)的IP地址*/if((char*)ptrh==null){/*檢查主機(jī)名的有效性,無效則退出*/fprintf(stderr,”invalidhost:%s\n”,host);exit(1);}memcpy(&servaddr.sin_addr,ptrh->h_addr,ptrh->h_length);
/*創(chuàng)建一個(gè)套接字*/sockfd=SOCKET(AF_INET,SOCK_STREAM,0);if(sockfd<0){fprintf(stderr,”socketcreationfailed\n”);exit(1);}
/*請(qǐng)求連接到服務(wù)器*/if(connect(sockfd,(structsockaddr*)&servaddr,sizeof(servaddr))<0){fprintf(stderr,”connectfailed\n”);/*連接請(qǐng)求被拒絕,報(bào)錯(cuò)并退出*/exit(1);}
/*從套接字反復(fù)讀數(shù)據(jù),并輸出到用戶屏幕上*/n=recv(sockfd,buf,sizeof(buf),0);while(n>0){write(1,buf,n);n=recv(sockfd,buf,sizeof(buf),0);}
/*關(guān)閉套接字*/closesocket(sockfd);
/*終止客戶程序*/exit(0);}
4.服務(wù)器實(shí)例代碼/**程序:server.c*目的:分配一個(gè)套接字,然后反復(fù)執(zhí)行如下幾步:*(1)等待客戶的下一個(gè)連接*(2)發(fā)送一個(gè)短消息給客戶*(3)關(guān)閉與客戶的連接*(4)轉(zhuǎn)向(1)步*命令行語法:server[port]*port–
服務(wù)器端監(jiān)聽套接字使用的協(xié)議端口號(hào)*注意:端口號(hào)可選。如果未指定端口號(hào),服務(wù)器使用PROTOPORT中指定的缺省*端口號(hào)**/
#include<sys/types.h>#include<sys/socket.h>#include<netinet/in.h>#include<netdb.h>#include<stdio.h>#include<string.h>#definePROTOPORT5188/*監(jiān)聽套接字的缺省協(xié)議端口號(hào)*/#define QLEN6/*監(jiān)聽套接字的請(qǐng)求隊(duì)列大小*/intvisits=0;/*對(duì)于客戶連接的計(jì)數(shù)*/
main(argc,argc)intargc;char*argv[];{structhostent*ptrh;/*指向主機(jī)列表中一個(gè)條目的指針*/structsockaddr_inservaddr;/*存放服務(wù)器網(wǎng)絡(luò)地址的結(jié)構(gòu)*/structsockaddr_inclientaddr;/*存放客戶網(wǎng)絡(luò)地址的結(jié)構(gòu)*/intlistenfd; /*監(jiān)聽套接字描述符*/intclientfd; /*響應(yīng)套接字描述符*/intport;/*協(xié)議端口號(hào)*/intalen;/*地址長度*/charbuf[1000];/*供服務(wù)器發(fā)送字符串所用的緩沖區(qū)*/
memset((char*)&servaddr,0,sizeof(servaddr));/*清空sockaddr結(jié)構(gòu)*/servaddr.sin_family=AF_INET;/*設(shè)置為因特網(wǎng)協(xié)議族*/servaddr.sin_addr.s_addr=INADDR_ANY;/*設(shè)置本地IP地址*/
/*檢查命令行參數(shù),如果指定了,就是用該端口號(hào),否則使用缺省端口號(hào)*/if(argc>1){port=atoi(argv[1]);/*如果指定了端口號(hào),就將它轉(zhuǎn)換成整數(shù)*/}else{port=PROTOPORT;/*否則,使用缺省端口號(hào)*/}
if(port>0)/*測試端口號(hào)是否合法*/servaddr.sin_port=htons((u_short)port);else{/*打印錯(cuò)誤信息并退出*/fprintf(stderr,”badport number%s\n”,argv[1]);exit(1);}
/*創(chuàng)建一個(gè)用于監(jiān)聽的流式套接字*/listenfd=SOCKET(AF_INET,SOCK_STREAM,0);if(listenfd<0){fprintf(stderr,“socketcreationfailed\n”);exit(1);}
/*將本地地址綁定到監(jiān)聽套接字*/if(bind(listenfd,(structsockaddr*)&servaddr,sizeof(servaddr))<0){fprintf(stderr,”bindfailed\n”);exit(1);}
/*開始監(jiān)聽,并指定監(jiān)聽套接字請(qǐng)求隊(duì)列的長度*/if(listen(listenfd,QLEN)<0){fprintf(stderr,”listenfiled\n”);exit(1);}
/*服務(wù)器主循環(huán)—接受和處理來自客戶端的連接請(qǐng)求*/while(1){alen=sizeof(clientaddr);/*接受客戶端連接請(qǐng)求,并生成響應(yīng)套接字*/if((clientfd=accept(listenfd,(structsockaddr*)&clientaddr,&alen))<0){fprintf(stderr,“acceptfailed\n”);exit(1);}visits++;/*累加訪問的客戶數(shù)*/sprintf(buf,“thisserverhasbeencontacted%dtime\n”,visits);send(clientfd,buf,strlen(buf),0);/*向客戶端發(fā)送信息*/closesocket(clientfd);/*關(guān)閉響應(yīng)套接字*/}}
關(guān)于阻塞的問題
圖2.7服務(wù)器進(jìn)程因調(diào)用ACCEPT()而被阻塞
2.3.4進(jìn)程的阻塞問題和對(duì)策
1.什么是阻塞阻塞是指一個(gè)進(jìn)程執(zhí)行了一個(gè)函數(shù)或者系統(tǒng)調(diào)用,該函數(shù)由于某種原因不能立即完成,因而不能返回調(diào)用它的進(jìn)程,導(dǎo)致進(jìn)程受控于這個(gè)函數(shù)而處于等待的狀態(tài),進(jìn)程的這種狀態(tài)稱為阻塞。圖2.8RECV()函數(shù)的兩種執(zhí)行方式
2.能引起阻塞的套接字調(diào)用在Berkeley套接字網(wǎng)絡(luò)編程接口的模型中,套接字的默認(rèn)行為是阻塞的,具體地說,在一定情況下,有多個(gè)操作套接字的系統(tǒng)調(diào)用會(huì)引起進(jìn)程阻塞。(1)ACCEPT()(2)READ()、RECV()和READFORM()(3)WRITE()、SEND()和SENDTO()(4)CONNECT()(5)SELECT()(6)CLOSESOCKET()
3.阻塞工作模式帶來的問題采用阻塞工作模式的單進(jìn)程服務(wù)器是不能很好地同時(shí)為多個(gè)客戶服務(wù)的。圖2.9是一個(gè)例子。
圖2.9采用阻塞工作模式的服務(wù)器不能很好地為多個(gè)客戶服務(wù)
4.一種解決方案
利用UNIX操作系統(tǒng)的FORK()系統(tǒng)調(diào)用,編制多進(jìn)程并發(fā)執(zhí)行的服務(wù)器程序??梢詣?chuàng)建子進(jìn)程。對(duì)于每一個(gè)客戶端,用一個(gè)專門的進(jìn)程為它服務(wù),通過進(jìn)程的并發(fā)執(zhí)行,來實(shí)現(xiàn)對(duì)多個(gè)客戶的并發(fā)服務(wù)?;镜木幊炭蚣苁牵焊高M(jìn)程代碼If((pid=FORK())==0){…….子進(jìn)程代碼}elseif(pid<0){報(bào)錯(cuò)信息}父進(jìn)程代碼
舉例:#include<sys/types.h>#include<sys/socket.h>#include<stdio.h>#include<arpa/inet.h>
voidmain(intargc,char**argv){intlistenfd,clientfd,pid;structsockaddr_inssockaddr,csockaddr;charbuffer[1024];intaddrlen,n;
/*創(chuàng)建監(jiān)聽套接字*/listenfd=socket(AF_INET,SOCK_STREAM,0);if(listenfd<0){fprintf(stderr,"socketerror!\n");exit(1);}
/*為監(jiān)聽套接字綁定網(wǎng)絡(luò)地址*/memset(&ssockaddr,0,sizeof(structsockaddr_in));ssockaddr.sin_family=AF_INET;ssockaddr.sin_addr.s_addr=htonl(INADDR_ANY);ssockaddr.sin_port=htons(8080);if(bind(listenfd,&ssockaddr,sizeof(structsockaddr_in))<0){fprintf(stderr,"binderror!\n");exit(2);}
/*啟動(dòng)套接字的監(jiān)聽*/listen(listenfd,5);addrlen=sizeof(sockaddr);
/*服務(wù)器進(jìn)入循環(huán),接受并處理來自不同客戶端的連接請(qǐng)求*/while(1){clientfd=accept(listenfd,(sockaddr*)(&csockaddr),&addrlen);/*accept調(diào)用返回時(shí),表明有客戶端請(qǐng)求連接,創(chuàng)建子進(jìn)程處理連接*/If((pid=FORK())==0){/*顯示客戶端的網(wǎng)絡(luò)地址*/printf("ClientAddr:%s%d\n",inet_ntoa(csockaddr.sin_addr),ntohs(csockaddr.sin_port));/*讀取客戶端發(fā)送來的數(shù)據(jù),在將它們返回到客戶端*/while((n=read(clientfd,buffer,1024))>0){buffer[n]=0;printf("ClientSend:%s",buffer);write(clientfd,buffer,n);}if(n<0){fprintf(stderr,"readerror!\n");exit(3);}
/*通信完畢,關(guān)閉與這個(gè)客戶連接的套接字*/printf("clent%sclosed!\n",inet_ntoa(csockaddr.sin_addr));close(clientfd);exit(1);}elseif(pid<0)printf("forkfailed!\n");close(clientfd);}close(listenfd);/*關(guān)閉監(jiān)聽套接字*/}
2.4無連接的套接字編程2.4.1無連接的套接字編程的兩種模式使用數(shù)據(jù)報(bào)套接字開發(fā)網(wǎng)絡(luò)應(yīng)用程序,既可以采用客戶/服務(wù)器模式,也可以采用對(duì)等模式。圖2.10對(duì)等模式的數(shù)據(jù)報(bào)套接字的編程模型
1.對(duì)等模式
2.客戶/服務(wù)器模式
圖2.11C/S模式的數(shù)據(jù)報(bào)套接字的編程模型
2.4.2兩個(gè)專用的系統(tǒng)調(diào)用1.發(fā)送數(shù)據(jù)報(bào)SENDTO()intSENDTO(intsockfd,constvoid*msg,intlen,unsignedintflags,structsockaddr*to,inttolen);
2.接收數(shù)據(jù)報(bào)RECVFROM()intRECVFROM(intsockfd,void*buf,intlen,unsignedintflags,structsockaddr*from,int*fromlen)
2.4.3數(shù)據(jù)報(bào)套接字的對(duì)等模式編程實(shí)例聊天程序#include<sys/types.h>#include<unistd.h>#include<error.h>#include<sys/socket.h>#include<arpa/inet.h>#include<stdio.h>
/*中斷處理過程*/voidint_proc(intsigno){}
voidmain(intargc,char**argv){structsockaddr_indaddr,saddr,cmpaddr;intsockfd;inttimer=3;charbuffer[1024];intaddrlen,n;
/*判斷用戶輸入的命令行是否正確,如果有錯(cuò),提示用法*/if(argc!=5){printf("用法:%s目的IP目的端口源IP源端口\n",argv[0]);exit(0);}
/*設(shè)定中斷處理函數(shù),并設(shè)置時(shí)間限制*/signal(SIGALRM,int_proc);alarm(timer);
/*建立數(shù)據(jù)報(bào)套接字*/sockfd=socket(AF_INET,SOCK_DGRAM,0);if(sockfd<0){fprintf(stderr,"socketerror!\n");exit(1);}
/*為結(jié)構(gòu)變量daddr的各個(gè)字段賦值*/addrlen=sizeof(structsockaddr_in);memset(&daddr,0,addrlen);daddr.sin_family=AF_INET;daddr.sin_port=htons(atoi(argv[2]));if(inet_pton(AF_INET,argv[1],&daddr.sin_addr)<=0){fprintf(stderr,"InvailddestIP!\n");exit(0);}
/*為結(jié)構(gòu)變量saddr的各個(gè)字段賦值*/addrlen=sizeof(structsockaddr_in);memset(&saddr,0,addrlen);saddr.sin_family=AF_INET;saddr.sin_port=htons(atoi(argv[4]));if(inet_pton(AF_INET,argv[3],&saddr.sin_addr)<=0){fprintf(stderr,"InvaildsourceIP!\n");exit(0);}
/*綁定地址*/if(bind(sockfd,&saddr,addrlen)<0){fprintf(stderr,"bindlocaladdrerror!\n");exit(1);}
/*從標(biāo)準(zhǔn)輸入獲得字符串,并發(fā)送給目標(biāo)地址*/if(fgets(buffer,1024,stdin)==NULL)exit(0);if(sendto(sockfd,buffer,strlen(buffer),0,&daddr,addrlen)){fprintf(stderr,"sendtoerror!\n");exit(2);}
while(1){/*接收信息并顯示*/n=recvfrom(sockfd,buffer,1024,0,&cmpaddr,&daddrlen);if(n<0){/*根據(jù)errno中的數(shù)值是否為常量EWOULDBLOCK,來區(qū)別超時(shí)錯(cuò)和一般性錯(cuò)。*/if(errno==EWOULDBLOCK)fprintf(stderr,"recvfromtimeouterror!\n");else{fprintf(stderr,"recvfromerror!\n");exit(3);}}else{/*比較數(shù)據(jù)報(bào)來源地址與保存的目標(biāo)地址是否一致*//*不同則返回非0,結(jié)束此循環(huán)*/if(memcmp(cmpaddr,daddr,addrlen))continue;buffer[n]=0;printf("Received:%s",buffer);}
圖3.1網(wǎng)絡(luò)應(yīng)用進(jìn)程利用Windock進(jìn)行通信
3.1.2WindowsSockets規(guī)范
WindowsSockets規(guī)范是一套開放的、支持多種協(xié)議的Windows下的網(wǎng)絡(luò)編程接口。從1991年到1995年,從1.0版發(fā)展到2.0.8版,已成為Windows網(wǎng)絡(luò)編程的事實(shí)上的標(biāo)準(zhǔn)。1.WindowsSockets1.1版本在Winsock.h包含文件中,定義了所有WinSock1.1版本庫函數(shù)的語法、相關(guān)的符號(hào)常量和數(shù)據(jù)結(jié)構(gòu)。庫函數(shù)的實(shí)現(xiàn)在WINSOCK.DLL動(dòng)態(tài)鏈接庫文件中。
(1)WinSock1.1全面繼承了BerkeleySockets規(guī)范,見表
3.1(2)數(shù)據(jù)庫函數(shù)表3.2列出了Winsock規(guī)范定義的數(shù)據(jù)庫查詢例程。其中六個(gè)采用getXbyY()的形式,大多要借助網(wǎng)絡(luò)上的數(shù)據(jù)庫來獲得信息,
(3)WinSock1.1擴(kuò)充了BerkeleySockets規(guī)范針對(duì)微軟
Windows的特點(diǎn),WinSock1.1定義了一批新的庫函數(shù),提供了對(duì)于消息驅(qū)動(dòng)機(jī)制的支持,有效地利用Windows多任務(wù)多線程的機(jī)制。見表3.3(4)WinSock1.1只支持TCP/IP協(xié)議棧
2.WinSock2.0WinSock2.0在源碼和二進(jìn)制代碼方面與WinSock1.1兼容,WinSock2.0增強(qiáng)了許多功能。(1)支持多種協(xié)議(2)引入了重疊I/O的概念(3)使用事件對(duì)象異步通知(4)服務(wù)的質(zhì)量(QOS)(5)套接口組(6)擴(kuò)展的字節(jié)順序轉(zhuǎn)換例程(7)分散/聚集方式I/O
(8)新增了許多函數(shù)。
3.WinSock1.1中的阻塞問題阻塞是在把應(yīng)用程序從Berkeley套接口環(huán)境中移植到Windows環(huán)境中的一個(gè)主要焦點(diǎn)。阻塞是指喚起一個(gè)函數(shù),該函數(shù)直到相關(guān)操作完成時(shí)才返回。在Berkeley套接口模型中,一個(gè)套接口的操作的缺省行為是阻塞方式的,除非程序員顯式地請(qǐng)求該操作為非阻塞方式。在Windows環(huán)境下,我們強(qiáng)烈推薦程序員在盡可能的情況下使用非阻塞方式(異步方式)的操作。因?yàn)榉亲枞绞降牟僮髂軌蚋玫卦诜钦枷鹊腤indows環(huán)境下工作。
3.1.3WinSock規(guī)范與Berkeley套接口的區(qū)別1.套接口數(shù)據(jù)類型和該類型的錯(cuò)誤返回值在UNIX中,包括套接口句柄在內(nèi)的所有句柄,都是非負(fù)的短整數(shù),在WinSock規(guī)范中定義了一個(gè)新的數(shù)據(jù)類型,稱作SOCKET,用來代表套接字描述符。
typedefu_intSOCKET;SOCKET可以取從0到INVALID_SOCKET-1之間的任意值。2.select()函數(shù)和FD_*宏在Winsock中,使用select()函數(shù)時(shí),應(yīng)用程序應(yīng)堅(jiān)持用FD_XXX宏來設(shè)置,初始化,清除和檢查fd_set結(jié)構(gòu)。
3.錯(cuò)誤代碼的獲得在UNIX套接字規(guī)范中,如果函數(shù)執(zhí)行時(shí)發(fā)生了錯(cuò)誤,會(huì)把錯(cuò)誤代碼放到errno或h_errno變量中。在Winsock中,錯(cuò)誤代碼可以使用WSAGetLastError()調(diào)用得到。4.指針?biāo)袘?yīng)用程序與WindowsSockets使用的指針都必須是FAR指針。5.重命名的函數(shù)(1)close()改變?yōu)閏losesocket()
(2)ioctl()改變?yōu)閕octlsocket()
6.Winsock支持的最大套接口數(shù)目在WINSOCK.H中缺省值是64,在編譯時(shí)由常量FD_SETSIZE決定。7.頭文件
Berkeley頭文件被包含在WINSOCK.H中。一個(gè)WindowsSockets應(yīng)用程序只需簡單地包含WINSOCK.H就足夠了。8.Winsock規(guī)范對(duì)于消息驅(qū)動(dòng)機(jī)制的支持體現(xiàn)在異步選擇機(jī)制、異步請(qǐng)求函數(shù)、阻塞處理方法、錯(cuò)誤處理、啟動(dòng)和終止等方面。
3.2Winsock1.1的庫函數(shù)3.2.1Winsock的注冊(cè)與注銷1.初始化函數(shù)WSAStartup()Winsock應(yīng)用程序要做的第一件事,就是必須首先調(diào)用WSAStartup()函數(shù)對(duì)Winsock進(jìn)行初始化。初始化也稱為注冊(cè)。注冊(cè)成功后,才能調(diào)用其他的WinsockAPI函數(shù)。(1)WSAStartup()函數(shù)的調(diào)用格式
intWSAStartup(WORDwVersionRequested,LPWSADATAlpWSAData);(2)WSAStartup()函數(shù)的初始化過程圖3.2說明了初始化的過程
圖3.2在一臺(tái)計(jì)算機(jī)中,使用同一Windock實(shí)現(xiàn)的多個(gè)網(wǎng)絡(luò)應(yīng)用程序
(3)WSADATA結(jié)構(gòu)的定義#defineWSADESCRIPTION_LEN256#defineWSASYS_STATUS_LEN128typedefstructWSAData{WORDwVersion;WORDwHighVersion;charszDescription[WSADESCRIPTION_LEN+1];charszSystemStatus[WSASYS_STATUS_LEN+1];unsignedshortiMaxSockets;unsignedshortiMaxUdpDg;char*lpVendorInfo;}WSADATA;
(4)初始化函數(shù)可能返回的錯(cuò)誤代碼WSASYSNOTREADY:
網(wǎng)絡(luò)通信依賴的網(wǎng)絡(luò)子系統(tǒng)沒有準(zhǔn)備好。WSAVERNOTSUPPORTED:找不到所需的WinsockAPI相應(yīng)的動(dòng)態(tài)連接庫。WSAEINVAL:
DLL不支持應(yīng)用程序所需的Winsock版本。WSAEINPROGRESS:
正在執(zhí)行一個(gè)阻塞的Winsock1.1操作。WSAEPROCLIM:
已經(jīng)達(dá)到Winsock支持的任務(wù)數(shù)上限。
WSAEFAULT:
參數(shù)lpWSAData不是合法指針。
(5)初始化Winsock的示例#include<winsock.h>//對(duì)于Winsock2.0,應(yīng)包括
Winsock2.h文件aa(){WORDwVersionRequested;//應(yīng)用程序所需的Winsock版本號(hào)WSADATAwsaData;//用來返回Winsock實(shí)現(xiàn)的細(xì)節(jié)信息。Interr;//出錯(cuò)代碼。
wVersionRequested=MAKEWORD(1,1);//生成版本號(hào)1.1。err=WSAStartup(wVersionRequested,&wsaData);//調(diào)用初始化函數(shù)。if(err!=0){return;}//通知用戶找不到合適的DLL文件。//確認(rèn)返回的版本號(hào)是客戶要求的1.1if(LOBYTE(wsaData.wVersion)!=1||HYBYTE(wsaData.wVersion)!=1){WSACleanup();return;}/*至此,可以確認(rèn)初始化成功,Wi
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(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ì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 2024個(gè)人珠寶買賣合同范本
- 2024年度版權(quán)質(zhì)押合同:含版權(quán)內(nèi)容、質(zhì)押價(jià)值、質(zhì)權(quán)實(shí)現(xiàn)
- 旅游推廣合作合同實(shí)例
- 攝影棚居間服務(wù)合同樣本
- 房屋銷售合同模板手冊(cè)
- 樂團(tuán)合作合同范本大全
- 電子郵件服務(wù)租用協(xié)議
- 2024家教公司與兼職教師合作合同范本
- 企業(yè)房屋租賃合同范本
- 2024保密合同樣書范文
- 2024年廣西安全員C證考試題庫及答案
- 期末測試卷(試題)-2024-2025學(xué)年人教PEP版(2024)英語三年級(jí)上冊(cè)
- 2024至2030年中國手機(jī)配件產(chǎn)業(yè)需求預(yù)測及發(fā)展趨勢前瞻報(bào)告
- 2024年小學(xué)閩教版全冊(cè)英語詞匯表
- 課題開題匯報(bào)(省級(jí)課題)
- 清真食品安全管理制度
- 學(xué)校心理健康教育合作協(xié)議書
- 2024江蘇省沿海開發(fā)集團(tuán)限公司招聘23人(高頻重點(diǎn)提升專題訓(xùn)練)共500題附帶答案詳解
- 2024年初級(jí)社會(huì)體育指導(dǎo)員(游泳)技能鑒定考試題庫(含答案)
- 湖北省危險(xiǎn)廢物監(jiān)管物聯(lián)網(wǎng)系統(tǒng)管理計(jì)劃填報(bào)說明
- Unit6ADayintheLife教學(xué)設(shè)計(jì)2024-2025學(xué)年人教版(2024)英語七年級(jí)上冊(cè)
評(píng)論
0/150
提交評(píng)論