3.网络通信-UDP

网络通信-UDP

面向无连接

两台计算机通信的时候,不需要建立连接(逻辑)就可以进行数据的收发,数据可能会丢

  • 传输协议: UDP
面向有连接

两台计算机通信的时候,需要先建立连接,再能通信

  • 传输协议:TCP
socket简介

socket 网络通信的基本单元,提供的方法可以实现数据的发送和接收

发送数据

udp_socket.sendto(数据,ip和端口)

  • 数据必须是二进制格式 字符串.encode()
  • ip和端口必须是是元组,(“ip地址”, 端口号)
接收数据

recv_data = udp_socket.recvfrom(1024) 每次接收1024个字节

  • recv_data 是一个元组
  • 第一个元素 收到的数据的二进制
  • 第二个元素 元组,发送方的ip和端口
  • 把接收的数据解码 二进制 ---》 字符串 二进制数据.decode("GBK")
# 1、导入模块
import socket

# 2、创建套接字
# socket.socket(协议类型,传输方式)
# 参数一:
# socket.AF_INET 使用IPv4
# socket.AF_INET6 使用IPv6
# 参数二:
# socket.SOCK_DGRAM 使用UDP的传输方式(无连接)
# socket.SOCK_STREAM 使用TCP的传输方式(有连接)
udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
tcp_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 3、发送数据
udp_socket.sendto("helloword".encode("UTF-8"),("localhost", 8080))
# 4、接收数据 recv_data[0] 接收到的数据的二进制格式,recv_data[1] 元组,对方的ip和端口
recv_data = udp_socket.recvfrom(1024)
print(recv_data)
print(recv_data[0].decode("UTF-8"))

# (b'\xe4\xbd\xa0\xe5\xa5\xbd\xef\xbc\x81', ('127.0.0.1', 8080))
# 你好!

# 5、关闭套接字
udp_socket.close()
tcp_socket.close()
(b'\xe4\xbd\xa0\xe5\xa5\xbd\xef\xbc\x81', ('127.0.0.1', 8080))
你好!
python3编码转换
  • 编码

字符串.encode() 默认UTF-8字符串

  • 解码

二进制.decode() 默认UTF-8字符集

  • 解码失败的处理

decode(encoding="字符集", errors="错误处理方式")
错误处理方式有两种:ignore 忽略, strict 严格

# 1、导入模块
import socket

# 2、创建套接字
# socket.socket(协议类型,传输方式)
# 参数一:
# socket.AF_INET 使用IPv4
# socket.AF_INET6 使用IPv6
# 参数二:
# socket.SOCK_DGRAM 使用UDP的传输方式(无连接)
# socket.SOCK_STREAM 使用TCP的传输方式(有连接)
udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# 3、发送数据
udp_socket.sendto("helloword".encode(),("localhost", 8080))
# 4、接收数据 recv_data[0] 接收到的数据的二进制格式,recv_data[1] 元组,对方的ip和端口
recv_data = udp_socket.recvfrom(1024)
print(recv_data)
print(recv_data[0].decode(encoding="GBK", errors="ignore"))
# UnicodeDecodeError: 'gbk' codec can't decode byte 0x81 in position 8: incomplete multibyte sequence decoding with 'GBK' codec failed

# (b'\xe4\xbd\xa0\xe5\xa5\xbd\xef\xbc\x81', ('127.0.0.1', 8080))
# 你好!

# 5、关闭套接字
udp_socket.close()
(b'\xe4\xbd\xa0\xe5\xa5\xbd\xef\xbc\x81', ('127.0.0.1', 8080))
浣犲ソ锛
绑定端口 bind()

udp_socket.bind(address)

  • address 是一个元组,元组的第一个元素是字符串类型的IP地址,第二个元素 整数端口号
  • udp_socket.bind(("192.168.9.88", 8888))

udp_socket.bind(("", 8888))

  • ip地址尽可能写为"",好处当计算机由多个网卡的时候,不同网卡的数据都能被接收
# 1、导入模块
import socket

# 2、创建套接字
udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

# 3、绑定端口
udp_socket.bind(("", 8888))

# 4、数据传输
udp_socket.sendto("helloword".encode(),("localhost", 8080))
recv_data, ip_port = udp_socket.recvfrom(1024)
print(recv_data.decode("UTF-8", errors="ignore"))
print(f"接收到来自:%s的信息: %s" % (str(ip_port), recv_data.decode()))

