Socket網(wǎng)絡(luò)開發(fā)學(xué)習(xí)_第1頁
Socket網(wǎng)絡(luò)開發(fā)學(xué)習(xí)_第2頁
Socket網(wǎng)絡(luò)開發(fā)學(xué)習(xí)_第3頁
Socket網(wǎng)絡(luò)開發(fā)學(xué)習(xí)_第4頁
Socket網(wǎng)絡(luò)開發(fā)學(xué)習(xí)_第5頁
已閱讀5頁,還剩14頁未讀, 繼續(xù)免費閱讀

下載本文檔

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

文檔簡介

Socket網(wǎng)絡(luò)開發(fā)目標(biāo)什么是套接字?Internet套接字的兩種類型網(wǎng)絡(luò)理論IP地址和如何處理它們使用Socket開發(fā)網(wǎng)絡(luò)程序什么是socket?所謂socket通常也稱作“套接字”,用于描述IP地址和端口,是一個通信鏈的句柄。應(yīng)用程序通常通過“套接字”向網(wǎng)絡(luò)發(fā)出請求或者應(yīng)答網(wǎng)絡(luò)請求。生成套接字,主要有3個參數(shù):通信的目的IP地址、使用的傳輸層協(xié)議(TCP或UDP)和使用的端口號。Socket原意是“插座”。通過將這3個參數(shù)結(jié)合起來,與一個“插座”Socket綁定,應(yīng)用層就可以和傳輸層通過套接字接口,區(qū)分來自不同應(yīng)用程序進程或網(wǎng)絡(luò)連接的通信,實現(xiàn)數(shù)據(jù)傳輸?shù)牟l(fā)服務(wù)。Socket可以看成在兩個程序進行通訊連接中的一個端點,一個程序?qū)⒁欢涡畔懭隨ocket中,該Socket將這段信息發(fā)送給另外一個Socket中,使這段信息能傳送到其他程序中。

套接字的兩種類型一種是“StreamSockets”(流格式),另外一種是“DatagramSockets”(數(shù)據(jù)包格式)。我們以后談到它們的時候也會用到SOCK_STREAM和SOCK_DGRAM。數(shù)據(jù)報套接字有時也叫“無連接套接字”(如果你確實要連接的時候可以用connect()。)流式套接字是可靠的雙向通訊的數(shù)據(jù)流。如果你向套接字按順序輸出“1,2”,那么它們將按順序“1,2”到達另一邊。它們是無錯誤的傳遞的,除非自己的控制錯誤。例如:telnet、及ftp等都是使用流格式,這是因為它使用了“傳輸控制協(xié)議(TheTransmissionControlProtocol)”。流式套接字可以達到高質(zhì)量的數(shù)據(jù)傳輸。網(wǎng)絡(luò)理論網(wǎng)絡(luò)分層模型(LayeredNetworkModel)?,F(xiàn)在列出整個層次模型。如果你要參加網(wǎng)絡(luò)考試,可一定要記?。?/p>

應(yīng)用層(Application)

表示層(Presentation)

會話層(Session)

傳輸層(Transport)

網(wǎng)絡(luò)層(Network)

數(shù)據(jù)鏈路層(DataLink)

物理層(Physical)

物理層是硬件(串口,以太網(wǎng)等等)。應(yīng)用層是和硬件層相隔最遠(yuǎn)的--它是用戶和網(wǎng)絡(luò)交互的地方。

這個模型如此通用,如果你想,你可以把它作為修車指南。把它對應(yīng)到Unix,結(jié)果是:

應(yīng)用層(ApplicationLayer)(telnet,ftp,等等)

傳輸層(Host-to-HostTransportLayer)(TCP,UDP)

Internet層(InternetLayer)(IP和路由)

網(wǎng)絡(luò)訪問層(NetworkAccessLayer)

(網(wǎng)絡(luò)層,數(shù)據(jù)鏈路層和物理層)

