C语言网络编程:TCP/IP协议栈、套接字、服务器/客户端通信深度解析

C语言网络编程:TCP/IP协议栈、套接字、服务器/客户端通信深度解析

C语言网络编程:TCP/IP协议栈、套接字、服务器/客户端通信深度解析

在这里插入图片描述

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

学习目标

  • 理解网络编程的本质:编写程序实现不同设备之间的网络通信
  • 明确网络编程的重要性:支撑互联网、物联网、云计算等应用的基础
  • 掌握本章学习重点:TCP/IP协议栈、套接字、服务器/客户端通信的开发方法、避坑指南、实战案例分析
  • 学会使用C语言开发网络应用,实现数据传输和网络交互

重点提示

💡 网络编程是C语言开发的重要技能!互联网和物联网的普及,使得网络编程成为程序员的必备技能,C语言的高性能和可移植性使其在网络编程中具有重要地位。


二、模块1:TCP/IP协议栈基础

2.1 学习目标

  • 理解TCP/IP协议栈的本质:用于网络通信的协议集合,分为应用层、传输层、网络层、数据链路层
  • 掌握TCP/IP协议栈的结构:各层协议的功能和交互
  • 掌握TCP/IP协议栈的常用协议:TCP、UDP、IP、HTTP、FTP等
  • 掌握TCP/IP协议栈的避坑指南:避免协议栈配置错误、避免数据传输错误、避免连接失败
  • 避开TCP/IP协议栈使用的3大常见坑

2.2 TCP/IP协议栈的结构

应用层:提供用户服务,如HTTP、FTP、SMTP等
传输层:提供端到端的通信,如TCP、UDP
网络层:提供路由和转发功能,如IP、ICMP
数据链路层:提供物理连接和帧传输,如以太网、WiFi

2.3 TCP/IP协议栈的常用协议

TCP协议

  • 可靠的、面向连接的传输协议
  • 重传机制、滑动窗口、拥塞控制
  • 适合要求高可靠性的应用,如HTTP、FTP

UDP协议

  • 不可靠的、无连接的传输协议
  • 不需要建立连接,传输速度快
  • 适合要求高传输速度的应用,如视频会议、游戏

三、模块2:套接字编程基础

3.1 学习目标

  • 理解套接字的本质:网络通信的端点,用于实现服务器和客户端之间的通信
  • 掌握套接字编程的基本步骤:创建套接字、绑定地址、监听连接、接受连接、发送和接收数据、关闭套接字
  • 掌握套接字编程的避坑指南:避免地址绑定错误、避免连接失败、避免数据传输错误
  • 避开套接字编程使用的3大常见坑

3.2 套接字编程的基本步骤

创建套接字

intsocket(int domain,int type,int protocol);

绑定地址

intbind(int sockfd,conststructsockaddr*addr,socklen_t addrlen);

监听连接

intlisten(int sockfd,int backlog);

接受连接

intaccept(int sockfd,structsockaddr*addr,socklen_t*addrlen);

发送和接收数据

ssize_tsend(int sockfd,constvoid*buf,size_t len,int flags);ssize_trecv(int sockfd,void*buf,size_t len,int flags);

关闭套接字

intclose(int sockfd);

四、模块3:服务器/客户端通信开发

4.1 学习目标

  • 理解服务器/客户端通信的本质:服务器监听连接,客户端发起连接,实现数据传输
  • 掌握服务器/客户端通信的开发方法:使用TCP或UDP协议
  • 掌握服务器/客户端通信的避坑指南:避免并发连接处理错误、避免数据传输错误、避免连接超时
  • 避开服务器/客户端通信使用的3大常见坑

