当前位置: 首页 > web >正文

嵌入式解谜日志-网络编程(udp,tcp,(while循环原理))

一、OSI 模型的核心思想

  1. 分层解耦:将网络通信拆解为 7 个独立层,每层仅关注自身功能(如数据封装、路由选择、流量控制),降低复杂度。
  2. 标准化接口:每层通过 “服务访问点(SAP)” 为上层提供服务,同时通过 “协议” 与对等层(另一台设备的同一层)通信,屏蔽底层实现细节。
  3. 数据封装与解封装:发送端从应用层到物理层,每层为数据添加 “首部(Header)” 或 “尾部(Trailer)”;接收端从物理层到应用层,逐层剥离首部,最终获取原始数据。

二、OSI 7 层模型详解(从顶层到底层)


应用层:为网络用户提供各种服务,例如电子邮件、文件传输等。

提供服务

1. 应用层(Application Layer,第 7 层)

  • 核心功能:为应用程序提供网络服务接口,是用户与网络的直接交互层。
  • 典型协议:HTTP(网页访问)、FTP(文件传输)、SMTP(邮件发送)、DNS(域名解析)、Telnet(远程登录)。
  • 数据单位:用户数据(如网页内容、文件数据)。
  • 示例:浏览器通过 HTTP 协议向服务器请求网页,邮件客户端通过 SMTP 发送邮件。

表示层:为不同主机间的通信提供统一的数据表示形式

(压缩,加密)

2. 表示层(Presentation Layer,第 6 层)

  • 核心功能:处理数据的 “格式转换与加密”,确保发送端与接收端能理解数据格式。
  • 主要任务
    • 数据编码 / 解码(如 ASCII、UTF-8);
    • 数据压缩 / 解压缩(如 ZIP、GZIP);
    • 数据加密 / 解密(如 SSL/TLS 中的加密处理)。
  • 数据单位:PDU(Protocol Data Unit,协议数据单元,此处为 “表示层 PDU”)。
  • 示例:发送端将文本数据从 UTF-8 转换为网络通用格式,接收端再转换回本地格式;网银登录时对密码进行加密传输。

会话层:负责信息传输的组织和协调,管理进程会话过程。

网络链接的状态信息

3. 会话层(Session Layer,第 5 层)

  • 核心功能:建立、管理和终止 “会话连接”(应用程序之间的通信会话),确保通信双方的会话同步。
  • 主要任务
    • 会话建立(如三次握手类似的会话初始化);
    • 会话维护(如心跳检测、会话恢复);
    • 会话终止(释放会话资源)。
  • 数据单位:会话层 PDU。
  • 示例:视频会议中建立双方的实时会话,若网络中断,尝试恢复会话而非重新建立;数据库连接中的会话管理(如 MySQL 的连接会话)。

传输层:管理网络通信两端的数据传输,提供可靠或不可靠的传输服务

udp(用户数据包),tcp(传输控制协议)。数据以什么方式传输(可靠与否)

4. 传输层(Transport Layer,第 4 层)

  • 核心功能:提供 “端到端的可靠数据传输”,负责流量控制、差错控制和端口寻址(标识主机中的进程)。
  • 典型协议
    • TCP(Transmission Control Protocol):面向连接、可靠的字节流传输(重传丢失数据、按序交付);(文件传输)
    • UDP(User Datagram Protocol):无连接、不可靠的数据报传输(速度快,适合实时场景)。(视频和音频),网络开销小
  • 数据单位:TCP 段(Segment)、UDP 数据报(Datagram)。
  • 关键机制
    • 端口号(0-65535,如 80 端口对应 HTTP);
    • TCP 的流量控制(滑动窗口)、差错控制(ACK 确认、重传)。
  • 示例:浏览器(客户端进程,随机端口)通过 TCP 连接服务器的 80 端口,确保网页数据不丢失;视频通话通过 UDP 传输实时数据,容忍少量丢包。

网络层:负责数据传输的路由选择和网际互连。

IP地址(广域网)

5. 网络层(Network Layer,第 3 层)

  • 核心功能:实现 “跨网络的路由选择”,将数据从源主机发送到目标主机(可能经过多个路由器)。
  • 典型协议:IP(Internet Protocol,IPv4/IPv6)、ICMP(网络控制消息,如 ping 命令)、OSPF(路由协议)、ARP(地址解析,IP→MAC)。
  • 数据单位:数据包(Packet)。
  • 关键机制
    • IP 地址(标识网络中的主机,如 192.168.1.1);
    • 路由选择(路由器通过路由表决定数据包的下一跳);
    • 拥塞控制(避免网络过载)。
  • 示例:发送端通过 IP 协议为数据添加目标 IP 地址,路由器根据 IP 地址和路由表将数据包转发到目标网络;ping 命令通过 ICMP 协议检测主机可达性。

数据链路层,负责物理相邻(通过网络介质相连)的主机间的数据传输,主要作用包括物理地址寻址、数据帧封装、差错控制等。该层可分为逻辑链路控制子层(LLC)和介质访问控制子层(MAC)

租建链路(局域网),组织01数据成位帧(一包),校验

6. 数据链路层(Data Link Layer,第 2 层)

  • 核心功能:处理 “物理层的原始数据帧”,实现同一局域网内的可靠传输,负责 MAC 地址寻址、帧同步和差错检测。
  • 典型协议:Ethernet(以太网)、PPP(点对点协议,如拨号上网)、ARP(底层地址解析)。
  • 数据单位:帧(Frame)。
  • 关键机制
    • MAC 地址(硬件地址,如 00:1A:2B:3C:4D:5E,标识网卡);
    • 差错检测(如 CRC 校验,检测帧在传输中是否损坏);
    • 冲突避免(以太网的 CSMA/CD 机制,避免同一局域网内数据冲突)。
  • 示例:局域网内的主机通过 MAC 地址通信,路由器将 IP 数据包封装为以太网帧,通过 MAC 地址发送到下一跳设备;接收端通过 CRC 校验判断帧是否损坏,损坏则丢弃。

物理层,负责把主机中的数据转换成电信号,再通过网络介质(双绞线、光纤、无线信道等)来传输。该层描述了通信设备的机械、电气、功能等特性。

7. 物理层(Physical Layer,第 1 层)

  • 核心功能:定义 “物理介质的电气特性和信号传输规则”,将数据链路层的帧转换为物理信号(如电信号、光信号)在物理介质上传输。
  • 主要任务
    • 物理介质(如双绞线(短距离)、光纤(长距离)、无线电波((WiFi  2.4G和5G频段),无线局域网和 无线广域网));
    • 信号类型(如数字信号、模拟信号);
    • 引脚定义(如网线的 T568A/B 线序)、传输速率(如 100Mbps(双绞线)、10Gbps(光纤))。
  • 数据单位:比特(Bit,0 或 1)。
  • 示例:网线传输电信号,光纤传输光信号,WiFi 通过无线电波传输信号;网卡将帧转换为电信号,通过网线发送到交换机。

