UDP编程

mac2025-05-04  14

UDP服务端编程流程

创建socket对象。socket.SOCK_DGRAM绑定IP和Port,bind()方法传输数据 接收数据,socket.recvfrom(bufsize[,flags]),获得一个二元组(string, address)发送数据,socket.sendto(string, address)发给某地址某信息 释放资源 import socket server = socket.socket(type=socket.SOCK_DGRAM) server.bind('0.0.0.0', 9999) data = server.recv(1024) data = server.recvfrom(1024) server.sendto(b'7', ('192.168.0.2', 10000)) server.close()

UDP客户端编程流程

创建socket对象。socket.SOCK_DGRAM发送数据,socket.sendto(string, address)发送某地址某信息接收数据,socket.recvfrom(bufsize[,flags]),获得一个二元组(string, address)释放资源 import socket client = socket.socket(type=socket.SOCK_DGRAM) raddr = ('192.168.2.2', 9999) client.connect(raddr) client.sendto(b'8', raddr) client.send(b'9') data = client.recvfrom(1024) # 阻塞等待数据(value, (ip, port)) data = client.recv(1024) # 阻塞等待数据 client.close()

注意:UDP是无连接协议,所以可以只有任何一端,例如客户端数据发往服务端,服务端存在与否无所谓。

UDP编程中bind、connect、send、sendto、recv、recvfrom方法使用 UDP的socket对象创建后,是没有占用本地地址和端口的。

UDP版群聊

服务端代码

# 服务端类的基本架构 class ChatServer: def __init__(self, ip='127.0.0.1', port=9999): self.addr = ip, port self.sock = socket.socket(type=socket.SOCK_DGRAM) def start(self): self.sock.bind(self.addr) self.sock.recvfrom(1024) # 阻塞接收数据 def stop(self): self.sock.close() import socket import logging import datetime import threading Format = '%(asctime)s - %(threadName)s - %(thread)d - %(message)s)' logging.basicConfig(format=Format, level=logging.INFO) class ChatUDPServer: def __init__(self, rip='127.0.0.1', port=9999): self.sock = socket.socket(type=socket.SOCK_DGRAM) self.addr = rip, port self.clients = set() sefl.event = threading.Event() def start(self): self.sock.bind(self.addr) theading.Thread(target=self._sendhb, name='heartbeat', daemon=True).start() threading.Thread(target=self.recv, name='recv').start() def _sendhb(self): while not self.event.wait(5): self.send('^hb^')` def recv(self): while not self.event.is_set(): data, raddr = self.sock.recvfrom(1024) if data.strip() == b'quit' or data.strip() == b'': if raddr in self.clients: self.clients.remove(raddr) logging.info('{} leaving'.format(raddr)) continue self.clients.add(raddr) msg = '{}--> from {}:{}'.format(data.decode(), *raddr) logging.info(msg) msg = msg.encode() for c in self.clients: c.sendto(msg, c) # 不保证对方能收到 def stop(self): for c in self.clients: self.sock.sendto(b'bye', c) self.sock.close() self.event.set() def main(): cs = ChatUDPServer() cs.start() while True: cmd = input('>>>').strip() if cmd == 'quit' or cmd == '': cs.stop() break if __name__ == '__main__': main()

UDP群聊客户端代码

import threading import socket import logging Format = '%(asctime)s - %(threadName)s - %(thread)d - %(message)s)' logging.basicConfig(format=Format, level=logging.INFO) class ChatUDPClient: def __init__(self, ip='127.0.0.1', rport=9999): self.sock = socket.socket(type=socket.SOCK_DGRAMK) self.raddr = ip, rport self.event = threading.Event() def start(self): self.sock.connect(self.raddr) # 占用本地地址和端口,设置远端地址和端口 threading.Thread(target=self.recv, name='recv').start() def recv(self): while not self.event.is_set(): data, raddr = self.sock.recvfrom(1024) msg = '{} from {}:{}'.format(data, *raddr) logging.info(msg) def send(self): self.sock.sendto(msg.encode(), self.raddr) def stop(self): self.event.set() self.send('quit') self.sock.close() def main(): cc1 = ChatUDPClient() cc2 = ChatUDPClient() cc1.start() cc2.start() while True: cmd = input('>>>').strip() if cmd == 'quit' or cmd == '': cc1.stop() cc2.stop() break cc1.send(cmd) cc2.send(cmd) if __name__ == '__main__': main() File "<ipython-input-1-083d1559fc4b>", line 30 def stop(self): ^ SyntaxError: unexpected EOF while parsing

心跳机制

一般来说是客户端定时发往客户端的,服务端并不需要ack回复客户端,只需要记录该客户端还活着就行了。如果是服务端定时发往客户端的,一般需要客户端ack响应来表示活着,如果没有收到ack的客户端,服务端移除其信息。这种实现较为复杂,用的较少。也可以双向都发心跳,用的更少。

UDP协议应用

UDP是无连接协议,它基于以下假设:

网络足够好消息不会丢包包不会乱序

但是,即使在局域网,也不能保证不丢包,而且包的到达不一定有序。

应用场景

视频、音频传输,一般来说,丢些包,问题不大,最多丢写图像、听不清话语,可以重新话语来解决。 海量采集数据,例如传感器发来的数据,丢几十、几百条数据也没有关系。 DNS协议,数据内容小,一个包就能查询到结果,不存在乱序,丢包,重新请求解析。

一般来说,UDP性能优于TCP,但是可靠性要求高的场合还是要选择TCP协议。

最新回复(0)