UDP雙向通訊

banon發表於2024-06-10

UDP的雙向通訊

雙向交替通訊(Alternating Bidirectional Communication):在這種方式下,透過約定一方作為傳送方,一方作為接收方,雙方交替傳送和接收資料。例如,一方傳送資料包給另一方,然後等待對方的回應,對方接收資料包後進行處理,然後傳送回應給傳送方,交替進行下去。

UDP 客戶端-伺服器通訊程式碼說明

概述

本文件提供了用 C 語言編寫的簡單 UDP 客戶端-伺服器通訊系統的概覽和使用說明。伺服器接收來自客戶端的訊息,允許使用者輸入響應,並將其傳送回客戶端。客戶端向伺服器傳送訊息並顯示伺服器的響應。

  • 伺服器程式碼

伺服器程式碼設定了一個 UDP 套接字,繫結到指定埠,並監聽來自客戶端的訊息。收到訊息後,它會提示使用者輸入回覆,然後將回復傳送回客戶端。

  • 客戶端程式碼

客戶端程式碼建立了一個 UDP 套接字,並向伺服器的 IP 地址和埠傳送訊息。然後,它等待伺服器的響應並將其列印出來。

限制和改進

  • 限制:
  1. 阻塞 I/O:客戶端和伺服器都使用阻塞 I/O 操作(recvfromsendto)。這意味著伺服器一次只能處理一個客戶端,客戶端必須等待伺服器的響應才能繼續。
  2. 無併發處理:伺服器不支援同時處理多個客戶端,這限制了其可擴充套件性。
  3. 硬編碼值:IP 地址和埠被硬編碼,這降低了靈活性和可重用性。
  4. 缺乏錯誤處理:網路操作幾乎沒有錯誤處理,可能導致崩潰或未定義的行為。
  5. 安全性:程式碼沒有實現任何加密或認證,使其容易受到攔截和未經授權的訪問。
  • 改進:
  1. 非阻塞 I/O 或多執行緒:實現非阻塞 I/O 或使用多執行緒同時處理多個客戶端。
  2. 配置檔案:使用配置檔案或命令列引數來指定 IP 地址和埠。
  3. 健壯的錯誤處理:新增全面的錯誤處理,以優雅地管理網路錯誤。
  4. 安全增強:整合加密(如 TLS)和認證機制來保護通訊。
  5. 使用者介面:為伺服器開發一個使用者友好的介面,以更有效地管理收到的訊息和回覆。

使用說明

  1. 編譯:服務端**gcc UDP_server.c -o server** 客戶端 **gcc UDP_client.c -o client**
  2. 首先執行伺服器程式,確保它正在監聽傳入的訊息。
  3. 執行客戶端程式,向伺服器傳送訊息並接收回復。
  4. 本次所寫的程式碼只能實現1:1的回覆。

相關程式碼

服務端程式碼

  1. 初始化變數:定義套接字描述符 sockfd,緩衝區 bufferreply,以及伺服器和客戶端的地址結構 servaddrcliaddr
  2. 建立套接字:呼叫 socket() 函式建立一個 UDP 套接字。
  3. 設定伺服器地址:將伺服器地址結構 servaddr 清零,並設定地址族為 IPv4 (AF_INET),IP 地址設定為 INADDR_ANY(接受任何來自本機的 IP 地址),埠號設定為 PORT
  4. 繫結套接字:使用 bind() 函式將 sockfd 繫結到伺服器的 IP 地址和埠號。
  5. 處理客戶端訊息:進入一個無限迴圈,使用 recvfrom() 函式接收來自客戶端的訊息,並將其儲存在 buffer 中。
  6. 獲取併傳送回復:提示使用者輸入回覆訊息,使用 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;
}

客戶端程式碼

  1. 初始化變數:定義套接字描述符 sockfd,傳送和接收緩衝區 sendlinerecvline,以及伺服器地址結構 servaddr
  2. 建立套接字:呼叫 socket() 函式建立一個 UDP 套接字。
  3. 設定伺服器地址:將伺服器地址結構 servaddr 清零,並設定地址族為 IPv4 (AF_INET),伺服器 IP 地址和埠號。
  4. 傳送和接收訊息:進入一個無限迴圈,使用 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

相關文章