跳转到内容

C 语言网络编程基础

来自代码酷

C语言网络编程基础[编辑 | 编辑源代码]

C语言网络编程是指使用C语言及相关库函数实现计算机之间的网络通信。它是系统编程和网络技术的结合,广泛应用于服务器开发、嵌入式设备通信、网络安全工具等领域。本节将介绍基础概念、核心API及实际应用案例。

核心概念[编辑 | 编辑源代码]

网络编程的核心是套接字(Socket),它是网络通信的端点,允许不同主机或同一主机的不同进程间交换数据。主要分为两类:

  • 流式套接字(SOCK_STREAM):基于TCP协议,提供可靠、面向连接的通信
  • 数据报套接字(SOCK_DGRAM):基于UDP协议,提供无连接的快速通信

graph LR A[应用程序] -->|调用| B[socket()] B --> C[bind()] C --> D[listen()/connect()] D --> E[accept()/send()/recv()]

基础API详解[编辑 | 编辑源代码]

创建套接字[编辑 | 编辑源代码]

使用socket()函数创建通信端点:

#include <sys/socket.h>
int socket(int domain, int type, int protocol);

示例创建TCP套接字:

int sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd == -1) {
    perror("socket creation failed");
    exit(EXIT_FAILURE);
}

地址结构[编辑 | 编辑源代码]

网络地址使用sockaddr_in结构体:

struct sockaddr_in {
    sa_family_t    sin_family; // 地址族,如AF_INET
    in_port_t      sin_port;   // 端口号(网络字节序)
    struct in_addr sin_addr;   // IP地址
};

struct in_addr {
    uint32_t s_addr; // 32位IP地址
};

服务器端流程[编辑 | 编辑源代码]

1. 绑定地址:bind() 2. 监听连接:listen() 3. 接受连接:accept()

示例服务器代码:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>

#define PORT 8080
#define BACKLOG 5

int main() {
    int server_fd, new_socket;
    struct sockaddr_in address;
    int opt = 1;
    int addrlen = sizeof(address);
    
    // 创建套接字
    if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {
        perror("socket failed");
        exit(EXIT_FAILURE);
    }
    
    // 设置套接字选项
    if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt))) {
        perror("setsockopt");
        exit(EXIT_FAILURE);
    }
    
    address.sin_family = AF_INET;
    address.sin_addr.s_addr = INADDR_ANY;
    address.sin_port = htons(PORT);
    
    // 绑定
    if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) {
        perror("bind failed");
        exit(EXIT_FAILURE);
    }
    
    // 监听
    if (listen(server_fd, BACKLOG) < 0) {
        perror("listen");
        exit(EXIT_FAILURE);
    }
    
    printf("Server listening on port %d\n", PORT);
    
    // 接受连接
    if ((new_socket = accept(server_fd, (struct sockaddr *)&address, (socklen_t*)&addrlen)) < 0) {
        perror("accept");
        exit(EXIT_FAILURE);
    }
    
    char buffer[1024] = {0};
    read(new_socket, buffer, 1024);
    printf("Message from client: %s\n", buffer);
    
    char *hello = "Hello from server";
    send(new_socket, hello, strlen(hello), 0);
    
    close(new_socket);
    close(server_fd);
    return 0;
}

客户端流程[编辑 | 编辑源代码]

1. 连接服务器:connect() 2. 发送/接收数据:send()/recv()

示例客户端代码:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>

#define PORT 8080

int main() {
    int sock = 0;
    struct sockaddr_in serv_addr;
    char *hello = "Hello from client";
    char buffer[1024] = {0};
    
    if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
        printf("\n Socket creation error \n");
        return -1;
    }
    
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port = htons(PORT);
    
    // 转换IP地址
    if (inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr) <= 0) {
        printf("\nInvalid address/ Address not supported \n");
        return -1;
    }
    
    if (connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) {
        printf("\nConnection Failed \n");
        return -1;
    }
    
    send(sock, hello, strlen(hello), 0);
    printf("Hello message sent\n");
    read(sock, buffer, 1024);
    printf("Server: %s\n", buffer);
    
    close(sock);
    return 0;
}

字节序转换[编辑 | 编辑源代码]

网络字节序(大端序)与主机字节序转换函数:

  • htons() - 主机到网络(短整型)
  • htonl() - 主机到网络(长整型)
  • ntohs() - 网络到主机(短整型)
  • ntohl() - 网络到主机(长整型)

实际应用案例[编辑 | 编辑源代码]

简易聊天程序[编辑 | 编辑源代码]

结合多线程实现双向通信:

// 服务器端接收线程
void *receive_handler(void *socket_desc) {
    int sock = *(int*)socket_desc;
    char buffer[1024];
    
    while (1) {
        int read_size = recv(sock, buffer, sizeof(buffer), 0);
        if (read_size > 0) {
            buffer[read_size] = '\0';
            printf("Client: %s", buffer);
        }
    }
    return NULL;
}

// 主函数中创建线程
pthread_t recv_thread;
if (pthread_create(&recv_thread, NULL, receive_handler, (void*)&new_socket) < 0) {
    perror("could not create thread");
    return 1;
}

文件传输协议[编辑 | 编辑源代码]

实现基础文件传输功能:

// 发送文件函数
void send_file(FILE *fp, int sockfd) {
    char data[1024] = {0};
    
    while (fgets(data, sizeof(data), fp) != NULL) {
        if (send(sockfd, data, sizeof(data), 0) == -1) {
            perror("Error in sending file");
            exit(1);
        }
        bzero(data, sizeof(data));
    }
}

常见问题与调试[编辑 | 编辑源代码]

错误现象 可能原因 解决方案
Address already in use 端口被占用 使用setsockopt()设置SO_REUSEADDR
Connection refused 服务器未运行/防火墙阻止 检查服务器状态和防火墙设置
数据截断 缓冲区大小不足 增加缓冲区或分块传输

进阶主题[编辑 | 编辑源代码]

  • 多路复用(select/poll/epoll)
  • 非阻塞I/O
  • SSL/TLS加密通信
  • IPv6编程(AF_INET6

通过掌握这些基础知识,您已经具备了开发简单网络应用的能力。实际项目中还需考虑错误处理、性能优化和安全性等问题。