版權(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ì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 《第6課 甲午戰(zhàn)爭(zhēng)和八國(guó)聯(lián)軍侵華》(同步訓(xùn)練)高中歷史必修1-北師大版-2024-2025學(xué)年
- 2025年教師資格考試初中面試體育與健康試題及答案指導(dǎo)
- 中元節(jié)文明祭祀倡議書范文
- 中學(xué)生保護(hù)世界遺產(chǎn)倡議書
- 技術(shù)管理招聘筆試題及解答(某大型集團(tuán)公司)2025年
- 事業(yè)單位財(cái)務(wù)自查報(bào)告及整改措施范文
- 鋼結(jié)構(gòu)架子搭建工程承包協(xié)議2024
- 2024年度辦公設(shè)備租賃與維護(hù)合同
- 2024年工程代理營(yíng)運(yùn)協(xié)議
- 2024年幼兒園品牌保密協(xié)議
- 安徽省淮北市地方婚禮流程資料
- 附件3-4歐曼金融服務(wù)經(jīng)銷商融資業(yè)務(wù)介紹
- 中醫(yī)骨傷科學(xué)9肩周炎上肢傷筋
- 五年級(jí)分?jǐn)?shù)乘法口算練習(xí)
- 客戶服務(wù)管理七大原則
- 斜井常閉式防跑車裝置設(shè)計(jì)說明書
- 購(gòu)買文件登記表.doc
- [山東]建筑工程施工技術(shù)資料管理規(guī)程表格
- 《葫蘆絲演奏的入門練習(xí)》教學(xué)設(shè)計(jì)
- 噪聲傷害事故PPT課件
- 四川省農(nóng)業(yè)水價(jià)綜合改革試點(diǎn)末級(jí)渠系工程建設(shè)項(xiàng)目實(shí)施方案
評(píng)論
0/150
提交評(píng)論