45网络编程_UDP-创新互联

创新互联公司专注为客户提供全方位的互联网综合服务,包含不限于成都做网站、网站制作、成都外贸网站建设、浮山网络推广、小程序开发、浮山网络营销、浮山企业策划、浮山品牌公关、搜索引擎seo、人物专访、企业宣传片、企业代运营等,从售前售中售后,我们都将竭诚为您服务,您的肯定,是我们大的嘉奖;创新互联公司为所有大学生创业者提供浮山建站搭建服务,24小时服务热线:18982081108,官方网址:www.cdcxhl.com

UDP编程:

>netstat -anp tcp | find "9998"   #win下

#echo "123abc" | nc -u 127.0.0.1 9998

应用:

无连接协议,基于如下假设:网络足够好、消息不会丢包、包不会乱序;

1、音频、视频传输,一般丢些包,问题不大,最多丢些图像、听不清说话,再次说话即可;

2、海量采集数据,如传感器发来的数据,丢几十、几百条数据也没关系;

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

注:

即使在LAN,也不能保证不丢包,且包的到达不一定有序;

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

QUIC,quick udp internet connection,google,是谷歌制定的一种基于UDP的低时延的互联网传输层协议。在2016年11月国际互联网工程任务组(IETF)召开了第一次QUIC工作组会议,受到了业界的广泛关注。这也意味着QUIC开始了它的标准化过程,成为新一代传输层协议;

UDP服务端编程步骤:

创建socket对象,sock=socket.socket(type=socket.SOCK_DGRAM);

绑定ip和port,bind()方法;

传输数据:

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

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

释放资源;

例:

import socket

sock = socket.socket(type=socket.SOCK_DGRAM)

addr = ('127.0.0.1', 9998)

sock.bind(addr)

data, clientaddr = sock.recvfrom(1024)

print(clientaddr)

msg = 'ack: {}'.format(data.decode())

sock.sendto(msg.encode(), clientaddr)

输出:

('127.0.0.1', 9999)

45网络编程_UDP

45网络编程_UDP

例,ChatServerUdp:

ver1:

class ChatServerUdp:

def __init__(self, ip='127.0.0.1', port=9998):

self.sock = socket.socket(type=socket.SOCK_DGRAM)

self.addr = (ip, port)

self.event = threading.Event()

self.clients = set()   #集合,去重,client主动退出后要清此数据结构

def start(self):

self.sock.bind(self.addr)

threading.Thread(target=self._recv, name='recv').start()

def stop(self):

for c in self.clients:   #业务中udp的server关闭时不会通知client

self.sock.sendto(b'end', c)

self.sock.close()   #udp的socket关闭很快,不会有很多垃圾

self.event.set()

def _recv(self):   #_recv中使用多线程场景,在一对多情况下,server发送消息和接收消息出现不匹配时,用另一线程单独处理发送数据,否则接收和发送是同步,只有等发送完才能继续再次接收

while not self.event.is_set():

data, client = self.sock.recvfrom(1024)

data = data.strip().decode()

if data == 'quit':

self.clients.remove(client)

continue   #关键,接收下个client的消息

self.clients.add(client)

print(self.clients)

msg = 'ack: {}'.format(data)

for c in self.clients:

self.sock.sendto(msg.encode(), c)

if __name__ == '__main__':

cs = ChatServerUdp()

cs.start()

myutils.show_threads()

例:

ver2:

增加ack和heartbeat机制;

心跳即一端定时发往另一端信息,一般每次发的数据越少越好,心跳时间间隔约定好就行,ack响应,一端收到另一端的消息后返回的信息;

心跳包设计:

c主动,一般由client发hb-->server,server并不需要发ack-->client,只需要记录client还活着就行;

s主动,server发hb扫一遍client,一般需要client发ack响应来表示活着,server没收到ack就断开与client连接,server移除其信息,这种实现较为复杂,用的少;

c-s双向,用的更少;

class ChatServerUdp:

def __init__(self, ip='127.0.0.1', port=9998, interval=10):

self.sock = socket.socket(type=socket.SOCK_DGRAM)

self.addr = (ip, port)

self.event = threading.Event()

self.clients = {}

self.interval = interval

def start(self):

self.sock.bind(self.addr)

threading.Thread(target=self._recv, name='recv').start()

def stop(self):

for c in self.clients:

self.sock.sendto(b'end', c)

self.sock.close()

self.event.set()

def _recv(self):

while not self.event.is_set():

lostset = set()

data, client = self.sock.recvfrom(1024)

data = data.strip().decode()

current = datetime.datetime.now().timestamp()

if data == '^hb^' or data == 'reg':

print('hb')

