linux系统中网络编程的实现
目录
一、网络编程基本介绍
OSI参考模型及TCP/IP参考模型
1、OSI参考模型(Open Systems Interconnection Model)
📊 OSI七层结构:
🧭 功能总结:
2、TCP/IP参考模型
📊 TCP/IP四层结构:
3、OSI与TCP/IP模型的对比
4、层次关系图示 🧩
TCP协议
1、TCP概述
✅ TCP的主要特征
2、TCP的可靠性机制
1️⃣ 序列号(Sequence Number)
2️⃣ 确认应答与重传机制(ACK + Retransmission)
3️⃣ 滑动窗口(Sliding Window)
3、TCP连接的建立——三次握手(Three-Way Handshake)
🔁 三次握手过程:
⚙️ 三次握手示意图:
4、TCP首部格式(TCP报文段结构)
5、TCP的总结与应用场景
UDP协议
1、UDP概述
✅ UDP的主要特征
2、UDP的通信方式
通信模式:
3、UDP的工作原理
4、UDP报文格式(UDP首部结构)
5、UDP的优缺点
6、UDP的典型应用场景
7、TCP vs UDP 对比总结
协议的选择
1、基本概念回顾
2、TCP 与 UDP 的三大核心区别
1️⃣ 数据可靠性要求
2️⃣ 应用的实时性
3️⃣ 网络可靠性与环境适应性
3、其他常见区别(补充对比)
4、总结口诀 🧠
5、典型应用举例对比
二、网络编程相关概念
IP地址
1、IP地址的基本概念
✅ 网络通信的基本原理:
2、IPv4地址的表示方式
1️⃣ IPv4地址结构
2️⃣ IP地址的组成
3、IP地址的分类(传统分类法)
4、特殊IP地址
🔸 1️⃣ 回环地址(Loopback Address)
🔸 2️⃣ 私有地址(Private IP)
🔸 3️⃣ 广播地址(Broadcast Address)
5、子网掩码(Subnet Mask)
🧩 子网掩码的作用:
🔢 子网掩码的表示规则:
6、DNS与IP的关系
7、重点总结表
8、总结口诀 🧠
IPV4和IPV6
1、基本概念
2、IPv4 的特点
3、IPv6 的诞生与特点
IPv6 的主要特征:
4、IPv4 与 IPv6 的详细对比
5、IPv6 的优势总结
6、IPv4 与 IPv6 地址表示示例
7、IPv6 的过渡与兼容
8、总结口诀 🧠
端口号
1、端口号的基本定义
2、端口号的作用
3、端口号的范围与分类
4、常见的系统端口号(0~1023)
5、用户自定义端口(>1024)
6、端口号与通信示意
7、端口号的使用规则与注意事项
8、总结口诀 🧠
9、常见举例总结表
socket
1、什么是 Socket(套接字)
✅ Socket 的本质
2、Socket 的作用
3、Socket 通信方式分类
4、① 流式 Socket(Stream Socket)
🌍 对应协议:TCP
✅ 特点:
📄 示例结构(TCP Socket):
5、② 数据报 Socket(Datagram Socket)
🌍 对应协议:UDP
✅ 特点:
📄 示例结构(UDP Socket):
6、Socket 通信模型示意图
7、Socket 通信流程对比总结
8、总结口诀 🧠
linux网络相关的命令
1、系统网络连接前提
2、网络连通性测试命令
3、防火墙管理命令(Ubuntu)
4、常用网络状态查看命令
5、练习案例(虚拟机互联)
6、注意事项
7、总结口诀 🧠
三、TCP网络编程
相关API函数的介绍
socket() ------ 建立网络通信的通道
1.头文件
2.函数原型
3.函数参数
4.返回值
5.函数功能
6.示例代码
bind() ------ 绑定函数
1.头文件
2.函数原型
3.函数参数
4.返回值
5.函数功能
6.常见错误与解决办法
7.示例代码
listen() ------ 监听函数
1.头文件
2.函数原型
3.函数参数
4.返回值、
5.函数功能
6.示例代码
accept() ------ 接收客户端连接请求
1.头文件
2.函数原型
3.函数参数
4.返回值
5.函数功能
6.示例代码
recv() ------ 接收函数
1.头文件
2.函数原型
3.函数参数
4.返回值
5.函数功能
6.示例代码
send() ------ 发送函数
1.头文件
2.函数原型
3.函数参数
4.返回值
5.函数功能
6.示例代码
connect() ------ 连接函数
1.头文件
2.函数原型
3.函数参数
4.返回值
5.函数功能
6.示例代码
TCP实例:
题目:TCP 网络通信程序设计(基于 socket 的 C/S 模型)
要求:
主要步骤:
使用方法:
补充:ip地址的选择
代码:
四、UDP网络编程
UDP框架
⭐ UDP 的特点:
UDP 服务器端流程
UDP 客户端流程
UDP相关函数
sendto() ------ 发送信息
1.头文件
2.函数原型
3.函数参数
4.返回值
5.函数功能
6.示例代码
recvfrom() ------ 接收信息
1.头文件
2.函数原型
3.函数参数
4.返回值
5.函数功能
6.示例代码
UDP实例
一、网络编程基本介绍
OSI参考模型及TCP/IP参考模型
1、OSI参考模型(Open Systems Interconnection Model)
OSI参考模型 是由 ISO(国际标准化组织) 提出的开放式系统互连模型,用于指导网络通信体系结构的设计。它将网络通信过程划分为 七个层次。
📊 OSI七层结构:
| 层次 | 名称 | 功能 | 常见设备/协议 |
|---|---|---|---|
| 第7层 | 应用层(Application) | 为用户提供网络服务接口 | HTTP、FTP、SMTP、DNS |
| 第6层 | 表示层(Presentation) | 数据格式转换、加密、压缩 | JPEG、MP3、SSL |
| 第5层 | 会话层(Session) | 建立、管理和终止会话 | RPC、NetBIOS |
| 第4层 | 传输层(Transport) | 端到端的数据传输、差错控制 | TCP、UDP |
| 第3层 | 网络层(Network) | 路由选择与逻辑寻址 | IP、ICMP、IPX |
| 第2层 | 数据链路层(Data Link) | 成帧、差错检测、物理寻址 | MAC、PPP、以太网交换机 |
| 第1层 | 物理层(Physical) | 比特流传输、定义物理介质 | 网线、集线器、光纤 |
🧭 功能总结:
-
上三层(应用层、表示层、会话层):面向用户的服务。
-
下四层(传输层、网络层、数据链路层、物理层):面向数据传输的服务。
2、TCP/IP参考模型
TCP/IP参考模型 是互联网采用的事实标准模型,由 美国国防部(DoD) 提出,也称 DoD模型。
它比OSI模型更实际、更简单,仅分为 四层。
📊 TCP/IP四层结构:
| 层次 | 名称 | 对应OSI层 | 功能 | 常见协议 |
|---|---|---|---|---|
| 第4层 | 应用层 | 应用层 + 表示层 + 会话层 | 提供用户服务 | HTTP、FTP、SMTP、DNS |
| 第3层 | 传输层 | 传输层 | 端到端通信 | TCP、UDP |
| 第2层 | 网络层(网际层) | 网络层 | IP寻址与路由 | IP、ICMP、ARP |
| 第1层 | 网络接口层(链路层) | 数据链路层 + 物理层 | 物理传输 | Ethernet、PPP、Wi-Fi |
🌐 TCP/IP协议族各层作用(重点整理)
TCP/IP协议族由四层组成,每一层都有明确的功能分工与对应协议。
1️⃣ 网络接口层(Network Interface Layer)
作用:
负责在 物理网络上传输数据,实现主机与物理介质之间的通信。
将上层传下来的 IP数据报 封装成 帧(Frame),并转换为 比特流(0和1) 在物理介质上传输。
接收方则执行相反操作:比特流 → 帧 → 数据报。
关键功能:
成帧、差错检测(如CRC校验)
确定数据在局域网内的传输方式(MAC寻址)
控制物理传输介质(网线、光纤、Wi-Fi等)
常见协议/设备:
Ethernet(以太网)、Wi-Fi(802.11)、PPP、ARP、RARP、交换机、网卡等。🧠 记忆提示:
“帧”在这一层产生——负责数据帧的发送与接收。
2️⃣ 网络层(Internet Layer)
作用:
负责将数据帧封装成IP数据报(Packet)。
负责在不同网络之间**选择最佳路径(路由)**进行转发。
实现**逻辑寻址(IP地址)**与网络间互连。
关键功能:
路由选择与转发
分片与重组(Packet Fragmentation/Reassembly)
IP寻址与逻辑网络划分(子网)
差错检测与控制(ICMP)
常见协议:
IP(Internet Protocol)、ICMP、IGMP、ARP、RARP等。🧠 记忆提示:
网络层决定“怎么走、走哪条路”,即路由选择与IP封装。
3️⃣ 传输层(Transport Layer)
作用:
负责端到端通信(主机之间的进程通信)。
建立、维持和终止通信会话。
提供可靠传输(TCP)或非可靠传输(UDP)。
确定通信方式:面向连接 or 无连接。
关键功能:
分段与重组(Segment)
端口号寻址(区分不同进程)
差错检测与重传控制(TCP)
流量控制与拥塞控制(TCP)
常见协议:
TCP(Transmission Control Protocol)、UDP(User Datagram Protocol)🧠 记忆提示:
“端到端通信靠传输层,端口号识别靠它来。”
4️⃣ 应用层(Application Layer)
作用:
面向用户,为各种网络应用提供服务接口。
通过端口号区分不同应用进程,实现进程间通信。
用户通过这一层使用网络(浏览网页、收发邮件、聊天等)。
关键功能:
提供特定网络服务(文件传输、邮件、网页访问等)
定义数据格式与交换方式
处理应用程序之间的通信
常见协议:
HTTP(网页)、FTP(文件传输)、SMTP/POP3(邮件)、DNS(域名解析)、Telnet、SSH 等。🧠 记忆提示:
“用户能看到的都是应用层的功劳。”
例如:QQ、微信、浏览器等都运行在应用层,通过端口号区分不同进程。
🌐 TCP/IP 协议族主要协议及注解
1️⃣ 网络接口层(Network Interface Layer)协议
🧩 ARP(Address Resolution Protocol,地址解析协议)
作用:
用于在同一局域网中,通过 IP地址 → 物理地址(MAC) 的转换。
即:主机知道对方IP地址,但不知道其MAC地址时,通过ARP请求获取。举例:
A主机要给B主机发数据包,只知道B的IP。
A发送ARP广播请求:“谁是192.168.1.2?”
B回复:“我是192.168.1.2,我的MAC是00-1C-B3-xx-xx-xx。”
A将此映射关系缓存到ARP表中(ARP Cache)。
关键词: IP到MAC的映射。
应用场景: 局域网通信、交换机转发。
2️⃣ 网络层(Internet Layer)协议
🌍 IP(Internet Protocol,网际协议)
作用:
实现主机间的数据传输与逻辑寻址(IP地址),并负责路由选择。
它定义了数据包的基本格式、寻址方式与分片机制。特点:
无连接(每个数据报独立传输)
不可靠(不保证顺序与完整性)
提供最基础的“尽力而为”传输服务
关键任务:
寻址(IP地址分配)、路由(选择路径)。常见版本: IPv4、IPv6
🚦 MPLS(Multi-Protocol Label Switching,多协议标签交换)
作用:
一种高速网络传输机制,结合了“电路交换的快速”和“分组交换的灵活”的优点。
通过为每个数据包分配一个简短的标签(Label),实现快速转发而非复杂的路由查表。特点:
高效转发,减少路由器负载
支持多种网络协议(故称“多协议”)
应用于骨干网、VPN、QoS保障等场景
发展状态:
仍在持续发展中,是**下一代网络(NGN)**的重要基础技术。🧠 记忆提示:
MPLS 是“网络高速公路”的交通标签系统。
3️⃣ 传输层(Transport Layer)协议
🔒 TCP(Transmission Control Protocol,传输控制协议)
作用:
为应用层提供 可靠的、面向连接的 数据传输服务。
在通信前需建立连接(三次握手),传输过程中进行确认、重传与流量控制。特点:
面向连接(Connection-Oriented)
可靠传输(有确认应答、超时重传)
数据有序到达(序列号控制)
适合传输大批量、可靠性要求高的数据
应用场景:
网页浏览(HTTP/HTTPS)、文件传输(FTP)、电子邮件(SMTP)等。🧠 记忆提示:
“慢而稳”——TCP 可靠但开销大。
⚡ UDP(User Datagram Protocol,用户数据报协议)
作用:
提供无连接、不可靠的通信服务。
它不建立连接,也不确认是否成功接收,因此速度快、效率高。特点:
无连接(Connectionless)
不保证数据到达(无确认机制)
数据开销小、实时性高
适用场景:
视频会议、语音通话、网络直播、在线游戏等。🧠 记忆提示:
“快而轻”——UDP 快但不保证到达。
3、OSI与TCP/IP模型的对比
| 对比项 | OSI模型 | TCP/IP模型 |
|---|---|---|
| 层数 | 7层 | 4层 |
| 提出机构 | ISO | 美国国防部(DoD) |
| 是否理论化 | 理论模型 | 实际模型 |
| 应用范围 | 教学、研究 | 实际互联网架构 |
| 层间对应 | 应用层/表示层/会话层 → 应用层;传输层 → 传输层;网络层 → 网际层;数据链路+物理层 → 网络接口层 |
4、层次关系图示 🧩
TCP协议
1、TCP概述
TCP(Transmission Control Protocol,传输控制协议)
是 面向连接的、可靠的、全双工 的传输层协议。
它在通信的两端建立“虚拟连接”,保证数据能够正确、有序、完整地到达目标主机。
✅ TCP的主要特征
| 特征 | 说明 |
|---|---|
| 面向连接 | 通信前必须建立连接(三次握手),通信后释放连接(四次挥手)。 |
| 可靠传输 | 通过确认应答、重传、序列号等机制保证数据不丢失、不重复。 |
| 全双工通信 | 双方可同时发送与接收数据。 |
| 面向字节流 | 数据在TCP中被看作连续的字节流,而非独立的报文。 |
2、TCP的可靠性机制
TCP通过以下几种机制保证 端到端通信的可靠性:
1️⃣ 序列号(Sequence Number)
-
每个TCP报文段都会附带一个 序列号(Sequence Number)。
-
序列号标识了该报文段中第一个字节的编号。
-
接收方依据序列号判断数据的顺序,保证数据 按序组装。
🧠 例:
发送方分三次发送数据序号 1–100、101–200、201–300;
接收方收到后按序重新组装成完整数据流。
2️⃣ 确认应答与重传机制(ACK + Retransmission)
-
接收方每成功接收一个报文段,会发送 ACK确认报文。
-
若发送方在规定时间内未收到ACK,就会 自动重传 该数据包。
-
这确保了数据不会因为网络故障而丢失。
🧠 例:
A → B 发送 Seq=100;
若 B 未返回 ACK=101,则 A 超时重发该包。
3️⃣ 滑动窗口(Sliding Window)
-
用于流量控制,防止发送方发送过快导致接收方缓存溢出。
-
发送方根据接收方通告的“窗口大小”来控制发送速率。
-
窗口可以动态调整,使传输效率更高。
🧠 例:
若接收方窗口大小 = 5000 字节,则发送方最多可连续发送 5000 字节数据而不必等待确认。
3、TCP连接的建立——三次握手(Three-Way Handshake)
为了建立一个可靠的连接,TCP 使用 三次握手(Three-Way Handshake) 来同步双方的通信状态。
🔁 三次握手过程:
| 次数 | 发送方(A) | 接收方(B) | 说明 |
|---|---|---|---|
| 第一次握手 | A → B:发送连接请求(SYN=1, Seq=x) | 告诉B要建立连接,并发送序列号x | |
| 第二次握手 | B → A:确认连接(SYN=1, ACK=1, Seq=y, Ack=x+1) | A收到确认 | B同意建立连接,同时发送自己的序列号y |
| 第三次握手 | A → B:发送确认(ACK=1, Seq=x+1, Ack=y+1) | B收到确认 | A确认连接成功,双方建立通信通道 |
🧠 目的:
-
双方确认彼此的 接收与发送能力正常;
-
保证双方序列号与确认号 同步。
⚙️ 三次握手示意图:
🔸 建立后双方即可开始数据传输(全双工)。
4、TCP首部格式(TCP报文段结构)
每个TCP报文段都包含一个固定的 20字节首部(可变部分可增加选项)。
| 字段 | 长度 | 作用 |
|---|---|---|
| 源端口号(Source Port) | 16位 | 标识发送进程 |
| 目的端口号(Destination Port) | 16位 | 标识接收进程 |
| 序列号(Sequence Number) | 32位 | 数据流中第一个字节的编号 |
| 确认号(Acknowledgment Number) | 32位 | 下一个期望接收的字节序号 |
| 首部长度(Header Length) | 4位 | TCP首部占多少个32位字(即4字节) |
| 标志位(Flags) | 6~9位 | 控制连接状态(如SYN, ACK, FIN等) |
| 窗口大小(Window Size) | 16位 | 指明接收方可接收的数据量 |
| 校验和(Checksum) | 16位 | 检测数据传输中的错误 |
| 紧急指针(Urgent Pointer) | 16位 | 指示紧急数据的位置 |
| 选项(Options) | 可变长 | 如MSS、窗口扩大因子、时间戳等 |
🧠 重点记忆字段:
端口号、序列号、确认号、校验和、滑动窗口。
5、TCP的总结与应用场景
| 项目 | 内容 |
|---|---|
| 协议类型 | 面向连接、可靠传输 |
| 传输方向 | 全双工 |
| 主要机制 | 序列号、确认应答、重传、滑动窗口 |
| 优点 | 可靠性高、顺序传输 |
| 缺点 | 开销较大、延迟略高 |
| 典型应用 | HTTP/HTTPS、FTP、SMTP、Telnet、SSH 等 |
UDP协议
1、UDP概述
UDP(User Datagram Protocol,用户数据报协议)
是一种 无连接的、非可靠的、面向数据报的传输层协议。
它与TCP同属于传输层协议,但比TCP更简单、更高效。
✅ UDP的主要特征
| 特征 | 说明 |
|---|---|
| 无连接(Connectionless) | 通信前无需建立连接,发送数据后也无需确认。 |
| 不可靠传输 | 不保证数据一定到达、也不保证顺序正确。 |
| 面向报文 | 保持应用层数据报的边界,一次发送一次接收。 |
| 传输效率高 | 无需握手、无重传机制,开销小、速度快。 |
| 一对多通信 | 同一个UDP应用程序可以同时与多个主机通信。 |
🧠 记忆口诀:
“无连接、少控制、速度快、不可靠” —— 就是UDP。
2、UDP的通信方式
UDP 不像 TCP 那样建立“连接”,它是 直接将数据报(Datagram)发往目标地址和端口。
通信模式:
| 模式 | 说明 | 举例 |
|---|---|---|
| 一对一(单播) | 一个客户端与一个服务器通信 | DNS 查询、TFTP |
| 一对多(广播) | 一个主机向同一局域网内所有主机发送数据 | DHCP(IP地址分配) |
| 多对多(组播) | 一组主机同时接收数据 | IPTV、网络直播 |
3、UDP的工作原理
UDP 的工作过程非常简单,主要分为两个步骤:
1️⃣ 发送方:
将应用层的数据加上 UDP首部(包含端口号和校验信息) → 交给IP层封装成数据报 → 发送出去。
2️⃣ 接收方:
IP层接收UDP数据报 → 根据端口号交给相应的应用进程。
🧠 说明:
UDP没有连接的概念,因此:
-
没有三次握手;
-
没有确认与重传机制;
-
没有流量控制;
-
但能实现快速的“尽力而为”传输。
4、UDP报文格式(UDP首部结构)
UDP 报文首部固定为 8字节(64位),结构非常简单。
| 字段 | 长度 | 说明 |
|---|---|---|
| 源端口号(Source Port) | 16位 | 标识发送进程 |
| 目的端口号(Destination Port) | 16位 | 标识接收进程 |
| 长度(Length) | 16位 | 整个UDP报文(首部+数据)的长度 |
| 校验和(Checksum) | 16位 | 检测数据在传输中是否出错 |
📦 UDP报文结构示意图:
🧠 记忆提示:
UDP首部 = 4个字段 × 16位 = 8字节,非常简洁!
5、UDP的优缺点
| 优点 | 缺点 |
|---|---|
| 结构简单、传输速度快 | 不保证可靠性 |
| 不需建立连接,节省开销 | 无顺序控制、易丢包 |
| 支持一对多通信 | 无流量控制、无重传机制 |
| 实时性强(延迟低) | 数据可能重复或丢失 |
6、UDP的典型应用场景
UDP 常用于 对实时性要求高、可靠性要求低 的网络应用:
| 应用场景 | 协议示例 | 特点 |
|---|---|---|
| 视频会议、语音通话 | RTP、VoIP | 实时性强,允许少量丢包 |
| 网络直播、在线游戏 | 自定义UDP协议 | 速度优先 |
| 局域网广播/组播 | DHCP、SNMP | 快速分发信息 |
| DNS域名解析 | DNS | 请求小、响应快 |
🧠 例子:
网络视频会议时,如果偶尔丢一帧画面并不会影响整体交流,
因此采用UDP更合适。
7、TCP vs UDP 对比总结
| 比较项 | TCP | UDP |
|---|---|---|
| 是否连接 | 面向连接(三次握手) | 无连接 |
| 传输可靠性 | 可靠(有确认、重传、顺序控制) | 不可靠(尽力而为) |
| 传输方式 | 字节流 | 数据报 |
| 传输速度 | 较慢 | 较快 |
| 报头大小 | 20字节(以上) | 8字节 |
| 通信方式 | 一对一 | 一对多 |
| 典型应用 | HTTP、FTP、SMTP | DNS、视频通话、广播 |
🧭 一句话总结:
✅ TCP:可靠、慢、有保证
⚡ UDP:快速、轻、实时优先
协议的选择
1、基本概念回顾
| 协议 | 中文名称 | 类型 | 特点 |
|---|---|---|---|
| TCP | 传输控制协议(Transmission Control Protocol) | 面向连接 | 提供可靠、有序、双向的字节流传输 |
| UDP | 用户数据报协议(User Datagram Protocol) | 无连接 | 提供快速、不可靠的报文传输 |
2、TCP 与 UDP 的三大核心区别
1️⃣ 数据可靠性要求
-
TCP:可靠传输协议
-
通过 序列号、确认应答(ACK)、超时重传、滑动窗口 等机制,确保数据准确到达、不重复、不丢失。
-
适合对数据正确性要求高的场景。
📘 举例:
-
登录验证(用户名、密码)
-
文件传输(FTP)
-
邮件发送(SMTP)
-
🧠 记忆提示:
“数据要准确,选 TCP。”
-
UDP:不可靠传输协议
-
不提供确认与重传机制,可能出现数据丢失或乱序。
-
适用于实时性强但对数据准确性要求不高的场景。
📘 举例:
-
视频会议
-
网络语音(VoIP)
-
实时游戏通信
-
🧠 记忆提示:
“允许偶尔丢包,选 UDP。”
2️⃣ 应用的实时性
-
TCP:延迟高,实时性较差
-
在通信前要经过“三次握手”,结束时还有“四次挥手”。
-
在传输中每个报文都需确认、重传,造成延时大。
-
不适合实时通信(例如语音、直播)。
📘 举例:
-
发送消息要确认送达 → 延时明显。
-
-
UDP:实时性强
-
无需建立连接,数据直接发送。
-
没有重传确认机制,延迟低。
-
适用于对延迟敏感的实时通信场合。
📘 举例:
-
在线会议、网络直播、网络游戏等。
-
🧠 记忆提示:
“速度第一,选 UDP;可靠优先,选 TCP。”
3️⃣ 网络可靠性与环境适应性
-
TCP:适合网络不稳定的环境
-
内置错误检测与重传机制,可在丢包、乱序环境下保持数据可靠。
-
当网络质量差时仍能保证传输正确性。
📘 举例:
-
长距离或跨网段数据传输(如下载、远程控制)
-
-
UDP:适合网络稳定的环境
-
无纠错机制,若网络丢包严重则数据可能丢失。
-
但在良好的网络条件下,UDP更轻量高效,能加快传输速度。
📘 举例:
-
局域网游戏、局域网媒体分发
-
🧠 记忆提示:
“网络差用 TCP,网络好用 UDP。”
3、其他常见区别(补充对比)
| 对比项目 | TCP | UDP |
|---|---|---|
| 是否面向连接 | 是(三次握手) | 否 |
| 可靠性 | 高(有确认、重传、校验) | 低(尽力而为) |
| 传输方式 | 字节流(连续数据流) | 数据报(独立报文) |
| 报头大小 | 20字节以上(复杂) | 8字节(简单) |
| 传输效率 | 较低(开销大) | 较高(无握手) |
| 顺序保证 | 有序到达 | 可能乱序 |
| 流量控制 | 有滑动窗口控制 | 无流量控制 |
| 实时性 | 延迟较高 | 延迟极低 |
| 典型应用 | HTTP、FTP、SMTP、SSH | DNS、视频、语音、直播 |
4、总结口诀 🧠
✅ TCP:重可靠,轻速度 —— 适合重要数据传输
⚡ UDP:重速度,轻可靠 —— 适合实时性通信
5、典型应用举例对比
| 场景 | 推荐协议 | 原因 |
|---|---|---|
| 文件下载 / 登录验证 | TCP | 要求数据完整、准确 |
| 视频会议 / 网络语音 | UDP | 要求实时、延迟低 |
| 网页浏览(HTTP) | TCP | 需完整加载内容 |
| 网络直播 | UDP | 丢少量包不影响播放 |
| 邮件发送 | TCP | 需保证可靠送达 |
二、网络编程相关概念
IP地址
1、IP地址的基本概念
IP地址(Internet Protocol Address)
是网络中每台主机或设备的唯一标识符,用于实现网络设备之间的通信。
📘 通俗理解:
IP地址就像“网络中的门牌号”,
计算机之间要通信,必须知道对方的 IP 地址。
✅ 网络通信的基本原理:
-
通过 IP 地址 —— 定位到目标设备(主机)。
-
通过 端口号(Port) —— 定位到设备上的具体进程或应用程序。
🧠 举例:
访问网页 → 实际上是访问目标服务器的 IP 地址 + 端口号(80 或 443)。
2、IPv4地址的表示方式
1️⃣ IPv4地址结构
-
IPv4 地址长度: 32 位(二进制位),即 4 字节。
-
表示方法: 采用 点分十进制表示法(Dotted Decimal Notation)。
-
每 8 位(二进制)转换成一个十进制数,中间用“.”分隔。
-
📘 示例:
十进制表示:192.168.1.1
二进制表示:11000000.10101000.00000001.00000001
2️⃣ IP地址的组成
| 部分 | 说明 |
|---|---|
| 网络号(Network ID) | 标识网络所属的网段(类似于街区) |
| 主机号(Host ID) | 标识该网络中的具体主机(类似于门牌号) |
同一网络内的主机:
网络号相同
主机号必须唯一
3、IP地址的分类(传统分类法)
IPv4地址被分为 A、B、C、D、E 五类。
其中,A、B、C类地址最常用于主机和网络通信。
| 类别 | 地址范围(十进制) | 网络号位数 | 主机号位数 | 默认子网掩码 | 用途 |
|---|---|---|---|---|---|
| A类 | 1.0.0.0 ~ 126.255.255.255 | 8位 | 24位 | 255.0.0.0 | 超大型网络 |
| B类 | 128.0.0.0 ~ 191.255.255.255 | 16位 | 16位 | 255.255.0.0 | 中型网络 |
| C类 | 192.0.0.0 ~ 223.255.255.255 | 24位 | 8位 | 255.255.255.0 | 小型网络 |
| D类 | 224.0.0.0 ~ 239.255.255.255 | - | - | - | 多播(组播) |
| E类 | 240.0.0.0 ~ 255.255.255.255 | - | - | - | 保留实验用途 |
📘 注意:
-
127.0.0.0/8 (即127开头的地址)保留为 回环地址。
-
最常见的局域网是 C类地址(如 192.168.x.x)。
4、特殊IP地址
🔸 1️⃣ 回环地址(Loopback Address)
-
范围:127.0.0.0 ~ 127.255.255.255
-
常用:127.0.0.1
-
作用:用于 本机测试 或 本地进程间通信。
🧠 特点:
使用回环地址发送的数据不会经过网络,而是直接返回本机。
🔸 2️⃣ 私有地址(Private IP)
用于 局域网内部通信,不能在互联网上直接使用。
(若要访问互联网,需要通过 NAT 转换成公有地址。)
| 类别 | 地址范围 |
|---|---|
| A类 | 10.0.0.0 ~ 10.255.255.255 |
| B类 | 172.16.0.0 ~ 172.31.255.255 |
| C类 | 192.168.0.0 ~ 192.168.255.255 |
📘 举例:
家用路由器默认分配的内网地址通常为
192.168.1.x。
🔸 3️⃣ 广播地址(Broadcast Address)
-
表示网络中所有主机的地址。
-
主机号全部为 1。
🧠 举例:
子网 192.168.1.0/24 → 广播地址为 192.168.1.255
5、子网掩码(Subnet Mask)
🧩 子网掩码的作用:
-
用于从 IP 地址中提取网络号。
-
通过 与运算(AND) 得到网络地址。
📘 例:
IP地址:192.168.1.15
子网掩码:255.255.255.0
二进制与运算结果:192.168.1.0 (即网络地址)
🔢 子网掩码的表示规则:
-
子网掩码长度:32位
-
由一串 连续的1(表示网络号)和 连续的0(表示主机号)组成。
| 网络类别 | 默认掩码 | 网络位 | 主机位 |
|---|---|---|---|
| A类 | 255.0.0.0 | 8位 | 24位 |
| B类 | 255.255.0.0 | 16位 | 16位 |
| C类 | 255.255.255.0 | 24位 | 8位 |
📘 CIDR写法(无分类表示法):
192.168.1.1/24 ← “/24”表示网络位长度(即子网掩码255.255.255.0)
6、DNS与IP的关系
DNS(Domain Name System)
用于将人类容易记忆的“域名”转换为对应的“IP地址”。
🧠 例:
输入网址:www.baidu.com
↓
DNS解析 → 220.181.38.148(服务器IP地址)
↓
浏览器连接到对应的IP服务器
📘 作用:
DNS让用户通过“名字”访问网站,而不是直接记复杂的数字IP。
7、重点总结表
| 概念 | 说明 | 示例 |
|---|---|---|
| IP地址 | 网络中设备的唯一标识 | 192.168.1.10 |
| 子网掩码 | 区分网络号与主机号 | 255.255.255.0 |
| 网关地址 | 出口路由器的IP | 192.168.1.1 |
| 私有IP | 局域网使用地址 | 192.168.x.x |
| 公有IP | 互联网使用地址 | 8.8.8.8 |
| 回环地址 | 本机通信测试 | 127.0.0.1 |
| 广播地址 | 发送给同网段所有主机 | 192.168.1.255 |
8、总结口诀 🧠
📘 “IP标设备,掩码定网段;
回环测自己,广播找全班;
私有局域网,公有上外端。”
IPV4和IPV6
1、基本概念
| 协议 | 全称 | 中文名称 | 位数 |
|---|---|---|---|
| IPv4 | Internet Protocol version 4 | 互联网协议第4版 | 32 位 |
| IPv6 | Internet Protocol version 6 | 互联网协议第6版 | 128 位 |
2、IPv4 的特点
-
使用 32位地址(即 4 个字节)。
-
理论上可提供 约43亿个地址(2³² ≈ 4.29×10⁹)。
-
由于地址分配不均及保留部分地址,真正可供使用的约 5亿个左右。
-
采用 点分十进制 表示。
📘 示例:
IPv4 地址:192.168.1.1
🧠 缺陷:
随着互联网设备(电脑、手机、物联网设备)数量爆炸增长,IPv4 地址早已不够用。
3、IPv6 的诞生与特点
为了解决 IPv4 地址枯竭的问题,诞生了 IPv6(Internet Protocol version 6)。
IPv6 的主要特征:
-
地址长度:128位(16字节)
-
理论上可提供:
👉 2¹²⁸ ≈ 3.4×10³⁸ 个地址
相当于每平方米可分配约 1000 个地址!
-
-
地址表示方式:
-
使用 十六进制表示,每 16 位为一组,共 8组,中间用冒号“::”分隔。
-
例如:
2001:0db8:85a3:0000:0000:8a2e:0370:7334 -
可以省略连续的“0000”:
2001:db8:85a3::8a2e:370:7334
-
-
自动配置功能(无需DHCP)
-
支持 无状态地址自动配置(SLAAC),主机可自动获取IP地址。
-
-
更高的安全性
-
内置IPSec加密,在协议层支持端到端的数据安全。
-
-
改进的报文结构
-
简化了IPv4的头部格式,提高路由转发效率。
-
-
支持组播、多播
-
更高效的网络通信机制,适用于视频会议、流媒体、物联网等应用。
-
4、IPv4 与 IPv6 的详细对比
| 对比项目 | IPv4 | IPv6 |
|---|---|---|
| 地址长度 | 32 位 | 128 位 |
| 地址数量 | 约 43 亿 | 约 3.4 × 10³⁸ |
| 地址表示方式 | 点分十进制(如192.168.1.1) | 冒号分隔的十六进制(如2001:db8::1) |
| 地址类型 | 单播、广播、多播 | 单播、多播、任播(无广播) |
| 子网划分 | 使用子网掩码 | 使用前缀长度(/64、/48等) |
| 配置方式 | 手动或DHCP分配 | 自动配置(SLAAC) |
| 安全性 | 依赖外部协议(如IPSec可选) | 内置IPSec支持 |
| 路由效率 | 报头复杂、路由表大 | 报头简化、转发速度快 |
| 网络层协议 | ARP、ICMPv4 | 不使用ARP,改为ICMPv6 |
| 地址空间耗尽 | 是 | 否 |
| NAT(网络地址转换) | 需要 | 不需要 |
| 兼容性 | 广泛使用中 | 正在逐步推广 |
5、IPv6 的优势总结
| 优势 | 说明 |
|---|---|
| 🌍 地址空间巨大 | 彻底解决地址枯竭问题 |
| ⚡ 传输效率高 | 报文头更简洁,路由转发更快 |
| 🔒 安全性强 | 原生支持IPSec加密与身份认证 |
| 🤖 自动配置 | 设备可自行生成地址,无需人工配置 |
| 📡 更好的多媒体支持 | 支持组播,适合视频会议与物联网 |
| 🌐 消除NAT限制 | 实现真正的端到端通信 |
6、IPv4 与 IPv6 地址表示示例
| 类型 | IPv4 示例 | IPv6 示例 |
|---|---|---|
| 家庭内网 | 192.168.1.10 | fd00::1234:abcd |
| 公网地址 | 8.8.8.8(Google DNS) | 2001:4860:4860::8888(Google IPv6 DNS) |
| 回环地址 | 127.0.0.1 | ::1 |
| 默认路由 | 0.0.0.0 | ::/0 |
7、IPv6 的过渡与兼容
由于 IPv4 仍被广泛使用,目前网络正处于 IPv4 与 IPv6 共存阶段。
常见的过渡技术包括:
| 技术名称 | 作用 |
|---|---|
| 双栈(Dual Stack) | 同时运行 IPv4 与 IPv6 |
| 隧道(Tunneling) | 将IPv6数据封装在IPv4网络中传输 |
| 转换(Translation) | IPv4 与 IPv6 地址间的相互转换(如 NAT64) |
8、总结口诀 🧠
IPv4 位少快枯竭,
IPv6 位长天地阔;
十六进制冒号连,
自动加密更方便。
端口号
1、端口号的基本定义
端口号(Port Number) 是传输层协议(TCP、UDP)中用来标识主机上不同应用进程的数字编号。
📘 简单理解:
IP地址 → 定位到具体的设备
端口号 → 定位到设备上的具体程序或服务
🧠 举例:
访问一台服务器时,
IP地址:找到服务器
端口号:找到具体的服务(如网页、文件传输等)
2、端口号的作用
✅ 作用:
在网络通信中,通过端口号来区分不同的进程或服务,确保数据准确地交付给正确的应用程序。
🧩 TCP/IP通信模型中:
-
IP地址负责“找到哪台计算机”
-
端口号负责“找到哪一个程序”
3、端口号的范围与分类
端口号为 16位无符号整数,范围为:
🔢 0 ~ 65535
根据使用情况,端口号分为以下三类:
| 分类 | 范围 | 说明 |
|---|---|---|
| 系统端口(知名端口) | 0 ~ 1023 | 系统或特定应用程序使用(固定分配) |
| 注册端口(用户常用) | 1024 ~ 49151 | 用户进程或注册服务使用,可分配但需登记 |
| 动态/私有端口 | 49152 ~ 65535 | 临时通信时由系统动态分配(客户端常用) |
4、常见的系统端口号(0~1023)
| 服务名称 | 协议 | 端口号 | 作用说明 |
|---|---|---|---|
| HTTP | TCP | 80 | 网页访问服务 |
| HTTPS | TCP | 443 | 加密网页访问(安全HTTP) |
| FTP | TCP | 21 | 文件传输协议控制连接 |
| SSH | TCP | 22 | 安全远程登录 |
| TELNET | TCP | 23 | 远程终端连接 |
| SMTP | TCP | 25 | 邮件发送服务 |
| POP3 | TCP | 110 | 邮件接收服务 |
| DNS | UDP/TCP | 53 | 域名解析服务 |
| SNMP | UDP | 161 | 网络管理协议 |
| NTP | UDP | 123 | 网络时间同步协议 |
5、用户自定义端口(>1024)
-
用户或应用程序可以自定义使用 1024 以上的端口。
-
一般用于:
-
数据库服务(如 MySQL:3306)
-
游戏服务器
-
局域网通信
-
临时客户端连接(如网页浏览器)
-
📘 示例:
| 服务 | 协议 | 端口号 | 说明 |
|---|---|---|---|
| MySQL | TCP | 3306 | 数据库服务 |
| SQL Server | TCP | 1433 | 微软数据库服务 |
| Redis | TCP | 6379 | 内存数据库 |
| Tomcat | TCP | 8080 | Web服务(开发常用) |
6、端口号与通信示意
一个完整的网络通信需要四个要素(即 套接字 Socket):
| 要素 | 示例 | 说明 |
|---|---|---|
| 源IP地址 | 192.168.1.10 | 客户端主机 |
| 源端口号 | 50123 | 客户端程序临时分配 |
| 目的IP地址 | 172.16.0.5 | 服务器主机 |
| 目的端口号 | 80 | Web服务端口 |
🧠 理解:
浏览器访问网页时:
浏览器(客户端)用随机端口访问服务器(80端口),
服务器收到请求后,根据端口号知道应由“HTTP服务”处理该请求。
7、端口号的使用规则与注意事项
-
同一台主机中,端口号不能重复使用。
-
同一协议(TCP或UDP)下的端口唯一。
-
-
不同协议的端口号可以相同。
-
例如:TCP 80 与 UDP 80 可以同时存在。
-
-
系统端口需管理员权限才能使用(0~1023)。
-
防火墙可通过端口号限制访问。
-
例如:关闭80端口可阻止网页访问。
-
8、总结口诀 🧠
IP 定位主机,端口定位程序;
0到1023系统留,用户用大号自定义。
9、常见举例总结表
| 协议/服务 | 使用端口 | 传输层协议 | 备注 |
|---|---|---|---|
| HTTP | 80 | TCP | 网页访问 |
| HTTPS | 443 | TCP | 加密网页 |
| FTP | 21 | TCP | 文件传输 |
| SSH | 22 | TCP | 安全远程登录 |
| DNS | 53 | UDP/TCP | 域名解析 |
| DHCP | 67/68 | UDP | 自动分配IP |
| SMTP | 25 | TCP | 邮件发送 |
| POP3 | 110 | TCP | 邮件接收 |
| MySQL | 3306 | TCP | 数据库 |
| Tomcat | 8080 | TCP | Web应用开发 |
socket
1、什么是 Socket(套接字)
Socket(套接字) 是网络通信中最重要的一种编程接口。
它是对网络通信的抽象封装,是 应用层与传输层之间的桥梁。
📘 通俗理解:
Socket 就像一根“网络插头”,
程序通过插上这个插头(socket),就能在网络上收发数据。
✅ Socket 的本质
-
是一种 特殊的 I/O 接口。
-
在 Linux 中,一切皆文件,因此 Socket 也有一个 文件描述符(file descriptor)。
-
程序通过 读写 Socket 文件描述符 来进行网络通信,就像操作普通文件一样。
2、Socket 的作用
-
实现 进程间通信(IPC:Inter-Process Communication)。
-
可用于:
-
本地通信(同一主机上的不同进程)
-
网络通信(不同主机之间)
-
📘 总结一句话:
Socket 是实现“主机 ↔ 主机”或“进程 ↔ 进程”通信的桥梁。
3、Socket 通信方式分类
在 Linux 网络编程中,常见的 Socket 类型主要有两种:
| 类型 | 名称 | 对应协议 | 特点 | 应用场景 |
|---|---|---|---|---|
| 流式套接字(SOCK_STREAM) | 流方式 Socket | TCP | 面向连接、可靠传输、数据有序 | 文件传输、网页通信 |
| 数据报套接字(SOCK_DGRAM) | 数据报方式 Socket | UDP | 无连接、不可靠传输、数据无序 | 视频会议、语音通话 |
4、① 流式 Socket(Stream Socket)
🌍 对应协议:TCP
流式 Socket 提供一种 可靠的、面向连接的、双向字节流 通信方式。
✅ 特点:
-
面向连接(Connection-Oriented)
-
通信前必须建立连接(三次握手)。
-
-
数据可靠传输
-
采用 TCP 协议,保证数据无差错、不丢失、不重复。
-
-
顺序传输
-
数据按发送顺序到达接收方。
-
-
双向通信(Full Duplex)
-
双方可以同时发送与接收数据。
-
📘 适用场景:
-
Web服务器(HTTP)
-
文件传输(FTP)
-
邮件服务(SMTP)
📄 示例结构(TCP Socket):
// TCP服务器端流程
socket() → bind() → listen() → accept() → recv()/send() → close()// TCP客户端流程
socket() → connect() → send()/recv() → close()
5、② 数据报 Socket(Datagram Socket)
🌍 对应协议:UDP
数据报 Socket 提供一种 无连接的、面向报文的通信方式。
✅ 特点:
-
无连接(Connectionless)
-
通信前无需建立连接。
-
-
不可靠传输
-
数据可能丢失或乱序,应用层需自行处理。
-
-
报文独立传输
-
每个报文是独立的,不存在“流”的概念。
-
-
传输速度快,延迟低
📘 适用场景:
-
网络语音(VoIP)
-
视频会议
-
实时游戏
-
局域网广播通信
📄 示例结构(UDP Socket):
// UDP服务器端流程
socket() → bind() → recvfrom()/sendto() → close()// UDP客户端流程
socket() → sendto()/recvfrom() → close()
6、Socket 通信模型示意图
TCP(流式套接字)┌──────────────────┐ ┌──────────────────┐│ 客户端程序 │ <───────►──────►│ 服务器程序 ││ socket() │ │ socket() ││ connect() │ │ accept() ││ send() / recv() │ ◄──────────────►│ recv() / send() │└──────────────────┘ └──────────────────┘UDP(数据报套接字)┌──────────────────┐ ┌──────────────────┐│ 客户端程序 │ <───────►──────►│ 服务器程序 ││ sendto() │ │ recvfrom() ││ recvfrom() │ ◄──────────────►│ sendto() │└──────────────────┘ └──────────────────┘
🌐 TCP(流式套接字)
-
特点:
TCP 是一种 面向连接、可靠传输 的通信方式。
在通信前,客户端与服务器必须先建立连接,通信结束后再断开连接。 -
流程说明:
-
客户端 调用
socket()创建套接字,然后用connect()向服务器发起连接请求。 -
服务器 创建套接字后,通过
accept()接收连接请求。 -
双方连接建立后,使用
send()和recv()进行数据收发。 -
通信完成后,双方关闭套接字。
-
📌 关键特征:数据是连续的“流”,传输可靠但效率较低。
📡 UDP(数据报套接字)
-
特点:
UDP 是一种 无连接、不可靠传输 的通信方式。
发送数据时不需要建立连接,速度快但可能丢包。 -
流程说明:
-
客户端 直接通过
sendto()向服务器发送数据报。 -
服务器 使用
recvfrom()接收数据,同时获取发送方地址。 -
若需要回应,服务器再用
sendto()向客户端发送数据。
-
📌 关键特征:以独立“数据报”为单位传输,效率高但不保证可靠性。
7、Socket 通信流程对比总结
| 步骤 | TCP(SOCK_STREAM) | UDP(SOCK_DGRAM) |
|---|---|---|
| 是否建立连接 | 是(三次握手) | 否 |
| 数据传输方式 | 字节流(stream) | 报文(datagram) |
| 是否可靠 | 可靠(有确认、重传) | 不可靠(可能丢包) |
| 顺序性 | 有序 | 无序 |
| 速度 | 较慢(开销大) | 较快(轻量) |
| 应用场景 | 文件传输、Web通信 | 视频、语音、实时数据 |
| 典型函数 | socket(), connect(), send(), recv() | socket(), sendto(), recvfrom() |
8、总结口诀 🧠
💬 “流式连得稳,数据报传得快;
TCP保顺序,UDP抢时间。”
linux网络相关的命令
1、系统网络连接前提
-
操作系统:Ubuntu / Linux 系统
-
前提:确保网络已连接,可以上网。
-
提示:
-
Ubuntu 系统下使用
ifconfig或ip addr查看网络状态 -
Windows 下使用
ipconfig查看 IP 地址
-
2、网络连通性测试命令
1️⃣ ping 命令
作用:
-
测试主机与目标主机之间的网络连通性
-
可检测 网络延迟(RTT)和 丢包率
基本用法:
ping <IP地址或域名>
示例:
ping www.baidu.com # 测试访问百度网站是否通
ping 192.168.1.101 # 测试同一局域网内主机是否通
注意事项:
-
同一局域网内测试 IP 时,如果 ping 不通:
-
确认目标主机 IP 正确
-
检查本机和目标主机防火墙状态
-
关闭防火墙或允许 ICMP(Ping)通信
-
3、防火墙管理命令(Ubuntu)
Ubuntu 默认防火墙为 UFW(Uncomplicated Firewall)
| 操作 | 命令 | 说明 |
|---|---|---|
| 查询防火墙状态 | sudo ufw status | 查看防火墙是否启用 |
| 开启防火墙 | sudo ufw enable | 启用防火墙保护 |
| 关闭防火墙 | sudo ufw disable | 关闭防火墙,允许所有连接 |
提示:
-
Ping 命令依赖 ICMP 协议,如果防火墙阻止 ICMP,Ping 会失败
-
在 Ubuntu 虚拟机之间测试连通性时,可能需要关闭防火墙
-
Windows 防火墙也可能影响互通,需要根据实验要求关闭
4、常用网络状态查看命令
| 命令 | 作用 | 示例 |
|---|---|---|
ifconfig | 查看网络接口信息(IP、子网掩码、网关) | ifconfig |
ip addr | 查看 IP 地址和网络接口 | ip addr show |
netstat -rn | 查看路由表 | netstat -rn |
route -n | 查看路由信息 | route -n |
ping | 测试主机连通性 | ping www.baidu.com |
traceroute | 跟踪数据包路由 | traceroute www.baidu.com |
5、练习案例(虚拟机互联)
1.启动两个 Ubuntu 虚拟机,确保它们在同一网络(同一网段)。
2.使用 ifconfig 或 ip addr 获取 IP 地址。
3.关闭防火墙:
sudo ufw disable
4.使用 ping 测试互通:
ping 192.168.1.xxx
5.成功收到响应,即表明虚拟机互联成功。
6、注意事项
1.同一局域网测试时,确保 网关、子网掩码、IP 不冲突
2.Ping 不通时,可逐步检查:
-
IP 是否正确
-
防火墙是否开启
-
虚拟机网络配置(桥接、NAT)
3.Windows 系统 Ping Linux 时,也需关闭 Windows 防火墙或允许 ICMP。
7、总结口诀 🧠
“Ping 测连通,防火墙挡路;
IP、掩码、网关对齐,虚拟机互通顺畅。”
三、TCP网络编程
相关API函数的介绍
socket() ------ 建立网络通信的通道
1.头文件
#include <sys/types.h>
#include <sys/socket.h>
2.函数原型
int socket(int domain, int type, int protocol);
3.函数参数
| 参数 | 说明 | 常用值 |
|---|---|---|
| domain | 指定协议族(Address Family) | AF_INET(IPv4)AF_INET6(IPv6) |
| type | 套接字类型 | SOCK_STREAM(TCP/流式)SOCK_DGRAM(UDP/数据报) |
| protocol | 指定具体协议,通常填 0 | 0 表示根据 type 自动选择 TCP 或 UDP |
4.返回值
| 返回值 | 说明 |
|---|---|
| ≥0 | 成功,返回一个 文件描述符(fd),用于后续读写操作 |
| -1 | 出错,失败,通常设置 errno,可用 perror() 查看错误原因 |
5.函数功能
-
功能:创建一个 套接字(Socket),作为应用程序和网络通信的端点
-
用途:
-
用于后续 bind()/connect()/send()/recv() 等操作
-
可以在本地进程间通信或跨网络通信
-
6.示例代码
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>int main() {int sockfd;// 创建TCP套接字sockfd = socket(AF_INET, SOCK_STREAM, 0);if (sockfd < 0) {perror("socket创建失败");return -1;}printf("socket创建成功,文件描述符 = %d\n", sockfd);return 0;
}
现象:

