##網絡套接字程式設計##
1.計算機網絡基礎
每台計算機都需要一個IP位址以辨別自己。IP位址由IP協定規定的32位的二進制數表示,最新的IPv6将IP位址升為128位。32位的IP位址主要分為兩部分:字首和字尾。字首表示計算機所屬的實體網絡,字尾确定該網絡上的唯一一台計算機。在網際網路上,每一個實體網絡都有唯一的網絡号,根據網路号不同,可以将IP位址分5類,A類、B類和C類屬于基本類,D類用于多點傳播發送,E類屬于保留。
類型 範圍
A類 0.0.0.0~127.255.255.255
B類 128.0.0.0~191.255.255.255
C類 192.0.0.0~223.255.255.255
D類 224.0.0.0~239.255.255.255
E類 240.0.0.0~247.255.255.255
特殊的IP位址:
a.網絡位址:在IP位址中主機位址為0的表示網絡位址,例如 128.111.0.0。
b.廣播位址:在網絡号後跟所有位全是1的IP位址,表示廣播位址。
c.回送位址:127.0.0.1表示回送位址,用于測試。
開放系統互聯,簡稱OSI,是國際标準化組織為了實作計算機網絡的标準化而頒布的參考模型。該模型采用分層的劃分原則,将網絡中的資料傳輸劃分了7層,每層使用下層的服務,并向上層提供服務。
層次 名稱 功能描述
7 應用層 負責網絡中應用程式與網絡作業系統之間的聯系。
6 表示層 用于确定資料交換的格式,它能夠解決應用程式之間在資料格式上的差異,并負責裝置之間所需要的字元集和資料的轉換。
5 會話層 使用者應用程式與網絡層的接口,它能夠建立與其他裝置的連接配接,即會話。并且它能夠對會話進行有效的管理
4 傳輸層 提供會話層和網絡層之間的傳輸服務,該服務從會話層獲得資料,必要時對資料進行分割,然後傳輸層将資料傳遞到網絡層,并確定資料能正确無誤地轉送到網絡層。
3 網絡層 能夠将傳輸的資料封包,然後通過路由選擇、分段組合等控制,将資訊從源裝置傳送到目标裝置。
2 資料鍊路層 主要是修正傳輸過程中的錯誤信号,它能夠提供可靠的通過實體媒體傳輸資料的方法。
1 實體層 利用傳輸媒體為資料鍊路層提供實體連接配接,它規範了網絡硬體的特性、規格和傳輸速度。
位址分析,即将計算機的協定位址解析為實體位址(MAC位址,媒體通路控制位址)。通常,在網絡上由位址解析協定(ARP)來實作位址解析。兩機進行通信時,先從本地ARP緩存中查找對方IP對應的實體位址。若本機未發現,将發送ARP請求幀到本地網絡上的所有主機,在ARP請求幀中包含了通路方主機的實體位址和IP位址,找到後雙方互相添加對方實體位址和IP位址到自己的ARP緩存中。
域名系統,由于IP位址的易混淆,并且不容易記憶,出現了以主機名家上字尾名的方法辨別一台主機,其字尾稱為域名。eg:www.baidu.com,主機名:www,域名:baidu.com。這裡的域名為二級域名,其中com為一級域名,表示商業組織,baidu為本地域名。為了能夠利用域名進行不同主機間的通信,需要将域名解析為IP位址,稱為域名解析。域名解析是通過域名伺服器來完成的。
TCP/IP協定,即傳輸控制協定/網際協定,是網際網路上最流行的協定,它能夠實作網際網路上不同類型作業系統的計算機互相通信。該協定将網絡分為4層,分别對應OSI參考模型的7層結構。
TCP/IP協定 OSI參考模型
應用層(包括Telnet、FTP、SNTP協定) 會話層、表示層和應用層
傳輸層(包括TCP、UDP協定) 傳輸層
網絡層(包括ICMP、IP、ARP等協定) 網絡層
資料鍊路層 實體層和資料鍊路層
TCP/IP協定簇包含多種協定,其中主要的協定有網際協定(IP)和傳輸控制協定(TCP)等。
a.TCP協定
該協定是一種提供可靠資料傳輸的通用協定,它是TCP/IP體系結構中傳輸層上的協定。在發送資料時,應用層的資料傳輸到傳輸層,加上TCP的首部,資料就構成了封包。封包是網際層IP的資料,如果加上IP首部,就構成了IP資料報,TCP協定C語言資料描述如下:
typedef struct HeadTCP
{
WORD SourcePort; //16位源端口号
WORD DePort; //16位目的端口
DWORD SequanceNo; //32位序号
DWORD ConfirmNo; //32位确認序号
BYTE HeadLen; //與Flag為一個組成部分,首部長度,占4位,保留6位,6位辨別,共16位
BYTE Flag;
WORD WndSize; //16位視窗大小
WORD CheckSum; //16位校驗和
WORD UrgPtr; //16位i緊急指針
}HEADTCP;
b.IP協定
即網際協定,工作在網絡層,主要提供無連結資料報傳輸。IP協定不保證資料報的發送,但可以最大限度地發送資料。IP協定C語言資料描述如下:
typedef struct HeadIP
{
unsigned char headerlen:4; //首部長度,占4位
unsinged char version:4; //版本,占4位
unsigned char servertype; //服務類型,占8位,即一個位元組
unsigned short totallen; //總長度,占16位
unsigned short id; //與idoff構成辨別,共占16位,前3位是辨別,後13位時片偏移
unsigned short idoff;
unsigned char ttl; //生存時間,占8位
unsigned char proto; //協定,占8位
unsigned short checksum; //首部檢驗和,占32位
unsigned int sourceIP; //源IP位址,占32位
unsigned int destIP; //目的IP位址,占32位
}HEADIP;
c.ICMP協定
即網際控制封包協定,負責網絡上裝置狀态的發送和封包檢查,可以将某個裝置的故障資訊發送到其他裝置上。ICMP協定C語言資料描述如下:
typedef struct HeadICMP
{
BYTE Type; //8位類型
BYTE Code; //8位代碼
WORD ChkSum; //16位檢驗和
}HEADICMP;
d.UDP協定
即使用者資料報協定,是一個面向無連接配接的協定,可使兩個應用程式不需要先建立連接配接,它為應用程式提供一次性的資料傳輸服務。UDP協定不提供差錯恢複,不能提供資料重傳,是以該協定傳輸資料安全性略差。UDP協定C語言資料描述如下:
typedef struct HeadUDP
{
WORD SourcePort; //16位源端口号
WORD DePort; //16位目的端口
WORD Len; //16位UDP長度
WORD ChkSum; //16位UDP校驗和
}HEADUDP;
端口,用于辨別通信的應用程式。當應用程式(嚴格來說應該是程序)與某個端口綁定後,系統會将收到的給端口的資料送往該應用程式。端口是用一個16位的無符号整數值來表示的,範圍為0~65535,低于256的端口被作為系統的保留端口,用于系統程序的通信,不在這一範圍的端口号被稱為自由端口,可以由程序自由使用。
2.套接字的引入
套接字(socket)存在于通信區域(位址族)中,主要用于将通過套接字通信的程序的公有特性綜合在一起。套接字通常隻與同一區域的套接字交換資料。Windows Sockets隻支援一個通信區域——AF_INET網絡域,使用網際協定族通信的程序使用該域。網絡位元組順序采用16位整數和32位整數的高位先存格式(大端)。
所謂套接字,實際上是一個指向傳輸提供者的句柄,根據性質和作用的不同,可分為3種,分别為原始套接字,流式套接字(TCP)和資料包套接字(UDP)。
a.TCP的套接字的socket程式設計
TCP是面向連接配接的可靠的傳輸協定。利用TCP協定進行通信時,首先要建立通信雙方的連接配接。一旦連接配接建立完成,就可以進行通信。TCP提供了資料确認和資料重傳的機制,保證了發送的資料一定能到達通信的對方。
伺服器端程式流程如下:
1).建立套接字socket
2).将建立的套接字綁定(bind)到本地的位址和端口上
3).設定套接字的狀态為監聽狀态(listen),準備接受用戶端的連接配接請求
4).接受請求(accpet),同時傳回得到一個用于連接配接的新套接字
5).使用這個新套接字進行通信(通信函數使用send/recv)
6).通信完畢,釋放套接字資源(closesocket)
用戶端程式流程如下:
1).建立套接字socket
2).向伺服器發出連接配接請求(connect)
3).請求連接配接後與伺服器進行通信操作(send/recv)
4).釋放套接字資源(closesocket)
b.UDP的套接字的socket程式設計
UDP是無連接配接的、不可靠的傳輸協定。采用UDP進行通信時,不需要建立連接配接,可以直接向一個IP位址發送資料,但是不能保證對方能收到。對于UDP來說,可以把伺服器稱為接受端,用戶端就是發送資料的發送端。
發送端程式流程如下:
1).建立套接字socket
2).将套接字綁定(bind)到一個本地位址和端口上
3).等待接收資料(recvfrom)
4).釋放套接字資源(closesocket)
接收端程式流程如下:
1).建立套接字socket
2).向伺服器發送資料(sendto)
3).釋放套接字資源(closesocket)
基于UDP的套接字程式設計時,利用的是sendto和recvfrom這兩個函數實作資料的發送和接收;而基于TCP的套接字程式設計時,發送資料是調用send函數,接受資料使用的是recv函數。
3.套接字函數
a.WSAStartup函數
該函數的功能是初始化套接字庫。wVersionRequested:表示調用者使用的Windows Socket的版本,高位元組記錄修訂版本,低位元組記錄主版本。eg:Windows Socket 的版本為2.1,則高位元組記錄1,低位元組記錄2。IpWSAData:是一個WSADATA結構指針,該結構詳細記錄了Windows套接字的相關資訊。
該函數原型如下:
int WSAStartup(WORD wVersion,LPWSADATA IpWSAData);
注意:該函數用于初始化Ws2_32.dll動态連結庫,在使用套接字函數之前,一定要初始化該動态連結庫。
b.socket函數
該函數的功能是建立一個套接字。af:表示一個位址家族,通常為AF_INET。tpe:表示套接字類型,如果為SOCK_STREAM,表示建立面向連接配接的流式套接字;為SOCK_DGRAM,表示建立面向無連接配接的資料報套接字;為SOCK_RAM,表示建立原始套接字。對于這些值,使用者可以在Winsock2.h頭檔案中找到。potocol:表示套接口所用的協定,如果使用者不指定,可以設定為0。傳回值:建立的套接字句柄。
該函數的原型如下:
SOCKET socket(int af,int type, int protocol);
在代碼中,如果socket函數調用成功,它就會傳回一個新的SOCKET資料類型的套接字描述符。使用定義好的套接字socket_server進行儲存。
c.bind函數
該函數的功能是将套接字綁定到指定的端口和位址上。s:表示套接字辨別。name:是一個sockaddr結構指針,該結構中包含了要結合的位址和端口号。namelen:确定name緩沖區的長度。傳回值:如果函數執行成功,傳回值為0,否則為SOCKET_ERROR。
該函數的原型如下:
int bind (SOCKET s,const struct sockaddr FAR* name,int namelen);
在建立了套接字之後,應該将該套接字綁定到本地的某個位址和端口上,這時就需要該函數了。
d.listen函數
該函數的功能時将套接字設定為監聽模式。對于流式套接字,必須處于監聽模式才能夠接收用戶端套接字的連接配接。s:表示套接字辨別。backlog:表示等待連接配接的最大隊列長度。eg:如果backlog被設定為2,此時有3個用戶端同時發出連接配接請求,那麼前2個用戶端連接配接會放置在等待隊列中,第3個用戶端會得到錯誤資訊。
該函數的原型如下:
int listen(SOCKET s,int backlog);
設定套接字為監聽狀态,為連接配接做準備,最大等待的數目為5。
e.accpet函數
該函數的功能是u接受用戶端的連接配接,在流式套接字中,隻有在套接字處于監聽狀态,才能接受用戶端的連接配接。s:是一個套接字,它應處于監聽狀态。addr:是一個sockaddr_in結構指針,包含一組用戶端的端口号、IP位址等資訊。addrlen:用于接收參數addr的長度。傳回值:一個新的套接字,它對應于已接受的用戶端連接配接,對于該用戶端的所有後續操作,都應該使用這個新個套接字。
該函數的原型如下:
SOCKET accept(SOCLET s, struct sockaddr FAR* addr,int FAR* addrlen);
f.closesocket函數
該函數功能書關閉套接字。s:辨別一個套接字。如果參數s設定了SO_DONTLINGER選項,則調用該函數後會立即傳回,但此時如果有資料尚未傳送完畢,則會繼續傳遞資料,然後才關閉套接字。
該函數的原型如下:
int closesocket(SOCKET s);
在代碼中,socket_receive是一個套接字,當不使用時就可以利用closesocket函數将其套接字的資源進行釋放。
g.connect函數
該函數的功能是發送一個連接配接請求。s:表示一個套接字。name:表示套接字s要連接配接的主機位址和端口号。namelen:是name緩沖區的長度。傳回值:如果函數執行成功,傳回值為0,否則為SOCKET_ERROR。使用者可以通過WSAGETLASTERROR得到其錯誤i描述。
該函數的原型如下:
int connect(SOCKET s, const socketaddr FAR* name,int namelen);
h.htons函數
該函數的功能是将一個16位的無符号短整型資料由主機排列方式轉換為網絡排列方式。hostshort:是一個主機排列方式的無符号短整型資料。傳回值:函數傳回值是16位的網絡排列方式資料。
該函數的原型如下:
u_short htons(u_short hostshort);
i.htonl函數
該函數的功能是将一個無符号長整型資料由主機排列方式轉換為網絡排列方式。hostlong:表示一個主機排列方式的無符号長整型資料。傳回值:32位的網絡排列方式資料。
該函數的原型如下:
u_long htonl(u_long hostlong);
g.inet_addr函數
該函數的功能是将一個由字元表示的位址轉換為32位的無符号長整型資料。cp:表示一個IP位址的字元串。傳回值:32位無符号長整數。
該函數的原型如下:
unsigned long inet_addr(const char FAR*cp);
k.recv函數
該函數的功能是從面向連接配接的套接字中接收資料。s:表示一個套接字。buf:表示接收資料的緩沖區。len:表示buf的長度。flags:表示函數的調用方式。如果為MSG_PEEK則表示檢視傳來的資料,在序列前端的資料會被複制一份到傳回緩沖區中,但是這個資料不會從序列中移走;如果為MSG_OOB,則表示用來處理Out_Of_Band資料,也就是外帶資料。
該函數的原型如下:
int recv(SOCKET s,char FAR* buf,int len,int flags);
l.send函數
該函數的功能是在面向連接配接方式的套接字間發送資料。s:表示一個套接字。buf:表示存放要發送資料的緩沖區。len:表示緩沖區長度。flags:表示函數的調用方式。
該函數的原型如下:
int send(SOCKET s,const char FAR*buf,int len,int flags);
m.recvfrom函數
該函數用于接收一個資料寶資訊并儲存源位址。s:表示準備接收資料的套接字。buf:指向緩沖區的指針,用來接收資料。len:表示緩沖區的長度。flags:通過設定這個值可以影響函數調用的行為。from:是一個指向位址結構的指針,用來接收發送資料方的位址資訊。fromlen:表示緩沖區的長度。
該函數的原型如下:
int recvfrom(SOCKET s, char FAR* buf int len,intflags,const struct sockaddr FAR* to,int tolen);
n.sendto函數
該函數的功能是向一個特定的目的發送資料。s:表示一個(可能已經建立連接配接的)套接字的辨別符。buf:指向緩沖區的指針,該緩沖區包含将要發送的資料。len:表示緩沖區的長度。flags:通過設定這個值可以影響函數的調用的行為。to:指定目标套接字的位址。tolen:表示緩沖區的長度。
該函數的原型如下:
int sendto(SOCKET s, char FAR* buf int len,intflags,const struct sockaddr FAR* to,int tolen);
o.WSACleanup函數
該函數的功能是釋放為Ws2_32.dll動态連結庫初始化時配置設定的資源。
該函數的原型如下:
int WSACleanup(void);