WinSock API實現UDP協議的詳細代碼
UDP協議的使用在很多方面都可以實現。那么今天我們將要為大家介紹的就是WinSock API實現UDP的過程。首先來了解一下基本定義吧。UDP協議(User Datagram Protocol),即用戶數據報協議,是定義用來在互連網絡環境中提供包交換的計算機通信的協議。它是Internet上廣泛采用的通信協議之一。UDP協議直接位于IP協議的頂層,屬于傳輸層協議,它提供向另一用戶程序發送信息的最簡便的協議機制。
與TCP協議不同,UDP協議是一個無連接協議,發送端和接收端不建立連接;UDP協議不提供數據傳送的保證機制,可以說它是一種不可靠的傳輸協議;UDP協議也不能確保數據的發送和接收順序,實際上,這種亂序性很少出現,通常只是在網絡非常擁擠的情況下才可能發生。既然UDP協議有著如此多的缺點,那么它存在的意義何在?其實正是由于UDP協議的這些缺點,才使得它具有許多TCP協議所望塵莫及的優勢。TCP協議植入的各種安全保障功能加大了執行過程中的系統開銷,使速度受到嚴重的影響;而UDP不提供信息可靠傳遞機制,將安全和排序等功能移交給上層應用來完成,極大地提高了執行速度。UDP協議執行速度快,適合視頻、音頻、文件等大規模數據的網絡傳輸。
盡管UDP協議與TCP協議存在著巨大的差異,但程序設計的基本步驟還是差不多的。UDP協議不存在TCP協議中的服務端和客戶端之分,相對于TCP協議的C/S模型,UDP協議的通信模型更為對稱。在UDP協議網絡通信中,根據功能的不同,可以劃分為發送端和接收端,但這種劃分是一種動態的劃分,而不是絕對的,同一個套接字在某一時刻發送數據,那么就是發送端,而在另一時刻接收數據,那么就是接受端。也就是說,同一套接字既可以是發送端也可以是接收端。另外,前面提到了UDP協議是一個無連接協議,這就是說,我們在編寫基于UDP協議的網絡通信程序時,不需要監聽端口、請求連接、接受連接請求和斷開連接。
UDP協議的對稱性和無連接特性就決定了實現基于UDP協議的網絡通信比實現基于TCP協議的網絡通信在程序設計上要簡單得多。但是,在實際應用中,由于UDP協議的不可靠性和無序性,往往需要由上層應用來完成安全和排序等功能。
以下將給出利用WinSock API實現基于UDP協議的網絡編程的具體步驟和源代碼。
(1) 初始化通信端口。
可以在程序向導中添加Windows Sockets支持,或者直接添加代碼:
#include <afxsock.h>
if (!AfxSocketInit())
{
AfxMessageBox("Windows 通信端口初始化失敗!");
}
#p#(2) 實現UDP協議。
初始化Windows Sockets DLL。目前WinSock API有兩個版本,版本號分別為1.1和2.2,對應參數為0x101和0x202。
WSADATA wsaData;
if (WSAStartup(MAKEWORD(1,1), &wsaData) != 0)
{
AfxMessageBox("加載Windows Sockets DLL失敗!");
WSACleanup();
}
(3) 創建流式套接字。
請注意socket()函數的第二個參數相對于ICP協議有什么變化。套接字族:
AF_UNIX: UNIX內部協議族
AF_INET: Iternet協議
AF_NS: XeroxNs協議
AF_IMPLINK: IMP鏈接層
套接字類型:
SOCK_STREAM: 流式套接字
SOCK_DGRAM: 數據報套接字
SOCK_RAW: 原始套接字
SOCK_SEQPACKET: 定序分組套接字
SOCKET m_Socket;
m_Socket = INVALID_SOCKET;
if ((m_Socket=socket(AF_INET, SOCK_DGRAM,0))== INVALID_SOCKET)
{
AfxMessageBox("創建套接字失敗!");
}
#p#(4) 服務端綁定端口。
端口號范圍:1024到65535,低于1024的端口對應著因特網上的一些常見服務。
struct sockaddr {
u_short sa_family; // 地址族地址族 address family
address family char sa_data[14];// 14字節的協議地址 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;
字節順序轉換函數:
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,系統將隨機選擇一個未被使用的端口號
m_saAddr.sin_addr.s_addr = INADDR_ANY; // 填入本機IP地址
if (bind(m_Socket, (LPSOCKADDR) &m_saAddr, sizeof(m_saAddr)) == SOCKET_ERROR)
{
AfxMessageBox("綁定端口失敗!");
}
#p#(5) 注冊網絡事件。
網絡事件定義:
FD_READ: 網絡數據包到達
FD_WRITE: 發送網絡數據
FD_OOB: OOB數據到達
FD_ACCEPT: 收到連接請求
FD_CONNECT: 已建立連接
FD_CLOSE: 斷開連接
FD_QOS: 服務質量(QoS)發生變化
FD_GROUP_QOS: 保留事件
FD_ROUTING_INTERFACE_CHANGE: 指定地址的路由接口發生變化
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("注冊網絡事件失敗!");
}
(6) 處理網絡事件。
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:
// 接收數據
break;
}
return 0L;
}
#p#(7) 讀取數據。
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("讀取數據出錯!")
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) 發送數據。
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("發送數據失敗!");
return FALSE;
}
return TRUE;
}
(9) 關閉套接字。
if (m_Socket != INVALID_SOCKET)
{
closesocket(m_Socket);
}
m_Socket = INVALID_SOCKET;
WSACleanup();
具體WinSock API實現UDP協議的過程已經全部講完。UDP協議真正的優勢在于它具有TCP協議所不具備的功能,如:廣播、多播和穿透NAT等等。