一, udp 简介
udp --- 用户数据报协议, 是一个无连接的简单的面向数据报的运输层协议.
udp 不提供可靠性, 它只是把应用程序传给 IP 层的数据报发送出去, 但是并不能保证它们能到达目的地.
udp 在传输数据报前不用在客户和服务器之间建立一个连接, 且没有超时重发等机制, 故而传输速度很快.
udp 是一种面向无连接的协议, 每个数据报都是一个独立的信息, 包括完整的源地址或目的地址, 它在网络上以任何可能的路径传往目的地, 因此能否到达目的地, 到达目的地的时间以及内容的正确性都是不能被保证的.
二, udp 特点:
udp 是面向无连接的通讯协议, udp 数据包括目的端口号和源端口号信息, 由于通讯不需要连接, 所以可以实现广播发送. udp 传输数据时有大小限制, 每个被传输的数据报必须限定在 64KB 之内. udp 是一个不可靠的协议, 发送方所发送的数据报并不一定以相同的次序到达接收方. udp 是面向消息的协议, 通信时不需要建立连接, 数据的传输自然是不可靠的, udp 一般用于多点通信和实时的数据业务, 比如:
语音广播
- TFTP(简单文件传送)
- SNMP(简单网络管理协议)
- RIP(路由信息协议, 如报告股票市场, 航空信息)
- DNS(域名解释)
三, udp 网络程序 - 发送数据
创建一个 udp 客户端程序的流程是简单, 具体步骤如下:
创建客户端套接字
发送 / 接收数据
关闭套接字
代码如下:
- #coding=utf-8
- from socket import *
- #1, 创建 socket 套接字
- #socket(参数 1, 参数 2)
- # 参数 1 = AF_INET 固定的
- # 参数 2 = SOCK_DGRAM 表示 udp, 上篇文章中说过 SOCK_STREM 表示 tcp
- udpSocket = socket(AF_INET,SOCK_DGRAM)
- #2, 准备接收方的地址
- sendAddress = ("192.168.100.101",8080)
- #3, 从键盘输入需要发送的数据
- sendData = input("请输入要发送的数据:")
- #4, 发送数据到指定电脑
- udpSocket.sendto(sendData.encode(),sendAddress)
- #5, 关闭 socket 套接字
- udpSocket.close()
运行程序:
这个时候我就向我的另外一台 IP 地址为: 192.168.100.101 端口号为 8080 的程序发送了 "我是侯哥" 这一条消息. 我们借助于网络调试助手软件用于测试, 网络调试助手各个平台的系统都有, 大家可以自己下载使用.
说明: 我的代码是在 Windows 电脑上运行的, 我的网络调试助手是在 Mac 电脑上运行的, 如果没有两台电脑的, 也可以使用虚拟机测试.
四, udp 网络程序 - 接收数据
- #coding=utf-8
- from socket import *
- #1, 创建 socket 套接字
- udpSocket = socket(AF_INET,SOCK_DGRAM)
- #2, 准备接收方的地址
- sendAddress = ("192.168.100.101",8080)
- #3, 从键盘输入需要发送的数据
- sendData = input("请输入要发送的数据:")
- #4, 发送数据到指定电脑
- udpSocket.sendto(sendData.encode(),sendAddress)
- #5, 等待接收对方发送的数据
- receiveData = udpSocket.recvfrom(1024)
- #6, 显示对方发送的数据
- print(receiveData)
- #7, 关闭 socket 套接字
- udpSocket.close()
运行程序:
五, udp 网络程序 - 端口问题
会变的端口号: 重新运行多次脚本, 然后在 "网络调试助手" 中, 看到的现象如下:
说明:
每重新运行一次网络程序, 上图中红圈中的数字, 不一样的原因在于, 这个数字标识这个网络程序, 当重新运行时, 如果没有确定到底用哪个, 系统默认会随机分配
记住一点: 这个网络程序在运行的过程中, 这个就唯一标识这个程序, 所以如果其他电脑上的网络程序如果想要向此程序发送数据, 那么就需要向这个数字 (即端口) 标识的程序发送即可
六, udp 绑定信息
一般情况下, 在一天电脑上运行的网络程序有很多, 而各自用的端口号很多情况下不知道, 为了不与其他的网络程序占用同一个端口号, 往往在编程中, udp 的端口号一般不绑定, 但是如果需要做成一个服务器端的程序的话, 是需要绑定的. 就像报警电话每天都在变, 想必世界就会乱了, 所以一般服务性的程序, 往往需要一个固定的端口号, 这就是所谓的端口绑定
绑定示例
- #coding=utf-8
- from socket import *
- #1, 创建 socket 套接字
- udpSocket = socket(AF_INET,SOCK_DGRAM)
- #2, 绑定相关信息, 如果一个网络程序不绑定, 则系统会随机分配
- bindAddress = ("",7781)#ip 地址和端口号, ip 一般不用写, 表示本机的任何一个 ip
- udpSocket.bind(bindAddress)
- #3, 等待接收方发送消息
- receiveData = udpSocket.recvfrom(1024)
- #4, 显示对方发送的数据
- print(receiveData)
- #5, 关闭 socket 套接字
- udpSocket.close()
Windows 电脑发送信息
Mac 电脑接收信息如下:
说明:
一个 udp 网络程序, 可以不绑定, 此时操作系统会随机进行分配一个端口, 如果重新运行次程序端口可能会发生变化
一个 udp 网络程序, 也可以绑定信息(ip 地址, 端口号), 如果绑定成功, 那么操作系统用这个端口号来进行区别收到的网络数据是否是此进程的
七, udp 网络通信过程
八, udp 应用: 多线程聊天室
- #coding=utf-8
- from threading import Thread
- from socket import *
- # 接收数据
- def receiveInfo():
- while True:
- receiveData = udpSocket.recvfrom(1024)
- print("<<%s:%s"%(str(receiveData[1]),str(receiveData[0])))
- # 发送数据
- def sendInfo():
- while True:
- sendData = input("")
- udpSocket.sendto(sendData.encode("gb2312"),(destIp,destPort))
- udpSocket = None
- destIp = ""
- destPort = 0
- def main():
- global udpSocket
- global destIp
- global destPort
- destIp = input("对方的 IP:")
- destPort = int(input("对方的 Port:"))
- udpSocket = socket(AF_INET,SOCK_DGRAM)
- udpSocket.bind(("",4567))# 这里写两个 () 的原因是将 ("",4567) 当做一个整体元组使用
- tr = Thread(target = receiveInfo)
- ts = Thread(target = sendInfo)
- tr.start()
- ts.start()
- tr.join()
- ts.join()
- if __name__ == '__main__':
- main()
Mac 电脑上执行程序如下:
Windows 电脑上运行网络调试助手如下:
从而就是先了 socket 的 udp 的多程序通信.
来源: https://www.cnblogs.com/Se7eN-HOU/p/10742148.html