UDP(User Datagram Protocol)是無連線、不可靠的傳輸協定,適合即時性要求高、容忍少量封包遺失的場合。本文示範直接使用 WinSock API 實作 UDP 收發,而非使用封裝元件,以獲得更精細控制。
Destino:目標 IP 位址(無符號長整數)。0 表示接受任意來源封包,接收後自動更新為發送者位址;指定位址則只接受該來源資料。gvEncerrar:全域終止旗標,設為真時中斷處理迴圈。gvSocket:全域 Socket 控制代碼。gvPortUdp:UDP 通訊埠號。Inicio + 12 秒(12 秒逾時)。int ReadBufferUdp(unsigned long *Destino, void *T, int Size) {
char Buffer[128];
SOCKADDR_IN SockAddr;
int LenSockAddr = sizeof(SOCKADDR_IN);
fd_set FdRead;
struct timeval t_val;
int Ret;
time_t Inicio = time(NULL);
Application->ProcessMessages();
if (gvEncerrar) return false;
FD_ZERO(&FdRead);
FD_SET(gvSocket, &FdRead);
t_val.tv_sec = 0;
t_val.tv_usec = 0;
// 等待資料或逾時
while ((Ret = select(0, &FdRead, NULL, NULL, &t_val)) != 1 &&
(Inicio + 12) > time(NULL) && !gvEncerrar) {
FD_ZERO(&FdRead);
FD_SET(gvSocket, &FdRead);
t_val.tv_sec = 0;
t_val.tv_usec = 0;
Application->ProcessMessages();
}
if (Ret != 1) return false;
// 接收資料
if (recvfrom(gvSocket, Buffer, Size, 0, (LPSOCKADDR)&SockAddr, &LenSockAddr) != Size)
return false;
// 位址驗證與更新
if (*Destino == 0) {
*Destino = SockAddr.sin_addr.s_addr; // 記錄首次發送者位址
} else if (*Destino != SockAddr.sin_addr.s_addr) {
return false; // 非預期來源
}
memcpy(T, Buffer, Size);
return true;
}
int WriteBufferUdp(unsigned long Destino, void *T, int Size) {
SOCKADDR_IN SockAddr;
int Sent;
Application->ProcessMessages();
SockAddr.sin_family = AF_INET;
SockAddr.sin_port = gvPortUdp;
SockAddr.sin_addr.s_addr = Destino;
Sent = sendto(gvSocket, (char*)T, Size, 0, (LPSOCKADDR)&SockAddr, sizeof(SockAddr));
return (Sent == Size);
}
void InicializaTCPIP() {
WORD wVersionRequested = MAKEWORD(1, 1);
WSADATA wsaData;
IN_ADDR In;
PSERVENT PServent;
SOCKADDR_IN SockAddrIn;
// 載入 WinSock DLL
if (WSAStartup(wVersionRequested, &wsaData)) {
ShowMessage("TCP/IP 初始化失敗");
Application->Terminate();
return;
}
// 從服務檔取得埠號
if ((PServent = getservbyname("your_service_name", "udp")) == NULL) {
ShowMessage("無法取得服務埠號");
Application->Terminate();
return;
}
gvPortUdp = PServent->s_port;
// 記錄埠號日誌...
// 建立 UDP Socket
if ((gvSocket = socket(AF_INET, SOCK_DGRAM, 0)) == INVALID_SOCKET) {
ShowMessage("Socket 建立失敗");
Application->Terminate();
return;
}
// 記錄成功日誌...
// 綁定 Socket
SockAddrIn.sin_family = AF_INET;
SockAddrIn.sin_port = gvPortUdp;
SockAddrIn.sin_addr.s_addr = INADDR_ANY; // NULL 等同 INADDR_ANY
if (bind(gvSocket, (LPSOCKADDR)&SockAddrIn, sizeof(SockAddrIn)) == SOCKET_ERROR) {
ShowMessage("Socket 綁定失敗");
Application->Terminate();
return;
}
// 記錄綁定成功日誌...
}
select() 搭配零逾時結構實現非阻塞檢查,配合 Application->ProcessMessages() 保持介面回應。Destino=0 模式自動學習首個發送者位址,後續僅接受該位址資料,適合簡單點對點通訊。getservbyname() 從系統服務檔(%windir%\system32\drivers\etc\services)讀取埠號,提升可維護性。此架構可作為 UDP 通訊模組的基礎,擴充時可加入封包序號、重傳機制或加密模組,構建可靠的 UDP 應用協定。