作者:曹元其 來源:開放系統(tǒng)世界——賽迪網(wǎng)
???? Linux網(wǎng)絡(luò)服務(wù)能力非常強(qiáng)大,它的TCP/IP代碼是最高級(jí)的。Linux的網(wǎng)絡(luò)實(shí)現(xiàn)是模仿FreeBSD的,它支持FreeBSD的帶有擴(kuò)展的Sockets(套接字)和TCP/IP協(xié)議。它支持兩個(gè)主機(jī)間的網(wǎng)絡(luò)連接和Sockets通訊模型,實(shí)現(xiàn)了兩種類型的Sockets:BSD Sockets和INET Sockets。它為不同的通信模型和服務(wù)質(zhì)量提供了兩種傳輸協(xié)議,即不可靠的、基于消息的UDP傳輸協(xié)議和可靠的、基于流的傳輸協(xié)議TCP,并且都是在IP網(wǎng)絡(luò)協(xié)議上實(shí)現(xiàn)的。INET sockets是在以上兩個(gè)協(xié)議及IP協(xié)議之上實(shí)現(xiàn)的。它們之間的關(guān)系見圖1所示。
圖1 Linux網(wǎng)絡(luò)層
Socket在網(wǎng)絡(luò)編程中的實(shí)現(xiàn)
套接字是網(wǎng)絡(luò)通信的基本構(gòu)件,它提供了不同主機(jī)間進(jìn)程雙向通信的端點(diǎn)。如同電話一樣,只有當(dāng)一方撥通另一方的電話時(shí),雙方才能建立對(duì)話,套接字就好比是雙方的電話。通過Sockets編程,程序可以跳過復(fù)雜的網(wǎng)絡(luò)底層協(xié)議和結(jié)構(gòu),直接編制與平臺(tái)無關(guān)的應(yīng)用程序。隨著Internet的廣泛應(yīng)用,Sockets已逐漸成為網(wǎng)絡(luò)編程的通用接口。
套接字存在于特定的通信域(即地址族)中,只有隸屬于同一地址族的套接字才能建立對(duì)話。Linux支持AF_INET(IPv4協(xié)議)、AF_INET6(IPv6協(xié)議)和AF_LOCAL(Unix域協(xié)議)。
Linux支持以下的socket families或domain:
◆ Unix domain sockets;
◆ INET TneIntemet address family supports communications via;
◆ TCP/IP protocols;
◆ Amateur radio X.25;
◆ Novel IPX;
◆ Appletalk DDP;
◆ X.25。
套接口就是網(wǎng)絡(luò)進(jìn)程的ID。網(wǎng)絡(luò)通信也是一種進(jìn)程的通信,兩個(gè)網(wǎng)絡(luò)進(jìn)程通信時(shí)首先要確定各自所在網(wǎng)絡(luò)節(jié)點(diǎn)的網(wǎng)絡(luò)地址(IP地址)。網(wǎng)絡(luò)地址可以確定進(jìn)程所在的計(jì)算機(jī),一臺(tái)計(jì)算機(jī)上可能同時(shí)有多個(gè)網(wǎng)絡(luò)進(jìn)程。為了區(qū)別不同的進(jìn)程,套接口中還需要端口號(hào)(Port)信息。在一臺(tái)計(jì)算機(jī)中,一個(gè)端口一次只能分配給一個(gè)進(jìn)程。所以在一臺(tái)計(jì)算機(jī)中,端口號(hào)和進(jìn)程可以惟一確定整個(gè)Intemet中的一個(gè)網(wǎng)絡(luò)進(jìn)程。可以認(rèn)為,套接口=網(wǎng)絡(luò)地址+端口號(hào)。
Linux網(wǎng)絡(luò)數(shù)據(jù)結(jié)構(gòu)
在網(wǎng)絡(luò)實(shí)際傳送的數(shù)據(jù)中,有兩種字節(jié)排列順序:重要的字節(jié)在前面,或者不重要的字節(jié)在前面。前一種叫網(wǎng)絡(luò)字節(jié)順序(Network Byte Order,NBO),有些機(jī)器在內(nèi)部是按照這個(gè)順序儲(chǔ)存數(shù)據(jù)的。當(dāng)某數(shù)據(jù)必須按照NBO順序時(shí),那么要調(diào)用函數(shù)(例如htons())將它從本機(jī)字節(jié)順序(Host Byte Order,HBO)轉(zhuǎn)換過來,否則傳送過去的數(shù)據(jù)將使對(duì)方機(jī)器不可讀。這點(diǎn)對(duì)于網(wǎng)絡(luò)數(shù)據(jù)傳送來說是非常關(guān)鍵的。
在網(wǎng)絡(luò)中第一個(gè)被創(chuàng)造的結(jié)構(gòu)類型是sockaddr。這個(gè)數(shù)據(jù)結(jié)構(gòu)是為許多類型的套接口儲(chǔ)存地址信息。它的定義如下:
struct sockaddr{
unsigned short sa_family; /*這個(gè)是地址族,通常是AF-xxxx的形式*/
char sa_data[14]; /*14字節(jié)的地址信息*/
};
|
上面代碼中,sa_famdly是“AF_INET”,表示它使用的是Internet地址族;sa_data用于為套接口儲(chǔ)存目標(biāo)地址和端口信息。
為了解決struct sockaddr,創(chuàng)造了一個(gè)并列的結(jié)構(gòu)struct sockadd_in(“in”代表“Internet”),如下所示:
struct sockaddr_in{
short int sin_family; /*地址族信息,通常是AF-xxxx的形式*/
unsigned short int sin_port; /*端口信息*/
struct in_addr sin_addr; /*網(wǎng)絡(luò)地址*/
unsigned char sin_zero[8]; /*補(bǔ)位用的0*/
}
|
上面這個(gè)數(shù)據(jù)結(jié)構(gòu)可以輕松處理套接口地址的基本元素。需要解釋的是,sin_zero被加入到這個(gè)結(jié)構(gòu)中主要是為了保證struct sockaddr的數(shù)據(jù)長度和struct sockaddr_in的一樣,這樣在使用標(biāo)準(zhǔn)函數(shù)時(shí),就可以使用統(tǒng)一的數(shù)據(jù)接口。需要注意的是,應(yīng)該使用函數(shù)bzero()將sin_zero全部置零。最后,sin_port和sin_addb必須是網(wǎng)絡(luò)字節(jié)順序(Network Byte Order)。如果聲明“inadd”是數(shù)據(jù)結(jié)構(gòu)stmct sockaddr_in的實(shí)例,那么inadd.sinadd.s_addr就儲(chǔ)存了4個(gè)字節(jié)的IP地址(網(wǎng)絡(luò)字節(jié)順序)。
另一個(gè)常用到的是unsigned類型。它比上面介紹的struct sockaddr_in或struct sockaddr用得更普遍。對(duì)于變量類型unsigned,可以使用的兩種類型是short(兩個(gè)字節(jié))和long(四個(gè)字節(jié))。假設(shè)想將short從本機(jī)字節(jié)順序轉(zhuǎn)換為網(wǎng)絡(luò)字節(jié)順序,需用“h”表示本機(jī)(host),用“to”表示進(jìn)行轉(zhuǎn)換,然后用“n”表示網(wǎng)絡(luò),用“s”表示short,那么就是h-to-n-s或者h(yuǎn)tons()(“Host to Network Short”)。
考慮到對(duì)不同機(jī)器的可移值性,這樣的轉(zhuǎn)換是必需的。我們對(duì)“n”、“h”、“s”和“l(fā)”這幾個(gè)字母進(jìn)行組合,就可以得到Linux下的全部轉(zhuǎn)換函數(shù)。
IP地址在Linux網(wǎng)絡(luò)中的處理方法
假設(shè)使用struct sockaddr_in ina,想將IP地址“164.112.175.124”儲(chǔ)存到其中,那么所要做的是調(diào)用函數(shù)inet_addr(),轉(zhuǎn)換上面“數(shù)字 + 句點(diǎn)”格式的IP地址到unsigned long中。這個(gè)工作可以這樣來做:
ina.sin_addr.s_addr=inet_addr(”164.112.175.124”);
|
inet_addr()返回的地址已經(jīng)是按照網(wǎng)絡(luò)字節(jié)順序的,不用調(diào)用htonl()。在發(fā)生錯(cuò)誤的時(shí)候inet_addr()返回-1。調(diào)用后,需使用正確的錯(cuò)誤檢查,比如說當(dāng)IP地址為255.255.255.255的時(shí)候,返回的就是(unsigned)-1。因?yàn)檫@是個(gè)廣播地址,你的程序必需能夠?qū)⑦@類錯(cuò)誤捕獲出來。
你現(xiàn)在就可以轉(zhuǎn)換字符串形式的IP地址為1ong了。若有一個(gè)數(shù)據(jù)結(jié)構(gòu)struct in_addr,按照“數(shù)字+句點(diǎn)”格式打印時(shí),你要用函數(shù)inet_ntoa()(ntoa意思是network to ascⅡ),如下所示:
printf(“%s”,inet_ntoa(ina.sin_addr));
|
這樣就可以打印IP地址。注意:函數(shù)inet—ntoa()的參數(shù)是struct in_addr,而不是long,它返回的是一個(gè)指向字符的指針。
在inet_ntoa內(nèi)儲(chǔ)存了字符數(shù)組,因此它每次調(diào)用inet_ntoa()的時(shí)候?qū)⒏采w以前的內(nèi)容。
例如:
Char a1, *a2;
......
a1=inet_ntoa(ina1.sin_addr); /*假設(shè)地址是;164.112.175.124*/
a2=inet_ntoa(ina2.sin_addr);/*假設(shè)地址是:202.112.58.200*/
printf(“address 1:%s\n”,a1);
printf(“address 2:%s\n”,a2);
|
上面運(yùn)行結(jié)果是:
address l:202.112.58.200
address 2:202.112.58.200
|
如果想保存地址,那么可用strcpy()保存到自己的字符數(shù)組中。
以上介紹了Linux網(wǎng)絡(luò)編程的基礎(chǔ)知識(shí)和對(duì)網(wǎng)絡(luò)IP地址處理的一些技巧。如果能夠?qū)⑵渫琇inux下眾多的小工具整合在一起的話,那么所開發(fā)出來的程序的功能已經(jīng)不亞于一些專業(yè)的軟件了。