chapter12.2、socket编程的UDP编程

socket的UDP编程

UDP编程

 

UDP服务端流程

  1. UDP服务端

  2. 创建socket对象。socket.SOCK_DGRAM

  3. 绑定IP和Port,bind()方法

  4. 传输数据

    • 接收数据,socket.recvfrom(bufsize[, flags]),获得一个二元组(string, address)

    • 发送数据,socket.sendto(string, address) 发给某地址某信息

  5. 释放资源

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()
chat_udp_server

 

UDP客户端编程流程

  1. 创建socket对象。socket.SOCK_DGRAM

  2. 发送数据,socket.sendto(string, address) 发给某地址某信息

  3. 接收数据,socket.recvfrom(bufsize[, flags]),获得一个二元组(string, address)

  4. 释放资源 

每种协议的端口都有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()
chat_udp_server

 

使用心跳包实现客户端
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()
chat_udp_client

 

UDP协议应用

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

局域网路由好,消息不会丢失,包不会乱序

但是,实际上都不一定可以保证

 

应用场景:

视频、音频传输,一般来说,丢些包,问题不大,最多丢些图像、听不清话语,可以重新发话语来解决。

海量采集数据,例如传感器发来的数据,丢几十、几百条数据也没有关系。

DNS协议,数据内容小,一个包就能查询到结果,不存在乱序,丢包,重新请求解析。

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

udp53端口,是dns服务器的端口

 
 
 
 
 
 
 
 
 
 

 

posted on 2018-11-04 16:41  Riper  阅读(275)  评论(0编辑  收藏  举报

导航