1.TCP协议与UDP协议
TCP(Transmission Control Protocol,传输控制协议)和 UDP(User Datagram Protocol,用户数据报协议)是 TCP/IP 协议族中两种核心的传输层协议,它们在数据传输方式、可靠性、适用场景等方面有显著区别。
1、核心区别对比
特性 | TCP | UDP |
---|---|---|
连接方式 | 面向连接(需先建立连接) | 无连接(直接发送数据) |
可靠性 | 可靠传输(保证数据完整、有序到达) | 不可靠传输(不保证送达,可能丢失) |
传输效率 | 较低(需处理确认、重传等机制) | 较高(无额外控制开销) |
数据边界 | 无(流式传输,需应用层处理边界) | 有(以数据报为单位,一次发送一个完整报文) |
拥塞控制 | 支持(避免网络拥堵) | 不支持 |
首部开销 | 较大(固定 20 字节,可扩展) | 较小(固定 8 字节) |
适用场景 | 文件传输、网页浏览、邮件等 | 视频通话、实时游戏、广播等 |
2、TCP 协议详解
1. 核心特点:面向连接、可靠传输
连接建立:通过 “三次握手” 建立连接(类似打电话确认双方可通信)
- 客户端发送 SYN(请求连接)
- 服务器回复 SYN+ACK(同意连接)
- 客户端回复 ACK(确认连接建立)
可靠传输机制:
- 序号与确认机制:每个数据包有编号,接收方收到后需返回确认(ACK),未确认则重传
- 重传机制:发送方超时未收到确认则重传数据
- 流量控制:通过滑动窗口限制发送速率,避免接收方缓冲区溢出
- 拥塞控制:根据网络状况动态调整发送速率(慢启动、拥塞避免等算法)
连接终止:通过 “四次挥手” 关闭连接(确保双方数据都已传输完毕)
2. 工作流程示例(文件传输)
- 客户端向服务器发起连接请求(三次握手)
- 连接建立后,客户端分块发送文件数据
- 服务器每收到一块数据就返回确认
- 若某块数据丢失,客户端超时后重传
- 所有数据传输完成,双方通过四次挥手关闭连接
3. 优缺点
- 优点:数据可靠、有序,适合对准确性要求高的场景
- 缺点:延迟较高(握手、确认等耗时),开销大
3、UDP 协议详解
1. 核心特点:无连接、不可靠传输
- 无连接:发送数据前无需建立连接,直接封装数据并发送(类似邮寄信件)
- 不可靠:
- 不保证数据送达(无确认机制)
- 不保证顺序(数据包可能乱序到达)
- 不处理重传(丢失数据需应用层自行处理)
- 数据报传输:每个 UDP 报文独立发送,接收方一次读取一个完整报文
2. 工作流程示例(视频通话)
- 客户端直接向服务器发送视频帧数据(无需提前建立连接)
- 服务器收到数据后直接交给应用层处理
- 若部分数据包丢失,应用层可选择忽略(避免卡顿)或请求重传关键帧
- 通信结束无需 “关闭连接”,直接停止发送即可
3. 优缺点
- 优点:传输速度快、延迟低、开销小,适合实时性要求高的场景
- 缺点:数据可能丢失、乱序,需应用层自行处理可靠性
4、适用场景对比
协议 | 典型应用 | 选择原因 |
---|---|---|
TCP | 网页(HTTP/HTTPS)、文件传输(FTP)、邮件(SMTP) | 需保证数据完整、准确,允许一定延迟 |
UDP | 视频通话(如 Zoom)、实时游戏、DNS 查询、广播 | 需低延迟,可容忍少量数据丢失 |
总结:TCP就像打电话,需要先接通,建立联系,确保双方能同时接收或发送数据,而UDP更像送快递,虽然速度快,但是会有数据丢失的风险.
2.IP地址
1.IP地址的概念
IP 地址(Internet Protocol Address,互联网协议地址)是互联网中用于唯一标识网络设备(如计算机、路由器、服务器、手机等)的数字标签,相当于设备在网络中的 “门牌号码”。所有网络通信(如浏览网页、发送消息、下载文件)都依赖 IP 地址来定位目标设备,确保数据能准确从 “发送方” 传输到 “接收方”。
一、核心作用:定位与路由
IP 地址的核心功能可拆解为两点:
- 设备标识:在全球或局部网络中,每个设备的 IP 地址具有唯一性(同一网络内绝对唯一,全球范围内通过路由规则确保不冲突),避免数据传错目标。
- 路由指引:数据在网络中传输时,路由器会根据 IP 地址中的 “网络段” 信息,判断数据应转发到哪个子网,最终导向目标设备(类似快递根据 “省 - 市 - 区” 分层投递)。
二、IP 地址的两大版本:IPv4 与 IPv6
目前主流的 IP 地址分为两个版本,核心差异在于地址长度、容量和格式,本质是为解决 “地址耗尽” 问题(IPv4 地址不够用)。
对比维度 IPv4(互联网协议第 4 版) IPv6(互联网协议第 6 版) 地址长度 32 位(二进制) 128 位(二进制) 地址格式 点分十进制(如 192.168.1.1
)冒分十六进制(如 2001:0db8:85a3:0000:0000:8a2e:0370:7334
)地址容量 约 42.9 亿个(2³²),已基本耗尽 约 3.4×10³⁸个(2¹²⁸),足够支撑未来百年 配置方式 需手动配置或 DHCP 自动分配 支持自动配置(无状态地址自动配置 SLAAC) 安全性 无内置安全机制,需依赖 TCP 或应用层(如 SSL) 内置 IPsec 协议,原生支持加密和身份验证 主流程度 目前仍广泛使用(占比超 90%) 逐步推广(物联网、5G 场景优先部署) 三、IPv4 地址的结构与分类(重点)
IPv4 是目前最常用的版本,其 32 位地址分为 “网络位” 和 “主机位” 两部分:
- 网络位:标识设备所属的 “子网”(类似 “小区地址”);
- 主机位:标识子网内的具体设备(类似 “小区内的门牌号”)。
为区分网网络位,后 8 位是主机位)。
1. IPv4 地址的分类(传统分类,便于初期管理)
IPv4 根据首段数字(第 1 个十进制数)分为 5 类,其中 A、B、C 类为 “单播地址”(用于单个设备通信络位和主机位,IPv4 通过子网掩码(Subnet Mask) 标记(如
255.255.255.0
表示前 24 位是),D 类为 “组播地址”(一对多通信,如视频会议),E 类为 “保留地址”(未公开使用)。
地址类别 首段数字范围 网络位长度 主机位长度 适用场景 示例 A 类 1~126 8 位 24 位 大型网络(如早期互联网主干) 10.0.0.1
(私有地址)B 类 128~191 16 位 16 位 中型网络(如企业内网) 172.16.0.1
(私有)C 类 192~223 24 位 8 位 小型网络(如家庭 / 办公室) 192.168.1.1
(私有)D 类 224~239 -(无分类) - 组播通信 224.0.0.1
(所有主机)E 类 240~255 - - 科研保留 无公开示例 2. 特殊 IPv4 地址(需重点记忆)
- 回环地址(Loopback Address):
127.0.0.1
~127.255.255.254
,用于设备 “自身测试”(如浏览器访问127.0.0.1
可测试本地服务器是否正常),数据不会发送到外部网络。- 私有地址(Private Address):仅用于内网通信,无法直接访问互联网(需通过路由器的 NAT 技术转换为公网 IP),避免公网地址浪费。常见私有地址段:
- 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.1
)- 广播地址(Broadcast Address):子网内 “所有设备” 的地址(如子网
192.168.1.0/24
的广播地址是192.168.1.255
),发送到该地址的数据会被子网内所有设备接收。四、IP 地址的获取方式
设备不会天生拥有 IP 地址,需通过以下方式获取:
- 静态配置(Static IP):手动在设备(如服务器、打印机)上设置 IP 地址、子网掩码、网关等参数,适合需要固定地址的场景(如网站服务器,确保用户能稳定访问)。
- 动态配置(Dynamic IP):通过DHCP 协议(动态主机配置协议) 自动获取地址,由路由器或 DHCP 服务器分配临时 IP(租期通常几小时到几天),到期后可重新分配。家庭手机、电脑等设备默认使用此方式,无需手动设置。
五、公网 IP 与内网 IP 的区别
这是实际使用中最易混淆的概念,核心差异在于 “是否能被互联网直接访问”:
- 公网 IP:由运营商(如联通、电信)分配的 “全球唯一地址”,可直接被互联网上的其他设备访问(如网站服务器的 IP
202.108.22.5
,任何人都能通过该地址访问百度)。- 内网 IP:仅用于局域网(如家庭 WiFi、公司内网)的私有地址(如
192.168.1.100
),无法被互联网直接访问。内网设备要访问互联网,需通过路由器的NAT 转换(将内网 IP 转换为公网 IP),类似 “小区所有住户共用一个大门牌号”。
总结:
3. socket套接字
在网络编程中,socket()
函数是创建套接字(Socket)的核心接口,用于初始化一个网络通信端点.
1. socket () 函数的核心作用
socket()
函数用于创建一个套接字对象,该对象是后续网络通信(如连接、发送、接收数据)的基础。调用后会返回一个套接字描述符(或对象引用),后续操作都通过这个描述符进行。2. socket()函数的四个参数
import socket
# socket.socket 一共四个参数:
# 第一个参数:地址族,AF_INET表示使用IPv4协议,AF_INET6表示使用IPv6协议
# 第二个参数:套接字类型,SOCK_STREAM表示流式套接字,SOCK_DGRAM表示数据报式套接字
# 第三个参数:协议,一般不用设置,默认为0。也可以指定
# 第四个参数:本地地址,一般不用设置,默认为None。
# 第一个参数
# AF_UNIX(Adress Family,即地址族 unix)
# AF_INET(Adress Family,即地址族 internet)
# AF_INET6 用于 IPv6 地址(未来也许会用到)
# AF_BLUETOOTH 用于蓝牙地址
# AF_CAN 用于 CAN 总线地址
# 第二个参数
# SOCK_STREAM(Socket Type,即套接字类型,流式套接字)
# SOCK_DGRAM(Socket Type,即套接字类型,数据报式套接字)
# 第三个参数(2种示例)
# TCP(Transmission Control Protocol,即传输控制协议)
# SOCK_STREAM(Socket Type,即套接字类型,流式套接字)
# 协议:TCP
_tcp = socket.socket(socket.AF_INET, socket.SOCK_STREAM,socket.IPPROTO_TCP)
# UDP(User Datagram Protocol,即用户数据报协议)
# SOCK_DGRAM(Socket Type,即套接字类型,数据报式套接字)
# 协议:UDP
_udp = socket.socket(socket.AF_INET, socket.SOCK_DGRAM,socket.IPPROTO_UDP)
4. TCP通信
1.TCP单向通信(一个发一个收)
1.TCP服务器代码
import socketif __name__ =='__main__':server_socket =socket.socket(socket.AF_INET,socket.SOCK_STREAM)host ='127.0.0.1'port =8888server_socket.bind((host,port))server_socket.listen(5)client_socket,arrr =server_socket.accept()while True:data = client_socket.recv(1024).decode('utf-8')print(f"Receive from client: {data}")if data =="exit":message =="exit"client_socket.send(message.encode('utf-8'))breakelif data =="?":message ="你在?什么"client_socket.send(message.encode('utf-8'))else:client_socket.send('0'.encode('utf-8'))client_socket.close()server_socket.close()print("Server has stopped")
2. TCP客户端代码
import socketif __name__ =='__main__':client_socket =socket.socket(socket.AF_INET,socket.SOCK_STREAM)host ="127.0.0.1"port =8888client_socket.connect((host,port))while True:message =input("请输入:")client_socket.sendall(message.encode('utf-8'))mag =client_socket.recv(1024)if mag != b'0':print('对方发送的信息:',mag.decode('utf-8'))if message=="exit":breakclient_socket.close()
代码解析:
我们这里需要开两个不同的终端,保证两个终端能够建立起链接,这里两个终端各自有自己的任务
- 服务器:启动后监听本地 8888 端口,等待客户端连接。连接建立后,接收客户端发送的消息,并根据消息内容做出响应:
- 收到 "exit" 时,断开连接并停止服务
- 收到 "?" 时,回复 "你在?什么"
- 收到其他消息时,回复 "0"
- 客户端:连接到服务器后,可循环输入消息发送给服务器,同时接收服务器的响应,输入 "exit" 时断开连接
2.TCP双向通信(可以互相发送和接收)
注意:如果要实现双向通信是需要用到多线程的
1.TCP服务端代码
'''
1, 并发通信,实现多个客户端连接服务器
2, 实现全双工通信,服务端与客户端可以同时接收发数据
'''
import os
import signal
import socket
import threadingdef handle_signal(signum, frame):print('received signal {}'.format(signum))exit(0)#接收线程
def recv_data(socket):while True:recv_buffer = socket.recv(1024).decode('utf-8')if not recv_buffer:print('client is quit!')print(f'recv data from client: {recv_buffer}')if recv_buffer == 'exit':print('received exit from client')break#发送线程
def send_data(socket):while True:message = input('please input your message:')socket.send(message.encode('utf-8'))if message == 'exit':os.kill(os.getpid(), signal.SIGTERM)if __name__ == '__main__':#1, 创建socketserver_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)#2, 绑定端口host = socket.gethostname()port = 5002server_socket.bind((host, port))#3, 监听server_socket.listen(5)print('server is starting....., waiting for a connection')#4, 接收客户端连接client_socket, addr = server_socket.accept()print('Got connection from', addr)#4, 创建2个线程实现与客户端通信recv_thread = threading.Thread(target=recv_data,args=(client_socket,))send_thread = threading.Thread(target=send_data,args=(client_socket,))recv_thread.start()send_thread.start()# 4, 注册一个信号signal.signal(signal.SIGTERM, handle_signal)#5, 等待线程结束recv_thread.join()send_thread.join()#6, 关闭套接字server_socket.close()client_socket.close()
2.TCP客户端代码
'''
客户端
'''
import os
import socket
import threading
import signal#1, 接收线程
def recv_data(socket):while True:recv_buffer = socket.recv(1024).decode('utf-8')if not recv_buffer:print('server is quit!')breakprint(f'recv data from server {recv_buffer}')if recv_buffer == 'exit':print('received exit from server')# 终止进程os.kill(os.getpid(), signal.SIGTERM)#2, 发送线程
def send_data(socket):global recv_thread_flagwhile True:send_buffer = input('please input your data: ')socket.send(send_buffer.encode('utf-8'))if send_buffer == 'exit':#终止进程os.kill(os.getpid(), signal.SIGTERM)def handle_signal(signum, frame):print('received signal {}'.format(signum))exit(0)if __name__ == '__main__':#1, 创建socketclient_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)#2, 建立连接host = '127.0.0.1'port = 5002client_socket.connect((host, port))#3, 创建2个线程实现与server端通信recv_thread = threading.Thread(target=recv_data,args=(client_socket,))send_thread = threading.Thread(target=send_data,args=(client_socket,))#4, 注册一个信号signal.signal(signal.SIGTERM,handle_signal)#4, 等待线程结束recv_thread.start()send_thread.start()#5, 关闭socketrecv_thread.join()send_thread.join()#6, 关闭套接字client_socket.close()
代码解析:
我们这里需要开两个不同的终端,保证两个终端能够建立起链接,这里两个终端各自有自己的任务,与上面相比就是多了线程的部分,然后要实现不同设备之间的通信也是如此,还需要修改IP地址即可建立连接,如果使用公网的情况下,可能会造成无法连接的情况,这个时候我们可以让两个设备链接到同一个手机热点上,再绑定IP地址.
- 客户端:通过两个线程分别处理接收和发送数据,实现同时收发(全双工),支持输入消息发送给服务器,也能实时接收服务器消息,输入 "exit" 可退出。
- 服务器:能监听并接受多个客户端连接(通过循环接受连接实现),对每个连接也用两个线程处理收发数据,实现与客户端的全双工通信。
总结:如果是单方面传送数据的话只需要socket一个就行,一个负责发送,一个负责接收,但是在这个建立连接的过程,需要经过三次握手建立连接,否则我们不能知道是否已经连接上了,然后如果是双向传输接收数据的话,就需要多加一个多线程thread,在每个里面加入一个发送线程一个接收线程