《計(jì)算機(jī)組網(wǎng)實(shí)驗(yàn)教程》課件第11章_第1頁(yè)
《計(jì)算機(jī)組網(wǎng)實(shí)驗(yàn)教程》課件第11章_第2頁(yè)
《計(jì)算機(jī)組網(wǎng)實(shí)驗(yàn)教程》課件第11章_第3頁(yè)
《計(jì)算機(jī)組網(wǎng)實(shí)驗(yàn)教程》課件第11章_第4頁(yè)
《計(jì)算機(jī)組網(wǎng)實(shí)驗(yàn)教程》課件第11章_第5頁(yè)
已閱讀5頁(yè),還剩86頁(yè)未讀 繼續(xù)免費(fèi)閱讀

下載本文檔

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

文檔簡(jiǎn)介

第11章網(wǎng)絡(luò)編程11.1套接字概述11.2Winsock函數(shù)11.3通信程序設(shè)計(jì)11.4基于TCP的端口掃描程序

11.1套

11.1.1WindowsSocket簡(jiǎn)介在20世紀(jì)80年代,美國(guó)加利福尼亞大學(xué)Berkeley(伯克利)分校在BSDUNIX系統(tǒng)下實(shí)現(xiàn)了通信協(xié)議TCP/IP的開發(fā)接口Socket(套接字),并很快流行起來,成為主要應(yīng)用于BSDUNIX系統(tǒng)的通用網(wǎng)絡(luò)編程接口。Winsock(WindowsSocket的簡(jiǎn)稱)就是由BerkeleySocket演變而來的,它定義了Windows系統(tǒng)下的網(wǎng)絡(luò)編程接口。

WindowsSocket規(guī)范提供給應(yīng)用程序開發(fā)者一套簡(jiǎn)單的API,并讓各個(gè)網(wǎng)絡(luò)軟件供應(yīng)商共同遵守。該規(guī)范還定義了應(yīng)用程序開發(fā)者能夠使用,并且網(wǎng)絡(luò)軟件供應(yīng)商能夠?qū)崿F(xiàn)的一套庫(kù)函數(shù)調(diào)用和相關(guān)語(yǔ)義。遵守這套WindowsSocket規(guī)范的網(wǎng)絡(luò)軟件,我們稱之為WindowsSocket兼容的,而WindowsSocket兼容實(shí)現(xiàn)的提供者,我們稱之為WindowsSocket提供者。一個(gè)網(wǎng)絡(luò)軟件實(shí)現(xiàn)WindowsSocket規(guī)范才能做到與WindowsSocket兼容。任何能夠與WindowsSocket兼容的應(yīng)用程序就被認(rèn)為是具有WindowsSocket接口。WindowsSocket規(guī)范定義并記錄了如何使用API與TCP/IP協(xié)議連接,應(yīng)用程序調(diào)用WindowsSocket的API實(shí)現(xiàn)相互之間的通信,WindowsSocket又利用下層的網(wǎng)絡(luò)通信協(xié)議功能和操作系統(tǒng)調(diào)用實(shí)現(xiàn)實(shí)際的通信工作。

11.1.2Winsock基本概念

1.端口網(wǎng)絡(luò)中可以被命名和尋址的通信端口,是操作系統(tǒng)可分配的一種資源。按照OSI體系七層結(jié)構(gòu)的描述,傳輸層與網(wǎng)絡(luò)層在功能上的最大區(qū)別是傳輸層提供進(jìn)程間通信的能力,即網(wǎng)絡(luò)通信最終是在兩個(gè)應(yīng)用進(jìn)程之間交互的,可以用IP地址來標(biāo)示一臺(tái)主機(jī),而一臺(tái)主機(jī)上往往存在多個(gè)應(yīng)用進(jìn)程,每一個(gè)應(yīng)用進(jìn)程也需要一個(gè)惟一的標(biāo)識(shí)符。為此,TCP/IP協(xié)議提出了協(xié)議端口(ProtocolPort,簡(jiǎn)稱端口)的概念,用于標(biāo)識(shí)通信的應(yīng)用進(jìn)程。端口是一種抽象的軟件結(jié)構(gòu)(包括一些數(shù)據(jù)結(jié)構(gòu)和I/O緩沖區(qū))。應(yīng)用進(jìn)程通過系統(tǒng)調(diào)用與某端口建立連接后,傳輸層傳給該端口的數(shù)據(jù)都被相應(yīng)進(jìn)程所接收,且相應(yīng)進(jìn)程發(fā)給傳輸層的數(shù)據(jù)通過該端口輸出。在TCP/IP協(xié)議的實(shí)現(xiàn)中,端口操作類似于一般的I/O操作,進(jìn)程獲取一個(gè)端口,相當(dāng)于獲取本地惟一的I/O文件,可以用一般的讀寫原語(yǔ)訪問。類似于文件描述符,每個(gè)端口都擁有一個(gè)稱為端口號(hào)(PortNumber)的整數(shù)型標(biāo)識(shí)符,用于區(qū)別不同端口。端口號(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ù)器進(jìn)程都擁有一個(gè)全局公認(rèn)的端口號(hào)(即周知端口,Well-KnownPort),即使在不同機(jī)器上,其端口號(hào)也相同。剩余的為自由端口,以本地方式進(jìn)行分配。TCP和UDP均規(guī)定,小于1024的端口號(hào)才能作為保留端口。