4.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>#definePORT8080#defineBUFFER_SIZE1024intmain(){int server_fd, new_socket;structsockaddr_in address;int opt =1;int addrlen =sizeof(address);char buffer[BUFFER_SIZE]={0};char*hello ="Hello from server";// 创建套接字文件描述符if((server_fd =socket(AF_INET, SOCK_STREAM,0))==0){perror("socket() error");exit(EXIT_FAILURE);}// 设置套接字选项if(setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT,&opt,sizeof(opt))){perror("setsockopt() error");exit(EXIT_FAILURE);} address.sin_family = AF_INET; address.sin_addr.s_addr = INADDR_ANY; address.sin_port =htons(PORT);// 绑定套接字到指定地址和端口if(bind(server_fd,(structsockaddr*)&address,sizeof(address))<0){perror("bind() error");exit(EXIT_FAILURE);}// 监听连接if(listen(server_fd,3)<0){perror("listen() error");exit(EXIT_FAILURE);}// 接受连接if((new_socket =accept(server_fd,(structsockaddr*)&address,(socklen_t*)&addrlen))<0){perror("accept() error");exit(EXIT_FAILURE);}// 接收数据read(new_socket, buffer, BUFFER_SIZE);printf("收到客户端消息:%s\n", buffer);// 发送数据send(new_socket, hello,strlen(hello),0);printf("向客户端发送消息:%s\n", hello);// 关闭套接字close(new_socket);close(server_fd);return0;}

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>#definePORT8080#defineBUFFER_SIZE1024intmain(){int sock =0;structsockaddr_in serv_addr;char buffer[BUFFER_SIZE]={0};char*hello ="Hello from client";// 创建套接字文件描述符if((sock =socket(AF_INET, SOCK_STREAM,0))<0){perror("socket() error");exit(EXIT_FAILURE);} serv_addr.sin_family = AF_INET; serv_addr.sin_port =htons(PORT);// 将IPv4和IPv6地址从文本转换为二进制形式if(inet_pton(AF_INET,"127.0.0.1",&serv_addr.sin_addr)<=0){perror("无效的地址/地址不可支持");exit(EXIT_FAILURE);}// 连接服务器if(connect(sock,(structsockaddr*)&serv_addr,sizeof(serv_addr))<0){perror("连接失败");exit(EXIT_FAILURE);}// 发送数据send(sock, hello,strlen(hello),0);printf("向服务器发送消息:%s\n", hello);// 接收数据read(sock, buffer, BUFFER_SIZE);printf("收到服务器消息:%s\n", buffer);// 关闭套接字close(sock);return0;}

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>#definePORT8080#defineBUFFER_SIZE1024intmain(){int sockfd;structsockaddr_in servaddr, cliaddr;int opt =1;int len;char buffer[BUFFER_SIZE]={0};char*hello ="Hello from UDP server";// 创建套接字文件描述符if((sockfd =socket(AF_INET, SOCK_DGRAM,0))<0){perror("socket() error");exit(EXIT_FAILURE);}// 设置套接字选项if(setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT,&opt,sizeof(opt))){perror("setsockopt() error");exit(EXIT_FAILURE);} servaddr.sin_family = AF_INET; servaddr.sin_addr.s_addr = INADDR_ANY; servaddr.sin_port =htons(PORT);// 绑定套接字到指定地址和端口if(bind(sockfd,(structsockaddr*)&servaddr,sizeof(servaddr))<0){perror("bind() error");exit(EXIT_FAILURE);} len =sizeof(cliaddr);// 接收数据recvfrom(sockfd, buffer, BUFFER_SIZE,0,(structsockaddr*)&cliaddr,&len);printf("收到客户端消息:%s\n", buffer);// 发送数据sendto(sockfd, hello,strlen(hello),0,(structsockaddr*)&cliaddr, len);printf("向客户端发送消息:%s\n", hello);// 关闭套接字close(sockfd);return0;}

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>#definePORT8080#defineBUFFER_SIZE1024intmain(){int sockfd;structsockaddr_in servaddr;int len;char buffer[BUFFER_SIZE]={0};char*hello ="Hello from UDP client";// 创建套接字文件描述符if((sockfd =socket(AF_INET, SOCK_DGRAM,0))<0){perror("socket() error");exit(EXIT_FAILURE);} servaddr.sin_family = AF_INET; servaddr.sin_port =htons(PORT); servaddr.sin_addr.s_addr =inet_addr("127.0.0.1"); len =sizeof(servaddr);// 发送数据sendto(sockfd, hello,strlen(hello),0,(structsockaddr*)&servaddr, len);printf("向服务器发送消息:%s\n", hello);// 接收数据recvfrom(sockfd, buffer, BUFFER_SIZE,0,(structsockaddr*)&servaddr,&len);printf("收到服务器消息:%s\n", buffer);// 关闭套接字close(sockfd);return0;}

五、模块4:实战案例分析——HTTP服务器开发

5.1 学习目标

  • 掌握HTTP服务器开发:使用C语言实现一个简单的HTTP服务器,处理HTTP请求和响应
  • 学会使用套接字编程和HTTP协议实现网络应用
  • 避开实战案例使用的3大常见坑

