关注

C语言网络编程:TCP、UDP、HTTP深度解析

C语言网络编程:TCP、UDP、HTTP深度解析

在这里插入图片描述

一、前言:为什么网络编程是C语言开发的重要技能?

学习目标

  • 理解网络编程的本质:编写程序实现网络通信、数据传输和应用交互
  • 明确网络编程的重要性:支撑Web开发、网络游戏、物联网等领域的发展
  • 掌握本章学习重点:TCP、UDP、HTTP的开发方法、避坑指南、实战案例分析
  • 学会使用C语言开发网络应用,实现网络通信和数据传输

重点提示

💡 网络编程是C语言开发的重要技能!随着互联网和物联网的普及,网络编程的需求越来越大,C语言的高性能和可移植性使其在网络编程中具有重要地位。


二、模块1:TCP网络编程基础

2.1 学习目标

  • 理解TCP的本质:传输控制协议,提供可靠的、面向连接的通信
  • 掌握TCP的核心特点:可靠传输、面向连接、流量控制、拥塞控制
  • 掌握TCP网络编程的方法:使用套接字(Socket)进行TCP通信
  • 掌握TCP网络编程的避坑指南:避免连接建立失败、避免数据传输错误、避免连接关闭错误
  • 避开TCP使用的3大常见坑

2.2 TCP的核心特点

可靠传输

  • 使用序列号和确认号确保数据可靠传输
  • 使用超时重传机制处理丢包

面向连接

  • 在通信前建立连接,通信结束后关闭连接

流量控制

  • 使用滑动窗口机制控制发送方的发送速率

拥塞控制

  • 使用拥塞窗口机制避免网络拥塞

2.3 TCP网络编程的方法

代码示例1:TCP服务器

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>

#define PORT 8080
#define BUFFER_SIZE 1024

int main() {
    int server_fd, new_socket;
    struct sockaddr_in address;
    int opt = 1;
    int addrlen = sizeof(address);
    char buffer[BUFFER_SIZE] = {0};

    // 创建套接字文件描述符
    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);

    // 绑定套接字到8080端口
    if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) {
        perror("bind failed");
        exit(EXIT_FAILURE);
    }

    // 监听套接字,最多允许5个连接
    if (listen(server_fd, 5) < 0) {
        perror("listen");
        exit(EXIT_FAILURE);
    }

    // 接受客户端连接
    if ((new_socket = accept(server_fd, (struct sockaddr *)&address, (socklen_t*)&addrlen)) < 0) {
        perror("accept");
        exit(EXIT_FAILURE);
    }

    // 读取客户端发送的数据
    int valread = read(new_socket, buffer, BUFFER_SIZE);
    printf("收到客户端消息:%s\n", buffer);

    // 发送响应给客户端
    const char* response = "HTTP/1.1 200 OK\nContent-Type: text/plain\nContent-Length: 12\n\nHello World!";
    send(new_socket, response, strlen(response), 0);
    printf("响应已发送\n");

    // 关闭套接字
    close(new_socket);
    close(server_fd);

    return 0;
}

代码示例2:TCP客户端

#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 PORT 8080
#define BUFFER_SIZE 1024

int main() {
    int sock = 0;
    struct sockaddr_in serv_addr;
    char buffer[BUFFER_SIZE] = {0};

    // 创建套接字文件描述符
    if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
        perror("socket creation failed");
        exit(EXIT_FAILURE);
    }

    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port = htons(PORT);

    // 将IPv4地址和端口号转换为二进制格式
    if (inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr) <= 0) {
        perror("invalid address/ Address not supported");
        exit(EXIT_FAILURE);
    }

    // 连接到服务器
    if (connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) {
        perror("connection failed");
        exit(EXIT_FAILURE);
    }

    // 发送数据到服务器
    const char* message = "GET / HTTP/1.1\nHost: localhost:8080\n\n";
    send(sock, message, strlen(message), 0);
    printf("消息已发送\n");

    // 读取服务器响应
    int valread = read(sock, buffer, BUFFER_SIZE);
    printf("服务器响应:\n%s\n", buffer);

    // 关闭套接字
    close(sock);

    return 0;
}

