UDP Programming Network Programming

Creative Commons License Copyright: Attribution, allow others to create paper-based, and must distribute paper (based on the original license agreement with the same license Creative Commons )

1. UDP Programming

1.1 UDP programming process

1.1.1 UDP server-side programming process

  • Create a socket object, socket.SOCK_DGRAM
  • Bind IP and Port, bind () method
  • Data transmission: receiving data, socket.recvfrom (bufsize [, flags]), to obtain a tuple (string, address); the transmission data, socket.sendto (string, address), sending a message to an address
  • Release resources
import socket
import threading

event = threading.Event()
address = '0.0.0.0', 9999
server = socket.socket(type=socket.SOCK_DGRAM)  # 数据报协议
server.bind(address)  # 只能绑定一次

while not event.is_set():
    data, client_info = server.recvfrom(1024)  # 比recv安全,可以知道是谁发给你的
    print(data)
    # print(server.getpeername())  # 会报错OSError
    msg = "{} from {}-{}".format(data.decode(), *client_info).encode()
    # server.send(msg)  # 会报错,不知道发送给谁
    server.sendto(msg, client_info)

    print('~' * 30)

event.set()
server.close()

1.1.2 UDP Client Programming Process

  • Create a socket object, socket.SOCK_DGRAM
  • Transmission data, socket.sendto (string, address) transmitting an address information of a
  • Receiving data, socket.recvfrom (bufsize [, flags]), to obtain a tuple (string, address)
  • Release resources
import socket


address = '127.0.0.1', 10001
client = socket.socket(type=socket.SOCK_DGRAM)  # 数据报协议
client.connect(address)  # 会解决本地address和远端地址address
print(1, client)
print(1.5, client)
ms = b'111222333444555'
# client.sendto(ms + b'~~~~~~~', ('127.0.0.1', 10000))
#  会帮你抢一个本地地址和端口(端口是临时的),没有此句,recvfrom会报错
# 可以自己玩,因为它有本地address, 它不会记录远端地址address
client.send(ms)  # 必须和connect配合使用,什么都不解决

print(2, client)
data, info = client.recvfrom(1024)  # 它需要本地address
print(data, info)

# client.connect(address)  # 加了这一句,send就可以用了
# while True:
#     cmd = input(">>>").strip()
#     ms = cmd.encode()
#     # client.sendto(ms, address)
#     client.send(ms)  # 此句必须和connect配合使用
#     client.sendto(ms + b'~~~~~~~', ('127.0.0.1', 10000))
#     client.sendto(ms + b'+++++++', ('127.0.0.1', 10001))
#     data, info = client.recvfrom(1024)  # 比recv安全,可以知道是谁发给你的
#     print(data)
#     msg = "{} from {}".format(data.decode(), *info).encode()
#
#     print('~' * 30)

client.close()

Note: UDP is a connectionless protocol, all there may be only at either end, for example, sent to the server, the server does not matter whether or not the presence of the client data

1.2 UDP programming methods commonly used

UDP programming common method

 

Some explanations for common methods of programming udp: send method must connect method with the use of direct or error; recvfrom safer than recv, recvfrom know who sent the information to you, it needs to know the local address; sendto will grab a local address and port (port is temporary), it can play yourself, because it has a local address, it does not record the remote address; connect resolve local address and remote address.

1.3 UDP programming group chat

1.3.1 UDP version of Group Chat server code

import socket
import datetime
import logging
import threading


FORMAT = "%(threadName)s %(thread)d %(message)s"
logging.basicConfig(format=FORMAT, level=logging.INFO)


