zoukankan      html  css  js  c++  java
  • 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协议。
  • 相关阅读:
    Python for Data Science
    Python for Data Science
    Python for Data Science
    Python for Data Science
    Python for Data Science
    Python for Data Science
    Python for Data Science
    Python for Data Science
    Python for Data Science
    软件工程实践总结
  • 原文地址:https://www.cnblogs.com/yinzhengjie/p/11974525.html
Copyright © 2011-2022 走看看