在有了前一章的基礎知識後,我想我們該進入下一步的學習了。
一、socket的說明
二、internet套介面的兩種型別
一種是 "stream sockets",另外一種是 "datagram sockets"。我們以後談到他們的時候也會用到 "sock_stream
" 和 "sock_dgram
"。
流式套介面是可靠的雙向通訊的資料流。如果你向套介面安順序輸出「1,2」,那麼他們 將安順序「1,2」到達另一邊。他們也是無錯誤的傳遞的,有自己的錯誤控制。
資料報也使用 ip 作路由,但是他不選擇 tcp。他使用「使用者資料報協議 (user datagram protocol)」。
三、網路理論知識
四、網路程式設計常用結構體
第乙個結構(tm)--struct sockaddr
struct sockaddr ;
sa_family
能夠是各種各樣的事情,但是在這篇文章中是 "af_inet
"。 sa_data
為套介面儲存目標位址和
埠資訊。
為了對付 struct sockaddr
,程式設計師創造了乙個並列的結構: struct sockaddr_in
("in" 代表 "internet".)
struct sockaddr_in ;
這個資料結構讓可以輕鬆處理套介面位址的基本元素。注意sin_zero
(他 被加入到這個結構,並且長度
和 struct sockaddr
一樣) 應該使用函式 bzero()
或memset()
來全部置零。即使 socket()
想要的是 struct sockaddr *, 你仍然可以使用 struct sockaddr_in
同時,注意 sin_family
和 struct sockaddr
中的 sa_family
一致並能夠設定為 "af_inet
"。最後, sin_port
和 sin_addr
必須是網路位元組順序 (network byte order)。
你也許會反對道:"但是,怎麼讓整個資料結構 struct in_addr sin_addr
按照網路位元組順序呢?" 要知道這個問題的答案,我們就要仔細的看一 看這個資料結構: struct in_addr, 有這樣乙個聯合 (unions):
/* internet address (a structure for historical reasons) */
struct in_addr ;
它曾經是個最壞的聯合,但是現在那些日子過去了。如果你宣告 "ina
" 是 資料結構struct sockaddr_in
的例項,那麼 "ina.sin_addr.s_addr
" 就儲存4位元組的 ip 位址(網路位元組順序)。如果你不幸的 系統使用的還是恐怖的聯合struct in_addr
,你還是可以放心4字 節的 ip 位址是和上面我說的一樣(這是因為 #define。)
五、convert the natives(本機轉換)
我們又開始學習新的知識,也許聽說過不同的主機可能在機器中儲存資訊的位元組順序是不一樣的,這樣我們在網路傳輸中也會遇到這樣的問題,那麼就需要我們對網路到本機和本機到網路位元組順序的轉換。你能夠轉換兩種型別:short(兩個位元組)和long(四個位元組)。如果你想將short從本機位元組順序轉換為網路位元組順序,用"h"表示本機("host"),接著是"to",然後用"n"表示網路("nerwork"),最後用"s"表示"short":h-to-n-s或者htons()。當然這樣我可以想到還有其他的,如下:
htons() --"host to network short"
htonl() --"host to network long"
ntohs() --"network to host short"
ntohl() --"network to host long"
現在,你可能認為你已經知道它們了,也許你會想到你的主機上已經是使用了網路位元組順序,沒有必要去使用htonl()轉換ip位址。這個你沒有錯,可是程式將要移植到其他主機上去呢,那就可能存在著隱患了。所以記住:在你將資料放到網路上的時候,確信它們是網路位元組順序的。
最後一點,為什麼在資料結構struct sockaddr_in中,sin_addr和sin_port需要轉換為網路位元組順序,而sin_family是不是也需要呢?sin_addr和sin_port分別封裝在包ip和udp層,因此它們必須是網路位元組順序。但是sin_family域只是被核心使用來決定在資料結構中包含什麼型別的位址,所以它必須是本機位元組順序。同時,sin_family沒有傳送到網路上,它們可以是本機位元組順序。
六、ip 位址和如何處理它們
現在我們很幸運,因為我們有很多的函式來方便地操作 ip 位址。沒有必要用手工計算它們,也沒有必要用"<<"操作來儲存成長整字型。首先,假設你已經有了乙個sockaddr_in結構體ina,你有乙個ip位址"132.241.5.10"要儲存在其中,你就要用到函式inet_addr(),將ip位址從 點數格式轉換成無符號長整型。使用方法如下:
ina.sin_addr.s_addr = inet_addr("132.241.5.10");
注意,inet_addr()返回的位址已經是網路位元組格式,所以你無需再呼叫 函式htonl()。
我們現在發現上面的**片斷不是十分完整的,因為它沒有錯誤檢查。 顯而易見,當inet_addr()發生錯誤時返回-1。記住這些二進位制數字?(無符號數)-1僅僅和ip位址255.255.255.255相符合!這可是廣播位址!大錯特 錯!記住要先進行錯誤檢查。
好了,現在你可以將ip位址轉換成長整型了。有沒有其相反的方法呢? 它可以將乙個in_addr結構體輸出成點數格式?這樣的話,你就要用到函式 inet_ntoa()("ntoa"的含義是"network to ascii"),就像這樣: printf("%s",inet_ntoa(ina.sin_addr));
它將輸出ip位址。需要注意的是inet_ntoa()將結構體in-addr作為乙個引數,不是長整形。同樣需要注意的是它返回的是乙個指向乙個字元的指標。它是乙個由inet_ntoa()控制的靜態的固定的指標,所以每次呼叫 inet_ntoa(),它就將覆蓋上次呼叫時所得的ip位址。例如:
char *a1, *a2;
. .
a1 = inet_ntoa(ina1.sin_addr); /* 這是198.92.129.1 */
a2 = inet_ntoa(ina2.sin_addr); /* 這是132.241.5.10 */
printf("address 1: %sn",a1);
printf("address 2: %sn",a2);
輸出如下:
address 1: 132.241.5.10
address 2: 132.241.5.10
假如你需要儲存這個ip位址,使用strcopy()函式來指向你自己的字元指標。
C語言網路程式設計基礎
一 基礎知識介紹 tcp 傳輸控制協議 和udp 使用者資料報協議是網路體系結構tcp ip模型中傳輸層一層中的兩個不同的通訊協議。tcp 傳輸控制協議,一種面向連線的協議,給使用者程序提供可靠的全雙工的位元組流,tcp套介面是位元組流套介面 stream socket 的一種。udp 使用者資料報...
C語言基礎 11 巨集
巨集也可以用來給計算公式起名字 計算公式裡包含未知數字,需要使用巨集的引數表示這些未知數字 帶引數的巨集採用二次替換方式進行處理 巨集的引數不一定代表數字,所以沒有型別名稱 如果巨集有多個引數應該用逗號把相鄰的引數名稱分隔開 巨集不可以使用自己的儲存區和函式進行資料傳遞 巨集沒有形式引數也沒有用來存...
c 網路程式設計基礎
1 iphostentry iphost dns.gethostentry www.google.com.hk 2 ipaddress ip iphost.addresslist 獲取列表 3 endpoint ep new ipendpoint ip 0 80 建立結點 4 socket sock...