2.套接字套接字是支持TCP/IP協(xié)議網(wǎng)絡(luò)通信的基本操作單元。一個(gè)套接字是通信的一個(gè)端點(diǎn),通常由一個(gè)與進(jìn)程相關(guān)聯(lián)的端口號(hào)以及主機(jī)的IP地址來標(biāo)識(shí)。一個(gè)正在被使用的套接字有自己的類型和與其相關(guān)的進(jìn)程,相互交互的兩個(gè)進(jìn)程通過各自的套接字進(jìn)行通信。套接字存在于通信域(即地址族)中,是為了處理線程間通信而引進(jìn)的抽象概念,一般情況下通信發(fā)生在同一通信域中的套接字之間,Winsock規(guī)范只支持單一的通信域,即Internet域。套接字可以根據(jù)通信的性質(zhì)進(jìn)行分類,并且按這種方法可分為兩類:流式套接字和數(shù)據(jù)報(bào)套接字。應(yīng)用程序一般僅在同一類套接字之間通信。

3.字節(jié)順序不同的計(jì)算機(jī)使用不同的字節(jié)順序存儲(chǔ)數(shù)據(jù)。Intel處理器使用的字節(jié)順序稱為“Little-Endian”,即高字節(jié)在前,低字節(jié)在后;而Internet網(wǎng)絡(luò)的字節(jié)順序稱為“Big-Endian”,它和Little-Endian的字節(jié)順序是相反的。因此用戶在使用時(shí)要特別注意字節(jié)的正確順序。任何Winsock函數(shù)對(duì)IP地址和端口號(hào)的使用均是按照網(wǎng)絡(luò)字節(jié)順序組織的。在很多情況下,用戶要在本地主機(jī)字節(jié)順序和網(wǎng)絡(luò)字節(jié)順序之間進(jìn)行轉(zhuǎn)換,此時(shí)應(yīng)該使用WinsockAPI中標(biāo)準(zhǔn)的轉(zhuǎn)換函數(shù),而不要自己編寫轉(zhuǎn)換代碼。因?yàn)閷淼腤insock實(shí)現(xiàn)有可能在本地主機(jī)字節(jié)順序和網(wǎng)絡(luò)字節(jié)順序相同的機(jī)器上運(yùn)行,因此只有使用標(biāo)準(zhǔn)的轉(zhuǎn)換函數(shù)應(yīng)用程序才可以移植。為了統(tǒng)一起見,通常無(wú)論本地主機(jī)字節(jié)順序與網(wǎng)絡(luò)字節(jié)順序是否一致,在進(jìn)行網(wǎng)絡(luò)傳輸時(shí)都將本地主機(jī)字節(jié)順序轉(zhuǎn)換成網(wǎng)絡(luò)字節(jié)順序,在接收時(shí)再將網(wǎng)絡(luò)字節(jié)順序轉(zhuǎn)換成本地主機(jī)字節(jié)順序。

4.阻塞和非阻塞套接字可以處于阻塞模式或非阻塞模式。在阻塞模式下,I/O操作完成前,執(zhí)行操作的Winsock函數(shù)會(huì)一直等待下去,不會(huì)立即返回,這就意味著任一個(gè)線程在某一時(shí)刻只能執(zhí)行一個(gè)I/O操作,而且應(yīng)用程序很難同時(shí)通過多個(gè)建好連接的套接字進(jìn)行通信;而非阻塞模式下沒有這樣的要求,Winsock函數(shù)無(wú)論如何都會(huì)返回并交出程序的控制權(quán)。在默認(rèn)的情況下,套接字處于阻塞模式。

5.錯(cuò)誤檢查與控制要成功編寫Winsock應(yīng)用程序,錯(cuò)誤檢查和控制是至關(guān)重要的。因?yàn)閷?duì)于Winsock函數(shù)而言,返回錯(cuò)誤值是非常常見的。但是多數(shù)情況下,這些錯(cuò)誤是無(wú)關(guān)緊要的,通信仍可在套接字上進(jìn)行。返回的錯(cuò)誤值可以有多種,但最常見的錯(cuò)誤是SOCK_ERROR。SOCK_ERROR的值是-1,但這個(gè)值僅僅是函數(shù)的返回值,由此還不能看出錯(cuò)誤的具體類型。要想獲得具體的錯(cuò)誤代碼,還必須在調(diào)用Winsock函數(shù)之后,用WSAGetLastError函數(shù)來獲得錯(cuò)誤代碼,這個(gè)錯(cuò)誤代碼能明確地表明產(chǎn)生錯(cuò)誤的原因。該函數(shù)的定義為

int?WSAGetLastError(void);11.2Winsock函數(shù)

11.2.1Winsock初始化函數(shù)

1.WSAStartup()

WSAStartup()函數(shù)用于在應(yīng)用程序中初始化WindowsSocketsDLL,只有此函數(shù)調(diào)用成功后,應(yīng)用程序才可以再調(diào)用其他WindowsSocketsDLL中的API函數(shù)。此函數(shù)若調(diào)用成功,則返回0,否則返回錯(cuò)誤。

