首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

Socket 網絡編程完全指南:地址結構、TCP 流程與服務端/客戶端實現

     編輯:關於C語言

Socket 網絡編程完全指南:地址結構、TCP 流程與服務端/客戶端實現

一、Socket 地址數據類型與相關函數

Socket API 抽象了網絡協議差異,適用於 IPv4、IPv6、Unix Domain Socket 等。不同協議地址格式不同:

  • IPv4: `struct sockaddr_in` — 16 位端口 + 32 位 IP,定義於 ``
  • IPv6: `struct sockaddr_in6` — 16 位端口 + 128 位 IP + 控制字段
  • Unix Domain: `struct sockaddr_un` — 定義於 ``

所有 `sockaddr` 結構體開頭相同:前 16 位長度(Linux 無)、後 16 位地址族(`AF_INET`/`AF_INET6`/`AF_UNIX`)。API 參數用 `struct sockaddr*` 接收各類指針,調用時需強轉。

IP 地址字符串與二進制轉換

// 字符串 → 二進制
int inet_aton(const char *str, struct in_addr *addr);
in_addr_t inet_addr(const char *str); // 已棄用
int inet_pton(int family, const char *str, void *addr); // 支持 IPv4/IPv6

// 二進制 → 字符串
char *inet_ntoa(struct in_addr addr); // 非線程安全
const char *inet_ntop(int family, const void *addr, char *str, size_t len); // 推薦

二、基於 TCP 協議的網絡程序

1. TCP 通訊流程

建立連接(三次握手):
Server: `socket()` → `bind()` → `listen()` → `accept()` 阻塞等待
Client: `socket()` → `connect()` 發送 SYN 阻塞
Server 收到 SYN,回 SYN-ACK,`accept()` 返回
Client 收到 SYN-ACK,回 ACK,`connect()` 返回
雙方建立全雙工連接。

數據傳輸: 典型請求-響應模式:Server `read()` 阻塞 → Client `write()` 請求 → Server 處理 → `write()` 響應 → Client `read()` 接收 → 循環。

關閉連接(四次揮手):
Client `close()` → Server `read()` 返回 0 → Server `close()`。任意一方 `close()` 後雙向關閉;`shutdown()` 可半關閉。

2. 服務端代碼示例

/* server.c — 接收字符轉大寫返回 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#define MAXLINE 80
#define SERV_PORT 8000

int main() {
    struct sockaddr_in servaddr, cliaddr;
    socklen_t cliaddr_len;
    int listenfd, connfd;
    char buf[MAXLINE];
    char str[INET_ADDRSTRLEN];

    listenfd = socket(AF_INET, SOCK_STREAM, 0);
    bzero(&servaddr, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY); // 監聽所有網卡
    servaddr.sin_port = htons(SERV_PORT);

    bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr));
    listen(listenfd, 20);
    printf("Accepting connections...\n");

    while (1) {
        cliaddr_len = sizeof(cliaddr);
        connfd = accept(listenfd, (struct sockaddr*)&cliaddr, &cliaddr_len);
        int n = read(connfd, buf, MAXLINE);
        printf("Received from %s at PORT %d\n",
               inet_ntop(AF_INET, &cliaddr.sin_addr, str, sizeof(str)),
               ntohs(cliaddr.sin_port));
        for (int i = 0; i < n; i++) buf[i] = toupper(buf[i]);
        write(connfd, buf, n);
        close(connfd); // 斷開當前連接,listenfd 繼續監聽
    }
    return 0;
}

3. 客戶端代碼示例

/* client.c — 發送命令行參數字符串,顯示響應 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#define MAXLINE 80
#define SERV_PORT 8000

int main(int argc, char *argv[]) {
    if (argc != 2) {
        fprintf(stderr, "usage: ./client message\n");
        exit(1);
    }
    struct sockaddr_in servaddr;
    char buf[MAXLINE];
    int sockfd, n;

    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    bzero(&servaddr, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    inet_pton(AF_INET, "127.0.0.1", &servaddr.sin_addr);
    servaddr.sin_port = htons(SERV_PORT);

    connect(sockfd, (struct sockaddr*)&servaddr, sizeof(servaddr));
    write(sockfd, argv[1], strlen(argv[1]));
    n = read(sockfd, buf, MAXLINE);
    printf("Response from server:\n");
    write(STDOUT_FILENO, buf, n);
    close(sockfd);
    return 0;
}

4. 關鍵 API 詳解

函數作用關鍵參數
`socket(family, type, protocol)`創建套接字,返回文件描述符TCP: `AF_INET, SOCK_STREAM, 0`
`bind(sockfd, addr, addrlen)`綁定固定地址/端口服務端必調用,客戶端通常不調用(內核自動分配)
`listen(sockfd, backlog)`監聽,設置等待隊列長度`backlog` 為內核排隊上限
`accept(sockfd, cliaddr, addrlen)`接受連接,返回新 fd `connfd`阻塞直到有連接;`cliaddr` 輸出客戶端地址
`connect(sockfd, servaddr, addrlen)`主動發起連接客戶端調用,三次握手完成返回
`read/write(fd, buf, len)`數據收發同文件 IO,阻塞模式
`close(fd)`關閉連接引用計數歸零時發送 FIN

5. 編譯運行

gcc server.c -o server
gcc client.c -o client
./server &      # 後台運行服務端
./client "hello world"  # 客戶端發送消息

輸出示例:
Server: `Received from 127.0.0.1 at PORT 12345`
Client: `Response from server: HELLO WORLD`

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved