c++網路程式設計3:UDP程式設計
一.概念:
UDP是傳輸層中面向無連線的協議,所以UDP丟包後是不會重傳的,而且他在程式設計上服務端和客戶端是沒有區別的,有的只是“虛擬上”的服務端和客戶端,他在程式設計的實現上也很簡單,不像TCP那麼複雜。
二.UDP終端的程式設計
由於UDP在服務端和客戶端是一樣的,所以稱為UDP終端,程式設計步驟如下:
1.載入套接字
WORD wVersionRequested; wVersionRequested=MAKEWORD(1,1);//指定Winsock庫的版本:1.1 WSADATA wsaData; int err; //使用WSAStartup載入套接字型檔,以及確定使用的套接字型檔的版本,這裡使用的是1.1的版本 err=WSAStartup(wVersionRequested,&wsaData); if(err!=0) return false; if(LOBYTE(wsaData.wVersion)!=1||HIBYTE(wsaData.wVersion)!=1) { //如果套接字型檔載入成功,但是版本不正確,那麼呼叫WSACleanup 釋放對套接字型檔的佔用資源 WSACleanup(); return false; }
和TCP的載入套接字沒區別
2.建立套接字
SOCKET sock;
unsigned long mode=1;//0:阻塞
int nRet;
sock=socket(AF_INET,SOCK_DGRAM,0);
與TCP區別再與socket的第二個引數,這裡是SOCK_DGRAM(資料式套接字),TCP的是SOCK_STREAM(流式套接字)
3.繫結套接字(這部是必須的,在終端開啟埠)
//LPCTSTR在unicode環境下是const wchar *,非unicode環境下是const char * //INADDR_ANY表示如果一臺機器上(伺服器)有多塊網絡卡,每個網絡卡都有各自的IP,則INADDR_ANY //會將套接字繫結到該機器上的所有網絡卡地址和埠上 SOCKADDR_IN addrSrv; if(localIP==NULL) addrSrv.sin_addr.S_un.S_addr= htonl(INADDR_ANY);//由於這裡需要網路位元組序,所以將主機位元組序轉換成網路位元組序 else addrSrv.sin_addr.S_un.S_addr=inet_addr(localIP);//inet_addr將字串ip轉換成long型 addrSrv.sin_family=AF_INET; addrSrv.sin_port=htons(localPort);//由於這裡需要網路位元組序,所以將主機位元組序轉換成網路位元組序 //下面的程式碼等效於這行程式碼return bind(hSocket,(SOCKADDR *)&addrSrv,sizeof(SOCKADDR))==0; if(bind(sock,(SOCKADDR *)&addrSrv,sizeof(SOCKADDR))==0) return true; else return false;
4.上面三步做完,就可以開啟執行緒,線上程中使用recvfrom來接受資料(TCP使用的是recv)
RLen=recvfrom(sock,(char *)RecvBuffer,RecvBufferLen,0,(SOCKADDR *)&addrFrom,addrDataLen);
if(RLen<=0)
{
int error=WSAGetLastError();
TRACE("socket api error is %d\n",error);
}
RecvBuffer是接受到的資料,RecvBufferLen是需要一次接收的資料長度,RLen是實際接收的資料長度,addrFrom可以獲取傳送資料的UDP終端的地址和埠
注意: 雖然這裡可以設定一次接收的資料長度RecvBufferLen,但是這個長度必須需要系統快取的支援,系統有一個接收資料的快取,這個快取負責將網路傳過來的資料放入其中,然後recv是從這個系統快取取資料,系統預設的SOCKET接受快取的大小為8688B(8.6K左右),如果sock函式recv或recvFrom從系統快取中接受資料太慢,那麼系統接受到新資料後就會將原快取給覆蓋了,這樣在次呼叫recv函式就獲取到了新資料,老資料就丟失了,這時候就要使用setsockopt方法將系統快取開到32K最合適,如下:
if(setsockopt(sock,SOL_SOCKET,SO_RCVBUF,(const char *)&Max_RecvFrom_Buffer_Size,sizeof(int))!=0)
{
//設定失敗
return INVALID_SOCKET;
}
Max_RecvFrom_Buffer_Size=32*1024B,在建立一個客戶端的套接字之後就要使用setsockopt來設定系統快取的大小了,也就是第2部做完後就要設定系統快取
5.使用sendto來發送資料(TCP使用的是send)
SOCKADDR_IN addrTo;
addrTo.sin_addr.S_un.S_addr=inet_addr(IP);//inet_addr將字串ip轉換成long型
addrTo.sin_family=AF_INET;
addrTo.sin_port=htons(Port);
sendto(sock,SendBuffer,sendBufLen,0,(SOCKADDR*)&addrTo,sizeof(SOCKADDR));
SendBuffer是傳送的資料,sendBufLen是一次傳送資料的長度,SOCKADDR_IN這個結構填入需要傳送到的目的IP和埠,這裡的傳送長度要考慮網路最大的傳輸單元MTU=1500B,傳送的資料長度控制在1400B較好。