# 你好!
# 接收到来自:('127.0.0.1', 8080)的信息: 你好!

# 5、关闭套接字
udp_socket.close()
你好!
接收到来自:('127.0.0.1', 8080)的信息: 你好!
UDP广播
# 1、导入模块
import socket

# 2、创建套接字
udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

# 3、设置广播权限
# PermissionError: [Errno 13] Permission denied
# udp_socket.setsockopt(套接字,属性,属性值)
# socket.SOL_SOCKET 当前的套接字
# socket.SO_BROADCAST 广播属性
udp_socket.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, True)

# 4、发送广播
udp_socket.sendto("helloword".encode(),("255.255.255.255", 8080))
udp_socket.sendto("helloword".encode(),("168.12.15.255", 8080))

# 5、关闭套接字
udp_socket.close()
UDP聊天器
"""
一、功能
1、发送信息
2、接收信息
3、退出系统

二、框架的设计
1、发送信息 send_msg()
2、接收信息 recv_msg()
3、程序的主入口 main()
4、当程序独立运行的时候,才启动聊天器

三、实现步骤
1、发送信息 send_msg()
1) 定义变量接收用户与输入的接收方的IP地址
2)定义变量接收用户与输入的接收方的端口号
3)定义变量接收用户与输入的接收方的内容
4)使用socket的sendto() 发送信息

2、接收信息 recv_msg()
1) 使用socket 接收数据
2)解码数据
3)输出显示

3、主入口main()
1)创建套接字
2)绑定端口
3)打印菜单(循环)
4)接收用户输入的选项
5)判断用户的选择,并且调用对应的函数
6)关闭套接字
"""
import socket

def send_message(udp_socket):
    """返送信息的函数"""
    # 1) 定义变量接收用户与输入的接收方的IP地址
    ipaddr = input("请输入接收方的IP地址:\n")
    # 判断是否需要默认
    if len(ipaddr) == 0:
        ipaddr = "168.12.1.50"
        print("当前接收方默认IP设置为[%s]" % ipaddr)

    # 2)定义变量接收用户与输入的接收方的端口号
    port = input("请输入接收方的端口号:\n")
    if len(port) == 0:
        port = "8888"
        print("当前接收方默认端口设置为[%s]" % port)

    # 3)定义变量接收用户与输入的接收方的内容
    content = input("请输入要发送的内容:\n")

    # 4)使用socket的sendto()发送信息
    udp_socket.sendto(content.encode(), (ipaddr, int(port)))

def recv_msg(udp_socket):
    """接收信息的函数"""
    # 1) 使用socket接收数据
    recv_data, ip_port = udp_socket.recvfrom(1024)
    # 2)解码数据
    recv_txt = recv_data.decode()
    # 3)输出显示
    print('收到', ip_port, '发送的消息:', recv_txt)

def main():
    """程序主入口"""
    # 1)创建套接字
    udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    # 2)绑定端口
    udp_socket.bind(("", 6666))

    # 3)打印菜单(循环)
    while True:
        print('\n\n*****************************')
        print('*******   1、发送信息   *******')
        print('*******   2、接收信息   *******')
        print('*******   3、退出系统   *******')
        print('*****************************')

        # 4)接收用户输入的选项
        sel_num = int(input('请输入选项:\n'))

        # 5)判断用户的选择,并且调用对应的函数
        if sel_num == 1:
            print('您选择的是发送信息')
            send_message(udp_socket)
        elif sel_num == 2:
            print('您选择的是接收信息')
            recv_msg(udp_socket)
        elif sel_num == 3:
            print('系统正在退出中...')
            print('退出完成!')
            break
        else:
            print('选项输入错误,请重新输入')

    # 6)关闭套接字
    udp_socket.close()

if __name__ == '__main__':
    # 程序独立运行的时候,才去启动聊天器
    main()
*****************************
*******   1、发送信息   *******
*******   2、接收信息   *******
*******   3、退出系统   *******
*****************************
您选择的是发送信息

*****************************
*******   1、发送信息   *******
*******   2、接收信息   *******
*******   3、退出系统   *******
*****************************
您选择的是接收信息
收到 ('168.12.1.50', 8888) 发送的消息: 呵呵呵

*****************************
*******   1、发送信息   *******
*******   2、接收信息   *******
*******   3、退出系统   *******
*****************************
系统正在退出中...
退出完成!