函數(shù)原型:intPASCALFARWSAStartup(WORDwVersionRequested,LPWSADATAlpWSAData);參數(shù)說明:wVersionRequested:用于指定WindowsSocketsAPI版本。lpWSAData:是指向WSADATA結(jié)構(gòu)的指針,WSAData結(jié)構(gòu)用來存儲(chǔ)系統(tǒng)返回的關(guān)于Winsock的信息。

2.WSACleanup()

WSACleanup()函數(shù)用于結(jié)束對(duì)WinsockDLL的使用,并釋放資源,該函數(shù)不帶任何參數(shù),若調(diào)用成功則返回0,否則返回錯(cuò)誤。調(diào)用格式:intWSACleanup(void);11.2.2基本W(wǎng)insock函數(shù)

1.socket()初始化Winsock的動(dòng)態(tài)鏈接庫(kù)后,可以調(diào)用socket()函數(shù)來建立Socket,并定義此Socket所使用的通信協(xié)議。此函數(shù)調(diào)用成功返回Socket對(duì)象,失敗則返回INVALID_SOCKET(調(diào)用WSAGetLastError()函數(shù)可得知原因,所有Winsock的函數(shù)都可以使用這個(gè)函數(shù)來獲取失敗的原因)。

函數(shù)原型:

SOCKETsocket(intaf,inttype,intprotocol);

參數(shù)說明:

af:指協(xié)議簇,對(duì)于TCP/IP協(xié)議,為AF_INET;

type:Socket的類型,如果是TCP則為SOCK_STREAM,是UDP則為SOCK_DGRAM;

protocol:使用的通信協(xié)議,可以指定為IPPROTO_IP、IPPROTO_UDP等,如果使用者不指定,默認(rèn)為0,表示為TCP/IP協(xié)議,例如:

s=socket(AF_INET,SOCK_STREAM,0);注釋:WinsockAPI是建立在套接字基礎(chǔ)上的。套接字從實(shí)質(zhì)上講,就是一個(gè)指向傳輸提供者的句柄。Win32中套接字不同于其他文件描述符,它是一個(gè)獨(dú)立的類型——Socket。

2.bind()把定義的Socket同一個(gè)地址綁定,要調(diào)用bind()函數(shù),該函數(shù)調(diào)用成功返回0,否則返回SOCKET_ERROR。函數(shù)原型:

intbind(SOCKETs,conststructsockaddrFAR

*name,intnamelen);參數(shù)說明:

s:Socket對(duì)象名;

name:指向類型為structsockaddr的套接字地址結(jié)構(gòu)的指針。

namelen:name的長(zhǎng)度。

structsockaddr定義如下:structsockaddr{

unsignedshortsa_family;

charsa_data[14];

};

sa_family地址族,一般為AF_INET;sa_data則包含該Socket的IP地址和端口號(hào)。

另外還有一種結(jié)構(gòu)類型定義如下:

structsockaddr_in{

shortintsin_family; unsignedshortintsin_port;

structin_addrsin_addr; unsignedcharsin_zero[8];

};

這個(gè)結(jié)構(gòu)使用更為方便。sin_family通常被賦AF_INET;sin_port指端口號(hào);sin_addr填入IP地址;sin_zero,用來將sockaddr_in結(jié)構(gòu)的長(zhǎng)度填充到與structsockaddr同樣的長(zhǎng)度。如果使用者不在意地址或端口的值,那么可以設(shè)定sin_addr為INADDR_ANY,sin_port為0,WindowsSocket會(huì)自動(dòng)設(shè)置一個(gè)地址及端口值(1024~5000之間的值),通常在客戶端編程時(shí)采用。此后可以調(diào)用getsockname()函數(shù)來獲知其被設(shè)定的值。

注釋:bind()函數(shù)將指定的套接字同一個(gè)已知地址和端口綁定在一起。一旦出錯(cuò),bind()函數(shù)會(huì)返回SOCKET_ERROR。對(duì)bind()函數(shù)來說,最常見的錯(cuò)誤是WSAEADDRINUSE,表示另一個(gè)進(jìn)程已經(jīng)同本地IP和端口號(hào)綁定在一起,或者本地IP和端口號(hào)處于TIME_WAIT狀態(tài)。假如對(duì)一個(gè)已綁定套接字調(diào)用bind()函數(shù),便會(huì)返回WSAEFAULT錯(cuò)誤。

3.listen()

listen()函數(shù)使Socket進(jìn)入監(jiān)聽狀態(tài),并設(shè)定可以建立的最大連接數(shù)。該函數(shù)調(diào)用成功返回0,否則返回SOCKET_ERROR。函數(shù)原型:

intlisten(SOCKETs,intbacklog);參數(shù)說明:

s:需要建立監(jiān)聽的Socket。

backlog:正在等待連接的最大隊(duì)列長(zhǎng)度;在服務(wù)器端該參數(shù)的設(shè)置非常重要,因?yàn)榭赡芡瑫r(shí)有多個(gè)客戶端同時(shí)向服務(wù)器發(fā)出連接請(qǐng)求??蛻舳说恼?qǐng)求會(huì)存放在服務(wù)器的“等待處理”隊(duì)列中。服務(wù)器端的Socket調(diào)用完listen()函數(shù)后,如果此時(shí)客戶端調(diào)用connect()函數(shù)提出連接請(qǐng)求,則客戶端的請(qǐng)求信息會(huì)存放在服務(wù)器端的“等待處理”隊(duì)列中。接下來,服務(wù)器端必須調(diào)用accept()函數(shù),從“等待處理”隊(duì)列中提取客戶端的請(qǐng)求信息進(jìn)行處理,這樣服務(wù)器端和客戶端才算正式完成了通信程序的連接動(dòng)作。