三、模块2:UDP网络编程基础

3.1 学习目标

  • 理解UDP的本质:用户数据报协议,提供不可靠的、无连接的通信
  • 掌握UDP的核心特点:不可靠传输、无连接、低延迟
  • 掌握UDP网络编程的方法:使用套接字(Socket)进行UDP通信
  • 掌握UDP网络编程的避坑指南:避免数据包丢失、避免数据包乱序、避免缓冲区溢出
  • 避开UDP使用的3大常见坑

3.2 UDP的核心特点

不可靠传输

  • 不保证数据包的可靠传输,可能会丢失或乱序

无连接

  • 通信前不需要建立连接,通信结束后不需要关闭连接

低延迟

  • 没有连接建立和关闭的开销,延迟低

3.3 UDP网络编程的方法

代码示例3:UDP服务器

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>

#define PORT 8080
#define BUFFER_SIZE 1024

int main() {
    int sock_fd;
    struct sockaddr_in server_addr, client_addr;
    char buffer[BUFFER_SIZE] = {0};
    int n;
    socklen_t len;

    // 创建套接字文件描述符
    sock_fd = socket(AF_INET, SOCK_DGRAM, 0);
    if (sock_fd < 0) {
        perror("socket creation failed");
        exit(EXIT_FAILURE);
    }

    memset(&server_addr, 0, sizeof(server_addr));
    memset(&client_addr, 0, sizeof(client_addr));

    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.s_addr = INADDR_ANY;
    server_addr.sin_port = htons(PORT);

    // 绑定套接字到8080端口
    if (bind(sock_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
        perror("bind failed");
        exit(EXIT_FAILURE);
    }

    // 读取客户端发送的数据
    len = sizeof(client_addr);
    n = recvfrom(sock_fd, (char *)buffer, BUFFER_SIZE, MSG_WAITALL, (struct sockaddr *)&client_addr, &len);
    buffer[n] = '\0';
    printf("收到客户端消息:%s\n", buffer);

    // 发送响应给客户端
    const char* response = "Hello from UDP Server!";
    sendto(sock_fd, response, strlen(response), MSG_CONFIRM, (struct sockaddr *)&client_addr, len);
    printf("响应已发送\n");

    // 关闭套接字
    close(sock_fd);

    return 0;
}

代码示例4:UDP客户端

#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 PORT 8080
#define BUFFER_SIZE 1024

int main() {
    int sock_fd;
    struct sockaddr_in serv_addr;
    char buffer[BUFFER_SIZE] = {0};
    int n;
    socklen_t len;

    // 创建套接字文件描述符
    sock_fd = socket(AF_INET, SOCK_DGRAM, 0);
    if (sock_fd < 0) {
        perror("socket creation failed");
        exit(EXIT_FAILURE);
    }

    memset(&serv_addr, 0, sizeof(serv_addr));

    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port = htons(PORT);
    serv_addr.sin_addr.s_addr = inet_addr("127.0.0.1");

    // 发送数据到服务器
    const char* message = "Hello from UDP Client!";
    sendto(sock_fd, message, strlen(message), MSG_CONFIRM, (struct sockaddr *)&serv_addr, sizeof(serv_addr));
    printf("消息已发送\n");

    // 读取服务器响应
    len = sizeof(serv_addr);
    n = recvfrom(sock_fd, (char *)buffer, BUFFER_SIZE, MSG_WAITALL, (struct sockaddr *)&serv_addr, &len);
    buffer[n] = '\0';
    printf("服务器响应:%s\n", buffer);

    // 关闭套接字
    close(sock_fd);

    return 0;
}

四、模块3:HTTP网络编程基础

4.1 学习目标

  • 理解HTTP的本质:超文本传输协议,用于Web通信
  • 掌握HTTP的核心特点:无状态、面向文本、请求-响应模型
  • 掌握HTTP网络编程的方法:使用套接字(Socket)进行HTTP通信
  • 掌握HTTP网络编程的避坑指南:避免请求格式错误、避免响应解析错误、避免连接超时
  • 避开HTTP使用的3大常见坑

4.2 HTTP的核心特点

无状态

  • 服务器不保存客户端的状态信息
  • 每个请求都是独立的

面向文本

  • 请求和响应都是文本格式,易于阅读和解析

请求-响应模型

  • 客户端发送请求,服务器返回响应

4.3 HTTP网络编程的方法

代码示例5:HTTP服务器

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>

#define PORT 8080
#define BUFFER_SIZE 1024

int main() {
    int server_fd, new_socket;
    struct sockaddr_in address;
    int opt = 1;
    int addrlen = sizeof(address);
    char buffer[BUFFER_SIZE] = {0};

    // 创建套接字文件描述符
    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);

    // 绑定套接字到8080端口
    if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) {
        perror("bind failed");
        exit(EXIT_FAILURE);
    }

    // 监听套接字,最多允许5个连接
    if (listen(server_fd, 5) < 0) {
        perror("listen");
        exit(EXIT_FAILURE);
    }

    // 接受客户端连接
    if ((new_socket = accept(server_fd, (struct sockaddr *)&address, (socklen_t*)&addrlen)) < 0) {
        perror("accept");
        exit(EXIT_FAILURE);
    }

    // 读取客户端发送的HTTP请求
    int valread = read(new_socket, buffer, BUFFER_SIZE);
    printf("收到HTTP请求:\n%s\n", buffer);

    // 发送HTTP响应
    const char* response = "HTTP/1.1 200 OK\nContent-Type: text/plain\nContent-Length: 12\n\nHello World!";
    send(new_socket, response, strlen(response), 0);
    printf("响应已发送\n");

    // 关闭套接字
    close(new_socket);
    close(server_fd);

    return 0;
}

