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 课后练习
- 编写程序:实现一个简单的TCP服务器,支持多个客户端连接
- 编写程序:实现一个简单的TCP客户端,发送消息到服务器
- 编写程序:实现一个简单的UDP服务器,接收客户端数据并回复
- 编写程序:实现一个简单的UDP客户端,发送消息到服务器
- 编写程序:实现一个简单的HTTP服务器,支持静态HTML文件
- 编写程序:实现一个简单的HTTP客户端,发送HTTP请求并解析响应
- 编写程序:实现一个简单的Web服务器,支持GET和POST请求
- 编写程序:实现一个简单的Web服务器,支持动态页面生成
- 编写程序:实现一个简单的Web服务器,支持文件上传和下载
- 编写程序:实现一个简单的Web服务器,支持HTTPS
转载自CSDN-专业IT技术社区
原文链接:https://blog.csdn.net/COLLINSXU/article/details/157733253