4.a(chǎn)ccept()當(dāng)客戶提出連接請(qǐng)求時(shí),為了使服務(wù)器端接受客戶端的連接請(qǐng)求,就要使用accept()函數(shù),該函數(shù)新建一個(gè)Socket與客戶端的Socket相通,原先監(jiān)聽的Socket繼續(xù)進(jìn)入監(jiān)聽狀態(tài),等待其他連接要求。該函數(shù)調(diào)用成功,返回一個(gè)新產(chǎn)生的Socket對(duì)象,否則返回INVALID_SOCKET。函數(shù)原型:

SOCKETaccept(SCOKETs,structsockaddrFAR*addr,intFAR*addrlen);參數(shù)說明:

s:已經(jīng)建立的、處在監(jiān)聽模式的Socket;

addr:存放請(qǐng)求連接的客戶端的地址;

addrlen:addr的長(zhǎng)度。注釋:accept()函數(shù)返回一個(gè)新的套接字,其中addr參數(shù)變量中會(huì)包含發(fā)出連接請(qǐng)求的客戶機(jī)的地址信息,而addrlen參數(shù)指出該結(jié)構(gòu)的長(zhǎng)度。對(duì)于該客戶端后續(xù)的所有操作,都應(yīng)使用這個(gè)新套接字。至于原來那個(gè)套接字,它仍然用于接受其他客戶端的連接,而且仍然處于偵聽狀態(tài)。如果有新的連接請(qǐng)求,可以通過accept()函數(shù)的再一次調(diào)用而獲得接受;否則,服務(wù)進(jìn)程被阻塞。如果調(diào)用accept()函數(shù)出錯(cuò),會(huì)返回INVALID_SOCKET,如果要獲得具體錯(cuò)誤代碼可以調(diào)用WSAGetLastError()函數(shù)。

5.Connect()客戶端的Socket使用connect()函數(shù)來提出與服務(wù)器端的Socket建立連接的申請(qǐng),函數(shù)調(diào)用成功返回0,否則返回SOCKET_ERROR。函數(shù)原型:

intconnect(SOCKETs,conststructsockaddrFAR*name,intnamelen);參數(shù)說明:

s:標(biāo)識(shí)一個(gè)還未連接的Socket;

name:Socket想要連接的對(duì)方的地址信息;

namelen:name的長(zhǎng)度。

6.closesocket()結(jié)束服務(wù)器和客戶端的通信連接是很簡(jiǎn)單的,這一過程可以由服務(wù)器或客戶機(jī)的任一端啟動(dòng),只要調(diào)用closesocket()函數(shù)即可,而要關(guān)閉Server端監(jiān)聽狀態(tài)的Socket,同樣也是利用此函數(shù)。另外,與程序啟動(dòng)時(shí)調(diào)用WSAStartup()函數(shù)相對(duì)應(yīng),程序結(jié)束前,需要調(diào)用

WSACleanup()函數(shù)來通知WinsockStack釋放Socket所占用的資源。這兩個(gè)函數(shù)都是調(diào)用成功返回0,否則返回SOCKET_ERROR。

函數(shù)原型:

intclosesocket(SOCKETs);參數(shù)說明:

s:準(zhǔn)備關(guān)閉的Socket。注釋:closesocket()函數(shù)的調(diào)用會(huì)釋放套接字句柄s,以后再對(duì)該套接字的訪問均以WSAENOTSOCK錯(cuò)誤返回。

7.shutdown()

shutdown()函數(shù)用于禁止在一個(gè)套接字上進(jìn)行數(shù)據(jù)的接收與發(fā)送。函數(shù)原型:

intshutdown(SOCKETs,inthow);參數(shù)說明:

s:被禁止的Socket。

how:標(biāo)志,用于描述禁止哪些操作??赡艿娜≈凳荢D_REVIEVE,SD_SEND或SD_BOTH。SD_RECEIVE表示不允許再調(diào)用接收函數(shù);SD_SEND表示不允許再調(diào)用發(fā)送函數(shù)。SD_BOTH表示取消連接兩端的收發(fā)操作。

11.2.3數(shù)據(jù)傳輸函數(shù)

1.send()

send()函數(shù)用于在套接字連接成功的情況下發(fā)送數(shù)據(jù)。函數(shù)原型:

intsend(SOCKETs,constcharFAR*buf,intlen,intflags);參數(shù)說明:

s:已連接的Socket;

buf:存放要傳送資料的緩沖區(qū)的地址;

len:buf的長(zhǎng)度;

flags:此函數(shù)被調(diào)用的方式。

2.recv()

recv()函數(shù)用于在套接字連接成功的情況下接收數(shù)據(jù)。函數(shù)原型:

intrecv?(SOCKETs,charFAR*buf,intlen,intflags);參數(shù)說明:

s:已連接的Socket;

buf:存放接收到的資料的緩沖區(qū);

len:buf的長(zhǎng)度;

flags:此函數(shù)被調(diào)用的方式。

3.sendto()

sendto()函數(shù)用于向一指定目的地發(fā)送數(shù)據(jù)。函數(shù)原型:

intsendto(SOCKETs,constcharFAR*buf,intlen,intflags,conststructsockaddrFAR*to,

inttolen);參數(shù)說明:

s:表示Socket。

buf:待發(fā)送數(shù)據(jù)的緩沖區(qū)。

len:待發(fā)送的字節(jié)數(shù)。

flags:調(diào)用方式標(biāo)志位,可以為0、MSG_DONTROUTE、MSG_OOB或這些標(biāo)志按位“或”運(yùn)算的結(jié)果。MSG_DONTROUTE用于告知IP協(xié)議,目的主機(jī)在本地網(wǎng)絡(luò),沒有必要查找路由表,可以將報(bào)文直接傳送給目的地址。這個(gè)標(biāo)志一般用在網(wǎng)絡(luò)診斷和路由程序里面。MSG_OOB標(biāo)志數(shù)據(jù)應(yīng)被帶外發(fā)送,帶外數(shù)據(jù)指TCP緊急數(shù)據(jù)。

to:指針,指向目的套接字的地址結(jié)構(gòu)。

tolen:to所指地址結(jié)構(gòu)的長(zhǎng)度。

4.recvfrom()

recvfrom()函數(shù)用于接收一個(gè)數(shù)據(jù)報(bào)并保存、發(fā)送地方地址結(jié)構(gòu)信息。函數(shù)原型:

intrecvfrom(SOCKETs,constcharFAR*buf,intlen,intflags,structsockaddrFAR*from,intFAR*fromlen);參數(shù)說明:

s:表示Socket。

buf:接收數(shù)據(jù)緩沖區(qū)。

len:準(zhǔn)備接收的字節(jié)數(shù)或buf緩沖區(qū)長(zhǎng)度。

flags:調(diào)用操作方式,可以為0、MSG_PEEK、MSG_OOB或這些標(biāo)志按位“或”運(yùn)算的結(jié)果。其中:0表示無(wú)特殊行為;MSG_PEEK使有用的數(shù)據(jù)復(fù)制到提供的接收端緩沖區(qū)內(nèi),但沒有把它從系統(tǒng)緩沖區(qū)中刪除。

from:指針,發(fā)送方套接字地址結(jié)構(gòu)信息。

fromlen:指針,指向SOCKADDR地址結(jié)構(gòu)的長(zhǎng)度。注釋:recvfrom()函數(shù)調(diào)用成功則返回實(shí)際接收的字節(jié)數(shù),發(fā)生錯(cuò)誤時(shí),返回-1。

11.2.4網(wǎng)絡(luò)信息查詢函數(shù)

1.getpeername()

getpeername()獲取通信對(duì)方的套接字地址結(jié)構(gòu)信息。函數(shù)原型:

intgetpeername(SOCKETs,structsockaddrFAR*name,intFAR*namelen);參數(shù)說明:

s:已連接的socket;

name:通信對(duì)方的套接字地址結(jié)構(gòu);

namelen:指向地址結(jié)構(gòu)長(zhǎng)度的指針。

2.getsockname()

getsockname()函數(shù)是getpeer的對(duì)應(yīng)函數(shù),用于獲取指定套接字的本地地址結(jié)構(gòu)信息。函數(shù)原型::

intgetsockname(SOCKETs,structsockaddrFAR*name,intFAR*namelen);參數(shù)說明:

s:標(biāo)示一個(gè)已綁定套接字的句柄;

name:套接字的地址結(jié)構(gòu);

namelen:指向地址結(jié)構(gòu)長(zhǎng)度的指針。

3.gethostbyname()

gethostbyname()函數(shù)用于返回對(duì)應(yīng)于給定主機(jī)名的主機(jī)信息,在已知主機(jī)名并打算查找其IP時(shí),可以使用該函數(shù)。函數(shù)原型::

structhostentFAR*gethostbyname(constcharFAR*name);參數(shù)說明:

name:指向主機(jī)名的指針。

4.gethostbyaddr()

gethostbyaddr()函數(shù)用于返回對(duì)應(yīng)于給定IP地址的主機(jī)信息,在已知IP地址并打算查找其主機(jī)名時(shí),可以使用該函數(shù)。函數(shù)原型::

structhostentFAR*gethostbyaddr(constcharFAR*addr,intlen,inttype);參數(shù)說明:

addr:指向網(wǎng)絡(luò)字節(jié)順序IP地址的指針;

len:地址的長(zhǎng)度,在AF_INET類型地址為4;

type:地址類型,應(yīng)為AF_INET。注釋:gethostbyaddr()函數(shù)返回對(duì)應(yīng)于給定IP地址的包含主機(jī)名字和地址信息的hostent結(jié)構(gòu)指針。

5.getservbyname()

getservbyname()函數(shù)用于返回對(duì)應(yīng)于給定服務(wù)名字和協(xié)議名的相關(guān)服務(wù)信息,在已知服務(wù)的情況下,要查找其對(duì)應(yīng)的端口號(hào),可以使用該函數(shù)。函數(shù)原型::

sturctserventFAR*getservbyname(constcharFAR*name,constcharFAR*proto);參數(shù)說明:

name:指向服務(wù)名的指針;

proto:指向協(xié)議名的指針。

11.3通信程序設(shè)計(jì)

