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)

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM