UDP编程入门

UDP编程入门

Client端测试

  • UDP协议不需要建立连接
  • Client端
  • 连接和发送
    1. connect方法未连接,只是占用laddr(本地地址和端口),设置raddr(远端地址和端口)并且记录
    2. send方法,前提必须设置好laddr和raddr
    3. sendto方法,未使用connect方法,自己占用laddr,设置raddr并且记录;使用connect方法,使用其提供的laddr,远端可以使用connect设置的raddr,也可以设置成其他raddr
  • 接收
    1. recv方法,使用前提必须设置laddr
    2. recvfrom方法,同上;返回二元组,(Server_info,(‘ip地址’,port))
    3. 接收信息的同时,想知道raddr,使用recvfrom;只接收信息,使用recv
import logging
import socket

logging.basicConfig(format='%(asctime)s,%(thread)d: %(message)s',level=logging.INFO,datefmt='%Y/%m/%d %H:%M:%S')
raddr = ('127.0.0.1',9999)


class ChatClient:
    def __init__(self,raddr):
        self._client = socket.socket(type=socket.SOCK_DGRAM)
        self._raddr = raddr

    def send(self,msg:str):
        # self._client.connect(self._raddr)    # UDP不建立连接,
        # self._client.send('123'.encode())    # send方法前提是设置laddr和raddr
        self._client.sendto(msg.encode(),self._raddr))     

    def recv(self):
        msg = self._client.recv(1024)   #   recvfrom方法,前提必须有laddr
        # msg,raddr = self._client.recvfrom(1024)         #   recv方法,前提必须有laddr
        print(msg,*raddr)

    def stop(self):
        self._client.close()


c = ChatClient(raddr)
c.send('adfd')     #  Client端口,每次运行都会发生变化
c.recv()
while True:
    data = input('>>>>>>').strip()
    if data == 'quit':
        c.stop()

    c.send(data)

UDP - C / S 编程初步实现

Server端

import logging
import threading
import socket

logging.basicConfig(format='%(asctime)s %(thread)d %(message)s',level=logging.INFO)

class ChatServer:
    def __init__(self,ip='127.0.0.1',port=9999):
        self._laddr = ip,port
        self._server = socket.socket(type=socket.SOCK_DGRAM)
        self.clients = set()    #  记录客户端,去重
        self.event = threading.Event()       #  线程进同步
        self._lock = threading.Lock()

    def start(self):
        self._server.bind(self._laddr)
        threading.Thread(target=self.recv,name='recv').start()    #  recvfrom方法阻塞,另起工作线程,不影响主线程

    def recv(self):
        while not self.event.is_set():
            info,raddr = self._server.recvfrom(1024)  #  收到客户端的信息,就将客户端的raddr记录

            self.clients.add(raddr)     #
            logging.info((raddr,info))

            if info == b'' or info == b'quit':  #  client主动退出
                self.clients.discard(raddr)
                continue

            for addr in self.clients:
                self._server.sendto(info,addr)

    def stop(self):
        with self._lock:
            for c in self.clients:
                self._server.sendto(b'bye',c)

        self.event.set()
        self._server.close()


def main():
    c = ChatServer()
    c.start()
    while True:
        data = input('>>>>').strip()
        if data == 'quit':
            c.stop()
            break

        print(threading.enumerate())

if __name__ == '__main__':   #  放置测试代码
    main()

Client端

import logging
import threading
import socket

logging.basicConfig(format='%(asctime)s %(thread)d %(message)s',level=logging.INFO)

class ChatServer:
    def __init__(self,ip='127.0.0.1',port=9999):
        self._laddr = ip,port
        self._server = socket.socket(type=socket.SOCK_DGRAM)
        self.clients = set()    #  记录客户端,去重
        self.event = threading.Event()       #  线程进同步
        self._lock = threading.Lock()

    def start(self):
        self._server.bind(self._laddr)
        threading.Thread(target=self.recv,name='recv').start()    #  recvfrom方法阻塞,另起工作线程,不影响主线程

    def recv(self):
        while not self.event.is_set():
            info,raddr = self._server.recvfrom(1024)  #  收到客户端的信息,就将客户端的raddr记录

            self.clients.add(raddr)     #
            logging.info((raddr,info))

            if info == b'' or info == b'quit':  #  client主动退出
                self.clients.discard(raddr)
                continue

            for addr in self.clients:
                self._server.sendto(info,addr)

    def stop(self):
        with self._lock:
            for c in self.clients:
                self._server.sendto(b'bye',c)

        self.event.set()
        self._server.close()