11.3.1客戶/服務(wù)器模式網(wǎng)絡(luò)應(yīng)用進(jìn)程自身功能的實(shí)現(xiàn)往往是通過存在于不同主機(jī)中的多個(gè)應(yīng)用進(jìn)程間的通信和協(xié)同工作來完成的??蛻艉头?wù)器是通信中涉及的兩個(gè)應(yīng)用進(jìn)程??蛻羰侵鲃?dòng)請(qǐng)求服務(wù)的一方,服務(wù)器是接受請(qǐng)求,提供服務(wù)的一方。一個(gè)服務(wù)器進(jìn)程通常在一個(gè)眾所周知的端口監(jiān)聽是否有客戶的請(qǐng)求到達(dá),即服務(wù)器進(jìn)程總是處于等待狀態(tài),直到某一客戶進(jìn)程發(fā)出的請(qǐng)求到來,此時(shí),服務(wù)器進(jìn)程開始響應(yīng)客戶的請(qǐng)求,為客戶提供服務(wù)。

11.3.2面向連接服務(wù)和無(wú)連接服務(wù)

1.面向連接服務(wù)在面向連接的服務(wù)中,通信雙方在進(jìn)行數(shù)據(jù)交換之前,必須先建立一條連接,數(shù)據(jù)傳輸結(jié)束后釋放連接。面向連接服務(wù)確定通信雙方之間存在連接,保證通信雙方處于活動(dòng)狀態(tài),且數(shù)據(jù)是按序傳送的,從而保證了數(shù)據(jù)通信的可靠性。面向連接的服務(wù)只支持點(diǎn)到點(diǎn)通信。面向連接服務(wù)比較適合于在一段時(shí)間間隔內(nèi)向同一目的地發(fā)送大量報(bào)文的情況。對(duì)于發(fā)送少量零星報(bào)文的情況,連接的建立和釋放所帶來的開銷就顯得過大了。

2.無(wú)連接服務(wù)在無(wú)連接服務(wù)中,發(fā)送方可以隨時(shí)發(fā)送數(shù)據(jù)而無(wú)需事先建立連接。發(fā)送方并不清楚接收方的狀態(tài)以及網(wǎng)絡(luò)中是否有路徑可以到達(dá)接收方,因此數(shù)據(jù)包可能會(huì)丟失,且由于前后數(shù)據(jù)的傳輸路徑可能不同,因此無(wú)法保證數(shù)據(jù)按序到達(dá)目的地。這些問題留給應(yīng)用程序自行解決。無(wú)連接服務(wù)可以實(shí)現(xiàn)點(diǎn)對(duì)點(diǎn)通信以及點(diǎn)對(duì)多點(diǎn)通信,適合于少量零星報(bào)文的傳送以及對(duì)傳輸?shù)目煽啃詻]有要求的應(yīng)用。

11.3.3流式套接字和數(shù)據(jù)報(bào)套接字

1.流式套接字流式套接字提供雙向、有序、無(wú)重復(fù)并且無(wú)報(bào)文段邊界的數(shù)據(jù)流服務(wù),即一種可靠的面向連接的數(shù)據(jù)傳輸方法。流式套接字使用TCP協(xié)議,當(dāng)要發(fā)送大批量數(shù)據(jù)或要保證數(shù)據(jù)按順序、無(wú)重復(fù)地到達(dá)目的地時(shí),需要使用流式套接字。流式套接字是面向連接的,因此服務(wù)器進(jìn)程和客戶進(jìn)程在通信前必須建立各自的套接字,并進(jìn)行連接,然后才能對(duì)相應(yīng)的套接字進(jìn)行“讀”、“寫”操作,實(shí)現(xiàn)數(shù)據(jù)的傳輸。流式套接字編程的流程如圖11.1所示。

圖11.1流式套接字編程流程

2.?dāng)?shù)據(jù)報(bào)套接字?jǐn)?shù)據(jù)報(bào)套接字支持雙向通信,提供不可靠的、無(wú)連接的數(shù)據(jù)報(bào)通信方式。也就是說,一個(gè)從數(shù)據(jù)報(bào)套接字接收信息的進(jìn)程可能發(fā)現(xiàn)信息重復(fù)、接收順序與發(fā)送順序不同等情況,接收程序應(yīng)具有處理這些情況的能力。數(shù)據(jù)報(bào)套接字使用UDP協(xié)議,具有向多個(gè)目標(biāo)地址發(fā)送廣播數(shù)據(jù)報(bào)的能力。數(shù)據(jù)報(bào)套接字是無(wú)連接的,它不能保證接收端是否正在等待接收,因此,數(shù)據(jù)報(bào)并不十分可靠,需要應(yīng)用程序負(fù)責(zé)管理數(shù)據(jù)報(bào)的排序和可靠性。數(shù)據(jù)報(bào)套接字的編程過程比流式套接字簡(jiǎn)單。數(shù)據(jù)報(bào)套接字編程使用的基本函數(shù)與流式套接字使用的函數(shù)一樣,而數(shù)據(jù)傳輸函數(shù)則不同:發(fā)送數(shù)據(jù)使用sendto()函數(shù),接收數(shù)據(jù)使用recyfrom()函數(shù)。

圖11.2數(shù)據(jù)報(bào)套接字編程流程11.3.4實(shí)驗(yàn)