三,TCP/IP 模型(TCP/IP协议栈)(Transmission Control Protocol/Internet Protocol Model)是互联网的核心通信架构

一、TCP/IP 模型的分层结构(4 层划分)

TCP/IP 模型从顶层到底层分为以下 4 层,每层负责特定的通信功能,通过协议协作完成端到端的数据传输:

1. 应用层(Application Layer)

  • 核心功能:为用户应用程序提供网络服务接口,直接处理用户数据。
  • 包含协议:整合了 OSI 模型中应用层、表示层和会话层的功能,常见协议有:
    • HTTP/HTTPS:网页访问(超文本传输协议);
    • FTP:文件传输协议;
    • SMTP/POP3:邮件发送与接收;
    • DNS:域名解析(将域名转换为 IP 地址);
    • SSH/Telnet:远程登录。
  • 数据单位:用户数据(如网页内容、文件、邮件正文)。
  • 示例:浏览器通过 HTTP 协议向服务器请求网页,邮件客户端通过 SMTP 发送邮件。

2. 传输层(Transport Layer)

  • 核心功能:提供端到端的可靠数据传输,负责数据的分段、重组、流量控制和差错控制。
  • 核心协议
    • TCP(Transmission Control Protocol):面向连接、可靠的字节流传输。通过三次握手建立连接,四次挥手断开连接,使用确认机制、重传机制和滑动窗口实现流量控制,确保数据不丢失、不重复、按序到达。适用于文件传输、网页加载等对可靠性要求高的场景。
    • UDP(User Datagram Protocol):无连接、不可靠的数据报传输。无需建立连接,传输速度快,但不保证数据送达顺序和完整性。适用于视频通话、实时游戏、DNS 查询等对实时性要求高的场景。
  • 数据单位:TCP 称为 “段(Segment)”,UDP 称为 “数据报(Datagram)”。
  • 关键机制:通过端口号(0-65535)标识主机中的进程(如 80 端口对应 HTTP 服务,443 端口对应 HTTPS 服务)。

3. 网络层(Internet Layer)

  • 核心功能:实现跨网络的路由选择,将数据从源主机发送到目标主机(可能经过多个路由器)。
  • 核心协议
    • IP(Internet Protocol):为主机分配唯一的 IP 地址(如 IPv4 的 192.168.1.1,IPv6 的 2001:db8::1),定义数据包的格式和路由规则。
    • ICMP(Internet Control Message Protocol):用于网络诊断和控制(如 ping 命令检测主机可达性,traceroute 追踪路由路径)。
    • ARP(Address Resolution Protocol):将 IP 地址转换为物理 MAC 地址(同一局域网内通信需要)。
    • 路由协议:如 OSPF、RIP,用于路由器之间交换路由信息,生成路由表。
  • 数据单位:数据包(Packet)。
  • 关键机制:路由器根据 IP 地址和路由表,决定数据包的下一跳转发路径。

4. 网络接口层(Network Interface Layer)

  • 核心功能:处理物理层的原始数据,实现同一局域网内的帧传输,负责 MAC 地址寻址和数据链路管理。
  • 包含内容:对应 OSI 模型的数据链路层和物理层,涉及:
    • 数据链路层协议:如以太网(Ethernet)、PPP(点对点协议),定义帧的格式(包含 MAC 地址、校验码等)。
    • 物理层规范:如网线、光纤等物理介质的电气特性,信号传输规则(如比特流转换为电信号 / 光信号)。
  • 数据单位:帧(Frame)。
  • 关键机制:通过 MAC 地址(硬件地址,如 00:1A:2B:3C:4D:5E)标识局域网内的设备,使用 CRC 校验检测帧传输是否损坏。

http:超文本传输协议

1.DNS(Domain Name System,域名系统)域名解析服务

