UDP协议编程_udp c语言

UDP协议编程_udp c语言UDP 编程与 Socket 文章目录 UDP 编程与 Socket UDP 服务端编程 练习 UDP 版本群聊 UDP 协议的应用 相关测试命令 windows 查找 udp 是否启动端口 netstart anp udp find 9999 netstart anbp udp findstr 9999 linux 下发给服务端数据 echo 123abc nc u 172 0 0

UDP编程与Socket

文章目录

UDP编程与Socket

UDP服务端编程

练习–UDP版本群聊

UDP协议的应用

相关测试命令

windows查找udp是否启动端口:

netstart -anp udp | find "9999"

netstart -anbp udp | findstr 9999

linux下发给服务端数据

echo "123abc" | nc -u 172.0.0.1 9999

UDP服务端编程

UDP服务端编程流程

创建socket对象。socket.SOCK_DGRAM

绑定IP和Port,bind()方法

传输数据

接收数据,socket.recvfrom(bufsize[,flags]),获得一个二元组(string,address)

发送数据,socket.sendto(string,address)发给某地址某信息

释放资源

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客户端编写流程

创建socket对象。socket.SOCK_DGRAM

发送数据,socket_sendto(string,address)发给某地址信息

接收数据,socket.recvfrom(bufsize[,flags]),获得一个二元组(string,address)

释放资源

第一个版本

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()

第二个版本,不使用connect指定目标

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()

注意:UDP是无链接协议,所以可以只有任何一端,例如客户端数据发往服务端,服务端存在与否无所谓。

UDP编程中bind、connect、send、sendto、recv、recvfrom方法使用

UDP的socket对象创建后,是没有占用本地地址和端口的。

方法说明
bind(laddr)可以指定本地地址和端口laddr,会立即占用,laddr为一个元组,(ip,prot)
connect(raddr)会随机分配一个本地的端口laddr,会绑定远端地址和端口raddr,raddr是个元组,(ip,prot)
sendto(msg,raddr)可以立即占用本地地址和端口laddr,并把数据发往指定远端。只有有了本地绑定的端口,sendto就可以向任何远端发送数据msg #要发送的数据。bytes类型。raddr#远端地址和端口组成的一个元组(ip,prot)
send(msg)需要和connect方法配合,可以使用已经从本地端口把数据发往raddr指定的远端msg#需要发送的消息bytes类型
recv(buffersize)要求一定要在占用了本地端口后,返回接受的数据,buffersize指定一个缓冲区大小
recvfrom(buffersize)要求一定要占用了本地端口后,返回接收的数据和对端地址的二元组(msg,raddr)buffersize指定一个缓冲区大小。

练习–UDP版本群聊

服务端代码

""" 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版本客户端代码

""" 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()

服务端代码

增加心跳heartbeat机制或ack机制。这些机制同样可以用在TCP通信的时候。

心跳,就是一端定时发往另一端的信息,一般每次数据越少越好。心跳时间间隔约定好就行。

ack即响应,一端收到另一端的消息后返回的确认信息。

心跳机制

一般来说是客户端定时发往服务端的,服务端并不需要ack回复客户端,只需要记录该客户端还活着就行了。

如果是服务端定时发往客户端的,一般需要客户端ack响应来表示活着,如果没有收到ack的客户端,服务端 移除其信息。这种实现较为复杂,用的较少。

也可以双向都发心跳的,用的更少。

UDP协议的应用

UDP是无连接协议,它基于以下假设:

网络足够好

消息不会丢包

包不会乱序

但是,即使是在局域网,也不能保证不丢包,而且包的到达不一定有序。

应用场景

视频、音频传输,一般来说,丢些包,问题不大,最多丢些图像、听不清话语,可以重新发话语来解决。 海量采集数据,例如传感器发来的数据,丢几十、几百条数据也没有关系。

DNS协议,数据内容小,一个包就能查询到结果,不存在乱序,丢包,重新请求解析。

一般来说,UDP性能优于TCP,但是可靠性要求高的场合的还是要选择TCP协议。

编程小号
上一篇 2025-03-07 09:06
下一篇 2025-01-28 17:21

相关推荐

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/hz/130538.html