Socket programming with UDP
Article Directory
- Related test command
- Find whether to activate windows udp ports:
netstart -anp udp | find "9999"
netstart -anbp udp | findstr 9999
- data sent to the server under linux
echo "123abc" | nc -u 172.0.0.1 9999
- Find whether to activate windows udp ports:
UDP server-side programming
- UDP server-side programming process
- Create a socket object. socket.SOCK_DGRAM
- Bind IP and Port, bind () method
- transfer data
- Receiving data, socket.recvfrom (bufsize [, flags]), to obtain a tuple (string, address)
- Transmission data, socket.sendto (string, address) information is sent to an address of a
- Release resources
import logging
import sys
import socket
logging.basicConfig(format="%(asctime)s %(threadName)s %(thread)d %(message)s",stream=sys.stdout,level=logging.INFO)
server = socket.socket(type=socket.SOCK_DGRAM) #创建一个基于UDP的socket
server.bind(("127.0.0.1",3999)) #立即绑定一个udp端口
# data = server.recv(1024) #阻塞等待数据
data,radde = server.recvfrom(1024) #阻塞等待数据(value,(ip,port))
logging.info("{}-{}".format(radde,data))
server.sendto("{} server msg = {}".format(server.getsockname(),data).encode(),radde)
server.close()
-
UDP client authoring process
- Create a socket object. socket.SOCK_DGRAM
- Transmission data, socket_sendto (string, address) information is sent to an address
- Receiving data, socket.recvfrom (bufsize [, flags]), to obtain a tuple (string, address)
- Release resources
-
The first version
import logging
import sys
import socket
logging.basicConfig(format="%(asctime)s %(threadName)s %(thread)d %(message)s",stream=sys.stdout,level=logging.INFO)
client = socket.socket(type=socket.SOCK_DGRAM)
raddr = "127.0.0.1",3999
client.connect(raddr) #connect方法会自动分配一个本地的UDP地址,和设置UDP的链接对象raddr地址
logging.info(client)
client.send(b"hello") #由于使用了connect方法,所以不指定终端也能发送
client.sendto(b"why",raddr) #也可以使用指定地址发送
data,radde = client.recvfrom(1024)
logging.info("{}-{}".format(radde,data))
client.close()
- The second version, do not use the connect target
import logging
import sys
import socket
logging.basicConfig(format="%(asctime)s %(threadName)s %(thread)d %(message)s",stream=sys.stdout,level=logging.INFO)
client = socket.socket(type=socket.SOCK_DGRAM)
raddr = "127.0.0.1",3999
client.sendto(b"hello",raddr)
logging.info(client)
data,laddr = client.recvfrom(1024)
logging.info("{}-{}".format(data,laddr))
client.close()
- Note : UDP protocol is no link, so you can only either end, such as client data sent to the server, the server exists or not does not matter.
- UDP programming bind, connect, send, sendto, recv, recvfrom method
- After the UDP socket object creation, no occupying a local address and port.
method | Explanation |
---|---|
bind(laddr) | You can specify a local address and port laddr, will take up immediately, laddr a tuple, (ip, prot) |
connect(raddr) | Randomly assigned a local port laddr, remote address and port binds raddr, raddr a tuple, (ip, prot) |
sendto(msg,raddr) | You can occupy immediately laddr local address and port, and the specified data to the distal end. Only with local port binding, sendto can send data to any remote data msg # to be sent. bytes type. a tuple (ip, prot) raddr # remote address and port composition |
send(msg) | And with the need to connect method can be used already from the local port data to the raddr distal specified msg # bytes need to be sent a message type |
recv(buffersize) | After required by law to occupy the local port, accept the return of data, buffersize specify a buffer size |
recvfrom(buffersize) | After required by law to take up a local port, the received data and returns the address of the peer-tuple (MSG, RADDR) buffersize specify a buffer size. |
Exercise -UDP version of Group Chat
- Server code
"""
author:xdd
date:2019-06-17 09:20
"""
import logging
import sys
import socket
import threading
import datetime
logging.basicConfig(format="%(asctime)s %(threadName)s %(thread)d %(message)s",stream=sys.stdout,level=logging.INFO)
class ChatUDPServer:
def __init__(self,ip="127.0.0.1",port=3999,timeout=10):
self.socket = socket.socket(type = socket.SOCK_DGRAM)
self.laddr = ip,port
self.event = threading.Event()
self.timeout = timeout
self.clients = {}
def start(self):
socket = self.socket
socket.bind(self.laddr)
threading.Thread(target=self.run,name="run").start()
def run(self):
socket = self.socket
clients = self.clients
while not self.event.is_set():
try:
data,raddr = socket.recvfrom(1024)
except:
continue
utctime = datetime.datetime.now().timestamp()
if data.strip() == b"by": #如果用户自己发送by表示要退出
self.clients.pop(raddr)
continue
clients[raddr] = utctime
if data.strip() == b"^hh^": #如果是心跳,就忽略
continue
msg = "[{}] {}".format(raddr,data)
logging.info(msg)
outclient = [] #记录超时的链接地址
for cr,tm in clients.items():
if 0 <= utctime - tm <= self.timeout:
socket.sendto(msg.encode(),cr)
else:
outclient.append(cr)
for cr in outclient: #超时后删除
self.clients.pop(cr)
def stop(self):
try:
self.event.set()
self.socket.close()
finally:
self.clients.clear()
@classmethod
def main(cls):
chserver = cls()
chserver.start()
while True:
cmd = input(">>>>")
if cmd.strip() == "quit":
chserver.stop()
threading.Event().wait(1)
break
logging.info(threading.enumerate())
logging.info(chserver.clients)
ChatUDPServer.main()
- UDP client code version
"""
author:xdd
date:2019-06-17 10:26
"""
import logging
import sys
import socket
import threading
logging.basicConfig(format="%(asctime)s %(thread)d %(threadName)s %(message)s",stream=sys.stdout,level=logging.INFO)
class UdpClient:
def __init__(self,ip="127.0.0.1",prost = 3999,heartbeattime = 5):
self.socket = socket.socket(type=socket.SOCK_DGRAM)
self.raddr = ip,prost
self.event = threading.Event()
self.heartbeattime = heartbeattime
def start(self):
self.socket.connect(self.raddr)
self.send("hello")
threading.Thread(target=self.heartbeat,name="heartbeat").start()
threading.Thread(target=self.run,name="client").start()
#发送心跳包,保持链接
def heartbeat(self):
while not self.event.wait(self.heartbeattime):
self.send("^hh^")
logging.info("心跳结束")
def run(self):
while not self.event.is_set():
try:
data,raddr = self.socket.recvfrom(1024)
except:
continue
logging.info("[ {} msg ] {}".format(raddr,data))
def send(self,msg):#发送消息
socket = self.socket
socket.send(msg.encode())
def stop(self):#停止
self.event.set()
self.socket.close()
@classmethod
def main(cls):
client = cls()
client.start()
while True:
cmd = input(">>>")
if cmd.strip() == "quit":
client.stop()
break
else:
client.send(cmd)
UdpClient.main()
- Server code
- Increased heartbeat heartbeat ack mechanism or mechanisms. These mechanisms can also be used when TCP communication.
- Heartbeat information is sent to the other end of the timing end, each time data is generally better. Heartbeat interval convention trying to be difficult.
- i.e. ack response confirmation is returned after the end of the other end of the received message.
- Heartbeat mechanism
- Generally the client regularly to the server, the server does not respond to client needs ack, you only need to record the client is still alive on the line.
- If the server is timed sent to the client, the general need to represent a client in response ack alive, if the client does not receive ack, the server remove their information. This implementation is more complex, with less.
- Can also have made two-way heartbeat, less used.
UDP application protocol
- UDP is a connectionless protocol, which is based on the following assumptions:
- Network is good enough
- The message will not loss
- Packets are not out of order
- However, even in the local area, we can not guarantee no packet loss, and packet arrival is not necessarily in order.
- Scenarios
- Video, audio transmission, in general, lose some packets, not a big problem, lost most of these images, hear words, words can be re-sent to solve. Massive data acquisition, the sensor data sent to e.g., lost dozens, hundreds of data does not matter.
- DNS protocol, a small content data, a packet will be able to query a result, there is no disorder, loss, re-request resolution.
- In general, UDP performance is better than TCP, but the high reliability requirements of the occasion or to choose TCP protocol.