def main():
    c = ChatServer()
    c.start()
    while True:
        data = input('>>>>').strip()
        if data == 'quit':
            c.stop()
            break

        print(threading.enumerate())

if __name__ == '__main__':   #  放置测试代码
    main()

增加心跳包

Server端

import logging
import threading
import socket
import datetime

logging.basicConfig(format='%(asctime)s %(thread)d %(message)s',level=logging.INFO)

class ChatServer:
    def __init__(self,ip='127.0.0.1',port=9999,interval=7):
        self._laddr = ip,port
        self._server = socket.socket(type=socket.SOCK_DGRAM)
        self.clients = {}    # 记录心跳时间和客户端addr,并且去重
        self.event = threading.Event()       # 线程进同步
        self._lock = threading.Lock()
        self.interval = interval

    def start(self):
        self._server.bind(self._laddr)

        threading.Thread(target=self.recv,name='recv').start()    #  recvfrom方法阻塞,另起工作线程,不影响主线程
        # threading.Thread(target=self.heartbeat,name='heartbeat',daemon=True).start()   #  监听heartbeat

    # def heartbeat(self):    # Server端,专门启动一个线程,线程不安全,而是心跳会发给多个用户,效果不好
    #     while self.event.wait(3):
    #         msg,raddr = self._server.recvfrom(1024)
    #         current = datetime.datetime.now().timestamp()
    #
    #         if msg == '^_^':
    #             self.clients[raddr] = current
    #         else:
    #             for c in self.clients:
    #                 if current - c > 6:
    #                     self.clients.pop(c)

    def recv(self):
        c_remove = set()   # 记录超时的Client端,且删除
        while not self.event.is_set():
            try:
                info,raddr = self._server.recvfrom(1024)  #  收到客户端的信息,就将客户端的raddr记录
            except Exception as e:
                logging.info(e)
            else:
                current = datetime.datetime.now().timestamp()   # 时间戳,传送成本低,且不用关注时区,时间同步,很重要
                if info.strip() == b'^_^':   #  收到Client的心跳包,重置Client时间
                    self.clients[raddr] = current
                    continue

                if info == b'' or info == b'quit':  #  client主动退出
                    self.clients.pop(raddr)
                    continue

                logging.info((raddr,info))
                self.clients[raddr] = current   # Client端发送信息,也重置时间

                for addr,stamp in self.clients.items():  # 字典、set在遍历的时候,不能改变size
                    if current - stamp > self.interval:
                        c_remove.add(addr)
                    self._server.sendto(info,addr)

                for c in c_remove:
                    print(c_remove)
                    self.clients.pop(c)
                c_remove.clear()

    def stop(self):
        self.event.set()
        self._server.close()


def main():
    c = ChatServer()
    c.start()
    while True:
        data = input('>>>>').strip()
        if data == 'quit':
            c.stop()
            break

        print(c.clients)
        print(threading.enumerate())

if __name__ == '__main__':   #  放置测试代码
    main()

Client端

import logging
import socket
import threading

logging.basicConfig(format='%(asctime)s,%(thread)d: %(message)s',level=logging.INFO,datefmt='%Y/%m/%d %H:%M:%S')
raddr = ('127.0.0.1',9999)


class ChatClient:
    def __init__(self,raddr):
        self._client = socket.socket(type=socket.SOCK_DGRAM)
        self._raddr = raddr
        self.event = threading.Event()

    def start(self):
        self._client.connect(self._raddr)    # 未连接,占用本地地址和端口,这只远端地址和端口

        threading.Thread(target=self.heartbeat,name='heartbeat',daemon=True).start()
        threading.Thread(target=self.recv,name='recv').start()

    def heartbeat(self):
        while not self.event.wait(3):
            self.send('^_^')

    def send(self,msg:str,raddr=None):
        if raddr == None:
            raddr = self._raddr

        self._client.sendto(msg.encode(),raddr)

    def recv(self):
        while not self.event.is_set():
            try:
                msg = self._client.recv(1024)
            except Exception as e:
                logging.info(e)
                self.event.set()
            else:
                logging.info(msg)

    def stop(self):
        self.event.set()
        self.send('quit')    # 主动通知Server端退出
        self._client.close()

def main():      #  可以在测试函数中,多起几个Client端
    c1 = ChatClient(raddr)
    c1.start()

    while True:
        data = input('>>>>>>').strip()
        if data == 'quit':
            c1.stop()
            break
        c1.send(data)

if __name__ == '__main__':    # 客服端测试使用
    main()

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值