• Socket网络编程-UDP编程


                Socket网络编程-UDP编程

                                       作者:尹正杰

    版权声明:原创作品,谢绝转载!否则将追究法律责任。

    一.UDP编程概述

    1>.UDP服务端编程流程

      创建socket对象。socket.SOCK_DGRAM 

      绑定IP和Port,bind()方法
      传输数据     接收数据,socket.recvfrom(bufsize[, flags]),获得一个二元组(string, address)     发送数据,socket.sendto(string, address) 发给某地址某信息

      释放资源

    2>.UDP客户端编程流程

      创建socket对象。socket.SOCK_DGRAM
      发送数据,socket.sendto(string, address)发给某地址某信息
        
      接收数据,socket.recvfrom(bufsize[, flags]),获得一个二元组(string, address)

      释放资源

    3>.UDP编程中常用的方法

      bind方法
        可以指定本地地址和端口laddr,会立即占用
    
      connect方法     可以立即占用本地地址和端口laddr,填充远端地址和端口raddr
      sendto方法     可以立即占用本地地址和端口laddr,并把数据发往指定远端。只有有了本地绑定端口,sendto就可以向任何远端发送数据
      send方法     需要和connect方法配合,可以使用已经从本地端口把数据发往raddr指定的远端
      recv方法     要求一定要在占用了本地端口后,返回接收的数据
      recvfrom方法     要求一定要占用了本地端口后,返回接收的数据和对端地址的二元组

    4>.心跳机制

      增加心跳heartbeat机制或ack机制。这些机制同样可以用在TCP通信的时候。 心跳,就是一端定时发往另一端的信息,一般每次数据越少越好。心跳时间间隔约定好就行。 ack即响应,一端收到另一端的消息后返回的确认信息。
    
      心跳机制实现策略:
        1.一般来说是客户端定时发往服务端的,服务端并不需要ack回复客户端,只需要记录该客户端还活 着就行了。当然服务端也可响应客户端
        2.如果是服务端定时发往客户端的,一般需要客户端ack响应来表示活着,如果没有收到某客户端的ack响应,服务端移除其信息。这种实现较为复杂,用的较少
        3.也可以双向都发心跳的,用的更少 

    二.UDP版群聊案例

    1>.UDP版群聊服务端代码 

     1 #!/usr/bin/env python
     2 #_*_conding:utf-8_*_
     3 #@author :yinzhengjie
     4 #blog:http://www.cnblogs.com/yinzhengjie
     5 
     6 import socket
     7 import threading
     8 import datetime
     9 import logging
    10 
    11 FORMAT = "%(asctime)s %(threadName)s %(thread)d %(message)s"
    12 logging.basicConfig(format=FORMAT, level=logging.INFO)
    13 
    14 
    15 #在服务器端代码中使用第一种心跳机制改进
    16 class ChatUDPServer:
    17     def __init__(self, ip='127.0.0.1', port=6688, interval=10):
    18         self.addr = (ip, port)
    19         self.sock = socket.socket(type=socket.SOCK_DGRAM)
    20         self.clients = {} # 记录客户端,改为字典
    21         self.event = threading.Event()
    22         self.interval = interval # 默认10秒,超时就要移除对应的客户端
    23 
    24     def start(self):
    25         self.sock.bind(self.addr) # 立即绑定
    26         # 启动线程
    27         threading.Thread(target=self.recv, name='recv').start()
    28 
    29     def recv(self):
    30         removed = set()  # 超时的
    31         while not self.event.is_set():
    32             data, raddr = self.sock.recvfrom(1024)  # 阻塞接收数据
    33             current = datetime.datetime.now().timestamp()  # float
    34             if data.strip() == b'^hb^': # 心跳信息
    35                 print('^^^^^^^^hb', raddr)
    36                 self.clients[raddr] = current
    37                 continue
    38             elif data.strip() == b'quit':
    39                 #有可能发来数据的不在clients中
    40                 self.clients.pop(raddr, None)
    41                 logging.info('{} leaving'.format(raddr))
    42                 continue
    43 
    44             #有信息来就更新时间
    45             #什么时候比较心跳时间呢? 发送信息的时候,反正要遍历一遍
    46             self.clients[raddr] = current
    47             msg = '{}. from {}:{}'.format(data.decode(), *raddr)
    48             logging.info(msg)
    49             msg = msg.encode()
    50 
    51             for c, stamp in self.clients.items():
    52                 if current - stamp > self.interval:
    53                     removed.add(c)
    54                 else:
    55                     self.sock.sendto(msg, c)  # 不保证对方能够收到
    56 
    57             for c in removed:
    58                self.clients.pop(c)
    59                removed.clear()
    60 
    61     def stop(self):
    62         self.event.set()
    63         self.clients.clear()
    64         self.sock.close()
    65 
    66 def main():
    67     server = ChatUDPServer()
    68     server.start()
    69 
    70     while True:
    71         cmd = input(">>> ")
    72         if cmd.strip() == 'quit':
    73             server.stop()
    74             break
    75         logging.info(threading.enumerate())
    76         logging.info(server.clients)
    77 
    78 if __name__ == '__main__':
    79     main()

     2>.UDP版群聊客户端代码

     1 #!/usr/bin/env python
     2 #_*_conding:utf-8_*_
     3 #@author :yinzhengjie
     4 #blog:http://www.cnblogs.com/yinzhengjie
     5 
     6 import threading
     7 import socket
     8 import logging
     9 
    10 FORMAT = "%(asctime)s %(threadName)s %(thread)d %(message)s"
    11 logging.basicConfig(format=FORMAT, level=logging.INFO)
    12 
    13 
    14 
    15 #增加定时发送心跳代码
    16 class ChatUdpClient:
    17     def __init__(self, rip='127.0.0.1', rport=6688):
    18         self.sock = socket.socket(type=socket.SOCK_DGRAM)
    19         self.raddr = (rip, rport)
    20         self.event = threading.Event()
    21 
    22     def start(self):
    23         self.sock.connect(self.raddr) # 占用本地地址和端口,设置远端地址和端口
    24         threading.Thread(target=self._sendhb, name='heartbeat', daemon=True).start()
    25         threading.Thread(target=self.recv, name='recv').start()
    26 
    27     def _sendhb(self): # 心跳
    28         while not self.event.wait(5):
    29             self.send('^hb^')
    30 
    31     def recv(self):
    32         while not self.event.is_set():
    33             data, raddr = self.sock.recvfrom(1024)
    34             msg = '{}. from {}:{}'.format(data.decode(), *raddr)
    35             logging.info(msg)
    36 
    37     def send(self, msg:str):
    38         self.sock.send(msg.encode())
    39         # self.sock.sendto(msg.encode(), self.raddr)
    40 
    41     def stop(self):
    42         self.event.set()
    43         self.send('quit')   #通知服务端退出
    44         self.sock.close()
    45 
    46 
    47 def main():
    48     client1 = ChatUdpClient()
    49     client2 = ChatUdpClient()
    50     client1.start()
    51     client2.start()
    52     print(client1.sock)
    53     print(client2.sock)
    54 
    55     while True:
    56         cmd = input('Input your words >> ')
    57         if cmd.strip() == 'quit':
    58             client1.stop()
    59             client2.stop()
    60             break
    61         client1.send(cmd)
    62         client2.send(cmd)
    63 
    64 
    65 if __name__ == '__main__':
    66     main()

    三.UDP协议应用

      UDP是无连接协议,它基于以下假设:
        网络足够好
        消息不会丢包
        包不会乱序
    
      但是,即使是在局域网,也不能保证不丢包,而且包的到达不一定有序。
      应用场景:
        视频、音频传输,一般来说,丢些包,问题不大,最多丢些图像、听不清话语,可以重新发话语来解 决。     海量采集数据,例如传感器发来的数据,丢几十、几百条数据也没有关系。 DNS协议,数据内容小,一个包就能查询到结果,不存在乱序,丢包,重新请求解析。     一般来说,UDP性能优于TCP,但是可靠性要求高的场合的还是要选择TCP协议。 DNS使用的就是UDP协议和TCP协议。
  • 相关阅读:
    做项目时写的数据库操作类。SqlHelper.cs(三)
    写的登录三层结构demo(工厂模式)
    在GridView中进行排序
    微信小程序 POST请求
    mysql replace into用法详细说明
    ThinkPHP Where 条件中使用表达式
    Google发转码工具 可将安卓程序转至iOS
    IOS笔记 本地化多语言支持
    persits.jpeg 水印组件
    PHP 数组操作
  • 原文地址:https://www.cnblogs.com/yinzhengjie/p/11974525.html
Copyright © 2020-2023  润新知