網(wǎng)絡(luò)數(shù)據(jù)排列有兩種字節(jié)排列順序:重要的字節(jié)(有時叫“octet”,即八位位組)在前面,或者不重要的字節(jié)在前面。前一種叫“網(wǎng)絡(luò)字節(jié)順序(NetworkByteOrder)”。有些機器在內(nèi)部是按照這個順序儲存數(shù)據(jù),而另外一些則不然。當(dāng)我說某數(shù)據(jù)必須按照NBO順序,那么你要調(diào)用函數(shù)(例如htons())來將它從本機字節(jié)順序(HostByteOrder)轉(zhuǎn)換過來。如果我沒有提到NBO,那么就讓它保持本機字節(jié)順序。

Socket數(shù)據(jù)類型socket描述符:僅僅是一個常見的int。套接字地址信息structsockaddr{

unsignedshortsa_family;/*地址家族,AF_xxx*/

charsa_data[14];/*14字節(jié)協(xié)議地址*/

};

sa_family能夠是各種各樣的類型,但是在這篇文章中都是"AF_INET"。sa_data包含套接字中的目標(biāo)地址和端口信息。為了處理structsockaddr,程序員創(chuàng)造了一個并列的結(jié)構(gòu):structsockaddr_in("in"代表"Internet"。)

structsockaddr_in{

shortintsin_family;/*通信類型*/

unsignedshortintsin_port;/*端口*/

structin_addrsin_addr;/*Internet地址*/

unsignedcharsin_zero[8];/*與sockaddr結(jié)構(gòu)的長度相同*/};

IP地址如何處理假設(shè)你已經(jīng)有了一個sockaddr_in結(jié)構(gòu)體ina,你有一個IP地址"0"要儲存在其中,你就要用到函數(shù)inet_addr(),將IP地址從點數(shù)格式轉(zhuǎn)換成無符號長整型。使用方法如下:

ina.sin_addr.s_addr=inet_addr("");

好了,現(xiàn)在你可以將IP地址轉(zhuǎn)換成長整型了。有沒有其相反的方法呢?它可以將一個in_addr結(jié)構(gòu)體輸出成點數(shù)格式?這樣的話,你就要用到函數(shù)inet_ntoa()("ntoa"的含義是"networktoascii"),就像這樣:

printf("%s",inet_ntoa(ina.sin_addr));

socket()函數(shù)Windows包含文件:

#include<WinSock2.h>

