互联网协议按照功能不同分为osi七层或tcp/ip五层或tcp/ip四层
每层运行常见物理设备
为啥要分层?
网络通信的过程很复杂,为了降低复杂性
osi7层参考模型
osi
同层使用相同协议
底层为上层服务
应用层
表示层
会话层
负责建立和断开通信连接,以及数据的分割等数据传输相关的管理
传输层
确保数据能够可靠的传输到目标地址
作用:
- 建立端到端的连接,负责端到端的传输
- 传输层通过端口号区分上层服务
tcp协议
udp协议
网路层
寻ip地址和路由器的选择
作用:
1.为网络设备提供逻辑地址
2.负责将数据从源端发到目标端
3.负责数据传输的寻径和转发。
IP地址对应的是因特网(广域网)
这层设备:路由器
路由器:寻找最快最优的网略传输路径,将数据转发出去
数据链路层
将源计算机网络层来的数据可靠的传输到相邻节点的目标计算机的网络层
交换机通过管理Mac地址(网卡),Mac地址是硬件设备的唯一标识,有了Mac地址,你的这个设备才叫网络设备,才具备接收和发送数据的能力。
这层的设备:交换机
Mac地址对应的是以太网(局域网)
物理层
负责比特流和电子信号的转换
作用:产生并检测电压,发射和接收具有数据的电器信号
这层设备:双绞线,光纤
tcp/ip五层协议簇
我们主要研究tcpip5层协议。它把应用层、表示层、会话层都统称为应用层
互联网协议:定义计算机如何介入Internet,以及接入Internet的计算机的通信标准。互联网的本质就是一系列的协议。
数据的封装与解封装过程
物理层
物理层解决了如何在连接各种计算机的传输媒体上传输数据比特流.
物理层功能:定义了与传输媒体接口有关的一些特性.
- 机械特性.规定物理连接是所采用的规格.
- 电器特点:规定传输二进制时电压的传输范围.
- 功能特定
- 规程特性
数据链路层
数据链路层由来:单纯的0和1没有任何意义,必须规定电信号多少位一组,每组什么意思
数据链路层的传输单位: 帧
数据链路层的功能:
- 组帧.发送方把网络层的数据报添加帧首部和帧尾部形成帧,接收方根据帧的首部和尾部识别出帧的开始和结束
- 流量控制,当网络发成拥塞的时候,限制发送方的发送速度.
- 差错控制
- 链路管理,即连接的建立,维持和释放,主要面向tcp协议
以太网协议:早期的时候各个公司都有自己的分组方式,后来形成了统一的标准,即以太网协议ethernet
ethernet规定
- 一组电信号构成一个数据包,叫做‘帧’
- 每一数据帧分成:报头head和数据data两部分
head | data |
head包含:(固定18个字节)
- 发送者/源地址,6个字节
- 接收者/目标地址,6个字节
- 数据类型,6个字节
data包含:(最短46字节,最长1500字节)约1.5k
- 数据包的具体内容
head长度+data长度=最短64字节,最长1518字节,超过最大限制就分片发送
mac地址:
head中包含的源和目标地址由来:ethernet规定接入internet的设备都必须具备网卡,发送端和接收端的地址便是指网卡的地址,即mac地址。
mac地址:每块网卡出厂时都被烧制上一个世界唯一的mac地址,长度为48位2进制,通常由12位16进制数表示(前六位是厂商编号,后六位是流水线号)。
mac地址标识着目标计算机是局域网的哪台计算器
广播:
有了mac地址,同一网络内的两台主机就可以通信了(一台主机通过arp协议获取另外一台主机的mac地址),这种广播的形式只能在一个局域网内,出了局域网就不管用了。
ethernet采用最原始的方式,广播的方式进行通信,即计算机通信基本靠吼
工作在数据链路层的设备:
1.网卡 # 生成帧 2.交换机 #
网络层
网络层由来:有了ethernet、mac地址、广播的发送方式,世界上的计算机就可以彼此通信了,问题是世界范围的互联网是由一个个彼此隔离的小的局域网组成的,那么如果所有的通信都采用以太网的广播方式,那么一台机器发送的包全世界都会收到。
问题所在:必须找出一种方法来区分哪些计算机属于同一广播域,哪些不是,如果是就采用广播的方式发送,如果不是,就采用路由的方式(向不同广播域/子网分发数据包),mac地址是无法区分的,它只跟厂商有关。
网络层功能:
- 提供主机和主机之间的通信服务(完成点到点的服务)
- 路由选择与分组转发
- 异构网络的互联,比如手机网和电脑网
- 拥塞控制
网络层传输单元是数据报
IP协议:
- 规定网络地址的协议叫ip协议,它定义的地址称之为ip地址,广泛采用的v4版本即ipv4,它规定网络地址由32位2进制表示
- 范围0.0.0.0-255.255.255.255
- 一个ip地址通常写成四段十进制数,例:172.16.10.1
ip地址分成两部分 网络部分和主机部分
注意:单纯的ip地址段只是标识了ip地址的种类,从网络部分或主机部分都无法辨识一个ip所处的子网
例:172.16.10.1与172.16.10.2并不能确定二者处于同一子网
IP协议的作用主要有两个,一个是为每一台计算机分配IP地址,另一个是确定哪些地址在同一个子网络。
ARP协议
ARP协议即地址解析协议,是根据IP地址获取MAC地址的一个网络层协议。
请注意:arp协议是解决同一个局域网上的主机或路由器的ip地址和硬件地址的映射问题,在互联网中主要设计到ip协议和路由器不再使用mac地址
ARP(Address Resolution Protocol)是设计用于解决在局域网(LAN)中的 IP 地址到物理 MAC 地址的映射问题,以便在同一物理网络上进行设备间的通信。在非局域网环境中,例如广域网(WAN)或互联网,
通常不再使用 ARP 协议来执行这个任务。这是因为在这种环境下,设备分布在全球范围内,通信需要经过多个中间设备,而不再是在同一物理网络上进行。
在非局域网环境中,数据通常通过路由器和交换机等网络设备进行转发,而不是通过直接的广播机制,这与局域网中 ARP 的工作方式不同。以下是一些关于 ARP 在非局域网环境中的考虑: ARP 不在互联网中使用:互联网是一个全球范围的网络,设备分散在不同的地理位置。在互联网中,ARP 不再使用,而是使用更复杂的协议来实现路由、数据包交换和地址解析。 路由协议:在非局域网环境中,路由协议(如 BGP、OSPF、RIP)用于确定数据包的最佳路径,而不是 ARP。这些协议用于在全球范围内的路由器之间交换路由信息。 公共IP地址:在互联网中,设备通常使用公共IP地址,而不是在局域网中常见的私有IP地址(如 192.168.x.x 或 10.x.x.x)。这意味着每个设备都可以直接路由到全球范围内的其他设备,
而无需在本地网络中执行 ARP。 总之,ARP 主要用于局域网中,用于解析 IP 地址到 MAC 地址。在非局域网环境中,使用不同的协议和机制来进行路由、寻址和数据包交换,因此 ARP 不再是通信的核心组成部分。
通信过程如何:
当计算机在本地网络上需要确定目标IP地址对应的MAC地址时,它会首先检查自己的ARP缓存。如果缓存中有匹配的记录,它将使用缓存中的MAC地址。
如果ARP缓存中没有匹配的记录,计算机将广播一个ARP请求消息(ARP请求帧)到本地网络上的所有设备。该请求包含了它要解析的IP地址。
目标设备接收到ARP请求后,会检查请求中的IP地址是否与自己匹配。如果匹配,目标设备将在ARP响应消息(ARP响应帧)中提供自己的MAC地址。
发起ARP请求的计算机接收到ARP响应后,将缓存目标IP地址和对应的MAC地址,以便今后的通信。
arp协议由来:计算机通信基本靠吼,即广播的方式,所有上层的包到最后都要封装上以太网头,然后通过以太网协议发送,在谈及以太网协议时候,我门了解到
通信是基于mac的广播方式实现,计算机在发包时,获取自身的mac是容易的,如何获取目标主机的mac,就需要通过arp协议
arp协议功能:广播的方式发送数据包,获取目标主机的mac地址。
传输层
传输层的由来:网络层的ip帮我们区分子网,以太网层的mac帮我们找到主机,然后大家使用的都是应用程序,你的电脑上可能同时开启qq,暴风影音,等多个应用程序,
那么我们通过ip和mac找到了一台特定的主机,如何标识这台主机上的应用程序,答案就是端口,端口即应用程序与网卡关联的编号。
传输层是只有主机才有的层.
传输层功能:
- 提供进程和进程之间的逻辑通信.
- 提供复用和分用功能.复用指的是各个应用程序都可以通过传输层来到达网络层,分用指的是:传输层可以网络层传过来的不同的数据分发给对应的程序.
- 对应用层传过来的报文进行差错检测.
补充:端口范围0-65535,0-1023为系统占用端口
里边有:tcp协议
udp协议
应用层
应用层功能:规定应用程序的数据格式。
例:TCP协议可以为各种各样的程序传递数据,比如Email、WWW、FTP等等。那么,必须有不同协议规定电子邮件、网页、FTP数据的格式,这些应用程序协议就构成了”应用层”。
主要协议:http协议和ftp协议等
各个层的首部
首先应用层把数据传给传输层,传输层在首部加上源端口和目标端口,接着传给网络层,网络层在首部添加原地址和目标地址,数据链路层在首部添加源mac和目标Mac
数据 #应用层 port+数据=数据端 #传输层 ip+port+数据=数据包 (报文) # 网络层 Mac+ip+port+数据=数据帧(frame) # 数据链路层 交换机工作在这里,这里要添加FCS校验数据的完整性 交换机转发数据帧 # 数据链路层 路由器转发数据包 # 网络层
防火墙分为:应用层、传输层、网络层
各个层之间的作用
应用层:提供应用程序数据和管理
传输层:提供数据的端到端传输通信
网络层:地址管理和路由选择,提供主机到主机之间通信
数据链路层:负责设备之间的数据帧的传送和识别。
物理层:负责光/电信号的传递方式
socket
Socket(套接字)是计算机网络编程中的一种抽象,它提供了一种通用的编程接口,用于进行网络通信。Socket 允许应用程序通过网络发送和接收数据,实现不同计算机之间的通信。
套接字标识了一个主机和主机上的一个网络进程
socket=(主机IP地址:端口号)
而我们在python中的socket指的是套接字对象也就是一个接口
socket中文为“插座”,它本身不是一个协议而是一个套接字,socket套接字是操作系统为了方便大家使用tcp/ip协议而存在的一个接口,是在应用层和传输层之间的一个抽象层,它把TCP/IP层复杂的操作抽象为几个简单的接口供应用层调用以实现进程在网络中通信。
学习了很长时间我对套接字的概念很模糊,究竟什么是套接字呢?应用层通过传输层进行数据通信时,TCP和UDP会遇到同时为多个应用程序进程提供并发服务的问题。多个TCP连接或多个应用程序进程可能需要通过同一个TCP协议端口传输数据。为了区别不同的应用程序进程和连接,许多计算机操作系统为应用程序与TCP/IP协议交互提供了称为套接字 (Socket)的接口。
socket 和file 的区别:
file 是对某个件的打开-读写-关闭
socket 是对服务器和客户端进行打开读写关闭。
TCP协议
TCP(Transmission Control Protocol,传输控制协议)是面向连接的协议,也就是说,在收发数据前,必须和对方建立可靠的连接。一个TCP连接必须要经过三次“对话”才能建立起来,其中的过程非常复杂,只简单的描述下这三次对话的简单过程:主机A向主机B发出连接请求数据包:“我想给你发数据,可以吗?”,这是第一次对话;主机B向主机A发送同意连接和要求同步(同步就是两台主机一个在发送,一个在接收,协调工作)的数据包:“可以,你什么时候发?”,这是第二次对话;主机A再发出一个数据包确认主机B的要求同步:“我现在就发,你接着吧!”,这是第三次对话。三次“对话”的目的是使数据包的发送和接收同步,经过三次“对话”之后,主机A才向主机B正式发送数据
TCP的特点
1.tcp是面向连接的. 2.每一条tcp连接只有两个端点.端点就是套接字 3.tcp提供可靠交付 4.tcp是面向字节流 5.tcp提供全双工通信 ###具体见计算机网络课本
基于TCP协议的套接字
TCP编程的服务器端一般步骤是:
1、创建一个socket,用函数socket();
2、设置socket属性,用函数setsockopt(); * 可选
3、绑定IP地址、端口等信息到socket上,用函数bind();
4、开启监听,用函数listen();
5、接收客户端上来的连接,用函数accept();
6、收发数据,用函数send()和recv();
7、关闭网络连接;
8、关闭socket;
TCP编程的客户端一般步骤是:
1、创建一个socket,用函数socket();
2、设置socket属性,用函数setsockopt();* 可选 这步一般不写
3、绑定IP地址、端口等信息到socket上,用函数bind();* 可选 这步一般不写
4、设置要连接的对方的IP地址和端口等属性;
5、连接服务器,用函数connect();
6、收发数据,用函数send()和recv(),或者read()和write();
7、关闭socket;
范例一:
服务端:
import socket phone=socket.socket(socket.AF_INET,SOCKET.SOCK_STREAM) # socket.AF_INET指的是基于网络通信,socket.SOCK_STREAM指的是TCP协议 phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)#加上它可以重复使用一个端口 phone.bind(("192.168.21.37",8085)) #客户端的端口是随机的,服务端的端口是固定的,注意bind的里是一个元组,其中IP地址是一个字符串,端口范围是0-65535,其中端口1-1024被系统占用。 phone.listen(5) #写5,最多能挂起5个链接 print("starting....") while True:#链接循环 conn,addr=phone.accept()#accept接收客户端的链接和IP地址,得到的是一个元组。 print(conn) print(addr) while True: try: date=conn.recv(1024) #表示最大收多少个字节 conn.send(date.upper()) except ConnectionResetError: break conn.close() phone.close()
socket.SO_REUSEADDR
是一个套接字选项,用于允许在关闭套接字后立即重新绑定相同的地址和端口。这在网络编程中很有用,尤其是在服务器程序需要快速重启或迅速重新绑定端口时。
客户端:
import socket phone=socket.socket() # socket.AF_INET指的是基于网络通信,socket.STREAM值得是TCP协议 phone.connect(("192.168.21.37 ",8085)) #链接服务端IP地址,#端口范围0-65535 ,其中1-1024被系统占用了,不能用,注意这里是一个元组 while True: msg=input(">>>").strip() if not msg: continue phone.send(msg.encode("utf-8"))#必须要转化为二进制 date=phone.recv(1024) print(date.decode("utf-8")) phone.close()
客户端只有一个套接字,服务端有两个套接字
范例二
使用cmd命令操作系统
服务器端:
import subprocess import socket server=socket.socket(socket.AF_INET,socket.SOCK_STREAM) server.bind(("127.0.0.1",8081)) server.listen(5) print("starting.....") while True: conn,addr=server.accept() print(addr) while True: try: #加上异常处理的原因是如果客户端单方面的停止程序,服务器会捕获到,然后就从该循环中跳出来,避免一直等待接收数据 cmd=conn.recv(8096) if not cmd:break cmd=cmd.decode("utf-8")#把二进制解码为utf-8字符 obj=subprocess.Popen(cmd,shell=True, #这里得到一个对象 stdout=subprocess.PIPE, stderr=subprocess.PIPE) #如果把shell为True,指定的命令会在shell里执行,subprocess.popen返回的是一个对象 stdout=obj.stdout.read()#这里是正确的命令得到的bytes类型 stderr=obj.stderr.read()#错误的命令 conn.send(stdout+stderr) #发送bytes类型 except ConnectionResetError: break conn.close()
sever.close()
subprocess 模块 :在主进程下开启了一个子进程。这是一个强大的模块,它可以替代os模块,基本上包含了os模块,打开程序命令时,我们为什么不用os 模块中的system,而是用的是os.popen()? 原因在于system没有返回值,它直接把结果打印到屏幕上,我们无法获取,赋值也不行,只能得到0或1.,但是os。popen(),可以读出执行命令的内容。注意popen的到的是一个对象,对其进行读取使用read()。
客户端:
from socket import * #虽然这个方法,不推荐使用,但是在socket模块中可以使用这种方法 client=socket(AF_INET,SOCK_STREAM) client.connect(("127.0.0.1",8081)) while True: cmd=input(">>>:").strip() if not cmd: #避免发空字符,空字符操作系统不会执行 continue client.send(cmd.encode("utf-8")) data=client.recv(1024) print(data.decode("gbk")) #Windows系统下执行命令使用的是GBK编码方式 client.close()
粘包
# 问题现象 TCP粘包问题是在网络通信中常见的一个现象,它发生在数据被发送者拆分成多个数据包发送,而接收者在处理数据包时无法准确划分每个数据包的边界,导致数据包"粘"在一起。这可能会导致数据接收方无法
正确解析和处理数据包,从而引发通信问题。 # TCP粘包问题的主要原因包括: 1. 数据包大小不固定:TCP协议并不保证发送的数据包大小,因此数据包可以被分割成不同大小的块。 2. 操作系统缓冲:操作系统的TCP栈可能会对数据包进行缓冲,导致多个小数据包被合并成一个较大的数据包发送。 3. 网络拥塞:在拥塞的网络中,数据包可能会被延迟传输,这可能导致多个数据包在短时间内到达接收方,粘在一起。 #解决方法 1. 固定长度消息:发送方可以将每个数据包的长度固定为特定值,并在接收端按照固定长度截取数据包。这种方法适用于数据包大小稳定的情况。 2. 分隔符消息:发送方可以在数据包之间添加特定的分隔符,接收方根据分隔符来划分数据包。这种方法适用于数据包之间有固定的分隔符的情况。 3. 消息头包含长度信息:发送方在每个数据包的消息头中包含数据包的长度信息,接收方首先读取消息头以确定数据包的长度,然后读取相应长度的数据。这种方法适用于动态数据包大小的情况。 4. 使用应用层协议:在应用层实现协议来确保数据包的边界。例如,可以使用HTTP协议或自定义协议来确保数据包的分隔。
粘包问题:TCP粘包是指发送方发送的若干包数据到接收方接收时粘成一包,从接收缓冲区看,后一包数据的头紧接着前一包数据的尾,这个问题只会发生在TCP协议下。
为什么会发生粘包问题?
发送方原因:TCP协议使用nagle算法,会把数量较少并且发送时间间隔短的包合成一个包发送给客户端,客户端分辨不出来。
接收方原因: TCP 接收到数据时,并不会立即把数据传给物理层而是会在操作系统中缓存,如果TCP接受的速度大于应用程序读取的速度,那么多个包就会在操作系统中发生粘包现象。接收方不及时接收缓冲区的包,造成一次接收多个包。
分析的解决这个问题方法?
发送方:我们可以通过关闭Nagle算法来解决,使用TCP_NODELAY选项来关闭Nagle算法。这个我是从某个大神博客中看的,具体怎么操作不晓得。
接收方:我们只能在应用层解决问题。
解决方法就是循环处理,应用程序在处理从缓存读来的分组时,读完一条数据时,就应该循环读下一条数据,直到所有的数据都被处理,才能再处理下一个包。
针对这个问题,一般有2种解决方案:
(1)发送固定长度的消息
(2)把消息的尺寸与消息一块发送
服务端:
import subprocess import struct import json from socket import * server=socket(AF_INET,SOCK_STREAM) server.bind(('127.0.0.1',8081)) # print(server) server.listen(5) while True: conn,addr=server.accept() # print(conn) print(addr) while True: try: cmd=conn.recv(8096) if not cmd:break #针对linux #执行命令 cmd=cmd.decode('utf-8') #调用模块,执行命令,并且收集命令的执行结果,而不是打印 obj = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, ) stdout=obj.stdout.read() stderr=obj.stderr.read() # 1:先制作报头,报头里放:数据大小, md5, 文件 header_dic = { 'total_size':len(stdout)+len(stderr), 'md5': 'xxxxxxxxxxxxxxxxxxx', 'filename': 'xxxxx', 'xxxxx':'123123' } header_json = json.dumps(header_dic) header_bytes = header_json.encode('utf-8') header_size = struct.pack('i', len(header_bytes)) # 2: 先发报头的长度 conn.send(header_size) # 3:先发报头 conn.send(header_bytes) # 4:再发送真实数据 conn.send(stdout) conn.send(stderr) except ConnectionResetError: break conn.close() server.close()
struct 模块 一种机制将某些特定的结构体类型打包成二进制流的字符串然后再网络传输。按照给定的格式(fmt)把数据封装成二进制。
关于特定的fmt格式如下图所示:
struct模块有两种函数,struct.pack(),把任意数据类型转换为bytes。unpack 把bytes类型转换为任意数据类型。
客户端:
import struct import json from socket import * client=socket(AF_INET,SOCK_STREAM) client.connect(('127.0.0.1',8081)) while True: cmd=input('>>: ').strip() if not cmd:continue client.send(cmd.encode('utf-8')) # 1:先收报头长度 obj = client.recv(4) header_size = struct.unpack('i', obj)[0] # 2:先收报头,解出报头内容 header_bytes = client.recv(header_size) header_json = header_bytes.decode('utf-8') header_dic = json.loads(header_json) print(header_dic) total_size = header_dic['total_size'] # 3:循环收完整数据 recv_size=0 res=b'' while recv_size < total_size: recv_data=client.recv(1024) res+=recv_data recv_size+=len(recv_data) print(res.decode('gbk')) client.close()
tcp的连接建立和连接释放
三次握手
tcp建立连接的过程叫握手
1.客户端向服务端发送请求连接报文.第一次握手 2.服务端发送确认报文,第二次握手 3.客户端发送确认报文,并携带数据 .第三次握手.客户端发送确认报文的原因是防止已失效的连接请求报文再次传送到服务端,因而产生错误.
连接状态变化:
1.客户端向 服务器端 发送一个带有 SYN(同步)标志的数据包,表示客户端要建立连接。此时,客户端 进入 SYN_SENT 状态。 2.服务器端(S) 接收到客户端的请求后,回应一个带有 SYN/ACK 标志的数据包,表示同意建立连接。此时,服务器端(S) 进入 SYN_RECEIVED 状态。 3.客户端(C) 接收到服务器端的回应后,发送一个带有 ACK 标志的数据包,表示确认连接。此时,客户端(C) 和 服务器端(S) 都进入 ESTABLISHED 状态,连接建立成功。
四次挥手
1.客户端向服务端发送连接释放报文.第一次挥手 2.服务端向客户端发送确认报文,第二次挥手. 3.服务端向客户端发送连接释放报文,第三次挥手 4.客户端发送确认报文,第四次挥手.
连接变化:
1. 客户端向 服务器端发送一个带有 FIN(结束)标志的数据包,表示要断开连接。此时,客户端进入 FIN_WAIT_1 状态。 2. 服务器端接收到客户端的断开请求,回应一个带有 ACK 标志的数据包,表示确认接受断开请求。此时,服务器端进入 CLOSE_WAIT 状态。 3. 客户端收到确认后进入FIN_WAIT_2状态 4. 服务器端在准备好断开连接后,向 客户端发送一个带有 FIN 标志的数据包,表示服务器端也准备断开连接。此时,服务器端进入 LAST_ACK 状态。 5. 客户端接收到服务器端的断开请求,回应一个带有 ACK 标志的数据包,表示确认接受断开请求。此时,客户端 进入 TIME_WAIT 状态。 6.服务器端接收到客户端的确认后,客户端和 服务器端都进入 CLOSED 状态,连接完全终止。
连接状态解释:
LISTEN:服务端在等待客户端连接时的状态。 SYN-SENT:客户端发送连接请求后等待确认的状态。 SYN-RECEIVED:服务端接收到客户端连接请求后等待确认的状态。 ESTABLISHED:连接已经建立,双方可以相互传输数据的状态。 FIN-WAIT-1:连接被一方关闭,等待对方的确认。 FIN-WAIT-2:连接被一方关闭,等待对方的关闭请求。 CLOSE-WAIT:对方已经关闭连接,等待本地应用程序关闭连接。 CLOSING:双方都发送关闭请求,但仍在等待确认。 LAST-ACK:连接的一方发送关闭请求,等待对方的确认。 TIME-WAIT:连接已经关闭,但仍在等待一段时间以确保网络中的数据包都得以处理。 CLOSED:连接已经彻底关闭,不再处于活动状态。
连接状态变化的例子:
1.不管是服务端还是客户端一方调用了close函数,另一方没有调用close函数,调用的那方连接状态会立即释放掉,没调的那方状态为close_wati等待几分钟后会变成closed
UDP协议
UDP(User Data Protocol,用户数据报协议)
(1) UDP是一个非连接的协议,传输数据之前源端和终端不建立连接,当它想传送时就简单地去抓取来自应用程序的数据,并尽可能快地把它扔到网络上。在发送端,UDP传送数据的速度仅仅是受应用程序生成数据的速度、计算机的能力和传输带宽的限制;在接收端,UDP把每个消息段放在队列中,应用程序每次从队列中读一个消息段。
(2) 由于传输数据不建立连接,因此也就不需要维护连接状态,包括收发状态等,因此一台服务机可同时向多个客户机传输相同的消息。
(3) UDP信息包的标题很短,只有8个字节,相对于TCP的20个字节信息包的额外开销很小。
(4) 吞吐量不受拥挤控制算法的调节,只受应用软件生成数据的速率、传输带宽、源端和终端主机性能的限制。
(5)UDP使用尽最大努力交付,即不保证可靠交付,因此主机不需要维持复杂的链接状态表(这里面有许多参数)。
(6)UDP是面向报文的。发送方的UDP对应用程序交下来的报文,在添加首部后就向下交付给IP层。既不拆分,也不合并,而是保留这些报文的边界,因此,应用程序需要选择合适的报文大小。
UDP协议称为数据报协议SOCK_DGGAM.,
udp的特点
1UDP是无连接的,即传输数据前不需要建立连接 2.udp提供尽最大努力交付 3.udp是面向报文的,而tcp是面向字节流的. 4.udp提供1对1,一对多,多对多通信.由于传输数据不建立连接,因此也就不需要维护连接状态,包括收发状态等,因此一台服务机可同时向多个客户机传输相同的消息。 5.udp首部比tcp短,udp8个字节,tcp20个 6.udp没有拥塞控制
为什么基于UDP协议的套接字没有粘包现象?
什么是保护消息边界和流呢?
保护消息边界,就是指传输协议把数据当作一条独立的消息在网上传输,接收端只能接收独立的消息.也就是说存在保护消息边界,接收 端一次只能接收发送端发出的一个数据包。
TCP是个"流"协议,所谓流,就是没有界限的一串数据.大家可以想想河里的流水,是连成一片的,其间是没有分界线的.
对于UDP来说就不存在拆包的问题,因为UDP是个"数据包"协议,也就是两段数据间是有界限的,在接收端要么接收不到数据要么就是接收一个完整的一段数据。
不会少接收也不会多接收.
基于UDP协议的套接字
由于udp协议是无连接的协议,因为不使用listen和recv系统调用
与之对应的UDP编程步骤要简单许多,分别如下:
UDP编程的服务器端一般步骤是:
1、创建一个socket,用函数socket(AF_INET,SOCK_DGRAM);
2、设置socket属性,用函数setsockopt();* 可选
3、绑定IP地址、端口等信息到socket上,用函数bind();
4、循环接收,发送数据,用函数recvfrom(),sendto();
5、关闭socket连接;
UDP编程的客户端一般步骤是:
1、创建一个socket,用函数socket(AF_INET,SOCK_DGRAM);
2、循环接收,发送数据,用函数recvfrom(),sendto()注意括号中写上发送的bytes类型的内容和一个包括服务器端的IP和端口;
3、关闭socket;
服务端:
from socket import * server=socket(AF_INET,SOCK_DGRAM) #注意基于UDP协议的套接字,是SOCK_DGRAM server.bind(("127.0.0.1",8080)) #需要绑定IP地址 while True: data, client_addr = server.recvfrom(1024) print(data.decode("utf-8"),client_addr) server.sendto(data.upper(),client_addr) server.close()
客户端:
from socket import * client=socket(AF_INET,SOCK_DGRAM) while True: msg=input(">>>").strip() client.sendto(msg.encode("utf-8"),("127.0.0.1",8080)) data,addr=client.recvfrom(1024) print(data.decode("utf-8")) print(addr) client.close()