UDP编程

测试命令
#windows查找udp是否启动端口
> netstat -anp udp | find "9999"
> netstat -anbp udp | findstr 9999

UDP服务端编程

在这里插入图片描述

  • 创建socket对象。socket.SOCK_DGRAM

  • 绑定IP和Port,bind()方法

  • 传输数据

    • 接收数据,socket.recvfrom(bufsize[,flags]),获得一个二元组(string,address)
    • 发送数据,socket.sendto(string,address)发给某地址某信息
  • 释放资源

import socket
server=socket.socket(type=socket.SOCK_DGRAM)
server.bind(('172.22.141.176',9999))
data=server.recv(1024)
print(data)
data=server.recvfrom(1024)
print(data)
server.sendto(b'110110',('172.22.141.176',10000))
server.close()

在这里插入图片描述
在这里插入图片描述

UDP客户端编程流程

  • 创建socket对象,socket.SOCK_DGRAM
  • 发送数据,socket.sendto(string,address)发送给某地址某信息
  • 接收数据,socket.recvfrom(bufsize[,flags]),获得一个二元组(string,address)
  • 释放资源
import socket
client=socket.socket(type=socket.SOCK_DGRAM)
raddr=('172.22.141.176',11111)

client.connect(raddr)
client.sendto(b'1008611',raddr)
client.send(b'132132323')
data=client.recvfrom(1024)
print(data)
data=client.recv(1024)
print(data)

client.close()

在这里插入图片描述
注意:UDP是无连接协议,所以可以只有任何一端,例如客户端数据发往服务端,服务端存在与否无所谓。

UDP编程bind、connect、send、sendto、recv、recvfrom方法使用
UDP的socket对象创建后,是没有占用本地地址和端口的。

方法说明
bind方法可以指定本地地址和端口laddr,会立即占用
connect方法可以立即占用本地地址和端口laddr,填充远端地址和端口raddr
sendto方法可以立即占用本地地址和端口laddr,并发数据发往指定远端,只有有了本地绑定端口,sendto就可以向任何远端发送数据
send方法需要和connect方法配合,可以使用已经从本地端口把数据发往raddr指定的远端
recv方法要求一定要在占用了本地端口后,返回接收的数据
recvfrom方法要求一定要占用本地端口后,返回接收的数据和对端地址的二元组
  • UDP群聊服务端
import socket
import threading
class ChatUdpServer:
    def __init__(self,ip='0.0.0.0',port=11112):
        self.ipaddr=ip,port
        self.sock=socket.socket(type=socket.SOCK_DGRAM)
        self.clients=set()
        self.event=threading.Event()
        self.lock=threading.Lock()
    def start(self):
        self.sock.bind(self.ipaddr)
        threading.Thread(target=self.recv).start()
    def recv(self):
        while not self.event.is_set():
            try:
                data,other=self.sock.recvfrom(1024)
                print(data)
                if data==b'quit':
                    with self.lock:
                        self.clients.remove(other)
                    continue
                with self.lock:
                    self.clients.add(other)
                with self.lock:
                    for i in self.clients:
                        self.sock.sendto(data,i)
            except:
                print('error')
    def stop(self):
        self.event.set()
        self.clients.clear()
        try:
            self.sock.close()
        except:
            print('over')
        finally:
            print('over')
chat=ChatUdpServer()
chat.start()
while True:
    cmd=input(">>>").strip()
    if cmd=='quit':
        chat.stop()
        break
    print(threading.enumerate())
  • UDP客户端
import socket
import threading
import time
class ChatUdpClient:
    def __init__(self,ip='172.22.141.176',port=11113):
        self.sock=socket.socket(type=socket.SOCK_DGRAM)
        self.ipadd=ip,port
        self.event=threading.Event()
        self.lock=threading.Lock()
    def start(self):
        self.sock.connect(self.ipadd)
        threading.Thread(target=self.send).start()
        threading.Thread(target=self.recv).start()
    def send(self):
        while not self.event.is_set():
            with self.lock:
                data=input("你说话呀:").strip().encode()
                self.sock.sendto(data,self.ipadd)
    def recv(self):
        while not self.event.is_set():
            message=self.sock.recvfrom(1024)
            print(message)
    def stop(self):
        self.event.set()
        self.sock.close()
