2 - UDP
UDP介绍
UDP --- 用户数据报协议(User Datagram Protocol),是一个无连接的简单的面向数据报的运输层协议。UDP不提供可靠性,它只是把应用程序传给IP层的数据报发送出去,但是并不能保证它们能到达目的地。由于UDP在传输数据报前不用在客户和服务器之间建立一个连接,且没有超时重发等机制,故而传输速度很快。
UDP是一种面向无连接的协议,每个数据报都是一个独立的信息,包括完整的源地址或目的地址,它在网络上以任何可能的路径传往目的地,因此能否到达目的地,到达目的地的时间以及内容的正确性都是不能被保证的。
udp通信模型中,在通信开始之前,不需要建立相关的链接,只需要发送数据即可,类似于生活中,"写信""
UDP特点:
UDP是面向无连接的通讯协议,UDP数据包括目的端口号和源端口号信息,由于通讯不需要连接,所以可以实现广播发送。 UDP传输数据时有大小限制,每个被传输的数据报必须限定在64KB之内。 UDP是一个不可靠的协议,发送方所发送的数据报并不一定以相同的次序到达接收方。
【适用情况】
UDP是面向消息的协议,通信时不需要建立连接,数据的传输自然是不可靠的,UDP一般用于多点通信和实时的数据业务,比如
- 语音广播
- 视频
- TFTP(简单文件传送)
- SNMP(简单网络管理协议)
- RIP(路由信息协议,如报告股票市场,航空信息)
- DNS(域名解释)
注重速度流畅
UDP操作简单,而且仅需要较少的监护,因此通常用于局域网高可靠性的分散系统中client/server应用程序。例如视频会议系统,并不要求音频视频数据绝对的正确,只要保证连贯性就可以了,这种情况下显然使用UDP会更合理一些。
socket简介
1. 什么是socket
我们已经了解了计算机网络通讯的过程,但是我们如何用程序来实现这个过程呢?实际上对于传输层及其一下的网络层的实现,操作系统的内核(即操作系统的最核心功能程序)已经帮我们实现了这些机制流程,并且封装成了一套工具供我们使用,这套工具我们称之为socket(套接字)。socket的中文意思是接插件。
2. 创建socket
在 Python 中 使用socket 模块的类 socket 就可以完成:
socket.socket(AddressFamily, Type)
说明:
函数 socket.socket 创建一个 socket,返回该 socket 的描述符,该函数带有两个参数:
- Address Family:可以选择 AF_INET(用于 Internet 进程间通信) 或者 AF_UNIX(用于同一台机器进程间通信),实际工作中常用AF_INET
- Type:套接字类型,可以是 SOCK_STREAM(流式套接字,主要用于 TCP 协议)或者 SOCK_DGRAM(数据报套接字,主要用于 UDP 协议)
创建一个tcp socket(tcp套接字)
import socket s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) print 'Socket Created'
创建一个udp socket(udp套接字)
import socket s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) print 'Socket Created'
3. 标识网络通信的参数
首要解决的问题是如何唯一标识一个进程,否则通信无从谈起!
在本地可以通过进程PID来唯一标识一个进程,但是在网络中这是行不通的。
其实TCP/IP协议族已经帮我们解决了这个问题,网络层的“ip地址”可以唯一标识网络中的主机,而传输层的“协议+端口”可以唯一标识主机中的应用程序(进程)。
这样利用ip地址,协议,端口
就可以标识网络的进程了,网络中的进程通信就可以利用这个标志与其它进程进行交互。
udp程序流程
udp服务端
import socket # 1. 创建套接字 server_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) #2. bind绑定ip地址和端口,为元祖tuple类型 # ip如果不指明,表示本机的任何一个ip地址 server_addr = ("", 8080) server_sock.bind(server_addr) while True: # recv方法接收发送过来的数据 # 返回值为接收到的数据,参数(这里为1024)表示本次收取数据的最大字节数 # receive_data = server_sock.recv(1024) # recvfrom与recv方法类似,不同的是可以将发送数据的客户端的地址也返回 receive_data, client_addr = server_sock.recvfrom(1024) # 注意python3中收到的数据receive_data是bytes类型 # print(client_addr, ": ", receive_data) # 将bytes数据转换为字符串类型 msg = receive_data.decode("utf-8") # 将收到的数据显示输出 print(client_addr, ": ", msg) # 我们假定如果客户端发送了quit,我们就关闭服务端的套接字(即关闭服务端) if msg == "quit": server_sock.close() break
测试
我们暂时还没有写udp客户端,可以用nc命令来作为客户端进行测试。
# -u 表示使用udp协议 # nc -u 服务器ip 服务器端口 nc -u 127.0.0.1 8080
udp客户端
import socket # 1. 创建套接字 client_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # 服务器地址 server_addr = ('127.0.0.1', 8080) data = input("请输入要发送的内容:") # 只要用户输入的数据不为空,就向服务器端发送 while data: # 2. 使用sendto方法向服务器发送数据 # sendto(bytes类型要发送的数据, 对方的地址) client_sock.sendto(data.encode("utf-8"), server_addr) data = input("请输入要发送的内容:") # 当用户输入的数据为空("")时, 关闭客户端套接字 client_sock.close()
测试
服务端与客户端的程序我们都已完成,可以同时开启进行测试。
我们也可以用nc充当udf服务端来单独测试客户端程序。
# -l 表示作为服务端开启,进行监听listen # -u 表示使用udp协议 # nc -lu 绑定的服务器ip地址 端口 nc -lu 127.0.0.1 8080
udp客户端的端口
我们并没有为客户端bind绑定一个端口,操作系统可以为我们随机分配一个可用的端口用来发送数据,这样就不会因为需要绑定的端口被占用而导致程序无法运行的情况。
客户端服务端傻傻分不清
在双方的网络通讯中,客户端与服务端的角色并不是划分的很决定,任何一方都可以发送数据,也可以接收数据。
实现一个简易的DNS服务器模型
DNS服务器端
import socket server_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) server_addr = ("", 8053) server_sock.bind(server_addr) # 用来存放域名与ip对应关系的字典 domain_ip = { "www.itcast.cn": "192.168.1.2", "www.itheima.com": "192.168.1.3", "www.google.com": "192.168.1.4", } while True: # 接收用户要查询的域名 receive_data, client_addr = server_sock.recvfrom(1024) domain = receive_data.decode("utf-8") print(client_addr, ": ", domain) # 从字典中获取对应域名的ip地址 ip = domain_ip.get(domain, "i do not know") # 将ip地址返回给客户端 server_sock.sendto(ip.encode("utf-8"), client_addr)
客户端
import socket client_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) server_addr = ('127.0.0.1', 8053) domain = input("请输入要查询的域名:") while domain: # 向服务端发送要查询的域名 client_sock.sendto(domain.encode("utf-8"), server_addr) # 接收服务端发送过来的ip信息 ip = client_sock.recv(1024) print(ip.decode("utf-8")) domain = input("请输入要查询的域名:") client_sock.close()
udp广播
现实生活中的广播
网络编程中的广播
import socket # 创建udp套接字 s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # 告诉系统内核刚创建的套接字用来进行广播 s.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) data = input("请输入要广播的内容:") while data: # 注意对于广播对象地址的设置 # <broadcast>表示广播地址 s.sendto(data.encode("utf-8"), ("<broadcast>", 8080)) data = input("请输入要广播的内容:") s.close()
udp总结
1. udp是传输层的一种协议,不需要进行连接就可以用来发送和接收数据,但不保证数据的可靠传输。
2. udp服务器、客户端
- udp的服务器和客户端的区分:往往是通过
请求服务
和提供服务
来进行区分 - 请求服务的一方称为:客户端
- 提供服务的一方称为:服务器
3. udp绑定问题
- 一般情况下,服务器端,需要绑定端口,目的是为了让其他的客户端能够正确发送到此进程
- 客户端,一般不需要绑定,而是让操作系统随机分配,这样就不会因为需要绑定的端口被占用而导致程序无法运行的情况