基于UDP协议的socket套接字编程 基于UDP协议的socket套接字编程

一、UDP套接字简单示例

1.1 服务器

import socket

server = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)  # 数据报协议->UDP
server.bind(('127.0.0.1', 8080))

while True:
    data, client_addr = server.recvfrom(1024)
    print('===>', data, client_addr)
    server.sendto(data.upper(), client_addr)

server.close()

1.2 客户端

import socket

client = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)  # 数据报协议->UDP

while True:
    msg = input('>>: ').strip()  # msg=''
    client.sendto(msg.encode('utf-8'), ('127.0.0.1', 8080))
    data, server_addr = client.recvfrom(1024)
    print(data)

client.close()
  • UDP是无链接的,先启动哪一端都不会报错
  • UDP协议是数据报协议,发空的时候也会自带报头,因此客户端输入空,服务端也能收到

1.3 通信循环

服务端

import socket

server = socket.socket(type=socket.SOCK_DGRAM)
server.bind(('127.0.0.1', 8004))

# udp循环通信
while True:
    data, addr = server.recvfrom(1024)
    print(data)
    server.sendto(data.upper(), addr)

客户端

import socket

clinent = socket.socket(type=socket.SOCK_DGRAM)

while True:
    msg = input("请输入: ")

    # 直接发送
    clinent.sendto(msg.encode('utf-8'), ('127.0.0.1', 8004))
    data = clinent.recvfrom(1024)
    print(data)

二、UDP套接字无粘包问题

2.1服务器

import socket

# udp协议没有粘包问题,udp协议又叫数据包协议, 可以发空,tcp不行
# udp不管客户端或者服务端是否收到,他只管发,所以不可靠
"""
udp特点:
可以发空(数据包协议,自带头)
客户端和服务端可以有一方,没在线(因为不需要建立连接)
"""
server = socket.socket(type=socket.SOCK_DGRAM)
server.bind(('127.0.0.1', 8003))
# while True:
#     data, addr = server.recvfrom(1024)
#     print(data)
#
data,addr=server.recvfrom(1024)
print(data)
print(1)
data,addr=server.recvfrom(1024)
print(data)
print(2)
data,addr=server.recvfrom(1024)
print(data)
print(3)
data,addr=server.recvfrom(1024)
print(data)
print(4)
# OSError: [WinError 10040] 一个在数据报套接字上发送的消息大于内部消息缓冲区或其他一些网络限制,或该用户用于接收数据报的缓冲区比数据报小。

b'randy'
1

2.2 客户端

import socket

# udp不管客户端或者服务端是否收到,他只管发,所以不可靠

client = socket.socket(type=socket.SOCK_DGRAM)

client.sendto('randy'.encode('utf-8'),('127.0.0.1', 8003))
client.sendto('hello'.encode('utf-8'),('127.0.0.1', 8003))
client.sendto('world'.encode('utf-8'),('127.0.0.1', 8003))
client.sendto(''.encode('utf-8'),('127.0.0.1', 8003))
  • UPD协议一般不用于传输大数据。
  • UDP套接字虽然没有粘包问题,但是不能替代TCP套接字,因为UPD协议有一个缺陷:如果数据发送的途中,数据丢失,则数据就丢失了,而TCP协议则不会有这种缺陷,因此一般UPD套接字用户无关紧要的数据发送,例如qq聊天。

三、qq聊天

  • 由于UDP无连接,所以可以同时多个客户端去跟服务端通信

3.1 服务器

import socket
ip_port = ('127.0.0.1', 8081)
UDP_server_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)  #买手机
UDP_server_sock.bind(ip_port)

while True:
    qq_msg, addr = UDP_server_sock.recvfrom(1024)
    print('来自[%s:%s]的一条消息: 33[1;44m%s 33[0m' %
          (addr[0], addr[1], qq_msg.decode('utf-8')))
    back_msg = input('回复消息: ').strip()

    UDP_server_sock.sendto(back_msg.encode('utf-8'), addr)

3.2 客户端

import socket
BUFSIZE = 1024
UDP_client_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

qq_name_dic = {
    '狗哥alex': ('127.0.0.1', 8081),
    '瞎驴': ('127.0.0.1', 8081),
    '一棵树': ('127.0.0.1', 8081),
    '武大郎': ('127.0.0.1', 8081),
}

while True:
    qq_name = input('请选择聊天对象: ').strip()
    while True:
        msg = input('请输入消息,回车发送: ').strip()
        if msg == 'quit': break
        if not msg or not qq_name or qq_name not in qq_name_dic: continue
        UDP_client_socket.sendto(msg.encode('utf-8'), qq_name_dic[qq_name])

        back_msg, addr = UDP_client_socket.recvfrom(BUFSIZE)
        print('来自[%s:%s]的一条消息: 33[1;44m%s 33[0m' %
              (addr[0], addr[1], back_msg.decode('utf-8')))

UDP_client_socket.close()

3.3 客户端2

import socket
BUFSIZE = 1024
UDP_client_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

qq_name_dic = {
    '狗哥alex': ('127.0.0.1', 8081),
    '瞎驴': ('127.0.0.1', 8081),
    '一棵树': ('127.0.0.1', 8081),
    '武大郎': ('127.0.0.1', 8081),
}

while True:
    qq_name = input('请选择聊天对象: ').strip()
    while True:
        msg = input('请输入消息,回车发送: ').strip()
        if msg == 'quit': break
        if not msg or not qq_name or qq_name not in qq_name_dic: continue
        UDP_client_socket.sendto(msg.encode('utf-8'), qq_name_dic[qq_name])

        back_msg, addr = UDP_client_socket.recvfrom(BUFSIZE)
        print('来自[%s:%s]的一条消息: 33[1;44m%s 33[0m' %
              (addr[0], addr[1], back_msg.decode('utf-8')))

UDP_client_socket.close()