WinSock API實現(xiàn)UDP協(xié)議的詳細代碼
UDP協(xié)議的使用在很多方面都可以實現(xiàn)。那么今天我們將要為大家介紹的就是WinSock API實現(xiàn)UDP的過程。首先來了解一下基本定義吧。UDP協(xié)議(User Datagram Protocol),即用戶數(shù)據(jù)報協(xié)議,是定義用來在互連網(wǎng)絡(luò)環(huán)境中提供包交換的計算機通信的協(xié)議。它是Internet上廣泛采用的通信協(xié)議之一。UDP協(xié)議直接位于IP協(xié)議的頂層,屬于傳輸層協(xié)議,它提供向另一用戶程序發(fā)送信息的最簡便的協(xié)議機制。
與TCP協(xié)議不同,UDP協(xié)議是一個無連接協(xié)議,發(fā)送端和接收端不建立連接;UDP協(xié)議不提供數(shù)據(jù)傳送的保證機制,可以說它是一種不可靠的傳輸協(xié)議;UDP協(xié)議也不能確保數(shù)據(jù)的發(fā)送和接收順序,實際上,這種亂序性很少出現(xiàn),通常只是在網(wǎng)絡(luò)非常擁擠的情況下才可能發(fā)生。既然UDP協(xié)議有著如此多的缺點,那么它存在的意義何在?其實正是由于UDP協(xié)議的這些缺點,才使得它具有許多TCP協(xié)議所望塵莫及的優(yōu)勢。TCP協(xié)議植入的各種安全保障功能加大了執(zhí)行過程中的系統(tǒng)開銷,使速度受到嚴重的影響;而UDP不提供信息可靠傳遞機制,將安全和排序等功能移交給上層應(yīng)用來完成,極大地提高了執(zhí)行速度。UDP協(xié)議執(zhí)行速度快,適合視頻、音頻、文件等大規(guī)模數(shù)據(jù)的網(wǎng)絡(luò)傳輸。
盡管UDP協(xié)議與TCP協(xié)議存在著巨大的差異,但程序設(shè)計的基本步驟還是差不多的。UDP協(xié)議不存在TCP協(xié)議中的服務(wù)端和客戶端之分,相對于TCP協(xié)議的C/S模型,UDP協(xié)議的通信模型更為對稱。在UDP協(xié)議網(wǎng)絡(luò)通信中,根據(jù)功能的不同,可以劃分為發(fā)送端和接收端,但這種劃分是一種動態(tài)的劃分,而不是絕對的,同一個套接字在某一時刻發(fā)送數(shù)據(jù),那么就是發(fā)送端,而在另一時刻接收數(shù)據(jù),那么就是接受端。也就是說,同一套接字既可以是發(fā)送端也可以是接收端。另外,前面提到了UDP協(xié)議是一個無連接協(xié)議,這就是說,我們在編寫基于UDP協(xié)議的網(wǎng)絡(luò)通信程序時,不需要監(jiān)聽端口、請求連接、接受連接請求和斷開連接。
UDP協(xié)議的對稱性和無連接特性就決定了實現(xiàn)基于UDP協(xié)議的網(wǎng)絡(luò)通信比實現(xiàn)基于TCP協(xié)議的網(wǎng)絡(luò)通信在程序設(shè)計上要簡單得多。但是,在實際應(yīng)用中,由于UDP協(xié)議的不可靠性和無序性,往往需要由上層應(yīng)用來完成安全和排序等功能。
以下將給出利用WinSock API實現(xiàn)基于UDP協(xié)議的網(wǎng)絡(luò)編程的具體步驟和源代碼。
(1) 初始化通信端口。
可以在程序向?qū)е刑砑覹indows Sockets支持,或者直接添加代碼:
#include <afxsock.h>
if (!AfxSocketInit())
{
AfxMessageBox("Windows 通信端口初始化失?。?quot;);
}
#p#(2) 實現(xiàn)UDP協(xié)議。
初始化Windows Sockets DLL。目前WinSock API有兩個版本,版本號分別為1.1和2.2,對應(yīng)參數(shù)為0x101和0x202。
WSADATA wsaData;
if (WSAStartup(MAKEWORD(1,1), &wsaData) != 0)
{
AfxMessageBox("加載Windows Sockets DLL失??!");
WSACleanup();
}
(3) 創(chuàng)建流式套接字。
請注意socket()函數(shù)的第二個參數(shù)相對于ICP協(xié)議有什么變化。套接字族:
AF_UNIX: UNIX內(nèi)部協(xié)議族
AF_INET: Iternet協(xié)議
AF_NS: XeroxNs協(xié)議
AF_IMPLINK: IMP鏈接層
套接字類型:
SOCK_STREAM: 流式套接字
SOCK_DGRAM: 數(shù)據(jù)報套接字
SOCK_RAW: 原始套接字
SOCK_SEQPACKET: 定序分組套接字
SOCKET m_Socket;
m_Socket = INVALID_SOCKET;
if ((m_Socket=socket(AF_INET, SOCK_DGRAM,0))== INVALID_SOCKET)
{
AfxMessageBox("創(chuàng)建套接字失敗!");
}
#p#(4) 服務(wù)端綁定端口。
端口號范圍:1024到65535,低于1024的端口對應(yīng)著因特網(wǎng)上的一些常見服務(wù)。
struct sockaddr {
u_short sa_family; // 地址族地址族 address family
address family char sa_data[14];// 14字節(jié)的協(xié)議地址 up to 14 bytes of direct address
};
typedef struct sockaddr SOCKADDR;
typedef struct sockaddr *PSOCKADDR;
typedef struct sockaddr FAR *LPSOCKADDR;struct sockaddr_in {
short sin_family;// 地址族
u_short sin_port;// 端口號
struct in_addr sin_addr; // IP地址
char sin_zero[8];// 填充0
};
typedef struct sockaddr_in SOCKADDR_IN;
typedef struct sockaddr_in *PSOCKADDR_IN;
typedef struct sockaddr_in FAR *LPSOCKADDR_IN;
字節(jié)順序轉(zhuǎn)換函數(shù):
htons():"Host to Network Short"
htonl():"Host to Network long"
ntohs():"Network to Host Short"
ntohl():"Network to Host Long"SOCKADDR_IN m_saAddr;
u_short m_nPort = 20049;// 端口號
ZeroMemory(&m_saAddr, sizeof(m_saAddr));
m_saAddr.sin_family = AF_INET;
m_saAddr.sin_port= htons(m_nPort); // 如果此值為0,系統(tǒng)將隨機選擇一個未被使用的端口號
m_saAddr.sin_addr.s_addr = INADDR_ANY; // 填入本機IP地址
if (bind(m_Socket, (LPSOCKADDR) &m_saAddr, sizeof(m_saAddr)) == SOCKET_ERROR)
{
AfxMessageBox("綁定端口失敗!");
}
#p#(5) 注冊網(wǎng)絡(luò)事件。
網(wǎng)絡(luò)事件定義:
FD_READ: 網(wǎng)絡(luò)數(shù)據(jù)包到達
FD_WRITE: 發(fā)送網(wǎng)絡(luò)數(shù)據(jù)
FD_OOB: OOB數(shù)據(jù)到達
FD_ACCEPT: 收到連接請求
FD_CONNECT: 已建立連接
FD_CLOSE: 斷開連接
FD_QOS: 服務(wù)質(zhì)量(QoS)發(fā)生變化
FD_GROUP_QOS: 保留事件
FD_ROUTING_INTERFACE_CHANGE: 指定地址的路由接口發(fā)生變化
FD_ADDRESS_LIST_CHANGE: 本地地址變化
#define WM_NETWORK_EVENT WM_USER + 102
if (WSAAsyncSelect(m_Socket, m_hWnd, WM_NETWORK_EVENT, FD_READ) == SOCKET_ERROR)
{
AfxMessageBox("注冊網(wǎng)絡(luò)事件失敗!");
}
(6) 處理網(wǎng)絡(luò)事件。
afx_msg LRESULT OnNetworkEvent(WPARAM wParam, LPARAM lParam);
ON_MESSAGE(WM_NETWORK_EVEN, OnNetworkEvent)LRESULT OnNetworkEvent(WPARAM wParam, LPARAM lParam)
{
switch (WSAGETSELECTEVENT(lParam))
{
case FD_READ:
// 接收數(shù)據(jù)
break;
}
return 0L;
}
#p#(7) 讀取數(shù)據(jù)。
BOOL Read(void)
{
int nBytesRead;
int nBufferLength;
int nEnd;
int nSpaceRemaining;
char chIncomingDataBuffer[4096];
SOCKADDR_IN m_saFromAddr;
int nLenght = sizeof(m_saFromAddr);
ZeroMemory(&m_saFromAddr, sizeof(SOCKADDR_IN));
nEnd = 0;
nBufferLength = sizeof(chIncomingDataBuffer);
nSpaceRemaining = sizeof(chIncomingDataBuffer);
nSpaceRemaining -= nEnd;nBytesRead = recvfrom(m_Socket, (LPSTR) (chIncomingDataBuffer + nEnd), nSpaceRemaining, 0, (LPSOCKADDR) &m_saFromAddr, &nLenght);
nEnd += nBytesRead;
if (nBytesRead == SOCKET_ERROR)
{
AfxMessageBox("讀取數(shù)據(jù)出錯!")
return FALSE;
}
// IP地址:inet_ntoa(m_saFromAddr.sin_addr);
// 端口號:ntohs(m_saFromAddr.sin_port);
chIncomingDataBuffer[nEnd] = '\0';
if (lstrlen(chIncomingDataBuffer) != 0)
{
AfxMessageBox(chIncomingDataBuffer);
}
return TRUE;
}
#p# (8) 發(fā)送數(shù)據(jù)。
BOOL Send(CString sIP, u_short nPort, CString sSendData)
{
DWORDdwIP;
SOCKADDR_IN saAddr;
if (m_Socket == INVALID_SOCKET)
{
AfxMessageBox("套接字不可用!");
return FALSE;
}
if ((dwIP = inet_addr(sIP)) == INADDR_NONE)
{
AfxMessageBox("無法獲取目標IP!");
return FALSE;
}
saAddr.sin_family = AF_INET;
saAddr.sin_port= htons(nPort);
saAddr.sin_addr.s_addr = dwIP;
if (sendto(m_Socket, sSendData, sSendData.GetLength(), 0, (LPSOCKADDR) &saAddr, sizeof(saAddr)) == SOCKET_ERROR)
{
AfxMessageBox("發(fā)送數(shù)據(jù)失敗!");
return FALSE;
}
return TRUE;
}
(9) 關(guān)閉套接字。
if (m_Socket != INVALID_SOCKET)
{
closesocket(m_Socket);
}
m_Socket = INVALID_SOCKET;
WSACleanup();
具體WinSock API實現(xiàn)UDP協(xié)議的過程已經(jīng)全部講完。UDP協(xié)議真正的優(yōu)勢在于它具有TCP協(xié)議所不具備的功能,如:廣播、多播和穿透NAT等等。