UDP的雙向通訊
雙向交替通訊(Alternating Bidirectional Communication):在這種方式下,透過約定一方作為傳送方,一方作為接收方,雙方交替傳送和接收資料。例如,一方傳送資料包給另一方,然後等待對方的回應,對方接收資料包後進行處理,然後傳送回應給傳送方,交替進行下去。
UDP 客戶端-伺服器通訊程式碼說明
概述
本文件提供了用 C 語言編寫的簡單 UDP 客戶端-伺服器通訊系統的概覽和使用說明。伺服器接收來自客戶端的訊息,允許使用者輸入響應,並將其傳送回客戶端。客戶端向伺服器傳送訊息並顯示伺服器的響應。
- 伺服器程式碼
伺服器程式碼設定了一個 UDP 套接字,繫結到指定埠,並監聽來自客戶端的訊息。收到訊息後,它會提示使用者輸入回覆,然後將回復傳送回客戶端。
- 客戶端程式碼
客戶端程式碼建立了一個 UDP 套接字,並向伺服器的 IP 地址和埠傳送訊息。然後,它等待伺服器的響應並將其列印出來。
限制和改進
- 限制:
- 阻塞 I/O:客戶端和伺服器都使用阻塞 I/O 操作(
recvfrom
和sendto
)。這意味著伺服器一次只能處理一個客戶端,客戶端必須等待伺服器的響應才能繼續。 - 無併發處理:伺服器不支援同時處理多個客戶端,這限制了其可擴充套件性。
- 硬編碼值:IP 地址和埠被硬編碼,這降低了靈活性和可重用性。
- 缺乏錯誤處理:網路操作幾乎沒有錯誤處理,可能導致崩潰或未定義的行為。
- 安全性:程式碼沒有實現任何加密或認證,使其容易受到攔截和未經授權的訪問。
- 改進:
- 非阻塞 I/O 或多執行緒:實現非阻塞 I/O 或使用多執行緒同時處理多個客戶端。
- 配置檔案:使用配置檔案或命令列引數來指定 IP 地址和埠。
- 健壯的錯誤處理:新增全面的錯誤處理,以優雅地管理網路錯誤。
- 安全增強:整合加密(如 TLS)和認證機制來保護通訊。
- 使用者介面:為伺服器開發一個使用者友好的介面,以更有效地管理收到的訊息和回覆。
使用說明
- 編譯:服務端
**gcc UDP_server.c -o server**
客戶端**gcc UDP_client.c -o client**
- 首先執行伺服器程式,確保它正在監聽傳入的訊息。
- 執行客戶端程式,向伺服器傳送訊息並接收回復。
- 本次所寫的程式碼只能實現1:1的回覆。
相關程式碼
服務端程式碼
- 初始化變數:定義套接字描述符 sockfd,緩衝區 buffer 和 reply,以及伺服器和客戶端的地址結構 servaddr 和 cliaddr。
- 建立套接字:呼叫 socket() 函式建立一個 UDP 套接字。
- 設定伺服器地址:將伺服器地址結構 servaddr 清零,並設定地址族為 IPv4 (AF_INET),IP 地址設定為 INADDR_ANY(接受任何來自本機的 IP 地址),埠號設定為 PORT。
- 繫結套接字:使用 bind() 函式將 sockfd 繫結到伺服器的 IP 地址和埠號。
- 處理客戶端訊息:進入一個無限迴圈,使用 recvfrom() 函式接收來自客戶端的訊息,並將其儲存在 buffer 中。
- 獲取併傳送回復:提示使用者輸入回覆訊息,使用 fgets() 從標準輸入讀取字串,然後使用 sendto() 函式將回復傳送給客戶端。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#define PORT 12345
#define MAXLINE 1024
int main() {
int sockfd;
char buffer[MAXLINE];
char reply[MAXLINE];
struct sockaddr_in servaddr, cliaddr;
// 建立UDP套接字
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sockfd < 0) {
perror("socket creation failed");
exit(EXIT_FAILURE);
}
memset(&servaddr, 0, sizeof(servaddr));
memset(&cliaddr, 0, sizeof(cliaddr));
// 填充伺服器資訊
servaddr.sin_family = AF_INET; // IPv4
servaddr.sin_addr.s_addr = INADDR_ANY;
servaddr.sin_port = htons(PORT);
// 繫結套接字與地址
if (bind(sockfd, (const struct sockaddr *)&servaddr, sizeof(servaddr)) < 0) {
perror("bind failed");
exit(EXIT_FAILURE);
}
while (1) {
socklen_t len = sizeof(cliaddr);
int n = recvfrom(sockfd, buffer, MAXLINE, 0, (struct sockaddr *) &cliaddr, &len);
buffer[n] = '\0'; // null terminate
printf("Client's message: %s\n", buffer);
// 獲取伺服器的回覆
printf("Enter reply message: ");
fgets(reply, MAXLINE, stdin);
// 移除換行符
reply[strcspn(reply, "\n")] = 0;
// 回覆客戶端
sendto(sockfd, reply, strlen(reply), 0, (struct sockaddr *) &cliaddr, len);
printf("Reply sent to client.\n");
}
// 關閉套接字
close(sockfd);
return 0;
}
客戶端程式碼
- 初始化變數:定義套接字描述符 sockfd,傳送和接收緩衝區 sendline 和 recvline,以及伺服器地址結構 servaddr。
- 建立套接字:呼叫 socket() 函式建立一個 UDP 套接字。
- 設定伺服器地址:將伺服器地址結構 servaddr 清零,並設定地址族為 IPv4 (AF_INET),伺服器 IP 地址和埠號。
- 傳送和接收訊息:進入一個無限迴圈,使用 fgets() 函式從標準輸入獲取訊息,然後使用 sendto() 函式傳送訊息給伺服器。之後,使用 recvfrom() 函式接收伺服器的回覆。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define SERVER_PORT 12345
#define SERVER_IP "127.0.0.1"
#define MAXLINE 1024
int main() {
int sockfd;
struct sockaddr_in servaddr;
char sendline[MAXLINE], recvline[MAXLINE + 1];
socklen_t len;
// 建立UDP套接字
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sockfd < 0) {
perror("socket creation failed");
exit(EXIT_FAILURE);
}
memset(&servaddr, 0, sizeof(servaddr));
// 填充伺服器資訊
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(SERVER_PORT);
servaddr.sin_addr.s_addr = inet_addr(SERVER_IP);
while (1) {
printf("Enter the message to send: ");
fgets(sendline, MAXLINE, stdin);
// 傳送訊息給伺服器
sendto(sockfd, sendline, strlen(sendline), 0, (struct sockaddr *) &servaddr, sizeof(servaddr));
// 接收伺服器的回覆
len = sizeof(servaddr);
int n = recvfrom(sockfd, recvline, MAXLINE, 0, (struct sockaddr *) &servaddr, &len);
recvline[n] = '\0'; // null terminate
printf("Server's reply: %s\n", recvline);
}
// 關閉套接字
close(sockfd);
return 0;
}
相關連結:https://www.jianshu.com/p/373c2934e1fe