面向連接通信程序1.實(shí)驗(yàn)要求(1)掌握網(wǎng)絡(luò)編程的概念。(2)通過程序設(shè)計(jì)理解流式套接字編程流程。2.實(shí)驗(yàn)設(shè)備計(jì)算機(jī)1臺(tái)。

3.實(shí)驗(yàn)過程和主要步驟

(1)服務(wù)器。服務(wù)器程序先運(yùn)行,首先初始化Winsock,然后創(chuàng)建套接字,在2000端口上進(jìn)行綁定,接著在2000端口上進(jìn)行偵聽,并進(jìn)入等待連接狀態(tài)。當(dāng)與客戶建立連接后,服務(wù)器接收客戶發(fā)來的文件,并將其放在相應(yīng)位置。以上過程的實(shí)現(xiàn)程序如下:#include"stdafx.h"#include"winsock.h"#include"windows.h"#include"stdio.h"#pragmacomment(lib,"wsock32.lib")#defineRECV_PORT2000#defineSEND_PORT3000#defineMAX_FILESIZE32*1024

SOCKETsock,sock1;sockaddr_inServerAddr;sockaddr_inClientAddr;

structFiledata{

charffname[30]; charffdata[MAX_FILESIZE]; intlen;}DataPacket;intAddrlen;

DWORDStartSock(){ WSADATAWSAData; if(WSAStartup(MAKEWORD(2,2),&WSAData)!=0) {

printf("sockinitfail!\n"); return(-1); } return(1);}

DWORDCreatesocket(){ sock=socket(AF_INET,SOCK_STREAM,0); if(sock==SOCKET_ERROR) {printf("sockcreatefail!\n");

WSACleanup();

return(-1);

} ServerAddr.sin_family=AF_INET; ServerAddr.sin_addr.s_addr=htonl(INADDR_ANY);

ServerAddr.sin_port=htons(RECV_PORT);

if(bind(sock,(structsockaddrFAR*)&ServerAddr,sizeof(ServerAddr))==SOCKET_ERROR) { printf("bindistheerror)"); return(-1); } return(1);}DWORDWriteFile(char*fname,char*fdata,intflen){ inti; FILE*fp; fp=fopen(fname,"w"); if(fp==NULL) { printf("cannotopenthisifle\n"); } i=0; for(i=0;i<flen;i++) { fputc(fdata[i],fp);

} fclose(fp); return(1);}

DWORDConnectProcess(){ Addrlen=sizeof(sockaddr_in); if(listen(sock,5)<0) { printf("Listenerror"); return(-1); } printf("listening...\n");

for(;;) {

sock1=accept(sock,(structsockaddrFAR*)&ClientAddr,

&Addrlen);

if(sock1!=-1)

{ printf("Acceptedaconnection!\n");

for(;;) { memset(DataPacket.ffname,0,30); memset(DataPacket.ffdata,0,MAX_FILESIZE);

DataPacket.len=0;

if(recv(sock1,(char*)&DataPacket,

sizeof(DataPacket),0)<=0)

{

break;

}

printf("Hasrecievedfile:%s,lengthis%d",

DataPacket.ffname,DataPacket.len);

WriteFile(DataPacket.ffname,DataPacket.ffdata,DataPacket.len);

printf("\n");

}

}

}}intmain(intargc,char*argv[]){ if(StartSock()==-1) return(-1); if(Createsocket()==-1)

return(-1); if(ConnectProcess()==-1) return(-1);return(1);}

(2)客戶??蛻舫绦蚝筮\(yùn)行,同樣先初始化Winsock,然后創(chuàng)建套接字,接著嘗試與服務(wù)器連接,連接建立后,顯示“連接成功”,然后客戶端提示輸入要發(fā)送的文件名,該文件名可以帶路徑。以上過程的實(shí)現(xiàn)程序如下:#include"stdafx.h"#include"winsock.h"#include"windows.h"#include"stdio.h"#include"stdlib.h"#include"string.h"#pragmacomment(lib,"wsock32.lib")#defineRECV_PORT2000#defineSEND_PORT3000#defineMAX_FILESIZE32*1024

SOCKETsock;sockaddr_inServerAddr;

structFiledata{

charffname[30]; charffdata[MAX_FILESIZE]; intlen;}DataPacket;

DWORDStartSock(){ WSADATAWSAData; if(WSAStartup(MAKEWORD(2,2),&WSAData)!=0) { printf("sockinitfail!\n"); return(-1); }

ServerAddr.sin_family=AF_INET; ServerAddr.sin_addr.s_addr=inet_addr("127.0.0.1");

ServerAddr.sin_port=htons(RECV_PORT);

return(1);}

DWORDCreatesocket(){ sock=socket(AF_INET,SOCK_STREAM,0); if(sock==SOCKET_ERROR) {

printf("Sockcreatefail!\n"); WSACleanup(); return(-1); } return(1);}voidCallServer(){ Createsocket(); while(connect(sock,(structsockaddr*)&ServerAddr, sizeof(ServerAddr))==SOCKET_ERROR) {

printf("Connect...\n"); }}

