python之socket编程UDP API

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()

原文出处:/show_1EF3PAguEj3R.html