亚洲av成人无遮挡网站在线观看,少妇性bbb搡bbb爽爽爽,亚洲av日韩精品久久久久久,兔费看少妇性l交大片免费,无码少妇一区二区三区

  免費(fèi)注冊(cè) 查看新帖 |

Chinaunix

  平臺(tái) 論壇 博客 文庫(kù)
最近訪問(wèn)板塊 發(fā)新帖
查看: 1355 | 回復(fù): 0
打印 上一主題 下一主題

linux操作系統(tǒng)下c語(yǔ)言編程入門(mén)==網(wǎng)絡(luò)編程(轉(zhuǎn)) [復(fù)制鏈接]

論壇徽章:
0
跳轉(zhuǎn)到指定樓層
1 [收藏(0)] [報(bào)告]
發(fā)表于 2008-06-28 11:34 |只看該作者 |倒序?yàn)g覽
Linux程序設(shè)計(jì)入門(mén)--網(wǎng)絡(luò)編程
Linux系統(tǒng)的一個(gè)主要特點(diǎn)是他的網(wǎng)絡(luò)功能非常強(qiáng)大。隨著網(wǎng)絡(luò)的日益普及,基于網(wǎng)絡(luò)的
應(yīng)用也將越來(lái)越多。 在這個(gè)網(wǎng)絡(luò)時(shí)代,掌握了Linux的網(wǎng)絡(luò)編程技術(shù),將令每一個(gè)人處
于不敗之地,學(xué)習(xí)Linux的網(wǎng)絡(luò)編程,可以讓我們真正的體會(huì)到網(wǎng)絡(luò)的魅力。 想成為一
位真正的hacker,必須掌握網(wǎng)絡(luò)編程技術(shù)。
現(xiàn)在書(shū)店里面已經(jīng)有了許多關(guān)于Linux網(wǎng)絡(luò)編程方面的書(shū)籍,網(wǎng)絡(luò)上也有了許多關(guān)于
網(wǎng)絡(luò)編程方面的教材,大家都可以 去看一看的。在這里我會(huì)和大家一起來(lái)領(lǐng)會(huì)Linux網(wǎng)
絡(luò)編程的奧妙,由于我學(xué)習(xí)Linux的網(wǎng)絡(luò)編程也開(kāi)始不久,所以我下面所說(shuō)的肯定會(huì)有錯(cuò)
誤的, 還請(qǐng)大家指點(diǎn)出來(lái),在這里我先謝謝大家了。
在這一個(gè)章節(jié)里面,我會(huì)和以前的幾個(gè)章節(jié)不同,在前面我都是概括的說(shuō)了一下,
從現(xiàn)在開(kāi)始我會(huì)盡可能的詳細(xì)的說(shuō)明每一個(gè)函數(shù)及其用法。好了讓我們?nèi)ヮI(lǐng)會(huì)Linux的偉
大的魅力吧!
網(wǎng)絡(luò)編程(1)
1. Linux網(wǎng)絡(luò)知識(shí)介紹
1.1 客戶端程序和服務(wù)端程序
網(wǎng)絡(luò)程序和普通的程序有一個(gè)最大的區(qū)別是網(wǎng)絡(luò)程序是由兩個(gè)部分組成的--客戶端和服
務(wù)器端.
網(wǎng)絡(luò)程序是先有
服務(wù)器
程序啟動(dòng),等待客戶端的程序運(yùn)行并建立連接.一般的來(lái)說(shuō)是服務(wù)
端的程序 在一個(gè)端口上監(jiān)聽(tīng),直到有一個(gè)客戶端的程序發(fā)來(lái)了請(qǐng)求.
1.2 常用的命令
由于網(wǎng)絡(luò)程序是有兩個(gè)部分組成,所以在調(diào)試的時(shí)候比較麻煩,為此我們有必要知道一些
常用的網(wǎng)絡(luò)命令
netstat
命令netstat是用來(lái)顯示網(wǎng)絡(luò)的連接,路由表和接口統(tǒng)計(jì)等網(wǎng)絡(luò)的信息.netstat有許多的
選項(xiàng) 我們常用的選項(xiàng)是 -an 用來(lái)顯示詳細(xì)的網(wǎng)絡(luò)狀態(tài).至于其它的選項(xiàng)我們可以使用幫
助手冊(cè)獲得詳細(xì)的情況.
telnet
telnet是一個(gè)用來(lái)遠(yuǎn)程控制的程序,但是我們完全可以用這個(gè)程序來(lái)調(diào)試我們的服務(wù)端程
序的. 比如我們的服務(wù)器程序在監(jiān)聽(tīng)8888端口,我們可以用telnet localhost 8888來(lái)查
看服務(wù)端的狀況.
1.3 TCP/
UDP
介紹
TCP(Transfer Control Protocol)傳輸控制協(xié)議是一種面向連接的協(xié)議,當(dāng)我們的網(wǎng)絡(luò)程
序使用 這個(gè)協(xié)議的時(shí)候,網(wǎng)絡(luò)可以保證我們的客戶端和服務(wù)端的連接是可靠的,安全的.
UDP(User Datagram Protocol)用戶數(shù)據(jù)報(bào)協(xié)議是一種非面向連接的協(xié)議,這種協(xié)議并不
能保證我們 的網(wǎng)絡(luò)程序的連接是可靠的,所以我們現(xiàn)在編寫(xiě)的程序一般是采用TCP協(xié)議的
..
網(wǎng)絡(luò)編程(2)
2. 初等網(wǎng)絡(luò)函數(shù)介紹(TCP)
Linux系統(tǒng)是通過(guò)提供套接字(socket)來(lái)進(jìn)行網(wǎng)絡(luò)編程的.網(wǎng)絡(luò)程序通過(guò)socket和其它
幾個(gè)函數(shù)的調(diào)用,會(huì)返回一個(gè) 通訊的文件描述符,我們可以將這個(gè)描述符看成普通的文件
的描述符來(lái)操作,這就是linux的設(shè)備無(wú)關(guān)性的 好處.我們可以通過(guò)向描述符讀寫(xiě)操作實(shí)
現(xiàn)網(wǎng)絡(luò)之間的數(shù)據(jù)交流.
2.1 socket
int socket(int domain, int type,int protocol)
domain:說(shuō)明我們網(wǎng)絡(luò)程序所在的主機(jī)采用的通訊協(xié)族(AF_UNIX和AF_INET等). AF_UN
IX只能夠用于單一的Unix系統(tǒng)進(jìn)程間通信,而AF_INET是針對(duì)Internet的,因而可以允許在
遠(yuǎn)程 主機(jī)之間通信(當(dāng)我們 man socket時(shí)發(fā)現(xiàn) domain可選項(xiàng)是 PF_*而不是AF_*,因?yàn)?
glibc是posix的實(shí)現(xiàn) 所以用PF代替了AF,不過(guò)我們都可以使用的).
type:我們網(wǎng)絡(luò)程序所采用的通訊協(xié)議(SOCK_STREAM,SOCK_DGRAM等) SOCK_STREAM表明
我們用的是TCP協(xié)議,這樣會(huì)提供按順序的,可靠,雙向,面向連接的比特流. SOCK_DGRAM
表明我們用的是UDP協(xié)議,這樣只會(huì)提供定長(zhǎng)的,不可靠,無(wú)連接的通信.
protocol:由于我們指定了type,所以這個(gè)地方我們一般只要用0來(lái)代替就可以了 sock
et為網(wǎng)絡(luò)通訊做基本的準(zhǔn)備.成功時(shí)返回文件描述符,失敗時(shí)返回-1,看errno可知道出錯(cuò)
的詳細(xì)情況.
2.2 bind
int bind(int sockfd, struct sockaddr *my_addr, int addrlen)
sockfd:是由socket調(diào)用返回的文件描述符.
addrlen:是sockaddr結(jié)構(gòu)的長(zhǎng)度.
my_addr:是一個(gè)指向sockaddr的指針. 在中有 sockaddr的定義
struct sockaddr{
unisgned short as_family;
char sa_data[14];
};
不過(guò)由于系統(tǒng)的兼容性,我們一般不用這個(gè)頭文件,而使用另外一個(gè)結(jié)構(gòu)(struct sock
addr_in) 來(lái)代替.在中有sockaddr_in的定義
struct sockaddr_in{
unsigned short sin_family;
unsigned short int sin_port;
struct in_addr sin_addr;
unsigned char sin_zero[8];
我們主要使用Internet所以sin_family一般為AF_INET,sin_addr設(shè)置為INADDR_ANY表
示可以 和任何的主機(jī)通信,sin_port是我們要監(jiān)聽(tīng)的端口號(hào).sin_zero[8]是用來(lái)填充的
.. bind將本地的端口同socket返回的文件描述符捆綁在一起.成功是返回0,失敗的情況和
socket一樣
2.3 listen
int listen(int sockfd,int backlog)
sockfd:是bind后的文件描述符.
backlog:設(shè)置請(qǐng)求排隊(duì)的最大長(zhǎng)度.當(dāng)有多個(gè)客戶端程序和服務(wù)端相連時(shí), 使用這個(gè)表示
可以介紹的排隊(duì)長(zhǎng)度. listen函數(shù)將bind的文件描述符變?yōu)楸O(jiān)聽(tīng)套接字.返回的情況和b
ind一樣.
2.4 accept
int accept(int sockfd, struct sockaddr *addr,int *addrlen)
sockfd:是listen后的文件描述符.
addr,addrlen是用來(lái)給客戶端的程序填寫(xiě)的,服務(wù)器端只要傳遞指針就可以了. bind,li
sten和accept是服務(wù)器端用的函數(shù),accept調(diào)用時(shí),服務(wù)器端的程序會(huì)一直阻塞到有一個(gè)
客戶程序發(fā)出了連接. accept成功時(shí)返回最后的服務(wù)器端的文件描述符,這個(gè)時(shí)候服務(wù)
器端可以向該描述符寫(xiě)信息了. 失敗時(shí)返回-1
2.5 connect
int connect(int sockfd, struct sockaddr * serv_addr,int addrlen)
sockfd:socket返回的文件描述符.
serv_addr:儲(chǔ)存了服務(wù)器端的連接信息.其中sin_add是服務(wù)端的地址
addrlen:serv_addr的長(zhǎng)度
connect函數(shù)是客戶端用來(lái)同服務(wù)端連接的.成功時(shí)返回0,sockfd是同服務(wù)端通訊的文件
描述符 失敗時(shí)返回-1.
2.6 實(shí)例
服務(wù)器端程序
view plain
print
?
/******* 服務(wù)器程序 (server.c) ************/   #include    #include    #include    #include    #include    #include    #include    #include    int main(int argc, char *argv[])   {   int sockfd,new_fd;   struct sockaddr_in server_addr;   struct sockaddr_in client_addr;   int sin_size,portnumber;   char hello[]="Hello! Are You Fine?\n";   if(argc!=2)   {   fprintf(stderr,"Usage:%s portnumber\a\n",argv[0]);   exit(1);   }   if((portnumber=atoi(argv[1])){   fprintf(stderr,"Usage:%s portnumber\a\n",argv[0]);   exit(1);   }   /* 服務(wù)器端開(kāi)始建立socket描述符 */   if((sockfd=socket(AF_INET,SOCK_STREAM,0))==-1)   {   fprintf(stderr,"Socket error:%s\n\a",strerror(errno));   exit(1);   }   /* 服務(wù)器端填充 sockaddr結(jié)構(gòu) */   bzero(&server_addr,sizeof(struct sockaddr_in));   server_addr.sin_family=AF_INET;   server_addr.sin_addr.s_addr=htonl(INADDR_ANY);   server_addr.sin_port=htons(portnumber);   /* 捆綁sockfd描述符 */   if(bind(sockfd,(struct sockaddr *)(&server_addr),sizeof(struct sockaddr))==   -1)   {   fprintf(stderr,"Bind error:%s\n\a",strerror(errno));   exit(1);   }   /* 監(jiān)聽(tīng)sockfd描述符 */   if(listen(sockfd,5)==-1)   {   fprintf(stderr,"Listen error:%s\n\a",strerror(errno));   exit(1);   }   while(1)   {   /* 服務(wù)器阻塞,直到客戶程序建立連接 */   sin_size=sizeof(struct sockaddr_in);   if((new_fd=accept(sockfd,(struct sockaddr *)(&client_addr),&sin_size   ))==-1)   {   fprintf(stderr,"Accept error:%s\n\a",strerror(errno));   exit(1);   }   fprintf(stderr,"Server get connection from %s\n",   inet_ntoa(client_addr.sin_addr));   if(write(new_fd,hello,strlen(hello))==-1)   {   fprintf(stderr,"Write Error:%s\n",strerror(errno));   exit(1);   }   /* 這個(gè)通訊已經(jīng)結(jié)束 */   close(new_fd);   /* 循環(huán)下一個(gè) */   }   close(sockfd);   exit(0);   }   /******* 服務(wù)器程序 (server.c) ************/
#include  
#include  
#include  
#include  
#include  
#include  
#include  
#include  
int main(int argc, char *argv[])
{
int sockfd,new_fd;
struct sockaddr_in server_addr;
struct sockaddr_in client_addr;
int sin_size,portnumber;
char hello[]="Hello! Are You Fine?\n";
if(argc!=2)
{
fprintf(stderr,"Usage:%s portnumber\a\n",argv[0]);
exit(1);
}
if((portnumber=atoi(argv[1]))
客戶端程序
view plain
print
?
/******* 客戶端程序 client.c ************/   #include    #include    #include    #include    #include    #include    #include    #include    int main(int argc, char *argv[])   {   int sockfd;   char buffer[1024];   struct sockaddr_in server_addr;   struct hostent *host;   int portnumber,nbytes;   if(argc!=3)   {   fprintf(stderr,"Usage:%s hostname portnumber\a\n",argv[0]);   exit(1);   }   if((host=gethostbyname(argv[1]))==NULL)   {   fprintf(stderr,"Gethostname error\n");   exit(1);   }   if((portnumber=atoi(argv[2])){   fprintf(stderr,"Usage:%s hostname portnumber\a\n",argv[0]);   exit(1);   }   /* 客戶程序開(kāi)始建立 sockfd描述符 */   if((sockfd=socket(AF_INET,SOCK_STREAM,0))==-1)   {   fprintf(stderr,"Socket Error:%s\a\n",strerror(errno));   exit(1);   }   /* 客戶程序填充服務(wù)端的資料 */   bzero(&server_addr,sizeof(server_addr));   server_addr.sin_family=AF_INET;   server_addr.sin_port=htons(portnumber);   server_addr.sin_addr=*((struct in_addr *)host->h_addr);   /* 客戶程序發(fā)起連接請(qǐng)求 */   if(connect(sockfd,(struct sockaddr *)(&server_addr),sizeof(struct sockaddr)   )==-1)   {   fprintf(stderr,"Connect Error:%s\a\n",strerror(errno));   exit(1);   }   /* 連接成功了 */   if((nbytes=read(sockfd,buffer,1024))==-1)   {   fprintf(stderr,"Read Error:%s\n",strerror(errno));   exit(1);   }   buffer[nbytes]='\0';   printf("I have received:%s\n",buffer);   /* 結(jié)束通訊 */   close(sockfd);   exit(0);   }   /******* 客戶端程序 client.c ************/
#include  
#include  
#include  
#include  
#include  
#include  
#include  
#include  
int main(int argc, char *argv[])
{
int sockfd;
char buffer[1024];
struct sockaddr_in server_addr;
struct hostent *host;
int portnumber,nbytes;
if(argc!=3)
{
fprintf(stderr,"Usage:%s hostname portnumber\a\n",argv[0]);
exit(1);
}
if((host=gethostbyname(argv[1]))==NULL)
{
fprintf(stderr,"Gethostname error\n");
exit(1);
}
if((portnumber=atoi(argv[2]))h_addr);
/* 客戶程序發(fā)起連接請(qǐng)求 */
if(connect(sockfd,(struct sockaddr *)(&server_addr),sizeof(struct sockaddr)
)==-1)
{
fprintf(stderr,"Connect Error:%s\a\n",strerror(errno));
exit(1);
}
/* 連接成功了 */
if((nbytes=read(sockfd,buffer,1024))==-1)
{
fprintf(stderr,"Read Error:%s\n",strerror(errno));
exit(1);
}
buffer[nbytes]='\0';
printf("I have received:%s\n",buffer);
/* 結(jié)束通訊 */
close(sockfd);
exit(0);
}
MakeFile
這里我們使用GNU 的make實(shí)用程序來(lái)編譯. 關(guān)于make的詳細(xì)說(shuō)明見(jiàn) Make 使用介紹
######### Makefile ###########
all:server client
server:server.c
gcc $^ -o $@
client:client.c
gcc $^ -o $@
運(yùn)行make后會(huì)產(chǎn)生兩個(gè)程序server(服務(wù)器端)和client(客戶端) 先運(yùn)行./server port
number& (portnumber隨便取一個(gè)大于1204且不在/etc/services中出現(xiàn)的號(hào)碼 就用888
8好了),然后運(yùn)行 ./client localhost 8888 看看有什么結(jié)果. (你也可以用telnet和n
etstat試一試.) 上面是一個(gè)最簡(jiǎn)單的網(wǎng)絡(luò)程序,不過(guò)是不是也有點(diǎn)煩.上面有許多函數(shù)我
們還沒(méi)有解釋. 我會(huì)在下一章進(jìn)行的詳細(xì)的說(shuō)明.
2.7 總結(jié)
總的來(lái)說(shuō)網(wǎng)絡(luò)程序是由兩個(gè)部分組成的--客戶端和服務(wù)器端.它們的建立步驟一般是:
服務(wù)器端
socket-->bind-->listen-->accept
客戶端
socket-->connect
--
網(wǎng)絡(luò)編程(3)
3. 服務(wù)器和客戶機(jī)的信息函數(shù)
這一章我們來(lái)學(xué)習(xí)轉(zhuǎn)換和網(wǎng)絡(luò)方面的信息函數(shù).
3.1 字節(jié)轉(zhuǎn)換函數(shù)
在網(wǎng)絡(luò)上面有著許多類型的機(jī)器,這些機(jī)器在表示數(shù)據(jù)的字節(jié)順序是不同的, 比如i386芯
片是低字節(jié)在內(nèi)存地址的低端,高字節(jié)在高端,而alpha芯片卻相反. 為了統(tǒng)一起來(lái),在Li
nux下面,有專門(mén)的字節(jié)轉(zhuǎn)換函數(shù).
unsigned long int htonl(unsigned long int hostlong)
unsigned short int htons(unisgned short int hostshort)
unsigned long int ntohl(unsigned long int netlong)
unsigned short int ntohs(unsigned short int netshort)
在這四個(gè)轉(zhuǎn)換函數(shù)中,h 代表host, n 代表 network.s 代表short l 代表long 第一個(gè)函
數(shù)的意義是將本機(jī)器上的long數(shù)據(jù)轉(zhuǎn)化為網(wǎng)絡(luò)上的long. 其他幾個(gè)函數(shù)的意義也差不多
..
3.2 IP和域名的轉(zhuǎn)換
在網(wǎng)絡(luò)上標(biāo)志一臺(tái)機(jī)器可以用IP或者是用域名.那么我們?cè)趺慈ミM(jìn)行轉(zhuǎn)換呢?
struct hostent *gethostbyname(const char *hostname)
struct hostent *gethostbyaddr(const char *addr,int len,int type)
在中有struct hostent的定義
struct hostent{
char *h_name; /* 主機(jī)的正式名稱 */
char *h_aliases; /* 主機(jī)的別名 */
int h_addrtype; /* 主機(jī)的地址類型 AF_INET*/
int h_length; /* 主機(jī)的地址長(zhǎng)度 對(duì)于IP4 是4字節(jié)32位*/
char **h_addr_list; /* 主機(jī)的IP地址列表 */
}
#define h_addr h_addr_list[0] /* 主機(jī)的第一個(gè)IP地址*/
gethostbyname可以將機(jī)器名(如 linux.yessun.com)轉(zhuǎn)換為一個(gè)結(jié)構(gòu)指針.在這個(gè)結(jié)構(gòu)里
面儲(chǔ)存了域名的信息
gethostbyaddr可以將一個(gè)32位的IP地址(C0A80001)轉(zhuǎn)換為結(jié)構(gòu)指針.
這兩個(gè)函數(shù)失敗時(shí)返回NULL 且設(shè)置h_errno錯(cuò)誤變量,調(diào)用h_strerror()可以得到詳細(xì)的
出錯(cuò)信息
3.3 字符串的IP和32位的IP轉(zhuǎn)換.
在網(wǎng)絡(luò)上面我們用的IP都是數(shù)字加點(diǎn)(192.168.0.1)構(gòu)成的, 而在struct in_addr結(jié)構(gòu)中
用的是32位的IP, 我們上面那個(gè)32位IP(C0A80001)是的192.168.0.1 為了轉(zhuǎn)換我們可以
使用下面兩個(gè)函數(shù)
int inet_aton(const char *cp,struct in_addr *inp)
char *inet_ntoa(struct in_addr in)
函數(shù)里面 a 代表 ascii n 代表network.第一個(gè)函數(shù)表示將a.b.c.d的IP轉(zhuǎn)換為32位的I
P,存儲(chǔ)在 inp指針里面.第二個(gè)是將32位IP轉(zhuǎn)換為a.b.c.d的格式.
3.4 服務(wù)信息函數(shù)
在網(wǎng)絡(luò)程序里面我們有時(shí)候需要知道端口.IP和服務(wù)信息.這個(gè)時(shí)候我們可以使用以下幾
個(gè)函數(shù)
int getsockname(int sockfd,struct sockaddr *localaddr,int *addrlen)
int getpeername(int sockfd,struct sockaddr *peeraddr, int *addrlen)
struct servent *getservbyname(const char *servname,const char *protoname)
struct servent *getservbyport(int port,const char *protoname)
struct servent
{
char *s_name; /* 正式服務(wù)名 */
char **s_aliases; /* 別名列表 */
int s_port; /* 端口號(hào) */
char *s_proto; /* 使用的協(xié)議 */
}
一般我們很少用這幾個(gè)函數(shù).對(duì)應(yīng)客戶端,當(dāng)我們要得到連接的端口號(hào)時(shí)在connect調(diào)用成
功后使用可得到 系統(tǒng)分配的端口號(hào).對(duì)于服務(wù)端,我們用INADDR_ANY填充后,為了得到連
接的IP我們可以在accept調(diào)用成功后 使用而得到IP地址.
在網(wǎng)絡(luò)上有許多的默認(rèn)端口和服務(wù),比如端口21對(duì)ftp80對(duì)應(yīng)WWW.為了得到指定的端口號(hào)
的服務(wù) 我們可以調(diào)用第四個(gè)函數(shù),相反為了得到端口號(hào)可以調(diào)用第三個(gè)函數(shù).
3.5 一個(gè)例子
view plain
print
?
#include    #include    #include    #include    #include    int main(int argc ,char **argv)   {   struct sockaddr_in addr;   struct hostent *host;   char **alias;   if(argc{   fprintf(stderr,"Usage:%s hostname|ip..\n\a",argv[0]);   exit(1);   }   argv++;   for(;*argv!=NULL;argv++)   {   /* 這里我們假設(shè)是IP*/   if(inet_aton(*argv,&addr.sin_addr)!=0)   {   host=gethostbyaddr((char *)&addr.sin_addr,4,AF_INET);   printf("Address information of Ip %s\n",*argv);   }   else   {   /* 失敗,難道是域名?*/   host=gethostbyname(*argv); printf("Address information   of host %s\n",*argv);   }   if(host==NULL)   {   /* 都不是 ,算了不找了*/   fprintf(stderr,"No address information of %s\n",*arg   v);   continue;   }   printf("Official host name %s\n",host->h_name);   printf("Name aliases:");   for(alias=host->h_aliases;*alias!=NULL;alias++)   printf("%s ,",*alias);   printf("\nIp address:");   for(alias=host->h_addr_list;*alias!=NULL;alias++)   printf("%s ,",inet_ntoa(*(struct in_addr *)(*alias)));   }   }   #include  
#include  
#include  
#include  
#include  
int main(int argc ,char **argv)
{
struct sockaddr_in addr;
struct hostent *host;
char **alias;
if(argch_name);
printf("Name aliases:");
for(alias=host->h_aliases;*alias!=NULL;alias++)
printf("%s ,",*alias);
printf("\nIp address:");
for(alias=host->h_addr_list;*alias!=NULL;alias++)
printf("%s ,",inet_ntoa(*(struct in_addr *)(*alias)));
}
}
在這個(gè)例子里面,為了判斷用戶輸入的是IP還是域名我們調(diào)用了兩個(gè)函數(shù),第一次我們假
設(shè)輸入的是IP所以調(diào)用inet_aton, 失敗的時(shí)候,再調(diào)用gethostbyname而得到信息.
--
網(wǎng)絡(luò)編程(4)
4. 完整的讀寫(xiě)函數(shù)
一旦我們建立了連接,我們的下一步就是進(jìn)行通信了.在Linux下面把我們前面建立的通道
看成是文件描述符,這樣服務(wù)器端和客戶端進(jìn)行通信時(shí)候,只要往文件描述符里面讀寫(xiě)東
西了. 就象我們往文件讀寫(xiě)一樣.
4.1 寫(xiě)函數(shù)write
ssize_t write(int fd,const void *buf,size_t nbytes)
write函數(shù)將buf中的nbytes字節(jié)內(nèi)容寫(xiě)入文件描述符fd.成功時(shí)返回寫(xiě)的字節(jié)數(shù).失敗時(shí)
返回-1. 并設(shè)置errno變量. 在網(wǎng)絡(luò)程序中,當(dāng)我們向套接字文件描述符寫(xiě)時(shí)有倆種可能
..
1)write的返回值大于0,表示寫(xiě)了部分或者是全部的數(shù)據(jù).
2)返回的值小于0,此時(shí)出現(xiàn)了錯(cuò)誤.我們要根據(jù)錯(cuò)誤類型來(lái)處理.
如果錯(cuò)誤為EINTR表示在寫(xiě)的時(shí)候出現(xiàn)了中斷錯(cuò)誤.
如果為EPIPE表示網(wǎng)絡(luò)連接出現(xiàn)了問(wèn)題(對(duì)方已經(jīng)關(guān)閉了連接).
為了處理以上的情況,我們自己編寫(xiě)一個(gè)寫(xiě)函數(shù)來(lái)處理這幾種情況.
view plain
print
?
int my_write(int fd,void *buffer,int length)   {   int bytes_left;   int written_bytes;   char *ptr;   ptr=buffer;   bytes_left=length;   while(bytes_left>0)   {   /* 開(kāi)始寫(xiě)*/   written_bytes=write(fd,ptr,bytes_left);   if(written_bytes/* 出錯(cuò)了*/   {   if(errno==EINTR) /* 中斷錯(cuò)誤 我們繼續(xù)寫(xiě)*/   written_bytes=0;   else /* 其他錯(cuò)誤 沒(méi)有辦法,只好撤退了*/   return(-1);   }   bytes_left-=written_bytes;   ptr+=written_bytes; /* 從剩下的地方繼續(xù)寫(xiě) */   }   return(0);   }   int my_write(int fd,void *buffer,int length)
{
int bytes_left;
int written_bytes;
char *ptr;
ptr=buffer;
bytes_left=length;
while(bytes_left>0)
{
/* 開(kāi)始寫(xiě)*/
written_bytes=write(fd,ptr,bytes_left);
if(written_bytes
4.2 讀函數(shù)read
ssize_t read(int fd,void *buf,size_t nbyte) read函數(shù)是負(fù)責(zé)從fd中讀取內(nèi)容.當(dāng)讀
成功時(shí),read返回實(shí)際所讀的字節(jié)數(shù),如果返回的值是0 表示已經(jīng)讀到文件的結(jié)束了,小于
0表示出現(xiàn)了錯(cuò)誤.如果錯(cuò)誤為EINTR說(shuō)明讀是由中斷引起的, 如果是ECONNREST表示網(wǎng)絡(luò)
連接出了問(wèn)題. 和上面一樣,我們也寫(xiě)一個(gè)自己的讀函數(shù).
view plain
print
?
int my_read(int fd,void *buffer,int length)   {   int bytes_left;   int bytes_read;   char *ptr;   bytes_left=length;   while(bytes_left>0)   {   bytes_read=read(fd,ptr,bytes_read);   if(bytes_read{   if(errno==EINTR)   bytes_read=0;   else   return(-1);   }   else if(bytes_read==0)   break;   bytes_left-=bytes_read;   ptr+=bytes_read;   }   return(length-bytes_left);   }   int my_read(int fd,void *buffer,int length)
{
int bytes_left;
int bytes_read;
char *ptr;
bytes_left=length;
while(bytes_left>0)
{
bytes_read=read(fd,ptr,bytes_read);
if(bytes_read
4.3 數(shù)據(jù)的傳遞
有了上面的兩個(gè)函數(shù),我們就可以向客戶端或者是服務(wù)端傳遞數(shù)據(jù)了.比如我們要傳遞一
個(gè)結(jié)構(gòu).可以使用如下方式
view plain
print
?
/* 客戶端向服務(wù)端寫(xiě) */   struct my_struct my_struct_client;   write(fd,(void *)&my_struct_client,sizeof(struct my_struct);   /* 服務(wù)端的讀*/   char buffer[sizeof(struct my_struct)];   struct *my_struct_server;   read(fd,(void *)buffer,sizeof(struct my_struct));   my_struct_server=(struct my_struct *)buffer;   /* 客戶端向服務(wù)端寫(xiě) */
struct my_struct my_struct_client;
write(fd,(void *)&my_struct_client,sizeof(struct my_struct);
/* 服務(wù)端的讀*/
char buffer[sizeof(struct my_struct)];
struct *my_struct_server;
read(fd,(void *)buffer,sizeof(struct my_struct));
my_struct_server=(struct my_struct *)buffer;
在網(wǎng)絡(luò)上傳遞數(shù)據(jù)時(shí)我們一般都是把數(shù)據(jù)轉(zhuǎn)化為char類型的數(shù)據(jù)傳遞.接收的時(shí)候也是一
樣的 注意的是我們沒(méi)有必要在網(wǎng)絡(luò)上傳遞指針(因?yàn)閭鬟f指針是沒(méi)有任何意義的,我們必
須傳遞指針?biāo)赶虻膬?nèi)容)
--
網(wǎng)絡(luò)編程(5)
5. 用戶數(shù)據(jù)報(bào)發(fā)送
我們前面已經(jīng)學(xué)習(xí)網(wǎng)絡(luò)程序的一個(gè)很大的部分,由這個(gè)部分的知識(shí),我們實(shí)際上可以寫(xiě)出
大部分的基于TCP協(xié)議的網(wǎng)絡(luò)程序了.現(xiàn)在在Linux下的大部分程序都是用我們上面所學(xué)的
知識(shí)來(lái)寫(xiě)的.我們可以去找一些源程序來(lái)參考一下.這一章,我們簡(jiǎn)單的學(xué)習(xí)一下基于UDP
協(xié)議的網(wǎng)絡(luò)程序.
5.1 兩個(gè)常用的函數(shù)
int recvfrom(int sockfd,void *buf,int len,unsigned int flags,struct socka
ddr * from int *fromlen)
int sendto(int sockfd,const void *msg,int len,unsigned int flags,struct s
ockaddr *to int tolen)
sockfd,buf,len的意義和read,write一樣,分別表示套接字描述符,發(fā)送或接收的緩沖區(qū)
及大小.recvfrom負(fù)責(zé)從sockfd接收數(shù)據(jù),如果from不是NULL,那么在from里面存儲(chǔ)了信息
來(lái)源的情況,如果對(duì)信息的來(lái)源不感興趣,可以將from和fromlen設(shè)置為NULL.sendto負(fù)責(zé)
向to發(fā)送信息.此時(shí)在to里面存儲(chǔ)了收信息方的詳細(xì)資料.
5.2 一個(gè)實(shí)例
view plain
print
?
/* 服務(wù)端程序 server.c */   #include    #include    #include    #include    #include    #define SERVER_PORT 8888   #define MAX_MSG_SIZE 1024   void udps_respon(int sockfd)   {   struct sockaddr_in addr;   int addrlen,n;   char msg[MAX_MSG_SIZE];   while(1)   { /* 從網(wǎng)絡(luò)上度,寫(xiě)到網(wǎng)絡(luò)上面去 */   n=recvfrom(sockfd,msg,MAX_MSG_SIZE,0,   (struct sockaddr*)&addr,&addrlen);   msg[n]=0;   /* 顯示服務(wù)端已經(jīng)收到了信息 */   fprintf(stdout,"I have received %s",msg);   sendto(sockfd,msg,n,0,(struct sockaddr*)&addr,addrlen);   }   }   int main(void)   {   int sockfd;   struct sockaddr_in addr;   sockfd=socket(AF_INET,SOCK_DGRAM,0);   if(sockfd{   fprintf(stderr,"Socket Error:%s\n",strerror(errno));   exit(1);   }   bzero(&addr,sizeof(struct sockaddr_in));   addr.sin_family=AF_INET;   addr.sin_addr.s_addr=htonl(INADDR_ANY);   addr.sin_port=htons(SERVER_PORT);   if(bind(sockfd,(struct sockaddr *)&ddr,sizeof(struct sockaddr_in)))   {   fprintf(stderr,"Bind Error:%s\n",strerror(errno));   exit(1);   }   udps_respon(sockfd);   close(sockfd);   }   /* 服務(wù)端程序 server.c */
#include  
#include  
#include  
#include  
#include  
#define SERVER_PORT 8888
#define MAX_MSG_SIZE 1024
void udps_respon(int sockfd)
{
struct sockaddr_in addr;
int addrlen,n;
char msg[MAX_MSG_SIZE];
while(1)
{ /* 從網(wǎng)絡(luò)上度,寫(xiě)到網(wǎng)絡(luò)上面去 */
n=recvfrom(sockfd,msg,MAX_MSG_SIZE,0,
(struct sockaddr*)&addr,&addrlen);
msg[n]=0;
/* 顯示服務(wù)端已經(jīng)收到了信息 */
fprintf(stdout,"I have received %s",msg);
sendto(sockfd,msg,n,0,(struct sockaddr*)&addr,addrlen);
}
}
int main(void)
{
int sockfd;
struct sockaddr_in addr;
sockfd=socket(AF_INET,SOCK_DGRAM,0);
if(sockfd
view plain
print
?
/* 客戶端程序 */   #include    #include    #include    #include    #include    #include    #define MAX_BUF_SIZE 1024   void udpc_requ(int sockfd,const struct sockaddr_in *addr,int len)   {   char buffer[MAX_BUF_SIZE];   int n;   while(1)   { /* 從鍵盤(pán)讀入,寫(xiě)到服務(wù)端 */   fgets(buffer,MAX_BUF_SIZE,stdin);   sendto(sockfd,buffer,strlen(buffer),0,addr,len);   bzero(buffer,MAX_BUF_SIZE);   /* 從網(wǎng)絡(luò)上讀,寫(xiě)到屏幕上 */   n=recvfrom(sockfd,buffer,MAX_BUF_SIZE,0,NULL,NULL);   buffer[n]=0;   fputs(buffer,stdout);   }   }   int main(int argc,char **argv)   {   int sockfd,port;   struct sockaddr_in addr;   if(argc!=3)   {   fprintf(stderr,"Usage:%s server_ip server_port\n",argv[0]);   exit(1);   }   if((port=atoi(argv[2])){   fprintf(stderr,"Usage:%s server_ip server_port\n",argv[0]);   exit(1);   }   sockfd=socket(AF_INET,SOCK_DGRAM,0);   if(sockfd{   fprintf(stderr,"Socket Error:%s\n",strerror(errno));   exit(1);   }   /* 填充服務(wù)端的資料 */   bzero(&addr,sizeof(struct sockaddr_in));   addr.sin_family=AF_INET;   addr.sin_port=htons(port);   if(inet_aton(argv[1],&addr.sin_addr){   fprintf(stderr,"Ip error:%s\n",strerror(errno));   exit(1);   }   udpc_requ(sockfd,&addr,sizeof(struct sockaddr_in));   close(sockfd);   }   /* 客戶端程序 */
#include  
#include  
#include  
#include  
#include  
#include  
#define MAX_BUF_SIZE 1024
void udpc_requ(int sockfd,const struct sockaddr_in *addr,int len)
{
char buffer[MAX_BUF_SIZE];
int n;
while(1)
{ /* 從鍵盤(pán)讀入,寫(xiě)到服務(wù)端 */
fgets(buffer,MAX_BUF_SIZE,stdin);
sendto(sockfd,buffer,strlen(buffer),0,addr,len);
bzero(buffer,MAX_BUF_SIZE);
/* 從網(wǎng)絡(luò)上讀,寫(xiě)到屏幕上 */
n=recvfrom(sockfd,buffer,MAX_BUF_SIZE,0,NULL,NULL);
buffer[n]=0;
fputs(buffer,stdout);
}
}
int main(int argc,char **argv)
{
int sockfd,port;
struct sockaddr_in addr;
if(argc!=3)
{
fprintf(stderr,"Usage:%s server_ip server_port\n",argv[0]);
exit(1);
}
if((port=atoi(argv[2]))
引用########### 編譯文件 Makefile ##########
all:server client
server:server.c
gcc -o server server.c
client:client.c
gcc -o client client.c
clean:
rm -f server
rm -f client
rm -f core
上面的實(shí)例如果大家編譯運(yùn)行的話,會(huì)發(fā)現(xiàn)一個(gè)小問(wèn)題的. 在我機(jī)器上面,我先運(yùn)行服務(wù)
端,然后運(yùn)行客戶端.在客戶端輸入信息,發(fā)送到服務(wù)端, 在服務(wù)端顯示已經(jīng)收到信息,但
是客戶端沒(méi)有反映.再運(yùn)行一個(gè)客戶端,向服務(wù)端發(fā)出信息 卻可以得到反應(yīng).我想可能是
第一個(gè)客戶端已經(jīng)阻塞了.如果誰(shuí)知道怎么解決的話,請(qǐng)告訴我,謝謝. 由于UDP協(xié)議是不
保證可靠接收數(shù)據(jù)的要求,所以我們?cè)诎l(fā)送信息的時(shí)候,系統(tǒng)并不能夠保證我們發(fā)出的信
息都正確無(wú)誤的到達(dá)目的地.一般的來(lái)說(shuō)我們?cè)诰帉?xiě)網(wǎng)絡(luò)程序的時(shí)候都是選用TCP協(xié)議的
--
網(wǎng)絡(luò)編程(6)
6. 高級(jí)套接字函數(shù)
在前面的幾個(gè)部分里面,我們已經(jīng)學(xué)會(huì)了怎么樣從網(wǎng)絡(luò)上讀寫(xiě)信息了.前面的一些函數(shù)(r
ead,write)是網(wǎng)絡(luò)程序里面最基本的函數(shù).也是最原始的通信函數(shù).在這一章里面,我們一
起來(lái)學(xué)習(xí)網(wǎng)絡(luò)通信的高級(jí)函數(shù).這一章我們學(xué)習(xí)另外幾個(gè)讀寫(xiě)函數(shù).
6.1 recv和send
recv和send函數(shù)提供了和read和write差不多的功能.不過(guò)它們提供 了第四個(gè)參數(shù)來(lái)控制
讀寫(xiě)操作.
int recv(int sockfd,void *buf,int len,int flags)
int send(int sockfd,void *buf,int len,int flags)
前面的三個(gè)參數(shù)和read,write一樣,第四個(gè)參數(shù)可以是0或者是以下的組合
_______________________________________________________________
| MSG_DONTROUTE | 不查找路由表 |
| MSG_OOB | 接受或者發(fā)送帶外數(shù)據(jù) |
| MSG_PEEK | 查看數(shù)據(jù),并不從系統(tǒng)緩沖區(qū)移走數(shù)據(jù) |
| MSG_WAITALL | 等待所有數(shù)據(jù) |
|--------------------------------------------------------------|
MSG_DONTROUTE:是send函數(shù)使用的標(biāo)志.這個(gè)標(biāo)志告訴IP協(xié)議.目的主機(jī)在本地網(wǎng)絡(luò)上面
,沒(méi)有必要查找路由表.這個(gè)標(biāo)志一般用網(wǎng)絡(luò)診斷和路由程序里面.
MSG_OOB:表示可以接收和發(fā)送帶外的數(shù)據(jù).關(guān)于帶外數(shù)據(jù)我們以后會(huì)解釋的.
MSG_PEEK:是recv函數(shù)的使用標(biāo)志,表示只是從系統(tǒng)緩沖區(qū)中讀取內(nèi)容,而不清楚系統(tǒng)緩沖
區(qū)的內(nèi)容.這樣下次讀的時(shí)候,仍然是一樣的內(nèi)容.一般在有多個(gè)進(jìn)程讀寫(xiě)數(shù)據(jù)時(shí)可以使用
這個(gè)標(biāo)志.
MSG_WAITALL是recv函數(shù)的使用標(biāo)志,表示等到所有的信息到達(dá)時(shí)才返回.使用這個(gè)標(biāo)志的
時(shí)候recv回一直阻塞,直到指定的條件滿足,或者是發(fā)生了錯(cuò)誤. 1)當(dāng)讀到了指定的字節(jié)
時(shí),函數(shù)正常返回.返回值等于len 2)當(dāng)讀到了文件的結(jié)尾時(shí),函數(shù)正常返回.返回值小于
len 3)當(dāng)操作發(fā)生錯(cuò)誤時(shí),返回-1,且設(shè)置錯(cuò)誤為相應(yīng)的錯(cuò)誤號(hào)(errno)
如果flags為0,則和read,write一樣的操作.還有其它的幾個(gè)選項(xiàng),不過(guò)我們實(shí)際上用的很
少,可以查看 Linux Programmer's Manual得到詳細(xì)解釋.
6.2 recvfrom和sendto
這兩個(gè)函數(shù)一般用在非套接字的網(wǎng)絡(luò)程序當(dāng)中(UDP),我們已經(jīng)在前面學(xué)會(huì)了.
6.3 recvmsg和sendmsg
recvmsg和sendmsg可以實(shí)現(xiàn)前面所有的讀寫(xiě)函數(shù)的功能.
view plain
print
?
int recvmsg(int sockfd,struct msghdr *msg,int flags)   int sendmsg(int sockfd,struct msghdr *msg,int flags)   struct msghdr   {   void *msg_name;   int msg_namelen;   struct iovec *msg_iov;   int msg_iovlen;   void *msg_control;   int msg_controllen;   int msg_flags;   }   struct iovec   {   void *iov_base; /* 緩沖區(qū)開(kāi)始的地址 */   size_t iov_len; /* 緩沖區(qū)的長(zhǎng)度 */   }   int recvmsg(int sockfd,struct msghdr *msg,int flags)
int sendmsg(int sockfd,struct msghdr *msg,int flags)
struct msghdr
{
void *msg_name;
int msg_namelen;
struct iovec *msg_iov;
int msg_iovlen;
void *msg_control;
int msg_controllen;
int msg_flags;
}
struct iovec
{
void *iov_base; /* 緩沖區(qū)開(kāi)始的地址 */
size_t iov_len; /* 緩沖區(qū)的長(zhǎng)度 */
}
msg_name和 msg_namelen當(dāng)套接字是非面向連接時(shí)(UDP),它們存儲(chǔ)接收和發(fā)送方的地址
信息.msg_name實(shí)際上是一個(gè)指向struct sockaddr的指針,msg_name是結(jié)構(gòu)的長(zhǎng)度.當(dāng)套
接字是面向連接時(shí),這兩個(gè)值應(yīng)設(shè)為NULL. msg_iov和msg_iovlen指出接受和發(fā)送的緩沖
區(qū)內(nèi)容.msg_iov是一個(gè)結(jié)構(gòu)指針,msg_iovlen指出這個(gè)結(jié)構(gòu)數(shù)組的大小. msg_control和
msg_controllen這兩個(gè)變量是用來(lái)接收和發(fā)送控制數(shù)據(jù)時(shí)的 msg_flags指定接受和發(fā)送
的操作選項(xiàng).和recv,send的選項(xiàng)一樣
6.4 套接字的關(guān)閉
關(guān)閉套接字有兩個(gè)函數(shù)close和shutdown.用close時(shí)和我們關(guān)閉文件一樣.
6.5 shutdown
int shutdown(int sockfd,int howto)
TCP連接是雙向的(是可讀寫(xiě)的),當(dāng)我們使用close時(shí),會(huì)把讀寫(xiě)通道都關(guān)閉,有時(shí)侯我們希
望只關(guān)閉一個(gè)方向,這個(gè)時(shí)候我們可以使用shutdown.針對(duì)不同的howto,系統(tǒng)回采取不同
的關(guān)閉方式.
howto=0這個(gè)時(shí)候系統(tǒng)會(huì)關(guān)閉讀通道.但是可以繼續(xù)往接字描述符寫(xiě).
howto=1關(guān)閉寫(xiě)通道,和上面相反,著時(shí)候就只可以讀了.
howto=2關(guān)閉讀寫(xiě)通道,和close一樣 在多進(jìn)程程序里面,如果有幾個(gè)子進(jìn)程共享一個(gè)套接
字時(shí),如果我們使用shutdown, 那么所有的子進(jìn)程都不能夠操作了,這個(gè)時(shí)候我們只能夠
使用close來(lái)關(guān)閉子進(jìn)程的套接字描述符.
網(wǎng)絡(luò)編程(7)
7. TCP/IP協(xié)議
你也許聽(tīng)說(shuō)過(guò)TCP/IP協(xié)議,那么你知道到底什么是TCP,什么是IP嗎?在這一章里面,我們一
起來(lái)學(xué)習(xí)這個(gè)目前網(wǎng)絡(luò)上用最廣泛的協(xié)議.
7.1 網(wǎng)絡(luò)傳輸分層
如果你考過(guò)計(jì)算機(jī)等級(jí)考試,那么你就應(yīng)該已經(jīng)知道了網(wǎng)絡(luò)傳輸分層這個(gè)概念.在網(wǎng)絡(luò)上
,人們?yōu)榱藗鬏敂?shù)據(jù)時(shí)的方便,把網(wǎng)絡(luò)的傳輸分為7個(gè)層次.分別是:應(yīng)用層,表示層,會(huì)話層
,傳輸層,網(wǎng)絡(luò)層,數(shù)據(jù)鏈路層和物理層.分好了層以后,傳輸數(shù)據(jù)時(shí),上一層如果要數(shù)據(jù)的
話,就可以直接向下一層要了,而不必要管數(shù)據(jù)傳輸?shù)募?xì)節(jié).下一層也只向它的上一層提供
數(shù)據(jù),而不要去管其它東西了.如果你不想考試,你沒(méi)有必要去記這些東西的.只要知道是
分層的,而且各層的作用不同.
7.2 IP協(xié)議
IP協(xié)議是在網(wǎng)絡(luò)層的協(xié)議.它主要完成數(shù)據(jù)包的發(fā)送作用. 下面這個(gè)表是IP4的數(shù)據(jù)包格

0 4 8 16 32
--------------------------------------------------
|版本 |首部長(zhǎng)度|服務(wù)類型| 數(shù)據(jù)包總長(zhǎng) |
--------------------------------------------------
| 標(biāo)識(shí) |DF |MF| 碎片偏移 |
--------------------------------------------------
| 生存時(shí)間 | 協(xié)議 | 首部較驗(yàn)和 |
------------------------------------------------
| 源IP地址 |
------------------------------------------------
| 目的IP地址 |
-------------------------------------------------
| 選項(xiàng) |
=================================================
| 數(shù)據(jù) |
-------------------------------------------------
下面我們看一看IP的結(jié)構(gòu)定義
view plain
print
?
struct ip   {   #if __BYTE_ORDER == __LITTLE_ENDIAN   unsigned int ip_hl:4; /* header length */   unsigned int ip_v:4; /* version */   #endif   #if __BYTE_ORDER == __BIG_ENDIAN   unsigned int ip_v:4; /* version */   unsigned int ip_hl:4; /* header length */   #endif   u_int8_t ip_tos; /* type of service */   u_short ip_len; /* total length */   u_short ip_id; /* identification */   u_short ip_off; /* fragment offset field */   #define IP_RF 0x8000 /* reserved fragment flag */   #define IP_DF 0x4000 /* dont fragment flag */   #define IP_MF 0x2000 /* more fragments flag */   #define IP_OFFMASK 0x1fff /* mask for fragmenting bits */   u_int8_t ip_ttl; /* time to live */   u_int8_t ip_p; /* protocol */   u_short ip_sum; /* checksum */   struct in_addr ip_src, ip_dst; /* source and dest address */   };   struct ip
{
#if __BYTE_ORDER == __LITTLE_ENDIAN
unsigned int ip_hl:4; /* header length */
unsigned int ip_v:4; /* version */
#endif
#if __BYTE_ORDER == __BIG_ENDIAN
unsigned int ip_v:4; /* version */
unsigned int ip_hl:4; /* header length */
#endif
u_int8_t ip_tos; /* type of service */
u_short ip_len; /* total length */
u_short ip_id; /* identification */
u_short ip_off; /* fragment offset field */
#define IP_RF 0x8000 /* reserved fragment flag */
#define IP_DF 0x4000 /* dont fragment flag */
#define IP_MF 0x2000 /* more fragments flag */
#define IP_OFFMASK 0x1fff /* mask for fragmenting bits */
u_int8_t ip_ttl; /* time to live */
u_int8_t ip_p; /* protocol */
u_short ip_sum; /* checksum */
struct in_addr ip_src, ip_dst; /* source and dest address */
};
ip_vIP協(xié)議的版本號(hào),這里是4,現(xiàn)在IPV6已經(jīng)出來(lái)了
ip_hlIP包首部長(zhǎng)度,這個(gè)值以4字節(jié)為單位.IP協(xié)議首部的固定長(zhǎng)度為20個(gè)字節(jié),如果IP包
沒(méi)有選項(xiàng),那么這個(gè)值為5.
ip_tos服務(wù)類型,說(shuō)明提供的優(yōu)先權(quán).
ip_len說(shuō)明IP數(shù)據(jù)的長(zhǎng)度.以字節(jié)為單位.
ip_id標(biāo)識(shí)這個(gè)IP數(shù)據(jù)包.
ip_off碎片偏移,這和上面ID一起用來(lái)重組碎片的.
ip_ttl生存時(shí)間.沒(méi)經(jīng)過(guò)一個(gè)路由的時(shí)候減一,直到為0時(shí)被拋棄.
ip_p協(xié)議,表示創(chuàng)建這個(gè)IP數(shù)據(jù)包的高層協(xié)議.如TCP,UDP協(xié)議.
ip_sum首部校驗(yàn)和,提供對(duì)首部數(shù)據(jù)的校驗(yàn).
ip_src,ip_dst發(fā)送者和接收者的IP地址
關(guān)于IP協(xié)議的詳細(xì)情況,請(qǐng)參考 RFC791
7.3 ICMP協(xié)議
ICMP是消息控制協(xié)議,也處于網(wǎng)絡(luò)層.在網(wǎng)絡(luò)上傳遞IP數(shù)據(jù)包時(shí),如果發(fā)生了錯(cuò)誤,那么就
會(huì)用ICMP協(xié)議來(lái)報(bào)告錯(cuò)誤.
ICMP包的結(jié)構(gòu)如下:
0 8 16 32
---------------------------------------------------------------------
| 類型 | 代碼 | 校驗(yàn)和 |
--------------------------------------------------------------------
| 數(shù)據(jù) | 數(shù)據(jù) |
--------------------------------------------------------------------
ICMP在中的定義是
view plain
print
?
struct icmphdr   {   u_int8_t type; /* message type */   u_int8_t code; /* type sub-code */   u_int16_t checksum;   union   {   struct   {   u_int16_t id;   u_int16_t sequence;   } echo; /* echo datagram */   u_int32_t gateway; /* gateway address */   struct   {   u_int16_t __unused;   u_int16_t mtu;   } frag; /* path mtu discovery */   } un;   };   
struct icmphdr
{
u_int8_t type; /* message type */
u_int8_t code; /* type sub-code */
u_int16_t checksum;
union
{
struct
{
u_int16_t id;
u_int16_t sequence;
} echo; /* echo datagram */
u_int32_t gateway; /* gateway address */
struct
{
u_int16_t __unused;
u_int16_t mtu;
} frag; /* path mtu discovery */
} un;
};
               
               
               

本文來(lái)自ChinaUnix博客,如果查看原文請(qǐng)點(diǎn):http://blog.chinaunix.net/u2/63726/showart_1018979.html
您需要登錄后才可以回帖 登錄 | 注冊(cè)

本版積分規(guī)則 發(fā)表回復(fù)

  

北京盛拓優(yōu)訊信息技術(shù)有限公司. 版權(quán)所有 京ICP備16024965號(hào)-6 北京市公安局海淀分局網(wǎng)監(jiān)中心備案編號(hào):11010802020122 niuxiaotong@pcpop.com 17352615567
未成年舉報(bào)專區(qū)
中國(guó)互聯(lián)網(wǎng)協(xié)會(huì)會(huì)員  聯(lián)系我們:huangweiwei@itpub.net
感謝所有關(guān)心和支持過(guò)ChinaUnix的朋友們 轉(zhuǎn)載本站內(nèi)容請(qǐng)注明原作者名及出處

清除 Cookies - ChinaUnix - Archiver - WAP - TOP