萬盛學電腦網

您现在的位置: 萬盛學電腦網 >> 網路安全 >  >> 網路基礎知識

Socket編程核心API詳解:sendto、accept參數與服務端/客戶端完整示例

Socket關鍵函數原型與參數詳解

sendto() — UDP數據發送 / 無連接發送

int sendto(
    SOCKET s,                 // 已創建的套接字描述符
    const char FAR* buf,      // 待發送數據緩衝區指針
    int size,                 // 緩衝區長度 (字節數)
    int flags,                // 調用標志位,通常為0;可設MSG_DONTROUTE、MSG_OOB等
    const struct sockaddr FAR* to,  // 目標地址結構指針 (IP+端口)
    int tolen                 // 目標地址結構大小
);

返回值:成功返回實際發送字節數;失敗返回SOCKET_ERROR,調用WSAGetLastError()獲取錯誤碼。

accept() — TCP服務端接受連接

int accept(
    int fd,                   // 監聽套接字描述符 (由socket()+bind()+listen()獲得)
    struct sockaddr* addr,    // 輸出參數:返回客戶端地址信息
    socklen_t* len            // 輸入輸出參數:輸入addr緩衝區大小,返回實際填寫大小
);

返回值:成功返回新的已連接套接字描述符 (用於與該客戶端通信);失敗返回-1,errno指示錯誤。原監聽套接字繼續監聽新連接。

完整TCP回聲服務端/客戶端示例 (Windows/Winsock2)

服務端代碼

#include <winsock2.h>
#include <stdio.h>
#pragma comment(lib, "ws2_32.lib")

int main() {
    WSADATA wsaData;
    SOCKET sockServer, sockClient;
    SOCKADDR_IN addrServer, addrClient;
    char recvBuf[100], sendBuf[100];
    int len = sizeof(SOCKADDR);

    // 1. 初始化Winsock
    if (WSAStartup(MAKEWORD(2,2), &wsaData) != 0) {
        printf("WSAStartup failed: %d\n", WSAGetLastError());
        return 1;
    }

    // 2. 創建流式套接字
    sockServer = socket(AF_INET, SOCK_STREAM, 0);
    if (sockServer == INVALID_SOCKET) {
        printf("socket failed: %d\n", WSAGetLastError());
        WSACleanup(); return 1;
    }

    // 3. 綁定地址端口
    addrServer.sin_family = AF_INET;
    addrServer.sin_addr.S_un.S_addr = htonl(INADDR_ANY); // 綁定所有本地接口
    addrServer.sin_port = htons(6000);                   // 端口6000
    if (bind(sockServer, (SOCKADDR*)&addrServer, sizeof(SOCKADDR)) == SOCKET_ERROR) {
        printf("bind failed: %d\n", WSAGetLastError());
        closesocket(sockServer); WSACleanup(); return 1;
    }

    // 4. 監聽
    if (listen(sockServer, 5) == SOCKET_ERROR) { // 最大等待連接隊列5
        printf("listen failed: %d\n", WSAGetLastError());
        closesocket(sockServer); WSACleanup(); return 1;
    }
    printf("服務器已啟動,監聽端口 6000...\n");

    // 5. 接受連接 (阻塞)
    sockClient = accept(sockServer, (SOCKADDR*)&addrClient, &len);
    if (sockClient == INVALID_SOCKET) {
        printf("accept failed: %d\n", WSAGetLastError());
        closesocket(sockServer); WSACleanup(); return 1;
    }
    printf("客戶端 [%s:%d] 已連接\n",
           inet_ntoa(addrClient.sin_addr), ntohs(addrClient.sin_port));

    // 6. 接收數據
    int recvLen = recv(sockClient, recvBuf, sizeof(recvBuf)-1, 0);
    if (recvLen > 0) {
        recvBuf[recvLen] = '\0';
        printf("收到: %s\n", recvBuf);
        // 7. 回射發送
        sprintf(sendBuf, "Echo: %s", recvBuf);
        send(sockClient, sendBuf, (int)strlen(sendBuf), 0);
    }

    // 8. 清理
    closesocket(sockClient);
    closesocket(sockServer);
    WSACleanup();
    return 0;
}

客戶端代碼

#include <winsock2.h>
#include <stdio.h>
#pragma comment(lib, "ws2_32.lib")

int main() {
    WSADATA wsaData;
    SOCKET sockClient;
    SOCKADDR_IN addrServer;
    char sendBuf[100] = "Hello from Client!";
    char recvBuf[100];

    if (WSAStartup(MAKEWORD(2,2), &wsaData) != 0) return 1;

    sockClient = socket(AF_INET, SOCK_STREAM, 0);
    if (sockClient == INVALID_SOCKET) { WSACleanup(); return 1; }

    addrServer.sin_family = AF_INET;
    addrServer.sin_addr.S_un.S_addr = inet_addr("127.0.0.1"); // 服務端IP
    addrServer.sin_port = htons(6000);

    if (connect(sockClient, (SOCKADDR*)&addrServer, sizeof(SOCKADDR)) == SOCKET_ERROR) {
        printf("connect failed: %d\n", WSAGetLastError());
        closesocket(sockClient); WSACleanup(); return 1;
    }
    printf("已連接到服務器\n");

    send(sockClient, sendBuf, (int)strlen(sendBuf), 0);
    int recvLen = recv(sockClient, recvBuf, sizeof(recvBuf)-1, 0);
    if (recvLen > 0) {
        recvBuf[recvLen] = '\0';
        printf("服務器回應: %s\n", recvBuf);
    }

    closesocket(sockClient);
    WSACleanup();
    return 0;
}

關鍵注意點

  • 字節序轉換:htons/htonl (主機序→網絡序大端),ntohs/ntohl 逆向。IP地址、端口號必須轉換。
  • 阻塞模式:默認accept/recv/send/connect均阻塞。高併發需用select/poll/epoll/IOCP或非阻塞+異步模型。
  • TCP粘包:recv返回長度不固定,應用層需自定義幀格式 (長度前綴/分隔符/定長) 邊界處理。
  • 資源釋放:所有路徑確保closesocket與WSACleanup配對調用。
  • 錯誤處理:每個API調用後檢查返回值,WSAGetLastError()定位具體錯誤 (如WSAECONNREFUSED, WSAETIMEDOUT等)。

跨平台提示

Linux/Unix下頭文件換為<sys/socket.h>、<netinet/in.h>、<arpa/inet.h>、<unistd.h>;無需WSAStartup/WSACleanup;描述符類型為int;錯誤碼在errno;其餘邏輯一致。建議封裝跨平台Socket封裝層。

copyright © 萬盛學電腦網 all rights reserved