bind() ------ 绑定函数
1.头文件
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h> // sockaddr_in结构体
2.函数原型
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
3.函数参数
| 参数 | 说明 |
|---|---|
| sockfd | 由 socket() 返回的套接字文件描述符 |
| addr | 指向 本地地址结构 的指针(如 struct sockaddr_in),指定 IP 和端口号 |
| addrlen | 地址结构体长度,一般用 sizeof(struct sockaddr_in) |
1️⃣ 通用地址结构(IPv6 可用)
struct sockaddr {unsigned short int sa_family; // 地址族char sa_data[14]; // 地址数据
};
| 字段名 | 类型 | 含义 | 说明 |
|---|---|---|---|
| sa_family | unsigned short int | 地址族(Address Family) | 指明使用哪种通信协议,如:AF_INET(IPv4)、AF_INET6(IPv6)、AF_UNIX(本地通信) |
| sa_data | char[14] | 地址数据区 | 保存具体的地址和端口信息(实际含义取决于地址族) |
2️⃣ IPv4 地址结构
struct sockaddr_in {unsigned short int sin_family; // 地址族 AF_INETuint16_t sin_port; // 端口号 (使用 htons() 转换)struct in_addr sin_addr; // IP 地址unsigned char sin_zero[8]; // 填充位
};
| 成员名称 | 类型 | 说明 | 示例 |
|---|---|---|---|
| sin_family | unsigned short int | 地址族(Address Family)IPv4 通常设置为 AF_INET | seraddr.sin_family = AF_INET; |
| sin_port | uint16_t | 端口号(Port Number)⚠️ 必须使用 htons() 转换为网络字节序 | seraddr.sin_port = htons(8080); |
| sin_addr | struct in_addr | IP 地址信息,内部成员 s_addr 存储 32 位 IPv4 地址 | seraddr.sin_addr.s_addr = inet_addr("192.168.1.10"); |
| sin_zero[8] | unsigned char[8] | 保留字段(填充用,使结构体大小与 sockaddr 一致)通常清零即可 | bzero(seraddr.sin_zero, 8); |
3️⃣ IPv4 地址成员结构
struct in_addr {uint32_t s_addr; // 存放IP地址 (网络字节序)
};
4.返回值
| 返回值 | 说明 |
|---|---|
| 0 | 绑定成功 |
| -1 | 绑定失败,设置 errno 可查看错误原因(如端口已占用) |
5.函数功能
-
将 Socket 与本地 IP 地址和端口号绑定
-
使服务器 Socket 知道 客户端连接请求该发送到哪个端口
-
TCP:必须先
bind()(服务器端)才能listen() -
UDP:可选,若不绑定系统会自动分配端口
6.常见错误与解决办法
| 错误信息 | 原因 | 解决方法 |
|---|---|---|
bind: Address already in use | 端口未完全释放或处于 TIME_WAIT 状态 | 在 bind() 前调用:setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)); |
Permission denied | 尝试绑定 0~1023 端口(系统端口) | 使用普通端口号 >1024 |
Invalid argument | 地址结构传递错误或未初始化 | 检查 sockaddr_in 结构赋值是否完整 |
7.示例代码
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>#define SERVER_IP "127.0.0.1" // 定义服务器IP地址为本地回环地址
#define PORT_NUM 5000 // 定义服务器端口号为5000typedef struct sockaddr SA;
typedef struct sockaddr_in SIN;int main() {int sockfd;// 创建TCP套接字sockfd = socket(AF_INET, SOCK_STREAM, 0); // 创建一个IPv4的TCP套接字,返回套接字描述符sockfdif (sockfd < 0) {perror("socket创建失败");return -1;}// 初始化服务器地址结构:、设置IPv4协议族、端口号(PORT_NUM,网络字节序)和服务器IP地址(SERVER_IP)SIN seraddr; bzero(&seraddr, sizeof(SIN)); //清零seraddr.sin_family = AF_INET; // 指定使用IPv4地址族seraddr.sin_port = htons(PORT_NUM); // 将主机字节序的端口号转换为网络字节序并赋给端口字段seraddr.sin_addr.s_addr = inet_addr(SERVER_IP); // 将字符串形式的服务器IP地址转换为网络字节序并赋给地址字段int ret = bind(sockfd, (SA *)&seraddr, sizeof(SIN)); // 将套接字sockfd与服务器地址结构seraddr绑定if (ret == -1) {perror("bind failed");exit(0);}printf("Bind success! Socket fd = %d\n", sockfd);return 0;}
这段代码演示了一个最基本的 TCP 服务器套接字创建与绑定流程,为后续监听和接收客户端连接打基础。
现象:

listen() ------ 监听函数
1.头文件
#include <sys/types.h>
#include <sys/socket.h>
2.函数原型
int listen(int sockfd, int backlog);
3.函数参数
| 参数 | 说明 |
|---|---|
sockfd | 之前创建并绑定过的套接字文件描述符(socket + bind) |
backlog | 等待队列长度,表示在 accept() 之前可以排队等待的最大连接数 |
4.返回值、
| 返回值 | 说明 |
|---|---|
0 | 成功,将套接字置为监听状态 |
-1 | 失败(例如套接字未绑定、资源不足等) |
5.函数功能
-
将 已绑定的 TCP 套接字 转换为 被动监听套接字
-
告诉操作系统:准备好接受客户端连接请求
-
客户端的连接请求会排队到套接字的等待队列中,长度由
backlog决定
6.示例代码
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>#define SERVER_IP "127.0.0.1" // 定义服务器IP地址为本地回环地址
#define PORT_NUM 5000 // 定义服务器端口号为5000typedef struct sockaddr SA;
typedef struct sockaddr_in SIN;int main() {int sockfd;int ret;// 创建TCP套接字sockfd = socket(AF_INET, SOCK_STREAM, 0); // 创建一个IPv4的TCP套接字,返回套接字描述符sockfdif (sockfd < 0) {perror("socket创建失败");return -1;}printf("Socket fd = %d\n", sockfd);// 初始化服务器地址结构:、设置IPv4协议族、端口号(PORT_NUM,网络字节序)和服务器IP地址(SERVER_IP)SIN seraddr; bzero(&seraddr, sizeof(SIN)); //清零seraddr.sin_family = AF_INET; // 指定使用IPv4地址族seraddr.sin_port = htons(PORT_NUM); // 将主机字节序的端口号转换为网络字节序并赋给端口字段seraddr.sin_addr.s_addr = inet_addr(SERVER_IP); // 将字符串形式的服务器IP地址转换为网络字节序并赋给地址字段ret = bind(sockfd, (SA *)&seraddr, sizeof(SIN)); // 将套接字sockfd与服务器地址结构seraddr绑定if (ret == -1) {perror("bind failed");exit(0);}printf("Bind success!");ret = listen(sockfd, 10); // 设置监听队列长度为 10if(ret == -1) {perror("listen failed");exit(0);}printf("Listen success!");return 0;}
现象:

accept() ------ 接收客户端连接请求
1.头文件
#include <sys/types.h>
#include <sys/socket.h>
2.函数原型
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
3.函数参数
| 参数 | 说明 |
|---|---|
sockfd | 监听套接字的文件描述符(已 socket + bind + listen) |
addr | 指向 sockaddr 结构的指针,用于存放客户端地址信息 |
addrlen | 指向整数的指针,输入时为 addr 的长度,返回时为实际地址长度 |
4.返回值
| 返回值 | 说明 |
|---|---|
>=0 | 成功,返回 新套接字文件描述符,用于与该客户端通信 |
-1 | 失败(例如套接字未监听或被信号中断) |
5.函数功能
-
阻塞等待客户端连接请求(可改为非阻塞模式)
-
一旦有客户端连接,就返回 一个新的套接字 用于通信
-
原来的监听套接字
sockfd继续用于监听其他客户端
6.示例代码
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>#define SERVER_IP "127.0.0.1" // 定义服务器IP地址为本地回环地址
#define PORT_NUM 5000 // 定义服务器端口号为5000typedef struct sockaddr SA;
typedef struct sockaddr_in SIN;int main() {int sockfd;int ret;// 创建TCP套接字sockfd = socket(AF_INET, SOCK_STREAM, 0); // 创建一个IPv4的TCP套接字,返回套接字描述符sockfdif (sockfd < 0) {perror("socket创建失败");return -1;}printf("Socket fd = %d\n", sockfd);// 初始化服务器地址结构:、设置IPv4协议族、端口号(PORT_NUM,网络字节序)和服务器IP地址(SERVER_IP)SIN seraddr; bzero(&seraddr, sizeof(SIN)); //清零seraddr.sin_family = AF_INET; // 指定使用IPv4地址族seraddr.sin_port = htons(PORT_NUM); // 将主机字节序的端口号转换为网络字节序并赋给端口字段seraddr.sin_addr.s_addr = inet_addr(SERVER_IP); // 将字符串形式的服务器IP地址转换为网络字节序并赋给地址字段ret = bind(sockfd, (SA *)&seraddr, sizeof(SIN)); // 将套接字sockfd与服务器地址结构seraddr绑定if (ret == -1) {perror("bind failed");exit(0);}printf("Bind success!");ret = listen(sockfd, 10); // 设置监听队列长度为 10if(ret == -1) {perror("listen failed");exit(0);}printf("Listen success!");int clifd;SIN cliaddr;socklen_t addrlen = sizeof(cliaddr);clifd = accept(serfd, (SA*)&cliaddr, &addrlen);if(clifd == -1) {perror("accept failed");exit(0);}printf("客户端已连接,套接字 fd = %d\n", clifd);return 0;}
recv() ------ 接收函数
1.头文件
#include <sys/types.h>
#include <sys/socket.h>
2.函数原型
ssize_t recv(int sockfd, void *buf, size_t len, int flags);
3.函数参数
| 参数 | 说明 |
|---|---|
sockfd | 用于接收数据的套接字文件描述符(accept 或 connect 返回的 fd) |
buf | 指向接收数据缓冲区的指针,例如 char buf[40] |
len | 要接收的最大字节数(buf 的大小) |
flags | 通常填写 0,也可以使用位置参数,如 MSG_DONTWAIT(非阻塞) |
4.返回值
| 返回值 | 说明 |
|---|---|
>0 | 成功接收到的字节数 |
0 | 对端关闭连接(客户端或服务器退出/断开) |
-1 | 接收失败(网络错误、套接字错误等) |
5.函数功能
-
从指定套接字接收数据,存放到
buf中。 -
如果没有数据,默认会 阻塞等待(除非套接字设置为非阻塞)。
-
用于 TCP 通信 中接收数据,是客户端或服务端收消息的关键函数。
6.示例代码
char buf[1024];
int len = recv(sockfd, buf, sizeof(buf), 0);if (len > 0) {printf("收到数据: %s\n", buf);
} else if (len == 0) {printf("对端关闭连接\n");
} else {perror("recv 失败");
}
send() ------ 发送函数
1.头文件
#include <sys/types.h>
#include <sys/socket.h>
2.函数原型
ssize_t send(int sockfd, const void *buf, size_t len, int flags);
3.函数参数
| 参数 | 说明 |
|---|---|
sockfd | 用于发送数据的套接字文件描述符(accept 或 connect 返回的 fd) |
buf | 存放待发送数据的缓冲区地址 |
len | 待发送数据的字节数 |
flags | 通常填 0,也可以设置特殊发送选项(如 MSG_DONTWAIT、MSG_NOSIGNAL 等) |
4.返回值
| 返回值 | 说明 |
|---|---|
>0 | 成功发送的字节数 |
-1 | 发送失败(套接字错误、网络错误等) |
5.函数功能
-
将数据发送到指定套接字(TCP 或 UDP)。
-
对 TCP:可靠传输,数据按顺序送到对端。
-
对 UDP:无连接发送,可能丢包且无确认。
6.示例代码
char buf[] = "Hello TCP";
int len = send(sockfd, buf, strlen(buf), 0);if (len == -1) {perror("send 失败");
} else {printf("已发送 %d 字节\n", len);
}
connect() ------ 连接函数
1.头文件
#include <sys/types.h>
#include <sys/socket.h>
2.函数原型
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
3.函数参数
| 参数 | 说明 |
|---|---|
sockfd | 套接字文件描述符(socket() 的返回值) |
addr | 服务器地址结构(sockaddr_in 转型成 sockaddr) |
addrlen | 地址结构的长度,通常为 sizeof(struct sockaddr_in) |
4.返回值
| 返回值 | 说明 |
|---|---|
0 | 连接成功(TCP 三次握手建立完成) |
-1 | 连接失败(服务器未启动、IP/端口错误、拒绝访问等) |
5.函数功能
-
客户端用
connect()主动向服务器发起连接请求(TCP)。 -
执行过程中会触发 TCP 三次握手。
-
连接成功后,客户端就可以使用
send()/recv()与服务器通信。
6.示例代码
SIN seraddr;
seraddr.sin_family = AF_INET;
seraddr.sin_port = htons(PORT_NUM);
seraddr.sin_addr.s_addr = inet_addr(SERVER_IP);int ret = connect(sockfd, (SA*)&seraddr, sizeof(seraddr));
if (ret == -1) {perror("connect 失败");exit(1);
}
printf("连接服务器成功!\n");
TCP实例:
题目:TCP 网络通信程序设计(基于 socket 的 C/S 模型)
要求:
利用 Linux 下的 socket API 编写一对基于 TCP 协议的通信程序,实现:
-
服务器端(Server):等待客户端连接,建立通信,进行数据收发;
-
客户端(Client):连接服务器,建立通信,进行数据收发;
-
使用 父子进程 分别实现“发送”和“接收”功能,实现全双工通信。
代码逻辑说明
1️⃣ 服务器端程序(Server)
文件功能:创建 TCP 服务器,等待客户端连接并实现双向通信。
主要步骤:
-
创建套接字
sockfd = socket(AF_INET, SOCK_STREAM, 0);创建一个 IPv4、面向连接的 TCP 套接字。
-
设置服务器地址结构
seraddr.sin_family = AF_INET; seraddr.sin_port = htons(PORT_NUM); seraddr.sin_addr.s_addr = inet_addr(SERVER_IP);绑定本机 IP 和端口号(这里是 192.168.11.247:5000)。
-
端口复用、绑定和监听
setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)); bind(sockfd, (SA *)&seraddr, sizeof(SIN)); listen(sockfd, 10);-
允许端口复用;
-
将 socket 与 IP+端口绑定;
-
设置监听队列。
-
-
等待客户端连接
clifd = accept(sockfd, (SA*)&cliaddr, &addrlen);当客户端连接成功后,返回新的通信套接字。
-
父子进程通信机制
-
使用
fork()创建子进程。 -
子进程:
recv()接收客户端发来的消息; -
父进程:
send()发送数据到客户端。
-
逻辑总结:
-
服务端在 192.168.11.247:5000 等待连接;
-
一旦客户端连接上后,父子进程分别负责发送与接收,实现双向通信。
2️⃣ 客户端程序(Client)
文件功能:连接服务器并进行全双工通信。
主要步骤:
-
创建 TCP 套接字
sockfd = socket(AF_INET, SOCK_STREAM, 0); -
设置服务器地址
seraddr.sin_family = AF_INET; seraddr.sin_port = htons(PORT_NUM); seraddr.sin_addr.s_addr = inet_addr(SERVER_IP); -
连接服务器
connect(sockfd, (SA*)&seraddr, sizeof(SA));向服务器 IP(192.168.11.247)端口 5000 发起 TCP 连接。
-
父子进程机制
-
子进程: 循环
recv()接收服务器发送的数据; -
父进程: 使用
send()向服务器发送输入数据。
-
使用方法:
1️⃣ 编译命令:
gcc server.c -o server
gcc client.c -o client
2️⃣ 运行步骤:
-
在服务器主机上运行:
./server显示:
Socket fd = 3 Bind success! Listen success!等待客户端连接。
-
在另一台主机或同一主机上运行客户端:
./client若成功连接,会显示:
Connect success!同时服务器端显示:
客户端已连接,套接字 clifd = X
补充:ip地址的选择

在 socket 通信程序中:
-
服务器端(Server) 需要绑定一个本机可用的 IP 地址;
-
客户端(Client) 需要连接到服务器端的 IP 地址。
只有双方 IP 和端口匹配正确,且网络连通,TCP 连接才能建立成功。
用ifconfig查看本机可用的 IP 地址
截图可见:
ens33 Link encap:以太网 硬件地址 00:0c:29:5e:e7:acinet 地址:192.168.11.247 广播:192.168.15.255 掩码:255.255.248.0
lo Link encap:本地环回inet 地址:127.0.0.1 掩码:255.0.0.0
这表示主机有两个网络接口:
| 接口名 | IP 地址 | 说明 |
|---|---|---|
| ens33 | 192.168.11.247 | 以太网接口(局域网 IP,可与其他主机通信) |
| lo | 127.0.0.1 | 本地回环接口(仅限本机通信) |
这份代码中的具体设置
✅ 如果要本机测试(服务器和客户端都在同一台电脑):
修改两段代码中的:
#define SERVER_IP "127.0.0.1"
服务器绑定:
seraddr.sin_addr.s_addr = inet_addr("127.0.0.1");
客户端连接:
seraddr.sin_addr.s_addr = inet_addr("127.0.0.1");
运行方式:
# 打开两个终端
./server
./client
这样通信会成功(在同一主机上测试)。
✅ 如果你要两台电脑通信(局域网内):
假设服务器那台电脑 IP 为:
192.168.11.247
那么:
-
服务器代码
#define SERVER_IP "192.168.11.247" -
客户端代码
#define SERVER_IP "192.168.11.247"
并且要保证:
-
客户端能 ping 通服务器 IP;
-
服务器防火墙关闭或放行 5000 端口;
-
两台电脑在同一网段(都类似 192.168.11.xxx)。
-
IP 地址选择规则
场景 服务器绑定 IP 客户端连接 IP 说明 同一台主机通信(本机测试) 127.0.0.1或"0.0.0.0"127.0.0.1不经过网卡,最简单、安全 局域网两台主机通信 服务器使用本机局域网 IP,例如 192.168.11.247客户端填服务器那台主机的同一个 IP 地址 确保在同一网段 跨网段或公网通信 公网 IP(或端口映射后的 NAT 地址) 客户端连接公网 IP 需保证防火墙和 NAT 规则允许访问
代码:
服务端 ------ service.c
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>#define SERVER_IP "192.168.11.247" // 定义服务器IP地址
#define PORT_NUM 5000 // 定义服务器端口号为5000typedef struct sockaddr SA;
typedef struct sockaddr_in SIN;int main() {int sockfd;int ret;int reuse = 1;// 创建TCP套接字sockfd = socket(AF_INET, SOCK_STREAM, 0); // 创建一个IPv4的TCP套接字,返回套接字描述符sockfdif (sockfd < 0) {perror("socket创建失败");return -1;}printf("Socket fd = %d\n", sockfd);// 初始化服务器地址结构:、设置IPv4协议族、端口号(PORT_NUM,网络字节序)和服务器IP地址(SERVER_IP)SIN seraddr; bzero(&seraddr, sizeof(SIN)); //清零seraddr.sin_family = AF_INET; // 指定使用IPv4地址族seraddr.sin_port = htons(PORT_NUM); // 将主机字节序的端口号转换为网络字节序并赋给端口字段seraddr.sin_addr.s_addr = inet_addr(SERVER_IP); // 将字符串形式的服务器IP地址转换为网络字节序并赋给地址字段setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)); // 设置套接字选项,允许端口地址复用ret = bind(sockfd, (SA *)&seraddr, sizeof(SIN)); // 将套接字sockfd与服务器地址结构seraddr绑定if (ret == -1) {perror("bind failed");exit(0);}printf("Bind success!\r\n");ret = listen(sockfd, 10); // 设置监听队列长度为 10if(ret == -1) {perror("listen failed");exit(0);}printf("Listen success!\r\n");int clifd;SIN cliaddr;socklen_t addrlen = sizeof(cliaddr);clifd = accept(sockfd, (SA*)&cliaddr, &addrlen);if(clifd == -1) {perror("accept failed");exit(0);}printf("客户端已连接,套接字 clifd = %d\r\n", clifd);//创建父子进程ret = fork();if(ret == 0) { //子进程,接收客户端发送过来的数据char r_buf[10] = {0};while(1) {recv(clifd, r_buf, sizeof(r_buf), 0);printf("客户端:%s\r\n", r_buf);memset(r_buf, 0, sizeof(r_buf));}}else { //父进程,发送数据char t_buf[10] = {0};while (1) {scanf("%s", t_buf);getchar(); //吸收换行send(clifd, t_buf, sizeof(t_buf), 0);}}
}
客户端 ------ client.c
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>#define SERVER_IP "192.168.11.247" // 定义服务器IP地址为本地回环地址
#define PORT_NUM 5000 // 定义服务器端口号为5000typedef struct sockaddr SA;
typedef struct sockaddr_in SIN;
int main() {int sockfd;int ret;// 创建TCP套接字sockfd = socket(AF_INET, SOCK_STREAM, 0); // 创建一个IPv4的TCP套接字,返回套接字描述符sockfdif (sockfd < 0) {perror("socket创建失败");return -1;}printf("Socket fd = %d\n", sockfd);// 初始化服务器地址结构:、设置IPv4协议族、端口号(PORT_NUM,网络字节序)和服务器IP地址(SERVER_IP)SIN seraddr; bzero(&seraddr, sizeof(SIN)); //清零seraddr.sin_family = AF_INET; // 指定使用IPv4地址族seraddr.sin_port = htons(PORT_NUM); // 将主机字节序的端口号转换为网络字节序并赋给端口字段seraddr.sin_addr.s_addr = inet_addr(SERVER_IP); // 将字符串形式的服务器IP地址转换为网络字节序并赋给地址字段ret = connect(sockfd, (SA*)&seraddr, sizeof(SA));if(ret == -1) {perror("connect failed");exit(0);}printf("Connect success!\r\n");//创建父子进程ret = fork();if(ret == 0) { //子进程,接收服务端发送过来的数据char r_buf[10] = {0};while(1) {recv(sockfd, r_buf, sizeof(r_buf), 0);printf("服务端:%s\r\n", r_buf);memset(r_buf, 0, sizeof(r_buf));}}else { //父进程,发送数据char t_buf[10] = {0};while (1) {scanf("%s", t_buf);getchar(); //吸收换行符send(sockfd, t_buf, sizeof(t_buf), 0);}}}
四、UDP网络编程
UDP框架
⭐ UDP 的特点:
-
无连接:不需要三次握手
-
不可靠:不保证送达
-
速度快:更适合实时应用(语音、视频、广播)
UDP 服务器端流程
socket → bind → recvfrom → sendto → close
-
socket()
创建 UDP 套接字: -
bind()
绑定服务器 IP 和端口。 -
recvfrom()
接收客户端发送的数据(阻塞等待)。 -
sendto()
将处理后的数据发送回客户端。 -
close()
关闭套接字。
UDP 客户端流程
socket → sendto → recvfrom → close
-
socket()
创建 UDP 套接字。
-
sendto()
不需要 connect,直接把数据发给服务器。
-
recvfrom()
接收服务器返回的数据。
-
close()
关闭套接字。
UDP相关函数
sendto() ------ 发送信息
1.头文件
#include <sys/types.h>
#include <sys/socket.h>
2.函数原型
ssize_t sendto(int sockfd, const void *buf, size_t len,int flags, const struct sockaddr *dest_addr,socklen_t addrlen);
3.函数参数
| 参数 | 说明 |
|---|---|
sockfd | 套接字文件描述符(socket() 返回的 UDP 套接字) |
buf | 指向待发送数据的缓冲区 |
len | 数据长度(字节) |
flags | 通常填写 0,MSG_DONTWAIT 可实现非阻塞。 |
dest_addr | 目标地址结构(服务器或客户端的 IP + 端口) |
addrlen | 地址结构长度(sizeof(struct sockaddr_in)) |
4.返回值
| 返回值 | 说明 |
|---|---|
>=0 | 实际发送的字节数 |
-1 | 发送失败(网络错误、地址错误等) |
5.函数功能
-
使用 UDP 协议向指定 IP 和端口发送数据
-
不需要建立连接(无 connect,无三次握手)
-
可用于单播、广播、组播等应
6.示例代码
char buf[] = "hello udp";
int len;len = sendto(sockfd, buf, strlen(buf), 0,(struct sockaddr *)&seraddr, sizeof(seraddr));if (len == -1) {perror("sendto failed");exit(0);
}
recvfrom() ------ 接收信息
1.头文件
#include <sys/types.h>
#include <sys/socket.h>
2.函数原型
int recvfrom(int s, void *buf, int len, unsigned int flags,struct sockaddr *from, socklen_t *fromlen);
3.函数参数
| 参数 | 说明 |
|---|---|
| int s | 套接字文件描述符(UDP socket) |
| *void buf | 接收数据的缓冲区(如 char buf[40]) |
| int len | 缓冲区长度(期望接收多少字节) |
| unsigned int flags | 一般填 0(也可使用 MSG_DONTWAIT 非阻塞) |
| *struct sockaddr from | 用来存放发送方(对端)地址信息 |
| *socklen_t fromlen | 输入输出参数:地址结构大小(一般为 sizeof(struct sockaddr_in)) |
4.返回值
| 返回值 | 说明 |
|---|---|
| >0 | 实际接收到的数据长度 |
| 0 | 对端关闭(UDP 一般不会出现) |
| -1 | 接收失败 |
5.函数功能
-
从 UDP 套接字接收数据
-
获取对方(发送者)的 IP 和端口号
-
默认阻塞等待数据到来
-
常用于 UDP 服务器端和客户端消息接收
6.示例代码
SIN cliaddr;
socklen_t addrlen = sizeof(SIN);
char r_buf[40];int recv_len = recvfrom(serfd, r_buf, sizeof(r_buf), 0,(SA*)&cliaddr, &addrlen);if (recv_len == -1) {perror("recvfrom failed");exit(0);
}printf("接收到 %d 字节: %s\n", recv_len, r_buf);
UDP实例
利用UDP协议,实现服务器和客户端之间自由通信
服务端service.c
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>#define SERVER_IP "192.168.11.247" // 定义服务器IP地址
#define PORT_NUM 5000 // 定义服务器端口号为5000typedef struct sockaddr SA;
typedef struct sockaddr_in SIN;int main() {int sockfd;int ret;int reuse = 1;// 创建UDP套接字sockfd = socket(AF_INET, SOCK_DGRAM, 0); // 创建一个IPv4的UDP套接字,返回套接字描述符sockfdif (sockfd < 0) {perror("socket创建失败");return -1;}printf("Socket fd = %d\n", sockfd);// 初始化服务器地址结构:、设置IPv4协议族、端口号(PORT_NUM,网络字节序)和服务器IP地址(SERVER_IP)SIN seraddr; bzero(&seraddr, sizeof(SIN)); //清零seraddr.sin_family = AF_INET; // 指定使用IPv4地址族seraddr.sin_port = htons(PORT_NUM); // 将主机字节序的端口号转换为网络字节序并赋给端口字段seraddr.sin_addr.s_addr = inet_addr(SERVER_IP); // 将字符串形式的服务器IP地址转换为网络字节序并赋给地址字段setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)); // 设置套接字选项,允许端口地址复用ret = bind(sockfd, (SA *)&seraddr, sizeof(SIN)); // 将套接字sockfd与服务器地址结构seraddr绑定if (ret == -1) {perror("bind failed");exit(0);}printf("Bind success!\r\n");//接收对应客户端信息char buf[10] = {0};SIN cliaddr = {0};socklen_t addrlen = sizeof(SIN);ret = recvfrom(sockfd, buf, sizeof(buf), 0, (SA*)&cliaddr, &addrlen);if(ret > 0) {printf("客户端信息:%s\r\n", buf);}//打印地址信息--直接法--对应客户端的IP地址printf("new client address is:%d.%d.%d.%d:%d\r\n",(cliaddr.sin_addr.s_addr)&0xff,(cliaddr.sin_addr.s_addr>>8)&0xff,(cliaddr.sin_addr.s_addr>>16)&0xff ,(cliaddr.sin_addr.s_addr>>24)&0xff,cliaddr.sin_port);//可以输出对应的IP地址 printf("ip addr is:%s\n", inet_ntoa(cliaddr.sin_addr));//创建父子进程ret = fork();if(ret == 0) { //子进程,接收客户端发送过来的数据char r_buf[10] = {0};while(1) {if(recvfrom(sockfd, r_buf, sizeof(r_buf), 0, (SA*)&cliaddr, &addrlen)) {printf("客户端:%s\r\n", r_buf);}memset(r_buf, 0, sizeof(r_buf));}}else { //父进程,发送数据char t_buf[10] = {0};while (1) {fgets(t_buf, sizeof(t_buf), stdin); // 从标准输入读取一行数据到bufsendto(sockfd, t_buf, strlen(t_buf), 0, (SA*)&cliaddr, addrlen); // 将buf中的数据发送到指定客户端地址cliaddrmemset(t_buf, 0, sizeof(t_buf));}}
}
客户端client.c
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>#define SERVER_IP "192.168.11.247" // 定义服务器IP地址为本地回环地址
#define PORT_NUM 5000 // 定义服务器端口号为5000typedef struct sockaddr SA;
typedef struct sockaddr_in SIN;
int main() {int sockfd;int ret;// 创建TCP套接字sockfd = socket(AF_INET, SOCK_DGRAM, 0); // 创建一个IPv4的UDP套接字,返回套接字描述符sockfdif (sockfd < 0) {perror("socket创建失败");return -1;}printf("Socket fd = %d\n", sockfd);// 初始化服务器地址结构:、设置IPv4协议族、端口号(PORT_NUM,网络字节序)和服务器IP地址(SERVER_IP)SIN seraddr; socklen_t addrlen = sizeof(seraddr);bzero(&seraddr, sizeof(SIN)); //清零seraddr.sin_family = AF_INET; // 指定使用IPv4地址族seraddr.sin_port = htons(PORT_NUM); // 将主机字节序的端口号转换为网络字节序并赋给端口字段seraddr.sin_addr.s_addr = inet_addr(SERVER_IP); // 将字符串形式的服务器IP地址转换为网络字节序并赋给地址字段ret = sendto(sockfd, "hello", sizeof("hello"), 0, (SA*)&seraddr, addrlen);if(ret > 0) {printf("sendto success\r\n");}//创建父子进程ret = fork();if(ret == 0) { //子进程,接收服务端发送过来的数据char r_buf[10] = {0};while(1) {if(recvfrom(sockfd, r_buf, sizeof(r_buf), 0, (SA*)&seraddr, &addrlen)) {printf("服务端:%s\r\n", r_buf);}memset(r_buf, 0, sizeof(r_buf));}}else { //父进程,发送数据char t_buf[10] = {0};while (1) {fgets(t_buf, sizeof(t_buf), stdin); // 从标准输入读取一行数据到bufsendto(sockfd, t_buf, strlen(t_buf), 0, (SA*)&seraddr, addrlen); // 将buf中的数据发送到指定客户端地址cliaddrmemset(t_buf, 0, sizeof(t_buf));}}}
现象:
1.打开服务端

2.用另一个终端打开客户端

3.服务器和客户端之间自由通信

