chapter12.2socket编程的UDP编程
Posted rprp789
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了chapter12.2socket编程的UDP编程相关的知识,希望对你有一定的参考价值。
socket的UDP编程和socketserver模块
UDP编程
UDP服务端流程
- UDP服务端
- 创建socket对象。socket.SOCK_DGRAM
- 绑定IP和Port,bind()方法
- 传输数据
- 接收数据,socket.recvfrom(bufsize[, flags]),获得一个二元组(string, address)
- 发送数据,socket.sendto(string, address) 发给某地址某信息
- 释放资源
import socket import threading import logging import datetime FORMAT = "%(asctime)s %(threadName)s %(message)s" logging.basicConfig(level=logging.INFO,format=FORMAT) class ChatUdpServer: def __init__(self,ip="127.0.0.1",port=9999,interval=10): self.sock = socket.socket(type=socket.SOCK_DGRAM) self.addr = ip,port self.event = threading.Event() self.clients = {} self.interval = interval def start(self): self.sock.bind(self.addr) threading.Thread(target=self.recv,name=‘recv‘).start() def recv(self): while not self.event.is_set(): data,raddr = self.sock.recvfrom(1024) logging.info(data) localkeys = set() if data.strip() == b"^hb^": self.clients[raddr] = datetime.datetime.now().timestamp() continue if data.strip() == b"quit": if raddr in self.clients: self.clients.pop(raddr) logging.info(("quit__+_++++")) continue self.clients[raddr] = datetime.datetime.now().timestamp() #add client current = datetime.datetime.now().timestamp() msg = "{} . form {}:{}".format(data.decode(),*raddr).encode() for r,ts in self.clients.items(): if current - ts > self.interval: localkeys.add(r) else: self.sock.sendto(msg,r) for k in localkeys: self.clients.pop(k) def stop(self): self.sock.close() self.event.set() def main(): cs = ChatUdpServer() cs.start() while True: cmd = input() if cmd.strip() == "quit": logging.info("quit") break logging.info(threading.enumerate()) print(cs.clients) if __name__ == ‘__main__‘: main()
UDP客户端编程流程
- 创建socket对象。socket.SOCK_DGRAM
- 发送数据,socket.sendto(string, address) 发给某地址某信息
- 接收数据,socket.recvfrom(bufsize[, flags]),获得一个二元组(string, address)
- 释放资源
每种协议的端口都有65535个
无连接协议使用connect 只是提供了远端地址,不是建立连接
无连接协议,可以只有任何一端
UDP的socket对象创建后,不占用本地地址和端口
bind方法 | 可以指定本地地址和端口laddr,会立即占用 |
connect方法 | 可以立即占用本地地址和端口laddr,填充远端地址和端口raddr |
sendto方法
|
可以立即占用本地地址和端口laddr,并把数据发往指定远端。只有有了本地绑定端口,sendto就可以向任何远端发送数据 |
send方法 | 需要和connect方法配合,可以使用已经从本地端口把数据发往raddr指定的远端 |
recv方法 | 要求一定要在占用了本地端口后,返回接收的数据 |
recvfrom方法 | 要求一定要占用了本地端口后,返回接收的数据和对端地址的二元组 |
心跳机制
心跳包越小越好
TCP也使用心跳包,隔一段时间可能会假死,心跳包是一种保活机制
可以使用这种方法:
服务器发往客户端,判断是否有回复,设定次数内没有回复,就判断客户端下线,用的较少
也可以使用,
客户端定时发送心跳包到服务端。记录最后一次通讯的时间的方法,服务端知道客户端或者就ok,不需要响应
使用心跳包实现服务器
import threading import socket import logging,datetime FORMAT = "%(asctime)s %(threadName)s %(message)s" logging.basicConfig(level=logging.INFO,format=FORMAT) class ChatUdpServer: def __init__(self,ip="127.0.0.1",port=9999,interval=10): self.sock = socket.socket(type=socket.SOCK_DGRAM) self.event = threading.Event() self.clients = {} self.addr = ip,port self.interval = interval #超时移除对应的客户端,超时时间,默认10s def start(self): self.sock.bind(self.addr) threading.Thread(target=self.recv,name="recv").start() def recv(self): logging.info("start now") while not self.event.is_set(): clientset = set() try: data,raddr = self.sock.recvfrom(1024) except Exception as e: logging.error(e) break if data.strip() == b"^hb^": self.clients[raddr] = datetime.datetime.now().timestamp() #logging.info("hb^^^^^^^^^^^") continue if data.strip() == b"quit" or b"": self.clients.pop(raddr) logging.info("{} logout".format(raddr)) continue self.clients[raddr] = datetime.datetime.now().timestamp() logging.info(data) current = datetime.datetime.now().timestamp() for r,t in self.clients.items(): if current - t > self.interval: clientset.add(r) else: self.sock.sendto(data,r) for i in clientset: self.clients.pop(i) def stop(self): for c in self.clients.keys(): self.sock.sendto(b"bye",c) self.sock.close() self.event.set() def main(): cs = ChatUdpServer() cs.start() while True: cmd = input(">>>") if cmd.strip() == "quit": cs.stop() threading.Event().wait(3) break logging.info(cs.clients) if __name__ == ‘__main__‘: main()
使用心跳包实现客户端
import socket import threading import logging FORMAT = "%(asctime)s %(threadName)s %(message)s" logging.basicConfig(level=logging.INFO, format=FORMAT) class ChatUdpClient: def __init__(self, ip="127.0.0.1", port=9999): self.sock = socket.socket(type=socket.SOCK_DGRAM) self.raddr = ip, port self.event = threading.Event() def start(self): self.sock.connect(self.raddr) threading.Thread(target=self._heratbeat, name="hb", daemon=True).start() threading.Thread(target=self.recv, name="recv").start() print(self.sock) def recv(self): while not self.event.is_set(): try: data, raddr = self.sock.recvfrom(1024) except Exception as e: logging.error(e) break logging.info(data) logging.info(raddr) def _heratbeat(self): while not self.event.wait(5): self.send("^hb^") def send(self, msg): msg = "{}".format(msg).encode() self.sock.sendto(msg, self.raddr) def stop(self): self.sock.close() self.event.set() def main(): cc = ChatUdpClient() cc.start() while True: cmd = input() if cmd.strip() == "quit": cc.stop() logging.info("quit") threading.Event().wait(3) break if cmd == "th": logging.info(threading.enumerate()) continue cc.send(cmd) if __name__ == ‘__main__‘: main()
UDP协议应用
UDP是无连接协议,基于以下假设
局域网路由好,消息不会丢失,包不会乱序
但是,实际上都不一定可以保证
应用场景:
视频、音频传输,一般来说,丢些包,问题不大,最多丢些图像、听不清话语,可以重新发话语来解决。
海量采集数据,例如传感器发来的数据,丢几十、几百条数据也没有关系。
DNS协议,数据内容小,一个包就能查询到结果,不存在乱序,丢包,重新请求解析。
一般来说,UDP性能优于TCP,但是可靠性要求高的场合的还是要选择TCP协议。
udp53端口,dns服务器的端口
以上是关于chapter12.2socket编程的UDP编程的主要内容,如果未能解决你的问题,请参考以下文章