1. 程式人生 > >c++網路程式設計3:UDP程式設計

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較好。