代码示例6:HTTP客户端

#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 PORT 8080
#define BUFFER_SIZE 1024

int main() {
    int sock = 0;
    struct sockaddr_in serv_addr;
    char buffer[BUFFER_SIZE] = {0};

    // 创建套接字文件描述符
    if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
        perror("socket creation failed");
        exit(EXIT_FAILURE);
    }

    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port = htons(PORT);

    // 将IPv4地址和端口号转换为二进制格式
    if (inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr) <= 0) {
        perror("invalid address/ Address not supported");
        exit(EXIT_FAILURE);
    }

    // 连接到服务器
    if (connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) {
        perror("connection failed");
        exit(EXIT_FAILURE);
    }

    // 发送HTTP请求
    const char* message = "GET / HTTP/1.1\nHost: localhost:8080\n\n";
    send(sock, message, strlen(message), 0);
    printf("HTTP请求已发送\n");

    // 读取服务器响应
    int valread = read(sock, buffer, BUFFER_SIZE);
    printf("服务器响应:\n%s\n", buffer);

    // 关闭套接字
    close(sock);

    return 0;
}

五、模块4:实战案例分析——使用C语言实现简单的Web服务器

5.1 学习目标

  • 掌握使用C语言实现简单的Web服务器:通过套接字(Socket)实现一个简单的Web服务器,支持HTTP请求和响应
  • 学会处理HTTP请求,解析请求头,发送HTTP响应
  • 避开实战案例使用的3大常见坑

5.2 使用C语言实现简单的Web服务器

代码示例7:简单的Web服务器

#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 PORT 8080
#define BUFFER_SIZE 1024
#define MAX_CLIENTS 5

