zoukankan      html  css  js  c++  java
  • Python之socket_udp

    UDP服务端&客户端编程

    '''
    udp编程
    创建socket对象,socket.SOCK_DGRAM
    绑定ip和port,bind()方法
    传输数据
            1.接收数据,socket.recvfrom(bufsize[,flags]),获得一个2元祖(string,address)
            2.发送数据,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) #阻塞等待数据(value,(ip,port))
    server.sendto(b'hello',('127.0.0.1',10000))
    server.close()
    
    '''
    udp客户端编程流程
    创建socket对象,socket.SOCK_DGRAM
    发送数据,socket.sendto(string,address)发送给某地址信息
    接收数据,socket.recvfrom(bufsize[,flags]),获取一个2元祖(string,address)
    释放资源
    '''
    client = socket.socket(type=socket.SOCK_DGRAM)
    raddr = ('127.0.0.1',10000)
    client.connect(raddr)
    client.sendto(b'hello',raddr)
    data = client.recv(1024)    #阻塞等待数据
    data = client.recvfrom(1024)#阻塞等待数据,(value,(ip,port))
    client.close()
    注意:udp时无连接协议,所以可以只有任何一端,例如客户端数据发往服务端,服务端存在与否不重要
    udp的socket对象创建后,时没有占用本地地址和端口的
    bind()        可以指定本地地址和端口laddr,会立即占用
    connect()   可以立即占用本地地址和端口,填充远端地址和端口raddr
    sendto()     可以立即占用本地地址和端口,并把数据发往指定远端,只有有了本地绑定端口,sendto就可以向任何远端发送数据
    send()        需要和connect()配合使用,可以使用已经从本地端口把数据发往raddr指定的远端
    recv()         要求一定要在占用可本地端口后,返回接收的数据
    recvfrom()   要求一定要占用了本地端口后,返回接收数据和对端地址的二元组

     udp聊天server

    import threading
    import socket
    import logging
    FORMAT = '%(asctime)s,%(threadName)s %(thread)d,%(message)s'
    logging.basicConfig(level=logging.INFO,format=FORMAT)
    
    
    class ChatUDPServer:
        def __init__(self,ip='127.0.0.1',port=9999):
            self.addr = (ip,port)
            self.sock = socket.socket(type=socket.SOCK_DGRAM)
            self.event = threading.Event()
            self.clients = set()
        def start(self):
            self.sock.bind(self.addr)
            threading.Thread(target=self.receive,name='receive').start()
    
        def receive(self):
            while not self.event.is_set():
                data,raddr= self.sock.recvfrom(1024)
                print(data)
                if data.strip() == b'quit':
                    if raddr in self.clients:
                        self.clients.remove(raddr)
                        logging.info('remove leave clients')
                    # self.sock.close() 面向无连接的 所以每天udp产生的时候不需要close
                    continue
                self.clients.add(raddr)
                for i in self.clients:
                    self.sock.sendto('ack {}'.format(data).encode(),i)
    
        def stop(self):
            for i in self.clients:
                self.sock.sendto(b'bye bye',i)
            self.sock.close()
            self.event.set()
    
    def main():
        cs = ChatUDPServer()
        cs.start()
        while True:
            cmd = input("please set stop command>>>>>>")
            if cmd == 'quit':
                cs.stop()
                break
            logging.info(cs.clients)
            logging.info(threading.enumerate())
    
    if __name__ == '__main__':
        main()

    心跳机制:客户端定时往服务端发送的,服务端不需要ack回复,只记录客户端存活

    class ChatUDPServer:
        def __init__(self,ip='127.0.0.1',port=9999,interval=10):
            self.addr = (ip,port)
            self.sock = socket.socket(type=socket.SOCK_DGRAM)
            self.event = threading.Event()
            self.clients = {}
            self.interval = interval
        def start(self):
            self.sock.bind(self.addr)
            threading.Thread(target=self.receive,name='receive').start()
    
        def receive(self):
            while not self.event.is_set():
                localset = set()      #迭代字典时不能操作字典,把超时的放在集合里面
                data,raddr= self.sock.recvfrom(1024)
                current = datetime.datetime.now().timestamp() #return float
                if data.strip == b'^hb^':  #从client接收到指定的字符串,做判断
                    print('~~~~~~~~',raddr)
                    self.clients[raddr] = current
                    continue
                elif data.strip() == b'quit':
                    if raddr in self.clients:
                        self.clients.pop(raddr,None)
                        logging.info('remove leave clients')
                    # self.sock.close() 面向无连接的 所以不需要close
                    continue
                self.clients[raddr] = current
                for c,stamp in self.clients.items():
                    if current - stamp > self.interval:
                        localset.add(c)
                    else:
                        self.sock.sendto('ack {}'.format(data).encode(), i)
                for  i in localset:    
                    localset.pop(i)
    
        def stop(self):
            for i in self.clients:
                self.sock.sendto(b'bye bye',i)
            self.sock.close()
            self.event.set()

    client端的更改

        def start(self):
            self.sock.connect(self.addr)
            self.sock.sendto(b'hello server',self.addr)
            threading.Thread(target=self.reveive,name='receive').start()
            threading.Thread(target=self._sendb,name="heartbeat",daemon=True).start()
         #daemon 随着主线程退出而退出,不用程序员关注线程退出的问题
    def _sendb(self): while True: self.sock.sendto(b'^hb^',self.addr)
    本文为原创文章,转载请标明出处
  • 相关阅读:
    用java实现输出英文小说飘中出现次数最多的前N个单词(附:使用文件读写)
    程序员修炼之道第一章读后感
    c++实现线性表中的顺序表(数据结构课程作业)
    java第二次课件课后动手动脑习题整理总结(2019年9月23号)
    课堂练习判断字符串是否为回文序列
    大二上学期九月周总结报告三
    以java实现的一个简单登录界面(带验证码)
    关于二进制的原码 、反码、补码的简要解释说明
    几种方式使用
    spring配置数据源的6种方式
  • 原文地址:https://www.cnblogs.com/harden13/p/9191470.html
Copyright © 2011-2022 走看看