5.2 HTTP服务器开发

代码示例5: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>#definePORT8080#defineBUFFER_SIZE4096#defineMAX_CLIENTS10voidhandle_client(int new_socket){char buffer[BUFFER_SIZE]={0};char*response ="HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nContent-Length: 12\r\n\r\nHello, World!";// 接收HTTP请求read(new_socket, buffer, BUFFER_SIZE);printf("收到HTTP请求:\n%s\n", buffer);// 发送HTTP响应send(new_socket, response,strlen(response),0);printf("向客户端发送响应:\n%s\n", response);// 关闭套接字close(new_socket);}intmain(){int server_fd, new_socket;structsockaddr_in address;int opt =1;int addrlen =sizeof(address);int clients[MAX_CLIENTS];int max_clients = MAX_CLIENTS;int i, j, valread, sd;int activity, max_sd; fd_set readfds;char buffer[BUFFER_SIZE]={0};// 初始化客户端数组for(i =0; i < max_clients; i++){ clients[i]=0;}// 创建套接字文件描述符if((server_fd =socket(AF_INET, SOCK_STREAM,0))==0){perror("socket() error");exit(EXIT_FAILURE);}// 设置套接字选项if(setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT,&opt,sizeof(opt))){perror("setsockopt() error");exit(EXIT_FAILURE);} address.sin_family = AF_INET; address.sin_addr.s_addr = INADDR_ANY; address.sin_port =htons(PORT);// 绑定套接字到指定地址和端口if(bind(server_fd,(structsockaddr*)&address,sizeof(address))<0){perror("bind() error");exit(EXIT_FAILURE);}// 监听连接if(listen(server_fd,3)<0){perror("listen() error");exit(EXIT_FAILURE);}printf("HTTP服务器启动成功,监听端口:%d\n", PORT);while(1){// 清空文件描述符集合FD_ZERO(&readfds);// 添加服务器套接字到集合FD_SET(server_fd,&readfds); max_sd = server_fd;// 添加客户端套接字到集合for(i =0; i < max_clients; i++){ sd = clients[i];if(sd >0){FD_SET(sd,&readfds);}if(sd > max_sd){ max_sd = sd;}}// 等待活动 activity =select(max_sd +1,&readfds,NULL,NULL,NULL);if(activity <0){perror("select() error");}// 如果服务器套接字有活动,说明有新连接if(FD_ISSET(server_fd,&readfds)){if((new_socket =accept(server_fd,(structsockaddr*)&address,(socklen_t*)&addrlen))<0){perror("accept() error");exit(EXIT_FAILURE);}// 打印客户端信息printf("新连接:套接字文件描述符=%d,IP=%s,端口=%d\n", new_socket,inet_ntoa(address.sin_addr),ntohs(address.sin_port));// 将新套接字文件描述符添加到数组for(i =0; i < max_clients; i++){if(clients[i]==0){ clients[i]= new_socket;break;}}if(i == max_clients){printf("客户端数量已达上限!\n");}}// 处理客户端活动for(i =0; i < max_clients; i++){ sd = clients[i];if(FD_ISSET(sd,&readfds)){ valread =read(sd, buffer, BUFFER_SIZE);if(valread ==0){// 客户端关闭连接getpeername(sd,(structsockaddr*)&address,(socklen_t*)&addrlen);printf("客户端断开连接:IP=%s,端口=%d\n",inet_ntoa(address.sin_addr),ntohs(address.sin_port));close(sd); clients[i]=0;}else{// 处理HTTP请求 buffer[valread]='\0';handle_client(sd);// 关闭套接字close(sd); clients[i]=0;}}}}return0;}

六、本章总结与课后练习

6.1 总结

TCP/IP协议栈:用于网络通信的协议集合,分为应用层、传输层、网络层、数据链路层
套接字编程:网络通信的端点,用于实现服务器和客户端之间的通信
服务器/客户端通信:服务器监听连接,客户端发起连接,实现数据传输
实战案例分析:HTTP服务器开发,使用套接字编程和HTTP协议实现网络应用

