python之socket编程UDP API 作者:马育民 • 2019-05-27 08:51 • 阅读:10153 # socket源码 socket源代码位置: ``` python安装根目录/Lib/socket.py ``` 导入```_socket```模块,socket类继承```_socket.socket```, 模块```_socket```的位置: ``` python安装根目录/DLLs/_socket.pyd ``` 什么是.pyd文件?见下面链接: http://www.malaoshi.top/show_1EF2Zg2nxB6r.html # socket 接口说明 ### 导入socket模块 ``` import socket ``` ### 创建socket对象 socket编程中,无论实现服务器端还是客户端,首先要创建socket对象,然后才能进行操作 **支持with语句** ##### 语法 ``` socket([family], [type], [proto]) ``` **参数说明:** - family:套接字家族,有三个值 - socket.AF_INET 使用IPv4(默认)。是Address Family Internet缩写 - socket.AF_INET6 使用IPv6 - socket.AF_UNIX 只用于单一的Unix系统进程间通信 - type:套接字类型,有五个值: - socket.SOCK_STREAM:流式socket , 即TCP (默认) - **socket.SOCK_DGRAM:数据报式socket ,即UDP**。是socket_datagram缩写 - socket.SOCK_RAW 原始套接字,普通的套接字无法处理ICMP、IGMP等网络报文,而SOCK_RAW可以;其次,SOCK_RAW也可以处理特殊的IPv4报文;此外,利用原始套接字,可以通过IP_HDRINCL套接字选项由用户构造IP头。 - socket.SOCK_RDM 是一种可靠的UDP形式,即保证交付数据报但不保证顺序。SOCK_RAM用来提供对原始协议的低级访问,在需要执行某些特殊操作时使用,如发送ICMP报文。SOCK_RAM通常仅限于高级用户或管理员运行的程序使用。 - socket.SOCK_SEQPACKET 可靠的连续数据包服务 - protocol:协议。默认是0,与特定的套接字家族相关的协议,如果是 0 ,则系统就会根据地址格式和套接类别,自动选择一个合适的协议 ##### 例子 ``` udp=socket.socket(socket.AF_INET, socket.SOCK_DGRAM) ``` ##### 支持with语句 ``` with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as udp: pass ``` ### 绑定地址、端口 - **接收端** 创建socket对象后,将套接字绑定本机 **网卡IP地址** 和 **端口号**。地址格式取决于套接字族。 绑定本机端口号,**一个端口号只能被一个进程绑定**,当操作系统底层接收到数据后,通过识别 **端口号**,将数据 **发送给相应的进程** - **发送端** 绑定本机地址和端口号后,使用该指定的 **端口号** 发送数据 ##### 语法 ``` bind(address) ``` **参数:** - address:在AF_INET下(即IPv4),以元组(ipaddr,port)的形式表示地址,ipaddr是本机网卡ip地址,port是本机要绑定该网卡的端口号 当有 **多个网卡时**,要绑定所有网卡,ip地址可设置成 ```"0.0.0.0"``` ##### 例子 ``` with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as udp: # 绑定本机所有网卡的9999端口号 s.bind(('0.0.0.0', 9999)) ``` 绑定IP地址0.0.0.0,端口号是9999 >关于IP地址0.0.0.0,见下面链接: http://www.malaoshi.top/show_1EF2SXCO6P0v.html 或者 ``` with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as udp: s.bind(('', 9999))#ip可空,同0.0.0.0 ``` ##### 注意: 端口不能重复绑定,**端口只能被一个进程绑定**,同时执行2次本文开始处的接收端程序,会提示错误,如下: ``` OSError: [WinError 10048] 通常每个套接字地址(协议/网络地址/端口)只允许使用一次。 ``` **原因:** 假设一个端口可以被多个进程绑定,当操作系统接收到数据后,不知道发给哪个进程 ### 接收UDP数据 接收UDP数据,执行本代码会 **阻塞**,直到接收数据为止。执行此方法前必须先执行bind()绑定 **注意:** 由于此方法会阻塞,所以在单线程的程序中,执行此方法后,程序将 **不会执行其他操作** ##### 语法 ``` s.recvfrom(buffersize) ``` **参数** buffersize:接收数据的长度 **返回值** 返回值是tuple类型,如:(data,address)。其中data是包含接收数据的字符串,address是 **发送方** 的IP地址和端口号 ### 发送UDP数据 ##### 语法 ``` s.sendto(data,address) ``` **参数** - data:发送数据,bytes类型 - address:tuple类型,如(ipaddr,port),第一个是接收地址,第二个是端口号 **返回值** 返回发送数据的长度,单位:byte ### 关闭套接字 使用后应及时关闭socket,回收资源。服务器端运行结束后,或者调用close()方法,才会释放端口 ##### 语法 ``` close() ``` **参数说明:** 无 **返回值:** 无 ## 更多api说明 见: http://www.runoob.com/python3/python3-socket.html # 优化 ### 接收端 上面的接收端代码只能接收一次就结束运行了,通过死循环让其一直接收数据 ``` import socket udp=socket.socket(socket.AF_INET, socket.SOCK_DGRAM) udp.bind(('0.0.0.0',8000)) print('绑定本机8000端口,开始接收数据...') while True: data,addr=udp.recvfrom(1024) print('接收来自%s:%d的消息:%s'%(addr[0],addr[1],data.decode('utf-8'))) udp.close() ``` ### 发送端 在上例中发送端只能发送一次数据,通过死循环让发送端可以一直发送数据 ``` import socket address=('127.0.0.1',8000) udp=socket.socket(socket.AF_INET, socket.SOCK_DGRAM) while True: s=input('>') udp.sendto(s.encode('UTF-8'),address) udp.close() ``` 原文出处:http://malaoshi.top/show_1EF3PAguEj3R.html