C语言必背100代码系列文章-第五篇:数据结构实现

C语言必背100代码系列文章目录

第一篇:基础语法与入门必背代码

    核心内容:Hello World、条件判断、循环结构、数组基础、函数定义等。

第二篇:数组与字符串操作

   核心内容:数组排序、字符串处理、多维数组、字符匹配、内存拷贝等。

第三篇:指针与内存管理

核心内容:指针运算、动态内存分配、结构体指针、函数指针、内存泄漏检测等。

第四篇:递归与算法

核心内容:阶乘递归、汉诺塔、分治算法、回溯算法、动态规划基础等。

第五篇:数据结构实现

核心内容:链表、栈、队列、二叉树、哈希表、图的基本操作。

第六篇:文件操作与IO

核心内容:文本文件读写、二进制文件操作、日志管理、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标准)。

扩展知识

  1. 高性能网络编程:
    • 使用epoll(Linux)或kqueue(macOS/BSD)处理高并发。
    • 零拷贝技术(如sendfile)。
  2. 安全建议:
    • 验证输入数据(防止缓冲区溢出)。
    • 使用加密协议(如TLS)。
  3. 协议设计:
    • 自定义二进制协议(如<length><data>格式)。
    • 使用现有协议(如HTTP/2、WebSocket)。

💡注意:本文所介绍的软件及功能均基于公开信息整理,仅供用户参考。在使用任何软件时,请务必遵守相关法律法规及软件使用协议。同时,本文不涉及任何商业推广或引流行为,仅为用户提供一个了解和使用该工具的渠道。

你在生活中时遇到了哪些问题?你是如何解决的?欢迎在评论区分享你的经验和心得!

希望这篇文章能够满足您的需求,如果您有任何修改意见或需要进一步的帮助,请随时告诉我!

感谢各位支持,可以关注我的个人主页,找到你所需要的宝贝。 ​ 
博文入口:https://blog.csdn.net/Start_mswin ​复制到【浏览器】打开即可,宝贝入口:https://pan.quark.cn/s/71742b5e7629 

作者郑重声明,本文内容为本人原创文章,纯净无利益纠葛,如有不妥之处,请及时联系修改或删除。诚邀各位读者秉持理性态度交流,共筑和谐讨论氛围~

评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

山峰哥

你的鼓励将是我创作的最大动力!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值