6.2 课后练习

  1. 编写程序:使用TCP协议实现服务器和客户端通信
  2. 编写程序:使用UDP协议实现服务器和客户端通信
  3. 编写程序:使用HTTP协议实现简单的Web服务器
  4. 编写程序:使用FTP协议实现文件传输
  5. 编写程序:使用SMTP协议实现邮件发送
  6. 编写程序:使用POP3协议实现邮件接收
  7. 编写程序:使用DNS协议实现域名解析
  8. 编写程序:使用ICMP协议实现Ping功能
  9. 编写程序:使用ARP协议实现地址解析
  10. 编写程序:使用SSL/TLS协议实现安全通信

Read more

从零开始学java--二叉树和哈希表

从零开始学java--二叉树和哈希表

数据结构基础 目录 数据结构基础 树 树形结构: 树的概念: 二叉树 概念: 两种特殊的二叉树: 二叉树的性质: 创建一个简单的二叉树: 二叉树的遍历 前序遍历: 中序遍历: 后序遍历: 层序遍历: 二叉查找树和平衡二叉树 二叉查找树: 平衡二叉树: 红黑树 哈希表 树 树形结构: 树是一种非线性的数据结构,它是由n(n>=0)个有限结点组成一个具有层次关系的集合。把它叫做树是因为它看起来像一棵倒挂的树,也就是说它是根朝上,而叶朝下的。它具有以下的特点: 1. 有一个特殊的结点,称为根结点,根结点没有前驱结点。 2. 除根结点外,其余结点被分成M(M > 0)个互不相交的集合T1、T2、......、Tm,其中每一个集合Ti (1 <= i

By Ne0inhk
Flutter for OpenHarmony: Flutter 三方库 path_to_regexp 揭秘路由匹配与参数提取的核心算法(路由管道工程师)

Flutter for OpenHarmony: Flutter 三方库 path_to_regexp 揭秘路由匹配与参数提取的核心算法(路由管道工程师)

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.ZEEKLOG.net 前言 在进行 OpenHarmony 的应用架构设计时,我们经常需要处理“动态路由”。 * 页面路径模式:/profile/:userId * 实际跳转路径:/profile/9527 如何在众多的路由规则中,快速匹配到正确的页面,并精准提取出其中的动态参数 userId = 9527?这背后的核心驱动力,正是 path_to_regexp。它是 go_router、auto_route 等几乎所有顶级路由框架共享的底层逻辑库。 一、路由解析链路模型 该库将人类易读的路径模式,转化为机器可高效执行的正规表达式。 路径模式 ('/user/:id') path_to_regexp 编译器 高性能 RegExp (正则) 路径匹配

By Ne0inhk
数据结构(2)常见概念

数据结构(2)常见概念

数据结构(2)常见概念 Author: Once Day Date: 2026年2月10日 一位热衷于Linux学习和开发的菜鸟,试图谱写一场冒险之旅,也许终点只是一场白日梦… 漫漫长路,有人对你微笑过嘛… 全系列文章可参考专栏: 数据结构与算法_Once-Day的博客-ZEEKLOG博客 参考文章:速成读者学习规划labuladong/fucking-algorithm: 刷算法全靠套路,认准 labuladong 就够了!学习数据结构和算法的框架思维《数据结构(C语言版)》 文章目录 * 数据结构(2)常见概念 * 1. 基本概念 * 2. 数据存储 * 3. 数据类型 * 4. 算法基本概念 * 5. 算法复杂度 1. 基本概念 程序设计的核心并不在于语法技巧,而在于对问题本质的抽象能力。面对一个确定的问题,开发者需要先识别其中的核心数据及其相互关系,再据此选择合适的数据结构,并设计与之匹配的算法。数据结构决定了数据的组织形态,而算法则定义了在这种组织形态上的操作效率,

By Ne0inhk
【算法通关指南:算法基础篇】二分答案专题:1.木材加工 2.砍树

【算法通关指南:算法基础篇】二分答案专题:1.木材加工 2.砍树

🔥小龙报:个人主页 🎬作者简介:C++研发,嵌入式,机器人方向学习者 ❄️个人专栏:《算法通关指南 》 ✨ 永远相信美好的事情即将发生 文章目录 * 前言 * 一、二分答案 * 二、二分答案经典算题 * 2.1 木材加工 * 2.1.1题目 * 2.1.2 算法原理 * 2.1.3 代码 * 2.2 砍树 * 2.2.1 题目 * 2.2.2 算法原理 * 2.2.3 代码 * 总结与每日励志 前言 二分答案是算法竞赛与笔试中极具技巧性的高分解法,核心思路是将复杂求解转化为简洁的二分+判定,

By Ne0inhk