浪潮君 的學生作業(yè):
#include // 標準輸入輸出庫
#include // 提供 exit(), malloc(), free()
#include // 提供字符串操作函數,如 strlen(), strncpy(), memcpy()
#include // 提供 close(), pthread_exit()
#include // POSIX 線程庫,用于創(chuàng)建和管理線程
#include // 提供 socket 通信接口
#include // 提供 sockaddr_in 結構和端口、地址常量
#include // 提供 inet_ntoa 等 IP 地址轉換函數
#define SERVER_PORT 8888 // 服務器監(jiān)聽的 UDP 端口號
#define BUFFER_SIZE 1024 // 緩沖區(qū)大?。ń邮?發(fā)送用)
#define PREFIX "回顯: " // 回復前綴,回顯消息統(tǒng)一加上該前綴
// 線程參數結構體:用于傳遞每條消息的上下文給線程處理
typedef struct {
int sockfd; // socket 文件描述符
struct sockaddr_in client_addr; // 客戶端地址結構體
char message[BUFFER_SIZE]; // 客戶端發(fā)送的消息內容
ssize_t msg_len; // 客戶端消息實際長度(字節(jié)數)
} thread_arg_t;
// 線程處理函數:每次接收到客戶端數據后就創(chuàng)建線程執(zhí)行該函數
void *handle_client(void *arg) {
thread_arg_t *client = (thread_arg_t *) arg; // 將 void* 參數轉換為 thread_arg_t*
char reply[BUFFER_SIZE]; // 構造回復消息的緩沖區(qū)
size_t prefix_len = strlen(PREFIX); // 獲取前綴長度
ssize_t reply_len = client->msg_len; // 獲取客戶端消息長度
// 防止拼接后超出 reply 緩沖區(qū),進行長度截斷處理
if (prefix_len + reply_len + 1 > BUFFER_SIZE) {
reply_len = BUFFER_SIZE - prefix_len - 1;
}
// 拼接回顯消息:將 "回顯: " 前綴和客戶端消息合并
memcpy(reply, PREFIX, prefix_len); // 復制前綴
memcpy(reply + prefix_len, client->message, reply_len); // 復制消息內容
reply[prefix_len + reply_len] = '\0'; // 添加字符串結束符
// 打印客戶端的 IP、端口和消息內容
printf("收到來自 %s:%d 的數據 :%s\n",
inet_ntoa(client->client_addr.sin_addr), // 將 IP 地址轉為字符串
ntohs(client->client_addr.sin_port), // 將端口號從網絡字節(jié)序轉為主機字節(jié)序
client->message); // 打印原始消息
// 發(fā)送回顯消息給客戶端
sendto(client->sockfd, reply, prefix_len + reply_len, 0,
(struct sockaddr*) &client->client_addr, sizeof(client->client_addr));
free(client); // 釋放線程使用的動態(tài)內存
pthread_exit(NULL); // 線程退出
}
// 主函數:負責 socket 初始化、接收數據并啟動線程
int main(int argc, char *argv[]) {
int sockfd; // socket 描述符
struct sockaddr_in server_addr, client_addr; // 服務器和客戶端地址結構
socklen_t addr_len = sizeof(client_addr); // 客戶端地址長度
// 創(chuàng)建 UDP socket,指定為 IPv4 + 無連接數據報類型
if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
perror("sock failed"); // 打印錯誤信息
exit(EXIT_FAILURE); // 創(chuàng)建失敗則退出
}
// 初始化服務器地址結構體,清零后設置協(xié)議族、IP 和端口
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET; // 設置地址族為 IPv4
server_addr.sin_addr.s_addr = INADDR_ANY; // 接收任意本地地址的數據
server_addr.sin_port = htons(SERVER_PORT); // 綁定端口(主機字節(jié)序轉網絡字節(jié)序)
// 將 socket 綁定到本地地址和端口
if (bind(sockfd, (struct sockaddr*) &server_addr, sizeof(server_addr)) < 0) {
perror("bind failed"); // 打印綁定失敗信息
close(sockfd); // 關閉 socket
exit(EXIT_FAILURE); // 退出程序
}
printf("多線程UDP服務器啟動成功,監(jiān)聽端口 %d...\n", SERVER_PORT);
// 主循環(huán):持續(xù)接收客戶端 UDP 消息,并為每條消息創(chuàng)建一個線程處理
while (1) {
char buffer[BUFFER_SIZE]; // 接收緩沖區(qū)
// 接收客戶端發(fā)送的 UDP 數據報
ssize_t recv_len = recvfrom(sockfd, buffer, BUFFER_SIZE - 1, 0,
(struct sockaddr*) &client_addr, &addr_len);
if (recv_len < 0) {
perror("recvfrom failed"); // 接收失敗,打印錯誤信息
continue; // 繼續(xù)接收下一個客戶端消息
}
buffer[recv_len] = '\0'; // 添加字符串結束符,確??纱蛴?
// 動態(tài)分配線程參數結構體
thread_arg_t *client = malloc(sizeof(thread_arg_t));
if (!client) {
fprintf(stderr, "內存分配失敗"); // 打印分配失敗信息
continue; // 不處理當前消息,繼續(xù)等待下一條
}
// 填充線程參數結構體
client->sockfd = sockfd; // socket 描述符
client->client_addr = client_addr; // 客戶端地址
client->msg_len = recv_len; // 消息長度
strncpy(client->message, buffer, BUFFER_SIZE); // 拷貝消息內容
client->message[BUFFER_SIZE - 1] = '\0'; // 手動添加結束符,避免溢出
// 創(chuàng)建線程處理當前客戶端請求
pthread_t tid;
if (pthread_create(&tid, NULL, handle_client, client) != 0) {
perror("線程創(chuàng)建失敗");
free(client); // 創(chuàng)建失敗需釋放內存
}
// 設置線程為分離狀態(tài),自動釋放資源(不需要 pthread_join)
pthread_detach(tid);
}
close(sockfd); // 程序退出前關閉 socket
return 0;
}