void handle_client(int new_socket) {
    char buffer[BUFFER_SIZE] = {0};
    char method[10], path[100], protocol[10];

    // 读取客户端发送的HTTP请求
    int valread = read(new_socket, buffer, BUFFER_SIZE);
    printf("收到HTTP请求:\n%s\n", buffer);

    // 解析HTTP请求行
    sscanf(buffer, "%s %s %s", method, path, protocol);

    // 准备HTTP响应
    char response[BUFFER_SIZE];
    if (strcmp(method, "GET") == 0) {
        if (strcmp(path, "/") == 0) {
            const char* html = "<html><head><title>简单的Web服务器</title></head><body><h1>欢迎访问简单的Web服务器!</h1></body></html>";
            sprintf(response, "HTTP/1.1 200 OK\nContent-Type: text/html\nContent-Length: %lu\n\n%s", strlen(html), html);
        } else {
            const char* not_found = "<html><head><title>404 Not Found</title></head><body><h1>404 Not Found</h1></body></html>";
            sprintf(response, "HTTP/1.1 404 Not Found\nContent-Type: text/html\nContent-Length: %lu\n\n%s", strlen(not_found), not_found);
        }
    } else {
        const char* not_implemented = "<html><head><title>501 Not Implemented</title></head><body><h1>501 Not Implemented</h1></body></html>";
        sprintf(response, "HTTP/1.1 501 Not Implemented\nContent-Type: text/html\nContent-Length: %lu\n\n%s", strlen(not_implemented), not_implemented);
    }

    // 发送HTTP响应
    send(new_socket, response, strlen(response), 0);
    printf("响应已发送:\n%s\n", response);

    // 关闭客户端套接字
    close(new_socket);
}

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);

    // 绑定套接字到8080端口
    if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) {
        perror("bind failed");
        exit(EXIT_FAILURE);
    }

    // 监听套接字,最多允许5个连接
    if (listen(server_fd, MAX_CLIENTS) < 0) {
        perror("listen");
        exit(EXIT_FAILURE);
    }

    printf("Web服务器已启动,监听端口 %d...\n", PORT);

    while (1) {
        // 接受客户端连接
        if ((new_socket = accept(server_fd, (struct sockaddr *)&address, (socklen_t*)&addrlen)) < 0) {
            perror("accept");
            exit(EXIT_FAILURE);
        }

        printf("新的客户端连接已建立,地址:%s:%d\n", inet_ntoa(address.sin_addr), ntohs(address.sin_port));

        // 处理客户端请求(这里使用简单的单线程处理,实际应用中应使用多线程或异步处理)
        handle_client(new_socket);
    }

    // 关闭服务器套接字
    close(server_fd);

    return 0;
}

六、本章总结与课后练习

6.1 总结

TCP网络编程:提供可靠的、面向连接的通信,适合需要可靠传输的应用
UDP网络编程:提供不可靠的、无连接的通信,适合低延迟的应用
HTTP网络编程:用于Web通信,支持HTTP请求和响应
实战案例分析:使用C语言实现简单的Web服务器

6.2 课后练习

  1. 编写程序:实现一个简单的TCP服务器,支持多个客户端连接
  2. 编写程序:实现一个简单的TCP客户端,发送消息到服务器
  3. 编写程序:实现一个简单的UDP服务器,接收客户端数据并回复
  4. 编写程序:实现一个简单的UDP客户端,发送消息到服务器
  5. 编写程序:实现一个简单的HTTP服务器,支持静态HTML文件
  6. 编写程序:实现一个简单的HTTP客户端,发送HTTP请求并解析响应
  7. 编写程序:实现一个简单的Web服务器,支持GET和POST请求
  8. 编写程序:实现一个简单的Web服务器,支持动态页面生成
  9. 编写程序:实现一个简单的Web服务器,支持文件上传和下载
  10. 编写程序:实现一个简单的Web服务器,支持HTTPS

转载自CSDN-专业IT技术社区

原文链接:https://blog.csdn.net/COLLINSXU/article/details/157733253

评论

赞0

评论列表

微信小程序
QQ小程序

关于作者

点赞数:0
关注数:0
粉丝:0
文章:0
关注标签:0
加入于:--