chat=ChatUdpClient()
chat.start()
while True:
    time.sleep(10)
    cmd=input('>>>').strip()
    if cmd=='quit':
        chat.stop()
    print(threading.enumerate())

上面的例子并不完善,如果客户端断开了,服务端不知道,每一个服务端还需要对所有客户端发送数据,包括已经断开的客户端

服务端代码改进

增加心跳机制heartbeat机制或ack机制,这些机制同样用在TCP通信的时候。
心跳,就是一端定时发往另一端的信息。
ack即相应,一端收到另一端的信息后返回的确认信息。
心跳机制:
1、一般来说是客户端定时发往服务端的,服务端并不需要ack回复客户端,只需要记录该客户端活着就行了。
2、如果是服务端定时发往客户端的,一般需要客户端ack相应来表示活着,如果没有收到ack的客户端,服务端移除其信息,这种实现较为复杂,用的较少
3、双向发心跳

  • 服务器端增加心跳机制
import socket
import threading
import datetime
class ChatUdpServer:
    def __init__(self,ip='172.22.141.176',port=11113,interval=10):
        self.ipaddr=ip,port
        self.sock=socket.socket(type=socket.SOCK_DGRAM)
        self.clients=dict()
        self.event=threading.Event()
        self.lock=threading.Lock()
        self.interval=interval
    def start(self):
        self.sock.bind(self.ipaddr)
        threading.Thread(target=self.recv).start()
    def recv(self):
        while not self.event.is_set():
            start = datetime.datetime.now().timestamp()
            try:
                data,other=self.sock.recvfrom(1024)
                if data.strip()==b'^bbb^':
                    self.clients[other]=start
                    continue
                elif data==b'quit':
                    with self.lock:
                        self.clients.pop(other)
                    continue
                with self.lock:
                    self.clients[other]=start
                with self.lock:
                    fail=set()
                    print(fail)
                    for i,k in self.clients.items():
                        print(self.clients)
                        print(k)
                        print(start)
                        if start-k>self.interval:
                            fail.add(i)
                        else:
                            self.sock.sendto(data,i)
                    for c in  fail:
                        self.clients.pop(c)
            except:
                print('error')
    def stop(self):
        self.event.set()
        self.clients.clear()
        try:
            self.sock.close()
        except:
            print('over')
        finally:
            print('over')
chat=ChatUdpServer()
chat.start()
while True:
    cmd=input(">>>").strip()
    if cmd=='quit':
        chat.stop()
        break
    print(threading.enumerate())
  • 客户端增加心跳机制
import socket
import threading
import time
class ChatUdpClient:
    def __init__(self,ip='172.22.141.176',port=11113):
        self.sock=socket.socket(type=socket.SOCK_DGRAM)
        self.ipadd=ip,port
        self.event=threading.Event()
        self.lock=threading.Lock()
    def start(self):
        self.sock.connect(self.ipadd)
        threading.Thread(target=self.send).start()
        threading.Thread(target=self.recv).start()
        threading.Thread(target=self.heartbeat).start()
    def heartbeat(self):
        while not self.event.wait(5):
            self.sock.send(b'^^bb^^')
    def send(self):
        while not self.event.is_set():
            with self.lock:
                data=input("你说话呀:").strip().encode()
                self.sock.sendto(data,self.ipadd)
    def recv(self):
        while not self.event.is_set():
            message=self.sock.recvfrom(1024)
            print(message)
    def stop(self):
        self.event.set()
        self.sock.close()
chat=ChatUdpClient()
chat.start()
while True:
    time.sleep(10)
    cmd=input('>>>').strip()
    if cmd=='quit':
        chat.stop()
    print(threading.enumerate())
  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值