#windows查找udp是否启动端口
> netstat -anp udp | find "9999"
> netstat -anbp udp | findstr 9999
创建socket对象。socket.SOCK_DGRAM
绑定IP和Port,bind()方法
传输数据
释放资源
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()
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方法 | 要求一定要占用本地端口后,返回接收的数据和对端地址的二元组 |
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())
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())