服务器是互联网的核心基础设施之一,其核心作用是将人类可读的域名(如 www.baidu.com)转换为计算机可识别的 IP 地址(如 202.108.2.147,解决 “记域名易、记 IP 难” 的问题,同时实现域名与 IP 地址的动态映射,支撑互联网服务的灵活部署。


2.DHCP(Dynamic Host Configuration Protocol,动态主机配置协议)

是一种运行在应用层的网络协议,主要用于自动为局域网内的设备分配 IP 地址及其他网络配置参数(如子网掩码、网关、DNS 服务器等),避免了手动配置 IP 的繁琐和地址冲突问题,是局域网中不可或缺的基础设施。


ip:互联网协议

icmp:互联网控制管理协议,网络诊断

ARP(Address Resolution Protocol)地址转换协议

是 TCP/IP 协议族中网络层的核心协议,用于将 IP 地址转换为物理 MAC 地址(Media Access Control Address),解决 “同一局域网内设备如何通过 IP 地址找到对方物理地址” 的问题,是局域网通信的基础。

RARP:将物理MAC地址转换成IP地址


OSI 模型(7 层)TCP/IP 模型(4 层)核心对应关系典型协议 / 功能
应用层应用层直接对应,包含 OSI 的 3 层功能HTTP、FTP、DNS、SMTP
表示层应用层合并到应用层数据加密、格式转换(如 SSL)
会话层应用层合并到应用层会话管理(如 HTTP 会话)
传输层传输层直接对应TCP、UDP
网络层网络层直接对应IP、ICMP、OSPF
数据链路层网络接口层合并物理层和数据链路层Ethernet、PPP、ARP
物理层网络接口层合并物理层和数据链路层网线、光纤、信号传输

IP 地址(Internet Protocol Address)是互联网中标识唯一标识设备的逻辑地址

(1)IP地址的组成:网络位+主机位

sudo vim/etc/network/interfaces 网络配置文件,配置网卡分配ip地址的方式。

(2)IP地址的分类:点分十进制 .ipv4

1. A 类地址(超大型网络)

  • 核心特征
    二进制前 1 位固定为 0(前 4 位范围:0000-0111),网络位长度为 8 位,主机位长度为 24 位
  • 地址范围
    十进制 1.0.0.0 ~ 126.255.255.255(注:0.0.0.0 表示本机未分配 IP,127.0.0.0 为回环地址,均不属于 A 类可用地址)。
  • 子网掩码:默认 255.0.0.0(二进制 11111111.00000000.00000000.00000000)。
  • 可容纳主机数
    主机位共 24 位,理论可容纳 2²⁴ - 2 = 16777214 台设备(减 2 是排除 “网络地址” 和 “广播地址”)。
  • 适用场景:早期互联网主干网、超大型机构(如早期的 IBM、AT&T),目前 A 类地址已基本分配完毕。

2. B 类地址(大中型网络)

  • 核心特征
    二进制前 2 位固定为 10(前 4 位范围:1000-1011),网络位长度为 16 位,主机位长度为 16 位
  • 地址范围
    十进制 128.0.0.0 ~ 191.255.255.255
  • 子网掩码:默认 255.255.0.0(二进制 11111111.11111111.00000000.00000000)。
  • 可容纳主机数
    主机位共 16 位,理论可容纳 2¹⁶ - 2 = 65534 台设备。
  • 适用场景:中型企业、高校、运营商的二级网络(如某省电信的城域网)。

3. C 类地址(小型网络)

  • 核心特征
    二进制前 3 位固定为 110(前 4 位范围:1100-1101),网络位长度为 24 位,主机位长度为 8 位
  • 地址范围
    十进制 192.0.0.0 ~ 223.255.255.255
  • 子网掩码:默认 255.255.255.0(二进制 11111111.11111111.11111111.00000000)。
  • 可容纳主机数
    主机位共 8 位,理论可容纳 2⁸ - 2 = 254 台设备(最适合小型局域网)。
  • 私有:192.168.0.0 - 192.168.255.255
  • 静态路由
    192.168.0.0 
    192.168.0.1网关
    192.168.0.255
  • 适用场景:家庭 WiFi、小型办公室、商铺等设备数量较少的网络(如家庭中 10 台以内的手机、电脑、智能设备)。

4. D 类地址(组播地址)

  • 核心特征
    二进制前 4 位固定为 1110(前 4 位范围:1110),无网络位和主机位之分,不用于单播通信,仅用于 “组播”(一对多通信)。
  • 地址范围
    十进制 224.0.0.0 ~ 239.255.255.255
  • 典型用途
    视频会议(向多个参会设备同步发送视频流)、IPTV(向多个用户推送电视信号)、路由器之间的路由协议通信(如 OSPF)。
  • 示例224.0.0.1 是 “所有主机” 的组播地址,224.0.0.2 是 “所有路由器” 的组播地址。

5. E 类地址(保留地址)

  • 核心特征
    二进制前 4 位固定为 1111(前 4 位范围:1111),无网络位和主机位之分,不对外开放使用
  • 地址范围
    十进制 240.0.0.0 ~ 255.255.255.255(注:255.255.255.255 是全局广播地址,属于特殊用途,不归属 E 类可用地址)。
  • 用途:仅用于科研、实验或未来扩展(如 IPv4 向 IPv6 过渡的技术测试),目前无实际商用场景。

1.配置网络设置:

命令:

①ifconfig:查询网络地址

ip: ifconfig ethX X.X.X.X/24 up ifconfig ens33 192.168.0.13/24 up 255.255.255.0
网关 :route add default gw x.x.x.x
DNS : vi /etc/resolv.conf ==>nameserver 8.8.8.8
测试 : ping www.baidu.com

②ping 网络诊断

③netstat -anp:查看本机所有的网络连接信息

2.网络接口:

1.socket 套接字:用于网络通信的一组接口函数。

本质:文件描述符(网络设备的)

2.ip+port:地址+端口:

                                          ip地址用来识别主机

                                          port端口用来识别应用程序(查找进程)

port(端口):分为TCP port/UDP port

                      范围:1-65535

                     1000以内的端口为系统使用

3.网络字节序:①pc,arm : 都是小端存储设备(数据的地位存放在低地址)

                        ②网络:大端存储

四,udp,不可靠(丢包)低延迟 网络开销小,无链接,一包正文相对多

数据报:

        1.数据与数据之间有边界

        2.发送的次数和接收的次数需要对应

        3.如果数据发送太快,就会丢包

 (1)server(服务端)

1.socket:创建套接字,打开网路设备

2.bind :给套接字,设置ip+port(地址+端口)

3.recvform :先阻塞等待客服端发送文件
4. sendto:送回客服端

#include<arpa/inet.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<unistd.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include<time.h>
typedef struct sockaddr * (SA);
int	main(int argc, char **argv)
{//创建套接字sockfd//AF_INET为互联网类型网络//SOCK_DGRAM为UDP端口类型(不可靠)int sockfd=socket(AF_INET,SOCK_DGRAM,0);if(-1==sockfd){perror("socket");return 1;}//man 7 ip查看地址结构体struct sockaddr_in ser,cli;ser.sin_family=AF_INET;ser.sin_port=htons(50000);ser.sin_addr.s_addr=inet_addr("192.168.1.23");int ret=bind(sockfd,(SA)&ser,sizeof(ser));if(-1==ret){perror("bind");return 1;}time_t tm;socklen_t len=sizeof(cli);while (1){char buf[512]={0};//等待接收客户端time(&tm);recvfrom(sockfd,buf,sizeof(buf),0,(SA)&cli,&len);sprintf(buf,"%s %s",buf,ctime(&tm));//向客户端发送printf("recv %s\n",buf);sendto(sockfd,buf,strlen(buf),0,(SA)&cli,len);}//system("pause");return 0;
}

(2)client(客户端)

1.socket,打开网络设备

2. sendto:向服务端发送信息

3.recvform:接受服务端传回的信息

#include<arpa/inet.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<unistd.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include<time.h>
typedef struct sockaddr * (SA);
int	main(int argc, char **argv)
{//创建套接字,打开网络int sockfd=socket(AF_INET,SOCK_DGRAM,0);if(-1==sockfd){perror("socket");return 1;}//struct sockaddr_in ser;ser.sin_family=AF_INET;ser.sin_port=htons(50000);ser.sin_addr.s_addr=inet_addr("192.168.1.23");int i=10;while (i--)//循环发送{char buf[512]="hello,白海英";//发送信息sendto(sockfd,buf,strlen(buf),0,(SA)&ser,sizeof(ser));//将buf清空,准备接收服务端bzero(buf,sizeof(buf));//接收服务端信息recvfrom(sockfd,buf,sizeof(buf),0,NULL,NULL);printf("from ser:%s\n",buf);sleep(1);}close(sockfd);//system("pause");return 0;
}

做服务端的ip地址是自己的,

做客户端的ip地址是服务端的。

复制图片

客户端:

​
#include<arpa/inet.h>   // 提供IP地址转换函数(如inet_addr)和网络数据结构
#include<stdio.h>        // 标准输入输出函数(如printf、perror)
#include<stdlib.h>       // 标准库函数(如exit,确保兼容性)
#include<string.h>       // 字符串操作函数(如bzero,可扩展使用)
#include<sys/types.h>    // 定义系统数据类型(如ssize_t、socklen_t)
#include<sys/socket.h>   // 提供套接字操作函数(如socket、sendto、recvfrom)
#include<unistd.h>       // 提供文件关闭函数(close)和睡眠函数(sleep)
#include <netinet/in.h>  // 定义网络地址结构(如struct sockaddr_in)
#include <netinet/ip.h>  // 提供IP协议相关定义(用于兼容性)
#include <fcntl.h>       // 提供文件打开控制选项(如O_RDONLY、O_WRONLY)// 类型重定义:将struct sockaddr*简化为SA,减少代码冗余
typedef struct sockaddr * (SA);int	main(int argc, char **argv)
{// 创建UDP套接字(打开网络通信通道)int sockfd = socket(AF_INET, SOCK_DGRAM, 0);if (-1 == sockfd){perror("socket");  // 打印套接字创建失败原因return 1;}// 初始化服务端地址结构(指定数据发送目标)struct sockaddr_in ser;ser.sin_family = AF_INET;        // 使用IPv4协议ser.sin_port = htons(50000);     // 服务端端口(网络字节序)ser.sin_addr.s_addr = inet_addr("192.168.1.123");  // 服务端IP地址// 打开源文件(待发送的图片)和目标文件(接收后保存的图片)int fd = open("1.png", O_RDONLY);                 // 只读打开源文件int fd_s = open("2.png", O_WRONLY | O_CREAT | O_TRUNC, 0666);  // 创建/打开目标文件if (fd == -1 || fd_s == -1) {perror("文件打开失败");  // 打印文件操作失败原因close(fd);close(fd_s);close(sockfd);return 1;}char buf[1024];          // 数据缓冲区(每次最多处理1024字节)ssize_t n;               // 存储实际读取/发送的字节数(支持负数表示错误)socklen_t ser_len = sizeof(ser);  // 服务端地址结构长度/***************************************************************************** 核心循环:分块读取文件 → 发送数据 → 接收反馈 → 写入目标文件* 循环逻辑:"读一点,发一点,收一点,写一点"(而非一次性处理整个文件)* 适用场景:大文件传输(避免占用过多内存)、网络分片传输(符合UDP特性)***************************************************************************/// read函数:从源文件fd读取数据到buf,最多读sizeof(buf)=1024字节// 循环条件:n > 0 → 读取到有效数据,继续处理;n ≤ 0 → 文件读完或出错,退出循环while ((n = read(fd, buf, sizeof(buf))) > 0) {/****************** 发送阶段:将刚读取的一块数据发送给服务端 ******************/// sendto参数n:使用实际读取的字节数(而非缓冲区大小),避免发送无效空数据// 例如:文件只剩500字节时,n=500,仅发送500字节有效数据sendto(sockfd, buf, n, 0, (SA)&ser, sizeof(ser));//清空buf,为下一次循环做准备bzero(buf, sizeof(buf));/****************** 接收阶段:等待服务端回传数据(确认或原数据) ******************/// 覆盖buf中的发送数据(因发送数据已通过网络发出,缓冲区可复用)recvfrom(sockfd, buf, sizeof(buf), 0, NULL, NULL);/****************** 写入阶段:将服务端回传的数据写入目标文件 ******************/// 写入字节数与发送时的n一致,确保每块数据大小匹配,最终文件完整write(fd_s, buf, n);}// 释放资源(关闭所有打开的文件和套接字,避免资源泄漏)close(sockfd);close(fd);close(fd_s);return 0;
}​

服务端:

#include<arpa/inet.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<unistd.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include<time.h>
#include <fcntl.h>
typedef struct sockaddr * (SA);
int	main(int argc, char **argv)
{//创建套接字sockfd//AF_INET为互联网类型网络//SOCK_DGRAM为UDP端口类型(不可靠)int sockfd=socket(AF_INET,SOCK_DGRAM,0);if(-1==sockfd){perror("socket");return 1;}//man 7 ip查看地址结构体struct sockaddr_in ser,cli;ser.sin_family=AF_INET;ser.sin_port=htons(50000);ser.sin_addr.s_addr=inet_addr("192.168.1.213");int ret=bind(sockfd,(SA)&ser,sizeof(ser));if(-1==ret){perror("bind");return 1;}//int fd_s = open("received_from_client.png", O_WRONLY | O_CREAT | O_TRUNC, 0666); // 保存接收文件//if (fd_s == -1) {//  perror("服务端目标文件创建失败");//close(sockfd);// return 1;//}ssize_t n;char buf[1024]={0};socklen_t len=sizeof(cli);const char *end_flag = "FILE_TRANSFER_COMPLETE";while (1){n=recvfrom(sockfd,buf,sizeof(buf),0,(SA)&cli,&len);/* if (n == 0) { // 接收到结束标记break;}*///write(fd_s,buf,n);sendto(sockfd,buf,n,0,(SA)&cli,len);break;}//printf("hello\n");//system("pause");// close(fd_s);//close(sockfd);return 0;
}

udp聊天。可以连续收发。其中#quit,c,s都退出。

客户端:

#include <arpa/inet.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
#include <pthread.h>
typedef struct sockaddr * (SA);
int sockfd;
struct sockaddr_in ser;
socklen_t len = sizeof(ser);
void *recv_thread(void *arg) 
{char buf[1024];while (1){bzero(buf, sizeof(buf));// 阻塞接收服务端消息(在独立线程中不影响主线程发送)recvfrom(sockfd, buf, sizeof(buf), 0, (SA)&ser, &len);// 检测服务端退出指令if (strcmp(buf, "#quit") == 0) {printf("\n服务端已退出聊天\n");close(sockfd);exit(0);  // 退出整个程序}// 显示收到的消息,并提示用户输入printf("\nxxy:%s\n", buf);printf("wjk: ");fflush(stdout);  // 刷新输出缓冲区,确保提示正常显示}
}
int main(int argc, char **argv)
{sockfd = socket(AF_INET, SOCK_DGRAM, 0);if (-1 == sockfd){perror("socket");return 1;}pthread_t tid;if (pthread_create(&tid, NULL, recv_thread, NULL) != 0) {perror("pthread_create failed");close(sockfd);return 1;}// man 7 ipser.sin_family = AF_INET;// host to net shortser.sin_port = htons(50000);ser.sin_addr.s_addr = inet_addr("192.168.1.23");while (1){char buf[1024]={0};printf("wjk:");fgets(buf, sizeof(buf), stdin);buf[strcspn(buf, "\n")] = '\0';sendto(sockfd,buf,strlen(buf),0,(SA)&ser,sizeof(ser));if (strcmp(buf, "#quit") == 0) {printf("已退出聊天\n");close(sockfd);return 0;}} close(sockfd);return 0;
}

服务端:

#include <arpa/inet.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
#include <pthread.h>
typedef struct sockaddr * (SA);
int sockfd;
struct sockaddr_in ser, cli;
socklen_t len =sizeof(cli);;
void *recv_thread(void *arg) 
{char buf[1024] = {0};while (1) {// 接收客户端消息ssize_t n = recvfrom(sockfd, buf, sizeof(buf), 0, (SA)&cli, &len);if (n <= 0) {continue;  // 接收失败则继续等待}// 检测退出指令if (strcmp(buf, "#quit") == 0) {printf("\n客户端已退出聊天\n");close(sockfd);exit(0);  // 退出整个程序}// 显示收到的消息printf("\nwjk:%s\n", buf);printf("xxy: ");  // 提示输入回复fflush(stdout);  // 刷新输出缓冲区,确保提示正常显示}
}
int main(int argc, char **argv)
{sockfd = socket(AF_INET, SOCK_DGRAM, 0);if (-1 == sockfd){perror("socket");return 1;}pthread_t tid;if (pthread_create(&tid, NULL, recv_thread, NULL) != 0) {perror("pthread_create failed");close(sockfd);return 1;}// man 7 ipser.sin_family = AF_INET;// host to net shortser.sin_port = htons(50000);ser.sin_addr.s_addr = inet_addr("192.168.1.23");int ret = bind(sockfd,(SA) &ser, sizeof(ser));if (-1 == ret){perror("bind");return 1;}socklen_t len = sizeof(cli);while (1){char buf[1024] = {0};printf("xxy:");fgets(buf, sizeof(buf), stdin);buf[strcspn(buf, "\n")] = '\0';sendto(sockfd,buf,strlen(buf),0,(SA)&cli,len);if (strcmp(buf, "#quit") == 0) {printf("已退出\n");close(sockfd);return 0;}}return 0;
}

①两端通信为服务端先运行,

②采用线程(pthread_create)的目的:同时处理接收消息和发送消息

③采用while(1)循环 目的:是为了实现可以持续接收和发送信息

总结:线程与循环的配合逻辑

  • 接收线程:用while(1)循环 “专职” 接收客户端消息,确保消息不被遗漏,且不阻塞主线程的输入操作。
  • 主线程:用while(1)循环 “专职” 处理用户输入和发送消息,确保可以连续发送多条消息。

重点理解while循环:

核心逻辑是 while ((n = read(fd, buf, sizeof(buf))) > 0) 循环—— 它的行为是 “读一点、发一点、收一点、写一点”,即每次从文件中读取一块数据(最多 1024 字节),立即发送给服务端,再接收服务端的反馈,最后将反馈数据写入目标文件,直到文件全部读完。

循环自动执行下一次的原理

循环的核心是条件判断数据读取的连续性

  1. 每次循环结束后,自动回到条件判断
    当循环体内的代码(发送、接收、写入)执行完毕后,程序会自动回到循环开头,重新执行 n = read(fd, buf, sizeof(buf))

  2. read 函数会 “记住” 上一次的读取位置
    操作系统会为打开的文件(fd)维护一个 “文件指针”,记录当前读取到的位置。每次 read 后,指针会自动移动到下一个未读取的字节。

    • 例如:第一次读取 1024 字节后,指针指向第 1025 字节;
    • 下一次 read 会从第 1025 字节开始读取,直到文件末尾。
  3. 循环终止条件
    当 read 返回 0(文件读完)或 -1(读取错误)时,n > 0 条件不成立,循环自动退出,不会进行下一次。

while 循环的自动迭代特性和文件指针的自动移动,实现了 “读完一块数据后,自动进行下一次循环处理下一块数据”,直到整个文件传输完成。无需添加额外代码控制 “下一次循环”,循环会根据文件读取状态自动执行或终止。


五,服务器 - 客户端(Server-Client)模型:

C/S(Client/Server,客户端 / 服务器) 和 B/S(Browser/Server,浏览器 / 服务器) 是两种

(1)C/S 模型(Client/Server,客户端 / 服务器)

C/S 是传统的服务器 - 客户端架构,其核心是专用客户端软件—— 用户必须在设备上安装专门开发的客户端程序,才能与服务器进行交互。

1. 核心结构

C/S 架构由 “客户端” 和 “服务器” 两个独立组件构成,二者分工明确:

  • 客户端(Client):安装在用户本地设备(PC、手机、嵌入式设备等)的专用软件,负责用户交互、本地数据处理、请求发起。例如:QQ 客户端、微信 PC 版、本地数据库客户端(Navicat)、游戏客户端(英雄联盟客户端)。
  • 服务器(Server):部署在远程机房 / 云端的高性能设备 / 程序,负责核心业务逻辑处理、数据存储与管理、响应客户端请求。例如:QQ 消息服务器、游戏后台服务器、企业数据库服务器。

(2)B/S 模型(Browser/Server,浏览器 / 服务器)

B/S 是基于互联网发展的轻量化架构,其核心是 “浏览器作为通用客户端”—— 用户无需安装专用软件,只需通过浏览器(如 Chrome、Edge、Safari)即可访问服务器,本质是 “C/S 的特殊形态(浏览器 = 通用客户端)”。


1. 核心结构

B/S 架构由 “浏览器(通用客户端)”“Web 服务器”“应用服务器 / 数据库服务器” 三层构成(部分场景 Web 服务器与应用服务器合并):

  • 浏览器(Browser):用户本地设备上的通用软件(无需额外开发),负责用户交互、HTML/CSS/JS 渲染、请求发起。例如:用 Chrome 打开百度、淘宝、企业官网。
  • Web 服务器:负责接收浏览器的 HTTP/HTTPS 请求,返回静态资源(HTML、CSS、JS、图片)。例如:Nginx、Apache 服务器。
  • 应用服务器 / 数据库服务器:负责处理动态业务逻辑(如用户登录验证、订单计算)、数据存储与查询。例如:Java 的 Tomcat(应用服务器)、MySQL(数据库服务器)。

(3)C/S 与 B/S 核心差异对比

对比维度C/S 模型B/S 模型
客户端形态专用软件(需安装)通用浏览器(无需安装)
开发成本高(多端客户端开发)低(仅服务器端开发)
维护成本高(客户端需逐个升级)低(仅升级服务器)
交互效率高(自定义协议,本地处理)中等(HTTP 协议,依赖网络)
跨设备兼容性差(需适配不同系统)好(浏览器跨设备通用)
本地功能支持强(可调用本地硬件:摄像头、打印机)弱(受浏览器沙箱限制,需特殊权限)
典型代表QQ、微信 PC 版、游戏客户端、Navicat淘宝网页版、百度、企业官网、

(4)总结:如何选择 C/S 还是 B/S?

  • 选 C/S:当场景需要 “高交互效率、强本地功能(如调用硬件)、高安全性”,且用户设备相对固定(如企业内部员工设备)时,优先用 C/S(如工业控制软件、专业设计工具)。
  • 选 B/S:当场景需要 “跨设备访问、低开发维护成本、用户无需安装软件”,且对本地功能要求不高时,优先用 B/S(如互联网公共服务、轻量企业系统)。

六,Tcp(传输控制协议):流式套接字,数据传输连续,可靠传输,应答,自动重传,有链路,全双工通信

因为tcp协议为流式套接字,会存在数据粘包的问题:

数据粘包:发送方发送数据,接收方无法解析数据。

解决方法:1.人为设置数据边界

                  2.固定一次接收数据大小

                  3.自定义协议。

(1)TCP 的核心特性

TCP 的设计围绕 “可靠性” 和 “有序性” 展开,核心特性可概括为以下 5 点:

特性核心作用实现机制简要说明
面向连接确保通信双方 “准备就绪” 后再传输数据,避免无效发送需通过 “三次握手” 建立连接,“四次挥手” 关闭连接,连接状态保存在双方的 TCP 协议栈中
可靠传输保证数据 “不丢失、不重复、无差错” 到达接收方1. 确认应答(ACK):接收方收到数据后回复确认;
2. 超时重传:发送方未按时收到 ACK 则重发
有序传输保证数据按发送顺序到达(TCP 是 “字节流协议”,需维护字节的顺序)1. 序号(Sequence Number):为每个字节分配唯一序号;
2. 确认号(Acknowledgment Number):告知发送方 “已收到到哪个序号的数据”
流量控制避免接收方因处理能力不足导致数据溢出(“接收方跟不上发送方”)滑动窗口(Sliding Window):接收方通过 TCP 头部的 “窗口大小” 字段,告知发送方可连续发送的字节数
拥塞控制避免网络因数据量过大导致拥堵(“发送方超过网络承载能力”)4 种算法:慢启动、拥塞避免、快重传、快恢复,动态调整发送方的 “拥塞窗口” 大小

(2)TCP 的关键工作流程:三次握手和四次挥手

在 TCP(传输控制协议)的通信流程中,

三次握手(Three-Way Handshake) 是建立可靠连接的核心过程

四次挥手(Four-Way Wavehand) 是关闭连接的标准流程。

两者均基于 “请求 - 应答” 机制,确保通信双方的状态同步,是 TCP 实现 “可靠、面向连接” 特性的关键。


1、三次握手:建立 TCP 连接

TCP 是面向连接的协议,通信前必须先建立双向连接(客户端和服务器都确认 “能发也能收”),三次握手的本质是 “双方交换初始序列号(ISN)并确认对方可达”。

核心背景
  • 通信双方:通常是发起连接的客户端(Client) 和等待连接的服务器(Server)
  • 关键标志位:TCP 头部中的 3 个控制位用于握手:
    • SYN(Synchronize):请求同步,用于发起连接、传递初始序列号。
    • ACK(Acknowledgment):确认应答,用于告知对方 “已收到你的数据”,附带确认号(收到数据的下一个期望序号)。
  • 初始序列号(ISN):TCP 为每个连接分配随机的初始序列号(避免历史数据干扰),后续数据的序号从 ISN 开始递增。
三次握手具体流程(步骤拆解)
步骤发起方接收方传递的关键信息(TCP 头部)核心目的
1客户端服务器SYN=1,ISN=C(客户端初始序号)客户端向服务器 “请求建立连接”,告知自己的初始序列号,此时客户端进入 SYN_SENT 状态。
2服务器客户端SYN=1,ACK=1,ISN=S(服务器初始序号),确认号 = C+1服务器 “应答客户端的连接请求”:
1. 用 SYN=1 告知客户端自己的初始序列号;
2. 用 ACK=1 和确认号 C+1 表示 “已收到客户端的 SYN 包”,此时服务器进入 SYN_RCVD 状态。
3客户端服务器ACK=1,确认号 = S+1客户端 “确认收到服务器的 SYN+ACK 包”,用确认号 S+1 告知服务器 “你的初始序号我已接收”,此时客户端进入 ESTABLISHED(连接建立)状态;服务器收到该 ACK 后,也进入 ESTABLISHED 状态,双方可开始传输数据。
为什么需要 “三次”?(而非两次)

核心是确保双方的 “发送能力” 和 “接收能力” 都正常

  • 若只做 “两次握手”(客户端发 SYN → 服务器发 SYN+ACK,连接直接建立),服务器无法确认 “客户端是否能收到自己的 SYN+ACK”—— 如果客户端的 ACK 丢失,服务器会误以为连接已建立,持续等待数据,造成资源浪费;
  • 第三次握手的 ACK,能让服务器明确 “客户端既能发(第一步 SYN),也能收(第二步 SYN+ACK)”,双方状态完全同步,避免无效连接。

2、四次挥手:关闭 TCP 连接

当通信结束后,双方需通过四次交互关闭连接(因 TCP 是全双工通信,双方需分别关闭 “发送通道” 和 “接收通道”)。

关键标志位

除了握手时的 SYN、ACK,关闭连接新增 1 个控制位:

  • FIN(Finish):表示 “我已无数据要发送,请求关闭我的发送通道”。
四次挥手具体流程(步骤拆解)

假设客户端先发起关闭请求(实际双方均可主动发起),流程如下:

步骤发起方接收方传递的关键信息(TCP 头部)核心目的
1客户端服务器FIN=1,ACK=1,确认号 = S_last+1(S_last 是服务器最后发的序号)客户端 “请求关闭自己的发送通道”:告知服务器 “我不再发数据了,但还能收你的数据”,此时客户端进入 FIN_WAIT_1 状态。
2服务器客户端ACK=1,确认号 = C_last+1(C_last 是客户端最后发的序号)服务器 “确认收到客户端的关闭请求”:告知客户端 “我知道你要关发送通道了,我会继续发剩余数据”,此时服务器进入 CLOSE_WAIT 状态;客户端收到 ACK 后,进入 FIN_WAIT_2 状态(等待服务器的剩余数据)。
3服务器客户端FIN=1,ACK=1,确认号 = C_last+1服务器 “发送完所有数据后,请求关闭自己的发送通道”:告知客户端 “我也没数据要发了,我的发送通道也关了”,此时服务器进入 LAST_ACK 状态(等待客户端的最终确认)。
4客户端服务器ACK=1,确认号 = S_last_new+1(S_last_new 是服务器 FIN 包的序号)客户端 “确认收到服务器的关闭请求”:告知服务器 “我知道你也关了发送通道,双方通道均关闭”,此时客户端进入 TIME_WAIT 状态;服务器收到 ACK 后,立即进入 CLOSED 状态(连接完全关闭)。
关键细节:TIME_WAIT 状态的作用

客户端发送第四次 ACK 后,不会立即关闭,而是进入 TIME_WAIT 状态(默认时长为 2 倍的 TCP 最大段生命周期,即 2MSL,通常约 1-4 分钟),目的是:

  1. 确保服务器能收到第四次 ACK:若第四次 ACK 丢失,服务器会重发 FIN 包,客户端在 TIME_WAIT 内可再次发送 ACK,避免服务器因未收到确认而持续等待;
  2. 避免历史连接干扰新连接:TIME_WAIT 期间,客户端会丢弃该连接端口上的所有旧数据报,防止旧连接的残留数据被新连接误接收。

3、三次握手 vs 四次挥手:核心差异

对比维度三次握手(建立连接)四次挥手(关闭连接)
核心目的建立双向连接,同步初始序列号关闭双向通道,释放资源
交互次数3 次4 次(因 FIN 和 ACK 需分开发送)
关键标志位主要用 SYN、ACK主要用 FIN、ACK
状态流转终点双方均进入 ESTABLISHED(连接就绪)双方均进入 CLOSED(连接释放)
是否有 TIME_WAIT

七、TCP 与 UDP 的核心区别

TCP 和 UDP 同属传输层协议,但设计目标完全不同,实际应用中需根据需求选择。两者的核心区别如下表:

对比维度TCP(传输控制协议)UDP(用户数据报协议)
连接类型面向连接(需三次握手建立连接)无连接(直接发送,无需建立连接)
传输可靠性可靠(不丢失、不重复、有序)不可靠(可能丢失、重复、无序)
传输方式字节流(无数据边界,需应用层自行处理)数据报(有明确边界,一次发送一个数据报)
流量控制 / 拥塞控制支持(滑动窗口 + 拥塞窗口)无拥塞控制(发送方无限制发送,易导致网络拥堵)
头部开销较大(固定 20 字节,可选扩展)较小(固定 8 字节)
适用场景需可靠传输的场景(HTTP/HTTPS、FTP、SSH)需低延迟 / 高并发的场景(DNS、直播、游戏)
  • sizeof(buf)

    • 运算符(不是函数),用于计算变量 / 类型所占用的内存字节数
    • 计算的是分配给变量的总内存空间,与内存中实际存储的数据无关。
  • strlen(buf)

    • 库函数(需要包含 <string.h>),用于计算字符串的长度
    • 计算的是从首地址开始到第一个 '\0'(字符串结束符)之间的字符数,不包含 '\0' 本身。

以复制一个图片为例:

 (1)server(服务端)

1.socket:创建套接字,打开网路设备

2.bind :给套接字,设置ip+port(地址+端口)

3.listen:(创建监听套接字)使套接字进入监听

4.accept:阻塞等待与接收客户端产生链接(三次握手)

5.recvform :接收客服端发送文件

6. sendto:送回客服端

服务端先运行的到accept位置等待接受请求,在接受到客户端请求后,运行后面的接收,发送

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<sys/socket.h>
#include<sys/types.h>
#include<unistd.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include<arpa/inet.h>
#include<fcntl.h>
typedef struct sockaddr * (SA);
int	main(int argc, char **argv)
{//监听套接字//AF_INET为互联网类型网络//SOCK_STREAM为tcp端口类型(可靠)int listfd=socket(AF_INET,SOCK_STREAM,0);if(-1==listfd){perror("socket error\n");return 1;}int fd_s=open("5.png", O_WRONLY | O_CREAT | O_TRUNC, 0666);if(-1==fd_s){perror("open error\n");return 1;}//man 7 ip 地址结构体struct sockaddr_in ser,cli;ser.sin_family=AF_INET;ser.sin_port=htons(50000);//ser.sin_addr.s_addr=inet_addr("127.0.0.1");ser.sin_addr.s_addr=INADDR_ANY;//创建ip和端口int ret=bind(listfd,(SA)&ser,sizeof(ser));if(-1==ret){perror("bind");return 1;}//第二个参数不是可以链接客户端的个数,而是三次握手一次能接纳客户端的排队个数listen(listfd,3);socklen_t len=sizeof(cli);//通信套接字conn;//accept阻塞等待接收客户端的链接//几个客户端就有几个套接字int conn=accept(listfd,(SA)&cli,&len);if(-1==conn){perror("accept");return 1;}char buf[1024]={0};while(1){//sizeof():buf占用内存字节int ret=recv(conn,buf,sizeof(buf),0);if(ret<=0){break;}write(fd_s,buf,ret);//sprintf(buf,"%s %s",buf);//strlen():字符串大小bzero(buf,sizeof(buf));send(conn,buf,ret,0);}close(listfd);close(conn);//system("pause");return 0;
}

(2)client(客户端)

1.socket,打开网络设备

2.connect:主动发起链接(三次握手)

3. sendto:向服务端发送信息

4.recvform:接受服务端传回的信息

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<sys/socket.h>
#include<sys/types.h>
#include<unistd.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include<arpa/inet.h>
#include<time.h>
#include<fcntl.h>
typedef struct sockaddr * (SA);
int	main(int argc, char **argv)
{int conn=socket(AF_INET,SOCK_STREAM,0);if(-1==conn){perror("socket");return 1;}struct sockaddr_in ser,cli;ser.sin_family=AF_INET;ser.sin_port=htons(50000);//ser.sin_addr.s_addr=inet_addr("127.0.0.1");ser.sin_addr.s_addr=INADDR_ANY;int ret=connect(conn,(SA)&ser,sizeof(ser));if(ret==-1){perror("connect error\n");return 1;}int fd=open("3.png",O_RDONLY);if(-1==fd){perror("open error\n");return 1;}ssize_t n;char buf[1024]={0};while((n=read(fd,buf,sizeof(buf)))>0){if(n<=0){break;}send(conn,buf,n,0);bzero(buf,sizeof(buf));//小阻塞recv(conn,buf,sizeof(buf),0);}printf("接收完成\n");close(conn);//system("pause");return 0;
}

程序:tcp 聊天。实时收发,#quit 双方都能退出。

服务端:

#include <arpa/inet.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
#include <pthread.h>
typedef struct sockaddr * (SA);
int listfd;
int conn;
struct sockaddr_in ser, cli;
socklen_t len =sizeof(cli);;
void *recv_thread(void *arg) 
{char buf[1024] = {0};while (1) {// 接收客户端消息ssize_t n = recvfrom(conn, buf, sizeof(buf), 0, (SA)&cli, &len);if (n <= 0) {continue;  // 接收失败则继续等待}// 检测退出指令if (strcmp(buf, "#quit") == 0) {printf("\n客户端已退出聊天\n");close(conn);exit(0);  // 退出整个程序}// 显示收到的消息printf("\nwjk:%s\n", buf);printf("xxy: ");  // 提示输入回复fflush(stdout);  // 刷新输出缓冲区,确保提示正常显示}
}
int main(int argc, char **argv)
{listfd = socket(AF_INET, SOCK_STREAM, 0);if (-1 == listfd){perror("socket");return 1;}// man 7 ipser.sin_family = AF_INET;// host to net shortser.sin_port = htons(50000);ser.sin_addr.s_addr=INADDR_ANY;int ret = bind(listfd,(SA) &ser, sizeof(ser));if (-1 == ret){perror("bind");return 1;}listen(listfd,3);socklen_t len = sizeof(cli);conn=accept(listfd,(SA)&cli,&len);if(-1==conn){perror("accept");return 1;}pthread_t tid;if (pthread_create(&tid, NULL, recv_thread, NULL) != 0) {perror("pthread_create failed");close(conn);return 1;}while (1){char buf[1024] = {0};printf("xxy:");fgets(buf, sizeof(buf), stdin);buf[strcspn(buf, "\n")] = '\0';sendto(conn,buf,strlen(buf),0,(SA)&cli,len);if (strcmp(buf, "#quit") == 0) {printf("已退出\n");close(conn);return 0;}}return 0;
}

客户端:

#include <arpa/inet.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
#include <pthread.h>
typedef struct sockaddr * (SA);
int conn;
struct sockaddr_in ser;
socklen_t len = sizeof(ser);
void *recv_thread(void *arg) 
{char buf[1024];while (1){bzero(buf, sizeof(buf));// 阻塞接收服务端消息(在独立线程中不影响主线程发送)recvfrom(conn, buf, sizeof(buf), 0, (SA)&ser, &len);// 检测服务端退出指令if (strcmp(buf, "#quit") == 0) {printf("\n服务端已退出聊天\n");close(conn);exit(0);  // 退出整个程序}// 显示收到的消息,并提示用户输入printf("\nxxy:%s\n", buf);printf("wjk: ");fflush(stdout);  // 刷新输出缓冲区,确保提示正常显示}
}
int main(int argc, char **argv)
{conn= socket(AF_INET, SOCK_STREAM, 0);if (-1 == conn){perror("socket");return 1;}pthread_t tid;if (pthread_create(&tid, NULL, recv_thread, NULL) != 0) {perror("pthread_create failed");close(conn);return 1;}// man 7 ipser.sin_family = AF_INET;// host to net shortser.sin_port = htons(50000);ser.sin_addr.s_addr=INADDR_ANY;int ret=connect(conn,(SA)&ser,sizeof(ser));if(ret==-1){perror("connect error\n");return 1;}while (1){char buf[1024]={0};printf("wjk:");fgets(buf, sizeof(buf), stdin);buf[strcspn(buf, "\n")] = '\0';sendto(conn,buf,strlen(buf),0,(SA)&ser,sizeof(ser));if (strcmp(buf, "#quit") == 0) {printf("已退出聊天\n");close(conn);return 0;}} close(conn);return 0;
}

①两端通信为服务端先运行,

②采用线程(pthread_create)的目的:同时处理接收消息和发送消息

③采用while(1)循环 目的:是为了实现可以持续接收和发送信息

总结:线程与循环的配合逻辑

  • 接收线程:用while(1)循环 “专职” 接收客户端消息,确保消息不被遗漏,且不阻塞主线程的输入操作。
  • 主线程:用while(1)循环 “专职” 处理用户输入和发送消息,确保可以连续发送多条消息。
http://www.xdnf.cn/news/19884.html

相关文章:

  • [特殊字符] 预告!我正在开发一款让自动化操作变得「像呼吸一样自然」的AI神器
  • 从静态到智能:用函数式接口替代传统工具类
  • 命令行小工具
  • Controller返回CompletableFuture到底是怎么样的
  • Ubuntu系统镜像源配置
  • 数据结构——树(03二叉树,与路径有关的问题,代码练习)
  • SPI片选踩坑实录(硬件片选和软件片选)
  • Base64编码的作用与应用场景
  • 利用 Java 爬虫获取淘宝商品 SKU 详细信息实战指南
  • 美团龙猫(longcat.AI)编写的利用二分查找优化Excel的sheet.xml指定范围输出C程序
  • 【数学建模学习笔记】时间序列分析:ARIMA
  • Scikit-learn从入门到实践:Scikit-learn入门-安装与基础操作
  • Qwen3-Reranker-0.6B 模型结构
  • Shell脚本一键监控平台到期时间并钉钉告警推送指定人
  • 自动化基本技术原理
  • 嵌入式解谜日志-网络编程
  • Kafka面试精讲 Day 5:Broker集群管理与协调机制
  • 基于SQLite的智能图片压缩存储系统:代码解析与实战应用
  • QuickUp-Ubuntu
  • FPGA AD7606串行驱动与并行驱动
  • 【Flask + Vue3 前后端分离管理系统】
  • 友思特案例 | 食品行业视觉检测案例集锦(三)
  • 利用 Python 获取微店商品关键词搜索 API 接口数据的实战指南
  • 利用飞算Java打造电商系统核心功能模块的设计与实现
  • 硬件开发(1)—单片机(1)
  • atomic常用类方法
  • VR智慧楼宇技术:打造智能办公空间的卓越方案​
  • 深圳外贸峰会究竟藏着啥秘密?能让外贸人收获满满?
  • RHEL9源码编译MySQL8.0.40
  • 图像加密安全传输--设备端视频流加密,手机端视频流解密,使用ChaCha20-Poly1305 进行系统分析