intsocket(intdomain,inttype,intprotocol);domain應(yīng)該設(shè)置成“AF_INET”,就象上面的數(shù)據(jù)結(jié)構(gòu)structsockaddr_in中一樣。參數(shù)type告訴內(nèi)核是SOCK_STREAM類型還是SOCK_DGRAM類型。把protocol設(shè)置為“0”。(注意:有很多種domain、type,我不一一列出了,請看socket()的幫助。socket()只是返回你以后在系統(tǒng)調(diào)用種可能用到的socket描述符,或者在錯誤的時候返回-1。使用WSAGetLastError()可以查看錯誤原因。bind()函數(shù)一旦你有一個套接字,你可能要將套接字和機器上的一定的端口關(guān)聯(lián)起來。intbind(intsockfd,structsockaddr*my_addr,intaddrlen);sockfd是調(diào)用socket返回的文件描述符。my_addr是指向數(shù)據(jù)結(jié)構(gòu)structsockaddr的指針,它保存你的地址(即端口和IP地址)信息。my_addr.sin_port是網(wǎng)絡(luò)字節(jié)順序,addrlen設(shè)置為sizeof(structsockaddr)。在處理自己的IP地址和/或端口的時候,有些工作是可以自動處理的。

my_addr.sin_port=0;/*隨機選擇一個沒有使用的端口*/

my_addr.sin_addr.s_addr=INADDR_ANY;/*使用自己的IP地址*/

通過將0賦給my_addr.sin_port,你告訴bind()自己選擇合適的端口。同樣,將my_addr.sin_addr.s_addr設(shè)置為INADDR_ANY,你告訴它自動填上它所運行的機器的IP地址。簡單得很不是嗎?再看看例子:bind()函數(shù)bind()在錯誤的時候依然是返回-1,并且使用WSAGetLastError()取錯。在你調(diào)用bind()的時候,你要小心的另一件事情是:不要采用小于1024的端口號。所有小于1024的端口號都被系統(tǒng)保留!你可以選擇從1024到65535的端口(如果它們沒有被別的程序使用的話)。你要注意的另外一件小事是:有時候你根本不需要調(diào)用它。如果你使用connect()來和遠(yuǎn)程機器進行通訊,你不需要關(guān)心你的本地端口號(就象你在使用telnet的時候),你只要簡單的調(diào)用connect()就可以了,它會檢查套接字是否綁定端口,如果沒有,它會自己綁定一個沒有使用的本地端口。connect()函數(shù)connect()程序

現(xiàn)在我們假設(shè)你是個telnet程序。你的用戶命令你得到套接字的文件描述符。你聽從命令調(diào)用了socket()。下一步,你的用戶告訴你通過端口23(標(biāo)準(zhǔn)telnet端口)連接到"0"。你該怎么做呢?幸運的是,你正在閱讀connect()--如何連接到遠(yuǎn)程主機這一章。你可不想讓你的用戶失望。

connect()系統(tǒng)調(diào)用是這樣的:

intconnect(intsockfd,structsockaddr*serv_addr,intaddrlen);sockfd是系統(tǒng)調(diào)用socket()返回的套接字文件描述符。serv_addr是保存著目的地端口和IP地址的數(shù)據(jù)結(jié)構(gòu)structsockaddr。addrlen設(shè)置為sizeof(structsockaddr)。想知道得更多嗎?讓我們來看個例子:之后,你應(yīng)該檢查connect()的返回值--它在錯誤的時候返回-1,并通過WSAGetLastError()取錯。

同時,你可能看到,我沒有調(diào)用bind()。因為我不在乎本地的端口號。我只關(guān)心我要去那。內(nèi)核將為我選擇一個合適的端口號,而我們所連接的地方也自動地獲得這些信息。一切都不用擔(dān)心。listen()函數(shù)是換換內(nèi)容得時候了。假如你不希望與遠(yuǎn)程的一個地址相連,或者說,僅僅是將它踢開,那你就需要等待接入請求并且用各種方法處理它們。處理過程分兩步:首先,你聽--listen(),然后,你接受--accept()(請看下面的內(nèi)容)。

除了要一點解釋外,系統(tǒng)調(diào)用listen也相當(dāng)簡單。

intlisten(intsockfd,intbacklog);

sockfd是調(diào)用socket()返回的套接字文件描述符。backlog是在進入隊列中允許的連接數(shù)目。什么意思呢?進入的連接是在隊列中一直等待直到你接受(accept()請看下面的文章)連接。它們的數(shù)目限制于隊列的允許。大多數(shù)系統(tǒng)的允許數(shù)目是20,你也可以設(shè)置為5到10。和別的函數(shù)一樣,在發(fā)生錯誤的時候返回-1,并通過WSAGetLastError()取錯。

你可能想象到了,在你調(diào)用listen()前你或者要調(diào)用bind()或者讓內(nèi)核隨便選擇一個端口。如果你想偵聽進入的連接,那么系統(tǒng)調(diào)用的順序可能是這樣的:

socket();

bind();

listen();

accept()函數(shù)準(zhǔn)備好了,系統(tǒng)調(diào)用accept()會有點古怪的地方的!你可以想象發(fā)生這樣的事情:有人從很遠(yuǎn)的地方通過一個你在偵聽(listen())的端口連接(connect())到你的機器。它的連接將加入到等待接受(accept())的隊列中。你調(diào)用accept()告訴它你有空閑的連接。它將返回一個新的套接字文件描述符!這樣你就有兩個套接字了,原來的一個還在偵聽你的那個端口,新的在準(zhǔn)備發(fā)送(send())和接收(recv())數(shù)據(jù)。這就是這個過程!

intaccept(intsockfd,void*addr,int*addrlen);

sockfd相當(dāng)簡單,是和listen()中一樣的套接字描述符。addr是個指向局部的數(shù)據(jù)結(jié)構(gòu)sockaddr_in的指針。這是要求接入的信息所要去的地方(你可以測定那個地址在那個端口呼叫你)。在它的地址傳遞給accept之前,addrlen是個局部的整形變量,設(shè)置為sizeof(structsockaddr_in)。accept將不會將多余的字節(jié)給addr。如果你放入的少些,那么它會通過改

變addrlen的值反映出來。同樣,在錯誤時返回-1,并通過WSAGetLastError()取錯。

現(xiàn)在是你應(yīng)該熟悉的代碼片段。注意,在系統(tǒng)調(diào)用send()和recv()中你應(yīng)該使用新的套接字描述符new_fd。如果你只想讓一個連接進來,那么你可以使用close()去關(guān)閉原來的文件描述符sockfd來避免同一個端口更多的連接。send()andrecv()函數(shù)這兩個函數(shù)用于流式套接字或者數(shù)據(jù)報套接字的通訊。如果你喜歡使用無連接的數(shù)據(jù)報套接字,你應(yīng)該看一看下面關(guān)于sendto()和recvfrom()的章節(jié)。

send()是這樣的:

intsend(intsockfd,constvoid*msg,intlen,intflags);

sockfd是你想發(fā)送數(shù)據(jù)的套接字描述符(或者是調(diào)用socket()或者是accept()返回的。)msg是指向你想發(fā)送的數(shù)據(jù)的指針。len是數(shù)據(jù)的長度。把flags設(shè)置為0就可以了。(詳細(xì)的資料請看send()的manpage)。

這里是一些可能的例子:

char*msg="Beejwashere!";

intlen,bytes_sent;

.

.

len=strlen(msg);

bytes_sent=send(sockfd,msg,len,0);

send()andrecv()函數(shù)send()返回實際發(fā)送的數(shù)據(jù)的字節(jié)數(shù)--它可能小于你要求發(fā)送的數(shù)目!注意,有時候你告訴它要發(fā)送一堆數(shù)據(jù)可是它不能處理成功。它只是發(fā)送它可能發(fā)送的數(shù)據(jù),然后希望你能夠發(fā)送其它的數(shù)據(jù)。記住,如果send()返回的數(shù)據(jù)和len不匹配,你就應(yīng)該發(fā)送其它的數(shù)據(jù)。但是這里也有個好消息:如果你要發(fā)送的包很小(小于大約1K),它可能處理讓數(shù)據(jù)一次發(fā)送完。它在錯誤的時候返回-1,并通過WSAGetLastError()取錯。recv()函數(shù)很相似:

intrecv(intsockfd,void*buf,intlen,unsignedintflags);

sockfd是要讀的套接字描述符。buf是要讀的信息的緩沖。len是緩沖的最大長度。flags可以設(shè)置為0。(請參考recv()的manpage。)recv()返回實際讀入緩沖的數(shù)據(jù)的字節(jié)數(shù)。或者在錯誤的時候返回-1,并通過WSAGetLastError()取錯。

很簡單,不是嗎?你現(xiàn)在可以在流式套接字上發(fā)送數(shù)據(jù)和接收數(shù)據(jù)了。close()和shutdown()函數(shù)你已經(jīng)整天都在發(fā)送(send())和接收(recv())數(shù)據(jù)了,現(xiàn)在你準(zhǔn)備關(guān)閉你的套接字描述符了。這很簡單,你可以使用一般的Unix文件描述符的close()函數(shù):

close(sockfd);

它將防止套接字上更多的數(shù)據(jù)

溫馨提示

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

最新文檔

評論

0/150

提交評論