目錄
1. 介紹
1.1 觀衆
1.2 平臺和編譯器
1.3 官方的主頁
1.4 Solaris/SunOS 程序員手冊
1.5 Windows程序員手冊
1.6 電子信函政策
1.7 反映
1.8 翻譯者手冊
1.9 版權和發行
2. 什麼是套接字?
2.1 二種類型的因特網套接字
2.2 低水平廢話和網絡理論
3. 結構和數據處理
3.1 轉換和本地
3.2 IP地址和和怎麼去處理他們
4. 系統調用或Bust
4.1 socket() – 取得文件描述符!
4.2 bind() -- 我在使用什麼端口?
4.3 connect() -- 喂,你!
4.4 listen() – 某人請Call我?
4.5 accept() – “感謝你呼叫端口3490”
4.6 send() 和 recv() – 寶貝,與我交談!
4.7 Sendto() 和 recvfrom() – 與我交談,DGRAM-類型
4.8 Close() 和 shutdown() – 取下我的臉!
4.9 Getpeername() – 你是誰?
4.10 Gethostname() – 我是誰?
4.11 域名服務器DNS – 你說“whitehouse.gov”, 我說“198.137.240.92”
5. 客戶-服務器後臺
5.1 一個簡單的流式服務
5.2 一個簡單的流式客戶
5.3 數據報套接字
6. 簡單的高級的技術
6.1 阻塞
6.2 Select() – 異步 I/O 多路技術
6.3 處理局部send()s
6.4 數據封閉因子
7.更多參考
7.1 操縱頁
7.2 書
7.3 Web參考
7.4 RFCs
8.常見問題
9.拒絕和請求幫助
1. 介紹
喂!套接字編程使你沮喪?這些材料只是有一點太難去解決操作頁?你想做冷靜的因特網編程,但你沒有時間去熟悉結構去解決,如果你在調用connect()前調用bind(),等等。
好,猜測一下!我已經做了這些繁瑣的事情,我將把這部分信息提供給你們每個人!你已經來到一個正確的地方。這個文檔將給那些能勝任的C程序員,他需要取得掌握這些網絡噪聲。
1.10 觀衆
這個文檔被寫作指南,而不是參考。這個可能是對於那些個別的只是想涉足套接字編程和尋找一個立足處的人是最好的。無論怎麼說,這當然不是一個完全的套接字編程指南。
雖然,它有希望只足以操縱頁去開始理解。
1.11 平臺和編譯器
包含在這個文檔中的代碼將被在Linux PC下用Gnu’s gcc編譯器編譯。無論如何,這將可在任憑平臺上使用gcc進行構造。自然地,如果你爲Windows編程,這將不能應用。瞭解windows編程的部分。
1.12 官方的主頁
這個文檔的官方地址是在芝加哥的加利福利亞州大學,http://www.ecst.csuchico.edu/~beej/guide/net/
1.13 Solaris/SunOS 程序員手冊
當在Solaris 或 SunOS下編譯時,你需要爲連接到合適的庫指定一些額外的命令行轉換。爲了實現這些,簡單的增加“-lnsl – lsocket –lresolv”到編譯命令的結尾,如下:
$ cc -9 server server.c –lnsl –lsocket –lresolv
如果你仍然得到錯誤,你應該嘗試進一步增加一個“-lxnet”到那個命令行的結尾。我不知道那將做什麼,但一些人看起來需要它。
另一個地方你將找到問題是在setsockopt()調用中。這個原型不同於在我的Linux box,所以代替:
Int yes = 1;
進入這裏:
Char yes = ‘1’;
當我沒有Sun box,我沒有嘗試其它超出的信息,只是人們通過email已經告訴我。
1.14 Windows程序員手冊
我特別厭惡Windows,鼓勵你們使用Linux,BSD, 或者Unix。但可以說,你任可以在Windows下使用這些材料。
首先,忽略恰當的我在這裏提到的大量的系統頭文件。所有我們需要包含的是:
#include <winsock.h>
等等!你還必須在利用套接字庫做任何事情前,調用WSAStartup()函數。這個代碼是使用方法如下:
#include <winsock.h>
{
WSADATA wsaData; // if this doesn’t work
//WSAData wsaData; // then try this instead
If (WSAStartup(MAKEWORD(1, 1), &wsaData) != 0)
{
Fprintf(stderr, “WSAStartup failed. /n”);
Exit(1);
}
}
你還需要告訴你的編譯器使它連接到的Winsock庫,通常調用wsock32.lib或winsock32.lib或諸如此類的東西。在VC++下,這能在菜單Project,Settings… 點擊Link tab,找到“Object/library 模塊”的列表框,增加“wsock32.lib?”到列表中。
或許我聽到。
最終,當你通過所有操作使用套接字庫,你必須調用WSACleanup()。爲了細節注意你的在線幫助。
一旦你這麼做了,在指南中的其餘的例子應該適用,但也有一些例外。例如,你不能使用closeup()來關閉一個套接字,你必須使用closesocket() 代替。同時,select()只能與套接字描述符一起工作,沒有文件描述符(例如0 for stdin)。
這也有一些套接字類你可以使用,CSocket。檢查你的編譯器幫助以獲取更多的信息。
想取得更多關於Winsock的信息,閱讀Winsock FAQ和去那兒。
最終,我聽說Windows 沒有fork()系統調用,不幸地,這將用於我的一些例子中。或許你有一個連接到POSIX庫或某些東西可以使它工作,或你能使用CreateProcess()代替fork()沒有衝突,CreateProcess()大約有480億論據。如果你沒有達到,CreateThread()比較容易消化。不幸地是在這個文檔中,很少討論關於多線程的情況,我只能討論這麼多了,你知道的!
1.15 電子信函政策
我一般使用email問題幫助別人,所以可以很自由的寫入,但我不能保證回答。我控制一個恰當的忙碌的生活,我只能回答我有時間回答的問題。當在那些情況下,我總是隻刪除消息。這是與個人無關的;我只能盡力花時間詳細的回答你提出的問題。
作爲規則,越複雜的問題,我回答的可能性越小。如果你能在發送前減少你的問題並且保證包含相關的信息(如平臺,編譯器,你取得的錯誤消息和其它任何你認爲可以對我的回答可以提供幫助的信息),你很可能獲取回答。爲了更多的要點,閱讀ESR’s 文檔,怎麼去靈活地回答問題。
如果你沒有得到回答,更多地去了解它,嘗試尋找答案,如果仍然沒有理解,然後重新發給我信息你已經找到的和希望這能幫助我找出問題。
現在我已經迫使你如果寫信給我和不寫,我只是想讓你知道我完全的感激所有的一年內收到的讚揚指南。這可以真實的推進士氣,而且當知道被很好的使用時這可以使我喜悅。謝謝你們!
1.16 反映
你可以通過進入這個鏡像網站,無論你公共的還是私人的。如果你公共的鏡像這個網站和希望我連接到這個主頁,點擊[email protected]。
1.17 翻譯者手冊
如果你想翻譯指南到另一種語言,發郵件到[email protected],,我將連接你的翻譯到主頁中。
可以自由的把你的名字和email地址加入到翻譯中。
對不起,但使用空間約束,我不能提供翻譯。
1.18 版權和發行
Beej’s 網絡編程指南Copyright@1995-2001 Brian “Beej” Hall。
這個指南或許可在任何媒體上自由再版,只要保證內容沒有改變,必須包含現在的內容,並保留版權信息。
教育家特別鼓勵推薦或提供這個指南的拷貝給他們的學生。
這個指南可以自由的翻譯到任何其他語言,只要翻譯是準確的,並且可以完全的再版。翻譯版本也可以包含名字和翻譯者的聯繫信息。
這個文檔中的C源代碼因此同意進行公共領域。
聯繫[email protected]獲取更多信息。
2. 什麼是套接字?
你經常聽說“套接字”,可能你會懷疑他們是不是正確的。好,他們這麼說:使用標準的Unix文件描述符與其它程序進行通訊的方法
好—你可能聽到一些Unix電腦黑客說,“呀,在Unix中,什麼都是文件!:”,當Unix程序使用任何I/O接口時,這些人可能會討論到,他們通過讀和寫入文件描述符來實現。文件描述符是一個被關聯到一個打開的文件的一個簡單的整數。但(這裏提到的),那個文件可以是一個網絡連接,一個FIFO,一個管道,一個終端,一個真實的磁盤文件,或是其它任何東西。在Unix中的任何東西都是文件!所以當你想通過Internet與另一個程序進行通訊時,你只能通過一個文件描述符來實現,你將得到確認。
“那我在哪兒獲得文件描述符用於網絡通訊呢,Smarty-Pants先生?”,這可能是你剛剛想到的最後一個問題,但我將在下面回答:你可以調用socket()系統例程。它返回套接字描述符,你通過使用專用的send()和recv()來通訊(操縱send,操縱recv)套接字調用。
“但是,喂!”你現在可以這樣驚叫,“如果這是一個文件描述符,爲什麼我不能使用一般的read()和write()調用來實現通訊?”。簡短的回答是,“你能!”,詳細的回答是,“你能,但send()和recv()提供更多的控制數據傳輸的功能。”
下面做什麼?這樣如何:這有所有類型的套接字。有DARPA Internet地址(Internet 套接字),在本地結點(Unix 套接字)的數徑名,CCITT X.25地址(你可以安全地忽略X.25套接字),或許還有很多其它的領帶於Unix想讓你運行。這個文檔只處理第一個:Internet套接字。
2.3 二種類型的因特網套接字
這是什麼?有兩種類型的Internet套接字?是的,好,不是的。我說謊。有更多,但我不想威嚇你。我只討論這兩種。除此之外,我將告訴你“Raw Sockets”功能也很強大,你可以查看一下。
好,準備好了。兩種類型是什麼?一個是“流式套接字”;另一個是“數據報套接字”,今後可能分別的被提及的“SOCK_STREAM”和“SOCK——DGRAM”。數據報套接字有時被稱爲“無連接套接字”。(雖然如果確實想,他們能connect(),下面看connect())
流式套接字是可靠的雙向的面向連接的通訊流。如果你輸出兩條順序爲“1,2”的消息到套接字中,他們在相反的另一端的到達順序也是“1,2”。他們也會出現錯誤。所有錯誤你遇到的都是你自己瘋狂的虛構的事,我們不在這兒討論。
怎麼使用流式套接字?好,你聽說過遠程登錄應用程序吧?它就是使用流式套接字的。所有你輸入的字符都要以相同的順序到達,是吧?同時,網頁瀏覽器使用HTTP協議,使用的也是流式套接字來取得頁面。甚至,如果你遠程登錄到一個網站的80端口,輸入“GET /”,它把HTML轉儲於你!
流式套接字是如何實現高可靠性的數據傳輸?他們使用一個稱爲“傳輸控制協議”,稱爲“TCP”(查看RFC-793獲得更多關於TCP的細節)。TCP保證你的數據到達是順序的和無差錯的。你可能在知道“TCP/IP”另一半“IP”標準是“因特網協議”(查看RFC-791)以前,聽說過“TCP”。IP主要處理因特網路由,不能保證數據的完整性。
好的。什麼是數據報套接字呢?爲什麼被稱之爲無連接的呢?在這兒做了什麼?爲什麼是不可靠的?好,這兒有一些原因:如果你發送一個數據報,它可能到達。它可能不是按順序到達。如果它到達,包中的數據可能有錯誤。
數據報套接字也使用IP路由,但它不使用TCP;它使用“用戶數據報協議”,也稱爲“UDP”(查看RFC-768)。
爲什麼是無連接的?好,主要地,是因爲你沒有像使用流式套接字一樣必須維持一個打開的連接。你只要建立一個信息包,把目的信息加入到IP頭中,把它發出去。不需要連接。他們一般使用包信息傳輸。應用實例:tftp,bootp等。
“夠了!”你可能叫到,“如果數據報丟失,那些程序怎麼工作?”好,我的人類的朋友,在UPD的上層有自己的協議。例如,tfpt協議提到爲每個發送和接收的包,每次做一個這樣的包應答,“我收到了!“(一個”ACK“包)。如果發送的包沒有收到應答,一般,五秒,它將重新發送包直到收到應答爲止。在實現SOCK_DGRAM應用中,這個應答程序是很重要的。
2.4 低水平廢話和網絡理論
由於我只提及協議分層,是時候說一個網絡是怎麼工作的了,講一些SOCK_DGRAM包怎麼建立的例子。特別地,你可能略過這部分。不論如何,有一個好的背景。
嗨,小子,是時候學習數據封裝了!這是很重要的。如果你在這兒取得了網絡課程,這是可能是你在這兒所學到的。主要地,這麼說:一個包是天生的,包是在頭(很少在腳)使用第一個協議(如,TFTP協議)封裝的(“壓縮”),然後所有的事情(包括TFTP頭)被使用下一個協議(如,UDP)重新壓縮,然後重新使用下一個協議(IP)壓縮,然後使用最終的物理層(如,以太網)重新壓縮。
當另一臺機器接收到包,硬件剝去以太網頭,內核剝去IP和UPD頭,TFTP程序剝去TFTP頭,最終得到數據。
現在我可以討論網絡分層模式了。網絡分層模型描述了一個功能性的網絡系統,比其它的模型有很多優點。例如,你可以寫相同的套接字程序,而不管物理層數據是怎麼傳輸的(串和行,以太網,AUI,或其它)。因爲程序爲你在低層次進行了處理。實現的網絡硬件和拓撲對套接字程序員是透明的。
除了更多的發展目標,我將在這兒列出模型中的所有的層次。記住這些:
應用層
表現層
會話層
傳輸層
網絡層
數據鏈路層
物理層
物理層是硬件(串行,以太網等)。應用層只是你可以想象的離物理層很遠的,它主要用於網絡中用戶相互影響。
現在,只要你真的想,這個模型你可以像汽車修理指南一樣使用,Unix一致的層模型如下:
應用層(telnet,ftp,etc)
主機到主機傳輸層(TCP,UDP)
網絡層(IP和路由)
網絡鏈路層(以太網,ATM或其它)
現在這個時候,你可能瞭解到這些層通信過程中的數據封裝過程。
瞭解建立一個簡單包需做多少工作?呀!你0必須自己輸入包頭使用“cat“!僅僅欺騙,所有你要做的只是爲流式套接字調用send()發出數據。所有你要做的只是爲數據報套接字使用你選擇的方法進行封裝,並調用sendto()發出去。內核爲你構造傳輸層和網絡層,硬件實現數據鏈路層。啊,現代技術。
所以以網絡理論結束我們的大綱。是的,我忘了告訴你我想說的關於路由的東西:沒什麼!正好,我還不準備討論它。路由器剝去了包的IP頭,參考路由表,無聊無聊無聊。如果你真的想了解,檢查IP RFC。如果你不想學,好,你是實在的。
3. 結構和數據處理
好,我們最終到這兒了。是時候討論編程了。在這一章,我將會提及所有套接字接口中使用的數據類型,因爲有些很難指出。
首先,簡單的:套接字描述符。套接字描述符是如下類型:
Int
只是一個規則的整型。
有些事情在這兒是神祕的,因此只要讀和忍受我。知道這些:這有兩字節的排序:大部分有意義的字節(有時也稱之爲“八位組”),或最小的有意義的字節。以前稱之爲“網絡字節順序”。一些機器在網絡字節序列中保存他們的數字,一些沒有。當我說一些必須有網絡字節序列時,你必須調用 一個函數(例如,htons())來改變它。如果我不說“主機字節序列”,然後你必須在主機字節序列中丟棄值。
(由於好奇,“網絡字節序列”也稱之爲“Bib-Endian字節序列)
我的第一個結構-sturct sockadd。這個結構爲許多套接字類型保存套接字地址信息。
Sa_family可以是多種類型,但在這個文檔中它不可以是AF_INET。
Sa_data包括套接字的目的地址和端口號。這是不實用的,因爲你不想冗長面乏味地手工地在sa_data中包裝地址。
爲處理sockaddr結構,程序員需創建一個類似的結構:struct sockaddr_in(“in”爲“Internet”
)
這個結構使套接字地址的參考基礎變得容易。注意sin_zero(包含在結構中的指示struct sockaddr的長度)應該使用函數memset()全部設爲0。同時,這是一個重要的位,這個指示器指出sockaddr_in結構能計算出sockaddr結構的指針。所以socket()需要一個struct sockaddr*,你仍然可以使用struct sockaddr_in,並在最後一分鐘算出它!同時,注意sin_family在struct sockaddr中協調sa_family,應該被設置爲“AF_INET”。最終,sin_port和sin_addr必須在網絡字節序列中!
好,它被用於聯合,但現在不是了。很好的解除了。所以如果你聲明ina到struct sockaddr_in中,然後ina.sin_addr.s_addr涉及一個4-字節的IP地址(在網絡字節序列中)。注意即使這樣,如果你的系統爲struct in_addr仍然使用God-awful聯合,你仍然可以像我上面一樣(這需要#defines)以相同的方法涉及4-字節IP地址。
3.3 轉換和本地
我們現在進入下一章的學習。討論了太多的關於網絡到主機字節序列轉換,現在可以行動了。
是的,這有兩種類型你可以轉換:short (two bytes) and long (four bytes)。這些函數可以無符號變量下工作。如果你想從主機字節序列轉換到short。爲“host“以”h“開始,緊跟着“to“,然後爲“network”加“n”,爲“short”加“s”:h-to-n-s,或htons()(讀作:“host to network short”)。
這個很簡章……
如果你想,“n”,“h”,“s”和“l”,你可以使用所有的組合,不能計算真實的結構。例如,這兒沒有stolh()(“short to long host”)函數,沒有這部分,無論如何,但有:
現在,你可以想你瞭解了這些。你可能會想,“如果我想在一個字節中改變字節序列,應該做什麼?”然後你可能會想,“哦,沒關係。”你或許也會想因爲你的68000機器準備使用網絡字節序列,你沒有必要在你的IP地址中調用htonl()。你是正確的,但如果你嘗試連接已經反轉了網絡字節序列的機器,你的程序將失敗。方便的!這是Unix的世界!(否則 Bill Gates 將這麼想)記住:在你將它們放入網絡前,將你的字節放入網絡字節序列中。
最終要點:爲什麼使用sin_addr和sin_port需要在網絡字節序列的struct sockaddr_in中,但sin_family不能?答案是:sin_addr和sin_port取得在包中取得壓縮分別在IP和UDP層,因此,他們必須在網絡字節序列中,不論如何,sin_family字段中能用於內核來決定結構中包括的地址類型,所以必須在主機字節序列中。同時,因爲sin_family沒有取得在網絡中發送,綜可以在主機字節序列中。
3.4 IP地址和和怎麼去處理他們
你是幸運的,這兒有一批函數讓你去操作IP地址。不需要手工地計算,填充他們長期使用操作《。
首先,你有一個struct sockaddr_in ina,你有一個IP地址“10.12.110.57”,你想把這個IP存儲在這個結構中。你想使用函數inet_addr(),轉換IP地址從數據-點符號到無符號長整型。過程如下:
注意inet_addr()返回網絡字節序列中的地址,你不一定要調用htonl()。增大!
現在,以上的代碼片斷不是足夠的,因爲沒有錯誤檢查。如,inet_addr()返回-1表示錯誤。記得二進制數字?(unsigned)-1只發出在協調IP地址255.255.255.255!那是一個廣播地址!記得使用合適的錯誤檢查。
事實上,有一個清楚的接口,你可以用來代替inet_addr():它被 inet_aton()調用(“aton”意思爲“ascii to network”)
當你包裝struct sockaddr_in,這裏有簡單的用法,(當你想在bind()和connect()中取得片斷時,這個例子將提供給你更多的含意。)
Inet_aton(),不像實際上的每一個其它的socket-related函數,返回非零表示成功,返回零表示失敗。地址在inp中傳遞。
不幸地是,不是所有平臺實現inet_aton()都是這樣的,雖然它的使用是更好的,以前的更一般在這個指南中使用inet_addr()。
好的,現在你能轉換字符串IP地址到它們的二進制表示。還有其它的方法嗎?如果你有struct in_addr,你想以數字-點符號打印它?在這種情況下,你想使用函數inet_ntoa()(“ntoa”意思爲:“network to ascii”)如下:
這將打印IP地址。注意inet_ntoa() 以參數的形式取得struct in_addr,不是long,同時注意它返回一個字符型指針。這個指針指向在inet_ntoa()中的一個靜態存儲的字符數組,所以當你調用 inet_ntoa(),它將寫上你要求的IP地址。例如:
如果你需要保存地址,strcpy()到你的字符數組中。
這是現在所有現在所討論的話題。下面,你將學到轉換一個字符,如:“whitehouse.gov”,到它的通訊的IP地址(查看DNS,下面)。
4. 系統調用或Bust
4.12 socket() – 取得文件描述符!
4.13 bind() -- 我在使用什麼端口?
4.14 connect() -- 喂,你!
4.15 listen() – 某人請Call我?
4.16 accept() – “感謝你呼叫端口3490”
4.17 send() 和 recv() – 寶貝,與我交談!
4.18 Sendto() 和 recvfrom() – 與我交談,DGRAM-類型
4.19 Close() 和 shutdown() – 取下我的臉!
4.20 Getpeername() – 你是誰?
4.21 Gethostname() – 我是誰?
4.22 域名服務器DNS – 你說“whitehouse.gov”, 我說“198.137.240.92”
5. 客戶-服務器後臺
5.4 一個簡單的流式服務
5.5 一個簡單的流式客戶
5.6 數據報套接字
6. 簡單的高級的技術
6.5 阻塞
6.6 Select() – 異步 I/O 多路技術
6.7 處理局部send()s
6.8 數據封閉因子
7.更多參考
7.1 操縱頁
7.2 書
7.3 Web參考
7.4 RFCs
8.常見問題
9.拒絕和請求幫助