python网络编程 - udp
UDP简介
用户数据报协议,是一个无连接的面向数据报的运输层协议。
UDP不提供可靠性,它只是把应用程序传给IP层的数据报发出去,但是并不能保证他们能到达目的地。
由于UDP在传输数据报前不用在客户和服务器之间建立一个连接,且没有超时重发等机制,故而传输速度很快。
UDP是一种面向无连接的协议,每个数据报都是一个独立的信息,包括完整的源地址或目的地址,它在网络上以任何可能的路径传往目的地,因此能否到达目的地,到达目的地的时间以及内容的正确性都是不能被保证的。
UDP可以理解为写信,TCP可以理解为打电话。
UDP在互联网中的丢包率几乎为0,在互联网中(美国到中国),丢包率也很低,但是始终还是存在丢包的可能
UDP特点
UDP是面向无连接的通讯协议
UDP数据包括目的端口号和源端口号信息
通讯不需要建立连接,所以可以实现广播发送
UDP传输数据有大小限制,每个被传输的数据报必须限定在64KB之内
UDP是一个不可靠的协议,发送方发送的数据报并不一定以相同的次序到达接收方
适用场景
多点通信和实时性要求高的场景
UDP是面向消息的协议,通信时不需要建立连接,数据的传输自然是不可靠的,UDP一般用于多点通信和实时的数据业务员,如:
语音广播、视频、QQ、TFTP(简单文件传送)、SNMP(简单网络管理协议)、RIP(路由信息协议,如报告股票市场,航空信息)、DNS(域名解释)
注重速度流畅的场景
UDP操作简单,而且仅需要较少的监护,因此通常用于局域网高可靠性的分散系统中的client/server应用程序。如:视频会议系统,并不要求音频视频数据绝对的正确,只要保证连贯性就可以了,这种情况下显然使用UDP会更合理。
UDP的Python程序
发送/接收数据
流程
创建一个UDP的程序的流程很简单,如下:
1、创建客户端套接字
2、发送/接收数据
3、关闭套接字
原理
UDP绑定信息
为什么要绑定信息
一般情况下,在一台电脑上运行的网络程序很多,而各自用的端口号很多情况下是不知道的,为了不与其他的网络程序占用同一个端口号,往往在代码中,udp的端口号是不绑定的。
对于发送方(客户端程序),端口号随机分配问题不大,但是对于接收方(服务器)来说,最好绑定一个固定的端口,那么别人往服务器发送数据的时候,只需要往约定好的端口发送数据即可。想想如果服务器的端口号是任意的,那么每次往服务器发送数据的时候还需要确定端口号,但是服务器的端口号是随机的,如何确定?!
如果是一个服务器端的程序的话,就需要绑定端口号了。想想如果报警电话每天都在变,想必世界就会乱了,所以一般服务性的程序,往往需要一个固定的端口号,这就是所谓的端口绑定。
报警电话为110,只能定死,而拨打110的号码,可以是手机,可以是别人的手机,可以是座机,可以是公用电话等等。可见绑定端口,一般是接收方(服务器)才需要绑定端口,而发送方是不需要绑定端口。
几个概念
听:接收
说:发送
单工:如收音机,只能听,不能说话
半双工:如对讲机,当某个人在说话时,你不能说话,当没人说话时,你可说话
全双工:如电话,可以同时说话。socket就是全双工的,在接收数据的同时也可以发送数据
python代码实现
服务端
# coding:utf-8 import socket import config def main(): # 创建udp的套接字 sk = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # 重用ip和port,防止报错 sk.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # 绑定ip和port sk.bind((config.host, config.port)) if config.connected: # 要建立连接 nsk, addr = sk.accept() # 通过新的socket去接收数据 data = nsk.recv(1024) else: # 不用建立连接,只有udp才能这么写,tcp是必须先建立连接的 data, addr = sk.recvfrom(1024) print(data) if __name__ == '__main__': main()
客户端
# coding:utf-8 import socket import config def main(): # 创建udp的套接字 sk = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # 地址 addr = (config.host, config.port) # 要发送的内容 send_data = b"hello" if config.connected: # 要建立连接 sk.connect(addr) sk.send(send_data) else: # 不用建立连接 sk.sendto(send_data, addr) if __name__ == '__main__': main()