DWORDGetFile(char*fname){ FILE*fp; intFilesize; inti; intcount,total=0; charbuffer[100]; charSenddata[MAX_FILESIZE]; fp=fopen(fname,"r");

if(fp==NULL) { printf("cannotopenthisfile\n"); return(0); } i=0; Filesize=0; memset(Senddata,0,MAX_FILESIZE); while(!feof(fp)) { count=fread(buffer,sizeof(char),100,fp); if(ferror(fp)) {

printf("ReadFileerror"); break; } Filesize+=count; if(Filesize>MAX_FILESIZE) { printf("yourfileistoobig\n"); fclose(fp); return(0); } memcpy(&Senddata[i],buffer,count); i+=count; }

fclose(fp); Senddata[i]='\0'; strcpy(DataPacket.ffname,fname); memcpy(DataPacket.ffdata,Senddata,Filesize); DataPacket.len=Filesize; printf("%s%d\n",DataPacket.ffname,DataPacket.len); return(1);

}

DWORDTCPSendPacket(structFiledataPacket)

{

intlength; length=send(sock,(char*)&Packet,sizeof(DataPacket),0); if(length<=0) { printf("sendFiledataerror!\n"); closesocket(sock); WSACleanup(); return(-1); } return(1);

}

intmain(intargc,char*argv[])

{ charsendfilename[30]; StartSock(); CallServer(); printf("connectok!\n"); while(1) {

printf("pleaseinputyourfilenamerosend(exit-退出)\n");

scanf("%s",sendfilename);

if(strcmp(sendfilename,"exit")==0) break;

if(GetFile(sendfilename)==0) continue;TCPSendPacket(DataPacket); } closesocket(sock); return(0);}

(3)讀者可以結(jié)合運(yùn)行結(jié)果,分析理解通信流程,并在此基礎(chǔ)上完成以下工作:①

修改程序參數(shù)實(shí)現(xiàn)客戶/服務(wù)器兩端分別在不同計(jì)算機(jī)上通信。②

修改程序,使得當(dāng)服務(wù)器關(guān)閉后,客戶/服務(wù)器間的連接斷開,在客戶端提示“連接斷開,請(qǐng)重新連接”等信息。

11.4基于TCP的端口掃描程序

11.4.1端口掃描的原理端口的作用就是讓應(yīng)用層的各種應(yīng)用進(jìn)程能將其數(shù)據(jù)通過端口向下交付給傳輸層,以及讓傳輸層將其報(bào)文段中的數(shù)據(jù)向上通過端口交付給應(yīng)用層相應(yīng)的進(jìn)程。從這個(gè)意義上講,端口是用來標(biāo)識(shí)應(yīng)用層的進(jìn)程。為了知道對(duì)方計(jì)算機(jī)相應(yīng)的端口是否在工作,可以采用TCP協(xié)議,通過Socket試圖連接對(duì)方計(jì)算機(jī)的某端口,和該端口建立連接。根據(jù)返回結(jié)果,判斷連接是否成功。如果連接建立成功,則說明對(duì)方開放了該端口,否則,對(duì)方?jīng)]有開放該端口。

11.4.2編程所用類庫(kù)介紹

1.CAsyncSockets類的編程簡(jiǎn)介(1)構(gòu)造CAsyncSockets。①

服務(wù)器端:CAsyncsocketssrvrsocket;intnPort=21;//nPort是一個(gè)端口地址,21只是一個(gè)例子srvrsocket.Create(nPort,SOCK_DGRAM);//數(shù)據(jù)報(bào)類型②

客戶端:Csocketclentsocket;clientsocket.Create();

(2)調(diào)用對(duì)象的成員函數(shù)來執(zhí)行通信任務(wù)。①

服務(wù)器端:

Srvsocket.Listen()

//重載OnAccept()函數(shù)處理FD_ACCEPT事件,調(diào)用Accept()函數(shù)

Srvsocket.OnAccept();②

客戶端:

Clientsocket.Connect(StrAddr,nPort);

(3)當(dāng)通信結(jié)束時(shí),刪除CasyncSocket對(duì)象。如果使用new運(yùn)算符在堆中構(gòu)造對(duì)象,必須調(diào)用delete運(yùn)算符。

2.Csocket類編程簡(jiǎn)介

1)Csocket類相關(guān)成員函數(shù)

Csocket類相關(guān)成員函數(shù)實(shí)際上是從CasyncSocket類繼承的。

BOOLCreate(UINTnSocketPort=0,int

nSocketType=SOCK_STREAM,longlEvent=FD_READ|FD_WRITE|FD_OOB|FD_ACCEPT|FD_CONNECT|FD_CLOSE,LPCTRTRlpszSocketAddress=NULL):該函數(shù)的作用是建立套接字。

BOOLBind(UINTnSocketPort,LPCTSTRlpszSocketAddress=NULL):該函數(shù)的作用是將Socket端口與網(wǎng)絡(luò)地址綁定起來。

BOOLListen(intnConnectionBacklog):該函數(shù)的作用是等待Socket請(qǐng)求,nConnection-Backlog表示等待隊(duì)列的長(zhǎng)度,默認(rèn)值為5。

VirtualBOOLAccept(CasyncSocket&rConnectedSocket,SOCKADDR*lpSockAddr=NULL,int*lpSockAddrLen=NULL):該函數(shù)的作用是取得隊(duì)列上第一個(gè)連接請(qǐng)求并建立一個(gè)具有與Sock

溫馨提示

  • 1. 本站所有資源如無(wú)特殊說明,都需要本地電腦安裝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)論