C语言必背100代码系列文章目录
核心内容:Hello World、条件判断、循环结构、数组基础、函数定义等。
核心内容:数组排序、字符串处理、多维数组、字符匹配、内存拷贝等。
核心内容:指针运算、动态内存分配、结构体指针、函数指针、内存泄漏检测等。
核心内容:阶乘递归、汉诺塔、分治算法、回溯算法、动态规划基础等。
核心内容:链表、栈、队列、二叉树、哈希表、图的基本操作。
核心内容:文本文件读写、二进制文件操作、日志管理、CSV/JSON解析等。
核心内容:快速幂、素数测试、进制转换、矩阵运算、蒙特卡洛模拟等。
核心内容:进程控制、线程同步、信号处理、套接字编程、系统调用封装等。
核心内容:TCP/UDP通信、HTTP请求、WebSocket、SSL/TLS加密、网络抓包等。
第五篇:数据结构实现
想系统掌握C语言网络开发?这篇硬核教程带你从零构建TCP/UDP通信框架,破解HTTP协议实现原理!通过10个实战案例+源码级解析,深度揭秘Socket编程、多线程服务器、广播通信等核心技术。从三次握手细节到字节序转换陷阱,从简易聊天室到HTTP客户端开发,本文涵盖网络编程全链路。无论你是刚接触Socket的萌新,还是想优化网络模块的工程师,这篇覆盖TCP粘包处理、UDP广播实战、端口扫描原理等干货的进阶指南,都能让你的网络开发能力实现质的突破!
1. TCP客户端(连接服务器并发送数据)
c
#include <stdio.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#include <unistd.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}; | |
// 创建Socket | |
if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) { | |
perror("Socket creation error"); | |
return -1; | |
} | |
serv_addr.sin_family = AF_INET; | |
serv_addr.sin_port = htons(PORT); | |
// 转换IP地址(示例:127.0.0.1) | |
if (inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr) <= 0) { | |
perror("Invalid address/Address not supported"); | |
return -1; | |
} | |
// 连接服务器 | |
if (connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) { | |
perror("Connection Failed"); | |
return -1; | |
} | |
// 发送数据 | |
const char *message = "Hello from client!"; | |
send(sock, message, strlen(message), 0); | |
printf("Message sent: %s\n", message); | |
// 接收响应 | |
read(sock, buffer, BUFFER_SIZE); | |
printf("Server response: %s\n", buffer); | |
close(sock); | |
return 0; | |
} |
- 详细注释:
- socket():创建Socket(AF_INET表示IPv4,SOCK_STREAM表示TCP)。
- inet_pton():将点分十进制IP转为二进制格式。
- connect():阻塞式连接,超时需设置SO_RCVTIMEO选项。
- 应用场景:客户端工具(如HTTP请求、即时通讯)。
- 常见问题:
- 服务器未启动导致connect()失败。
- 缓冲区溢出(需检查read()返回值)。
2. TCP服务器(多线程处理客户端)
c
#include <stdio.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#include <unistd.h> | |
#include <arpa/inet.h> | |
#include <pthread.h> | |
#define PORT 8080 | |
#define BUFFER_SIZE 1024 | |
void *handleClient(void *arg) { | |
int clientSock = *(int *)arg; | |
char buffer[BUFFER_SIZE] = {0}; | |
read(clientSock, buffer, BUFFER_SIZE); | |
printf("Received: %s\n", buffer); | |
const char *response = "Hello from server!"; | |
send(clientSock, response, strlen(response), 0); | |
close(clientSock); | |
return NULL; | |
} | |
int main() { | |
int serverSock, clientSock; | |
struct sockaddr_in address; | |
int opt = 1; | |
int addrlen = sizeof(address); | |
// 创建Socket | |
if ((serverSock = socket(AF_INET, SOCK_STREAM, 0)) == 0) { | |
perror("Socket failed"); | |
exit(EXIT_FAILURE); | |
} | |
// 设置Socket选项(允许地址重用) | |
if (setsockopt(serverSock, 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); | |
// 绑定Socket | |
if (bind(serverSock, (struct sockaddr *)&address, sizeof(address)) < 0) { | |
perror("Bind failed"); | |
exit(EXIT_FAILURE); | |
} | |
// 监听 | |
if (listen(serverSock, 3) < 0) { | |
perror("Listen"); | |
exit(EXIT_FAILURE); | |
} | |
printf("Server listening on port %d...\n", PORT); | |
while (1) { | |
// 接受客户端连接 | |
if ((clientSock = accept(serverSock, (struct sockaddr *)&address, (socklen_t *)&addrlen)) < 0) { | |
perror("Accept"); | |
continue; | |
} | |
printf("Client connected: %s:%d\n", | |
inet_ntoa(address.sin_addr), ntohs(address.sin_port)); | |
// 创建线程处理客户端 | |
pthread_t tid; | |
if (pthread_create(&tid, NULL, handleClient, &clientSock) < 0) { | |
perror("Thread creation failed"); | |
close(clientSock); | |
continue; | |
} | |
pthread_detach(tid); // 分离线程,避免资源泄漏 | |
} | |
return 0; | |
} |
- 详细注释:
- setsockopt():设置SO_REUSEADDR避免端口占用。
- pthread_create():为每个客户端创建独立线程。
- pthread_detach():线程退出时自动释放资源。
- 应用场景:Web服务器、聊天室。
- 扩展知识:
- 使用线程池(如pthread_pool)优化性能。
- 添加SSL/TLS加密(如OpenSSL库)。
3. UDP客户端(发送广播消息)
c
#include <stdio.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#include <unistd.h> | |
#include <arpa/inet.h> | |
#define PORT 8888 | |
#define BUFFER_SIZE 1024 | |
int main() { | |
int sock = 0; | |
struct sockaddr_in serv_addr; | |
char buffer[BUFFER_SIZE] = {0}; | |
// 创建UDP Socket | |
if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { | |
perror("Socket creation error"); | |
return -1; | |
} | |
// 启用广播选项 | |
int broadcastEnable = 1; | |
if (setsockopt(sock, SOL_SOCKET, SO_BROADCAST, &broadcastEnable, sizeof(broadcastEnable)) < 0) { | |
perror("setsockopt (SO_BROADCAST)"); | |
return -1; | |
} | |
serv_addr.sin_family = AF_INET; | |
serv_addr.sin_port = htons(PORT); | |
serv_addr.sin_addr.s_addr = inet_addr("255.255.255.255"); // 广播地址 | |
// 发送广播消息 | |
const char *message = "This is a broadcast message!"; | |
sendto(sock, message, strlen(message), 0, | |
(const struct sockaddr *)&serv_addr, sizeof(serv_addr)); | |
printf("Broadcast message sent: %s\n", message); | |
close(sock); | |
return 0; | |
} |
- 详细注释:
- SOCK_DGRAM:UDP协议。
- SO_BROADCAST:允许发送广播包。
- 255.255.255.255:本地广播地址(需在子网内有效)。
- 应用场景:局域网设备发现(如打印机、游戏服务器)。
- 常见问题:
- 路由器可能禁止广播包转发。
- 防火墙可能拦截UDP端口。
4. UDP服务器(接收广播消息)
c
#include <stdio.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#include <unistd.h> | |
#include <arpa/inet.h> | |
#define PORT 8888 | |
#define BUFFER_SIZE 1024 | |
int main() { | |
int sock = 0; | |
struct sockaddr_in serv_addr, cli_addr; | |
char buffer[BUFFER_SIZE] = {0}; | |
socklen_t addrlen = sizeof(cli_addr); | |
// 创建UDP Socket | |
if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { | |
perror("Socket creation error"); | |
return -1; | |
} | |
serv_addr.sin_family = AF_INET; | |
serv_addr.sin_addr.s_addr = INADDR_ANY; | |
serv_addr.sin_port = htons(PORT); | |
// 绑定Socket | |
if (bind(sock, (const struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) { | |
perror("Bind failed"); | |
return -1; | |
} | |
printf("UDP server listening on port %d...\n", PORT); | |
while (1) { | |
// 接收消息 | |
int n = recvfrom(sock, buffer, BUFFER_SIZE, 0, | |
(struct sockaddr *)&cli_addr, &addrlen); | |
buffer[n] = '\0'; | |
printf("Received from %s:%d: %s\n", | |
inet_ntoa(cli_addr.sin_addr), ntohs(cli_addr.sin_port), buffer); | |
} | |
close(sock); | |
return 0; | |
} |
- 详细注释:
- recvfrom():接收UDP数据包并返回发送方地址。
- INADDR_ANY:监听所有网络接口。
- 应用场景:网络监控、实时数据采集。
- 扩展知识:
- 多播(Multicast)替代广播(减少网络负担)。
- 使用epoll(Linux)处理高并发UDP连接。
5. HTTP GET请求(Socket实现)
c
#include <stdio.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#include <unistd.h> | |
#include <arpa/inet.h> | |
#define PORT 80 | |
#define BUFFER_SIZE 4096 | |
int main() { | |
int sock = 0; | |
struct sockaddr_in serv_addr; | |
char buffer[BUFFER_SIZE] = {0}; | |
// 创建Socket | |
if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) { | |
perror("Socket creation error"); | |
return -1; | |
} | |
serv_addr.sin_family = AF_INET; | |
serv_addr.sin_port = htons(PORT); | |
// 解析域名(示例:example.com) | |
struct hostent *host = gethostbyname("example.com"); | |
if (host == NULL) { | |
perror("Host not found"); | |
return -1; | |
} | |
memcpy(&serv_addr.sin_addr, host->h_addr, host->h_length); | |
// 连接服务器 | |
if (connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) { | |
perror("Connection Failed"); | |
return -1; | |
} | |
// 发送HTTP GET请求 | |
const char *request = "GET / HTTP/1.1\r\nHost: example.com\r\n\r\n"; | |
send(sock, request, strlen(request), 0); | |
// 接收响应 | |
int bytesRead; | |
while ((bytesRead = read(sock, buffer, BUFFER_SIZE - 1)) > 0) { | |
buffer[bytesRead] = '\0'; | |
printf("%s", buffer); // 输出HTML内容 | |
} | |
close(sock); | |
return 0; | |
} |
- 详细注释:
- gethostbyname():解析域名(需包含<netdb.h>)。
- HTTP请求格式:GET / HTTP/1.1\r\nHost: ...\r\n\r\n。
- 应用场景:爬虫、API调用。
- 常见问题:
- 服务器可能关闭连接(需处理Connection: close)。
- 大响应需分块处理(避免缓冲区溢出)。
6. 域名解析(gethostbyname)
c
#include <stdio.h> | |
#include <stdlib.h> | |
#include <netdb.h> | |
#include <arpa/inet.h> | |
int main() { | |
const char *hostname = "example.com"; | |
struct hostent *host = gethostbyname(hostname); | |
if (host == NULL) { | |
perror("Host not found"); | |
return 1; | |
} | |
printf("Official name: %s\n", host->h_name); | |
printf("IP addresses:\n"); | |
for (int i = 0; host->h_addr_list[i] != NULL; i++) { | |
char *ip = inet_ntoa(*(struct in_addr *)host->h_addr_list[i]); | |
printf(" %s\n", ip); // 输出:93.184.216.34等 | |
} | |
return 0; | |
} |
- 详细注释:
- hostent结构体:包含主机名、别名列表、IP地址列表。
- h_addr_list:以NULL结尾的IP地址数组。
- 应用场景:网络工具开发、日志分析。
- 扩展知识:
- 使用getaddrinfo()替代(支持IPv6)。
- 异步DNS解析(如c-ares库)。
7. 端口扫描(基础实现)
c
#include <stdio.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#include <unistd.h> | |
#include <arpa/inet.h> | |
#include <netdb.h> | |
#define START_PORT 1 | |
#define END_PORT 1024 | |
#define TIMEOUT 1 // 秒 | |
int isPortOpen(const char *ip, int port) { | |
int sock = socket(AF_INET, SOCK_STREAM, 0); | |
if (sock < 0) return 0; | |
struct sockaddr_in serv_addr; | |
serv_addr.sin_family = AF_INET; | |
serv_addr.sin_port = htons(port); | |
inet_pton(AF_INET, ip, &serv_addr.sin_addr); | |
// 设置超时 | |
struct timeval tv; | |
tv.tv_sec = TIMEOUT; | |
tv.tv_usec = 0; | |
setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)); | |
// 尝试连接 | |
if (connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) == 0) { | |
close(sock); | |
return 1; | |
} | |
close(sock); | |
return 0; | |
} | |
int main() { | |
const char *ip = "127.0.0.1"; | |
printf("Scanning ports %d-%d on %s...\n", START_PORT, END_PORT, ip); | |
for (int port = START_PORT; port <= END_PORT; port++) { | |
if (isPortOpen(ip, port)) { | |
printf("Port %d is open\n", port); | |
} | |
} | |
return 0; | |
} |
- 详细注释:
- SO_RCVTIMEO:设置连接超时。
- 扫描范围:1-1024(常见服务端口)。
- 应用场景:网络安全测试。
- 注意事项:
- 未经授权扫描可能违法。
- 需处理防火墙拦截(如RST包)。
8. 文件传输(TCP Socket)
c
#include <stdio.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#include <unistd.h> | |
#include <arpa/inet.h> | |
#define PORT 8080 | |
#define BUFFER_SIZE 1024 | |
void sendFile(int sock, const char *filename) { | |
FILE *file = fopen(filename, "rb"); | |
if (file == NULL) { | |
perror("Failed to open file"); | |
return; | |
} | |
char buffer[BUFFER_SIZE]; | |
size_t bytesRead; | |
while ((bytesRead = fread(buffer, 1, BUFFER_SIZE, file)) > 0) { | |
send(sock, buffer, bytesRead, 0); | |
} | |
fclose(file); | |
printf("File sent: %s\n", filename); | |
} | |
int main() { | |
int sock = 0; | |
struct sockaddr_in serv_addr; | |
// 创建Socket | |
if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) { | |
perror("Socket creation error"); | |
return -1; | |
} | |
serv_addr.sin_family = AF_INET; | |
serv_addr.sin_port = htons(PORT); | |
inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr); | |
// 连接服务器 | |
if (connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) { | |
perror("Connection Failed"); | |
return -1; | |
} | |
// 发送文件名 | |
const char *filename = "test.txt"; | |
send(sock, filename, strlen(filename), 0); | |
// 发送文件内容 | |
sendFile(sock, filename); | |
close(sock); | |
return 0; | |
} |
- 详细注释:
- 先发送文件名,再传输文件内容。
- 需服务器端配合接收并保存文件。
- 扩展知识:
- 添加校验和(如MD5)验证文件完整性。
- 分块传输(支持大文件)。
9. 简单聊天室(TCP多客户端)
c
#include <stdio.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#include <unistd.h> | |
#include <arpa/inet.h> | |
#include <pthread.h> | |
#define PORT 8080 | |
#define MAX_CLIENTS 10 | |
#define BUFFER_SIZE 1024 | |
int clientSocks[MAX_CLIENTS] = {0}; | |
void broadcastMessage(const char *message, int senderSock) { | |
for (int i = 0; i < MAX_CLIENTS; i++) { | |
if (clientSocks[i] != 0 && clientSocks[i] != senderSock) { | |
send(clientSocks[i], message, strlen(message), 0); | |
} | |
} | |
} | |
void *handleClient(void *arg) { | |
int clientSock = *(int *)arg; | |
char buffer[BUFFER_SIZE] = {0}; | |
int bytesRead; | |
while ((bytesRead = read(clientSock, buffer, BUFFER_SIZE)) > 0) { | |
buffer[bytesRead] = '\0'; | |
printf("Client %d: %s", clientSock, buffer); | |
char response[BUFFER_SIZE + 20]; | |
snprintf(response, sizeof(response), "Client %d: %s", clientSock, buffer); | |
broadcastMessage(response, clientSock); | |
} | |
// 客户端断开 | |
printf("Client %d disconnected\n", clientSock); | |
close(clientSock); | |
for (int i = 0; i < MAX_CLIENTS; i++) { | |
if (clientSocks[i] == clientSock) { | |
clientSocks[i] = 0; | |
break; | |
} | |
} | |
return NULL; | |
} | |
int main() { | |
int serverSock, clientSock; | |
struct sockaddr_in address; | |
int opt = 1; | |
int addrlen = sizeof(address); | |
// 创建Socket | |
if ((serverSock = socket(AF_INET, SOCK_STREAM, 0)) == 0) { | |
perror("Socket failed"); | |
exit(EXIT_FAILURE); | |
} | |
if (setsockopt(serverSock, 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(serverSock, (struct sockaddr *)&address, sizeof(address)) < 0) { | |
perror("Bind failed"); | |
exit(EXIT_FAILURE); | |
} | |
if (listen(serverSock, 3) < 0) { | |
perror("Listen"); | |
exit(EXIT_FAILURE); | |
} | |
printf("Chat server started on port %d...\n", PORT); | |
while (1) { | |
if ((clientSock = accept(serverSock, (struct sockaddr *)&address, (socklen_t *)&addrlen)) < 0) { | |
perror("Accept"); | |
continue; | |
} | |
// 查找空闲槽位 | |
int i; | |
for (i = 0; i < MAX_CLIENTS; i++) { | |
if (clientSocks[i] == 0) { | |
clientSocks[i] = clientSock; | |
break; | |
} | |
} | |
if (i == MAX_CLIENTS) { | |
printf("Max clients reached. Rejecting client %d\n", clientSock); | |
close(clientSock); | |
continue; | |
} | |
printf("New client connected: %d\n", clientSock); | |
pthread_t tid; | |
if (pthread_create(&tid, NULL, handleClient, &clientSock) < 0) { | |
perror("Thread creation failed"); | |
close(clientSock); | |
continue; | |
} | |
pthread_detach(tid); | |
} | |
return 0; | |
} |
- 详细注释:
- clientSocks数组:管理所有客户端连接。
- broadcastMessage():向其他客户端广播消息。
- 应用场景:实时通讯工具。
- 扩展知识:
- 添加用户认证(如用户名/密码)。
- 使用select/poll替代多线程(简化资源管理)。
10. 网络字节序转换
c
#include <stdio.h> | |
#include <arpa/inet.h> | |
int main() { | |
uint32_t hostValue = 0x12345678; | |
uint32_t netValue = htonl(hostValue); // 主机序转网络序 | |
printf("Host: 0x%08X, Network: 0x%08X\n", hostValue, netValue); | |
uint32_t backToHost = ntohl(netValue); // 网络序转主机序 | |
printf("Back to host: 0x%08X\n", backToHost); | |
// 16位整数转换 | |
uint16_t hostShort = 0x1234; | |
uint16_t netShort = htons(hostShort); | |
printf("Host short: 0x%04X, Network short: 0x%04X\n", hostShort, netShort); | |
return 0; | |
} |
- 详细注释:
- htonl()/ntohl():32位整数转换。
- htons()/ntohs():16位整数转换。
- 应用场景:跨平台网络协议实现。
- 常见问题:
- 忽略字节序导致数据解析错误。
- 浮点数需自定义序列化(如IEEE 754标准)。
扩展知识
- 高性能网络编程:
- 使用epoll(Linux)或kqueue(macOS/BSD)处理高并发。
- 零拷贝技术(如sendfile)。
- 安全建议:
- 验证输入数据(防止缓冲区溢出)。
- 使用加密协议(如TLS)。
- 协议设计:
- 自定义二进制协议(如<length><data>格式)。
- 使用现有协议(如HTTP/2、WebSocket)。
💡注意:本文所介绍的软件及功能均基于公开信息整理,仅供用户参考。在使用任何软件时,请务必遵守相关法律法规及软件使用协议。同时,本文不涉及任何商业推广或引流行为,仅为用户提供一个了解和使用该工具的渠道。
你在生活中时遇到了哪些问题?你是如何解决的?欢迎在评论区分享你的经验和心得!
希望这篇文章能够满足您的需求,如果您有任何修改意见或需要进一步的帮助,请随时告诉我!
感谢各位支持,可以关注我的个人主页,找到你所需要的宝贝。
博文入口:https://blog.csdn.net/Start_mswin 复制到【浏览器】打开即可,宝贝入口:https://pan.quark.cn/s/71742b5e7629
作者郑重声明,本文内容为本人原创文章,纯净无利益纠葛,如有不妥之处,请及时联系修改或删除。诚邀各位读者秉持理性态度交流,共筑和谐讨论氛围~