self.clients[client] = current

continue

elif data == 'quit':

self.clients.pop(client, None)

logging.info('{} leaving'.format(client))

         continue

self.clients[client] = current

print(self.clients)

msg = 'ack: {} {}\n{}\n'.format(*client, data)

logging.info(msg)

for c, stamp in self.clients.items():

if current - stamp > self.interval:

lostset.add(c)

else:

self.sock.sendto(msg.encode(), c)

for c in lostset:

self.clients.pop(c)

if __name__ == '__main__':

cs = ChatServerUdp()

cs.start()

myutils.show_threads()

UDP客户端编程步骤:

创建socket对象,socket.socket(type=socket.SOCK_DGRAM);

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

释放资源;

udp客户端编程中,只能在sendto()后,才能recvfrom(),否则OSError;

例:

sock = socket.socket(type=socket.SOCK_DGRAM)

addr = ('127.0.0.1', 9998)

data = 'test_data'.encode()

sock.sendto(data, addr)   #方式1,使用sendto()和recvfrom(),建议用此种方式

data, saddr = sock.recvfrom(1024)   #也可用recv(),只不过不知道谁发的消息了

print(data, saddr)

sock.close()

# sock.connect(addr)   #方式2,用connect()连接后才能用send()或sendto(),没有connect()连接只能用sendto();此方式可能会有问题,client-->server正常,server-->client,server上连client的端口不对了

# sock.send(data)

# data, saddr = sock.recvfrom(1024)

# print(data, saddr)

# sock.close()

例:

addr = ('127.0.0.1', 9998)

event = threading.Event()

def recv1(sock:socket.socket, event:threading.Event):

while not event.is_set():

data, saddr = sock.recvfrom(1024)

logging.info('recv: {} ; from: {}'.format(data, saddr))

sock1 = socket.socket(type=socket.SOCK_DGRAM)

sock1.sendto('udp client1 send'.encode(), addr)

threading.Thread(target=recv1, args=(sock1, event)).start()   #recvfrom()必须在sendto()或connect()之后,否则OSError,即recvfrom()操作之前应该先sendto()或connect();如果用connect(),则远端必须有服务

def recv2(sock:socket.socket, event:threading.Event):

while not event.is_set():

data, saddr2 = sock.recvfrom(1024)

logging.info('recv: {} ; from: {}'.format(data, saddr2))

sock2 = socket.socket(type=socket.SOCK_DGRAM)

sock2.connect(addr)

threading.Thread(target=recv2, args=(sock2, event)).start()

threading.Event().wait(5)

sock2.sendto('udp client2 send'.encode(), addr)

event.wait(2)

sock2.send('udp client2.1 send'.encode())

while True:

if input('>>> ').strip() == 'quit':

sock1.close()

sock2.close()

event.wait(3)

break

logging.info('end')

例,ChatClientUdp:

注:此代码有问题,在发送hb后一直阻塞

class ChatClientUdp:

def __init__(self, ip='127.0.0.1', port=9998, interval=5):

self.sock = socket.socket(type=socket.SOCK_DGRAM)

self.addr = (ip, port)

self.event = threading.Event()

self.interval = interval

self.sock.connect(self.addr)

self._sendhb()

def start(self):

# self.sock.send(b'reg')

threading.Thread(target=self._sendhb, name='hb', daemon=True).start()

# threading.Thread(target=self._recv, name='recv').start()

# self._recv()

def stop(self):

self.send()

self.sock.close()

self.event.wait(2)

self.event.set()

def _sendhb(self):

while not self.event.wait(5):

self.sock.sendto(b'^hb^', self.addr)

def send(self, msg:str='quit'):

self.sock.sendto(msg.encode(), self.addr)

def _recv(self):

while not self.event.is_set():

data, addr = self.sock.recvfrom(1024)

logging.info('recv {} from {}'.format(data, addr))

cc = ChatClientUdp()

cc.start()

while True:

data = input('plz input string>>> ')

if data == 'quit':

cc.stop()

break

else:

cc.send(data)

logging.info('end')

另外有需要云服务器可以了解下创新互联cdcxhl.cn,海内外云服务器15元起步,三天无理由+7*72小时售后在线,公司持有idc许可证,提供“云服务器、裸金属服务器、高防服务器、香港服务器、美国服务器、虚拟主机、免备案服务器”等云主机租用服务以及企业上云的综合解决方案,具有“安全稳定、简单易用、服务可用性高、性价比高”等特点与优势,专为企业上云打造定制,能够满足用户丰富、多元化的应用场景需求。


网站名称:45网络编程_UDP-创新互联
标题路径:http://scyanting.com/article/dejspg.html