class ChatServerUdp:
    # UDP群聊用的都是同一个socket,所以用字典浪费了,所有的value值都是一样的,列表可以,但是移除的话,效率低,所以考虑用集合
    # 但是添加了过期删除了client的话,集合就不合适了,此时还是要用字典
    def __init__(self, ip='127.0.0.1', port=9999, interval=10):  # 服务端的时间间隔一般是客户端的时间间隔的2到3倍
        self.sock = socket.socket(type=socket.SOCK_DGRAM)  # 数据报文协议
        self.address = ip, port
        self.event = threading.Event()
        # self.clients = set()
        self.clients = {}
        self.interval = interval

    def start(self):
        self.sock.bind(self.address)

        threading.Thread(target=self.rec, name='rec').start()

    def rec(self):
        while not self.event.is_set():
            data, client_info = self.sock.recvfrom(1024)
            current = datetime.datetime.now().timestamp()  # float
            # self.clients.add(client_info)

            if data.strip() == b'^hb^':  # b'^hb^'为自己设计的
                self.clients[client_info] = current
                logging.info('{} hb^^^^^'.format(client_info))
                continue

            if data.strip() == b'quit':
                # self.clients.remove(client_info)  # 注意remove相当于是按照key查找的,因为集合的值可以看做字典的key,所以比列表高效

                self.clients.pop(client_info)  # 客户端主动断开连接,就把该客户的ip从字典中删除
                logging.info("{} leaving".format(client_info))
                continue  # 不能用break,因为总共只有一个线程,break了,while循环结束了

            self.clients[client_info] = current

            # 在该位子遍历字典,删除过期的clients,比较耗时,因为如果一个都没有删除,每次都要遍历字典,会很耗时,可以考虑在发送信息时,
            # 遍历字典判断是否超时

            logging.info(data)
            msg = "{} [{}:{}] {}".format(datetime.datetime.now(), *client_info, data.decode())

            keys = set()
            for c, stamp in self.clients.items():  # 有线程安全问题,解决方法是加锁
                if current - stamp < 0 or current - stamp > self.interval:  # 小于0应该是时间出问题了
                    keys.add(c)  # 不能直接self.clients.pop(c),因为字典在遍历的过程中,其长度不能改变
                else:
                    self.sock.sendto(msg.encode(), c)
            for c in keys:
                self.clients.pop(c)

    def stop(self):
        self.event.set()
        self.clients.clear()
        self.sock.close()


csu = ChatServerUdp()
csu.start()

while True:
    cmd = input('>>>').strip()
    if cmd == 'quit':
        csu.stop()
        break
    logging.info(threading.enumerate())

1.3.2 UDP version of the Group Chat client code

import socket
import logging
import threading


FORMAT = "%(threadName)s %(thread)d %(message)s"
logging.basicConfig(format=FORMAT, level=logging.INFO)


class ChatClientUdp:
    def __init__(self, ip='127.0.0.1', port=9999, interval=5):
        self.sock = socket.socket(type=socket.SOCK_DGRAM)
        self.r_address = ip, port
        self.event = threading.Event()
        self.interval = interval

    def start(self):
        self.sock.connect(self.r_address)
        self.send('{} hello'.format(self.sock.getsockname()))
        threading.Thread(target=self.heart_beat, name='heartbeat', daemon=True).start()
        threading.Thread(target=self.rec, name='rec').start()

    def heart_beat(self):
        while not self.event.wait(self.interval):
            # self.sock.send(b'^hb^')  # 发送心跳包,记录最后一次发送的时间,此句比较浪费时间,换成下面的语句
            self.send('^hb^')

    def rec(self):
        while not self.event.is_set():
            data = self.sock.recv(1024)
            logging.info(data)

    def send(self, msg: str):
        self.sock.sendto(msg.encode(), self.r_address)

    def stop(self):
        self.event.set()
        self.send('quit')
        self.sock.close()


ccu = ChatClientUdp()
ccu.start()

while True:
    line = input('>>>').strip()
    if line == 'quit':
        ccu.stop()
        break
    ccu.send(line)
    logging.info(threading.enumerate())

Heartbeat mechanism:

  1. Generally the client regularly to the server, the server does not respond to client needs ack, as long as the record of the client is still alive on it
  2. If the server is timed sent to the client, the general need to represent a client in response ack alive, if the client does not receive ack, the server remove their information. This implementation is more complex, with less.
  3. Can also have made two-way heartbeat, less used

1.4 UDP protocol application

UDP protocol is a connectionless protocol, which is based on the following assumptions: (User Datagram Protocol)

  • Internet is good enough
  • The message will not loss
  • Packets are not out of order

However, even the local area, it can not guarantee no packet loss, and packet arrival is not necessarily in order.

Scenario: video, audio transmission, in general, lose some packets, not a big problem, lost most of these images, hear words, words can be re-sent to solve. Massive data acquisition, the sensor data sent to e.g., lost dozens, hundreds of data does not matter. DNS protocol, a small content data, a packet will be able to query a result, there is no disorder, loss, re-request resolution. In general, UDP performance is better than TCP, but the high reliability requirements of the occasion or to select the TCP protocol.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Guess you like

Origin blog.csdn.net/sqsltr/article/details/92553138