zoukankan      html  css  js  c++  java
  • Python-UDP编程

    1、UDP编程:

      测试命令:

        windows:

           netstat  -anp udp | findstr 9999
        Linux: 发给服务器数据

          echo '233' | nc -u 127.0.0.1 9999

    2、UDP服务器端编程:

      UDP服务器端编程流程:(从图中可以看到,服务器端只需要一个socket)

        

        • 创建socket 对象,socket.SOCK_DGRAM
        • 绑定IP 和 Port, bind() 方法
        • 传输数据:
          • 接受数据:socket.recvfrom(bufsize [, flags] ) ,获取一个二元组()
          • 发送数据:socket.sendto(string, address) 发给某地址某信息
        • 释放资源     
     1 import socket
     2 
     3 socket = socket.socket(type=socket.SOCK_DGRAM)
     4 
     5 ip = '127.0.0.1'
     6 port = 9999
     7 
     8 laddr = ip, port
     9 
    10 socket.bind(laddr) #  服务器端正式启动
    11 
    12 data, raddr = socket.recvfrom(1024) # 没有接收到,会处于阻塞状态
    13 
    14 socket.sendto(data, raddr)
    15 
    16 socket.close()

      UDP客户端编程流程

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

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

      UDP的socket对象创建后,是么有占用本地地址额端口的

      

        注意:

          1、UDP 创建socket后,不能直接recv,recvfrom,,只有知道了本地地址和端口,服务器端才能知道数据应该发给你

          2、send 和 connect 搭配使用。

       心跳机制:

        加一个ack机制 和 心跳 hearbeat

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

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

        心跳机制:

      1. 一般来说是客户端定时发往服务器端的,服务器端并不需要ACK 回复客户端,只需要记录该客户端还活着就行(比如群聊,如果客户端不活着,就没必要回复)
      2. 如果是服务器端定时发往客户端的,一般需要 客户端ack 响应,表示活着,,如果没有收到ack的客户端,服务器端移除其信息,这种实现比较复杂,较少使用。
      3. 也就是双向都发送心跳,很少使用

    UDP实现群聊:

      server端:

     1 import socket
     2 import threading
     3 import logging
     4 import datetime
     5 
     6 
     7 FORMAT = '%(asctime)s %(thread)s %(threadName)s %(message)s'
     8 logging.basicConfig(format=FORMAT, level=logging.INFO)
     9 
    10 class ChatServer:
    11     def __init__(self, ip='127.0.0.1', port=9999, interval=10):
    12         self.laddr = ip, port
    13         self.event = threading.Event()
    14         self.sock = socket.socket(type=socket.SOCK_DGRAM)
    15         self.clients = {}
    16         self.interval = interval
    17 
    18     def start(self):
    19         self.sock.bind(self.laddr)
    20 
    21         threading.Thread(target=self.recv, name='recvive').start()
    22 
    23     def recv(self):
    24         while not self.event.is_set():
    25             data, raddr = self.sock.recvfrom(1024)
    26             localkeys = set()
    27             logging.info(data)
    28 
    29             # 心跳信息
    30             if data.strip() == b'^hb^':
    31                 self.clients[raddr] = datetime.datetime.now().timestamp()
    32                 continue
    33 
    34             if data.strip() == b'quit':
    35                 # 若一个客户端刚进来,还没有加到字典中,直接pop,会报KerryError
    36                 # if raddr in self.clients.keys():
    37                 self.clients.pop(raddr, None)
    38                 continue
    39             self.clients[raddr] = datetime.datetime.now().timestamp()
    40 
    41             # z再次发送数据的时间,如果之后没有再次进入,这次的时间就作为下次比较时间,所以这次的时间可以认为是最新的心跳信息
    42             current = datetime.datetime.now().timestamp()
    43             msg = '{}--{}'.format(data.decode(), '******').encode()
    44 
    45             # 过期的就不在发数据给客户端了,并且剔除掉
    46             #z 字典不能再遍历的时候,删除内容
    47             for r, t in self.clients.items():
    48                 if current - t > self.interval:
    49                     localkeys.add(r)
    50                 self.sock.sendto(msg, r)
    51             for r in localkeys:
    52                 self.clients.pop(r)
    53 
    54     def stop(self):
    55         self.sock.close()
    56         self.event.set()
    57 
    58 
    59 def main():
    60     cs = ChatServer()
    61     cs.start()
    62 
    63     while True:
    64         cmd = input(">>>")
    65         if cmd == 'quit':
    66             cs.stop()
    67             break
    68         logging.info(threading.enumerate())
    69 
    70 
    71 if __name__ == "__main__":
    72     main()
    群聊server端

      client端:

     1 import socket
     2 import threading
     3 import logging
     4 import datetime
     5 
     6 
     7 FORMAT = '%(asctime)s %(thread)s %(threadName)s %(message)s'
     8 logging.basicConfig(format=FORMAT, level=logging.INFO)
     9 
    10 class ChatClient:
    11     def __init__(self, ip='127.0.0.1', port=9999, interval=3):
    12         self.raddr = ip, port
    13         self.event = threading.Event()
    14         self.sock = socket.socket(type=socket.SOCK_DGRAM)
    15         self.interval = interval
    16 
    17     def start(self):
    18         self.sock.connect(self.raddr)
    19 
    20         threading.Thread(target=self.recv, name='c-recv').start()
    21 
    22         #  每隔interval 就发送一次心跳信息
    23         threading.Thread(target=self.hearbeat, name='ht',daemon=True).start()
    24 
    25     def recv(self):
    26         while not self.event.is_set():
    27             data, raddr = self.sock.recvfrom(1024)
    28             print(data)
    29             print(raddr)
    30     def hearbeat(self):
    31         while not self.event.wait(self.interval):
    32             self.send('^hb^')
    33 
    34 
    35 
    36     def send(self, msg):
    37         self.sock.send(msg.encode())
    38 
    39     def stop(self):
    40         self.sock.close()
    41         self.event.set()
    42 
    43 
    44 def main():
    45     cc = ChatClient()
    46     cc.start()
    47 
    48     while True:
    49         cmd = input('>>')
    50         if cmd == 'quit':
    51             cc.stop()
    52             break
    53         cc.send(cmd)
    54         logging.info(threading.enumerate())
    55 
    56 
    57 
    58 if __name__ == "__main__":
    59     main()
    群聊client端

        

        注意:

          如果是如上图所示,是一个解释器进程,建立两个对象,但是这两个对象分别在不同的线程中跑。

          如果直接运行两次 client,就是两个客户端进程

    UDP 协议的应用:

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

        网络足够好
        消息不会丢包

        包不会乱序

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

      应用场景:

        视频,音频传输,一般来说丢一些包。

      

    为什么要坚持,想一想当初!
  • 相关阅读:
    【排序】紧急集合
    Tallest Cow(线段树较易)
    递归实现排列型枚举
    文件输入输出文件
    【分治】逃亡
    折半查找法(二分)
    递归/非递归实现组合型枚举(全排列问题)
    9.25DAY1T2
    9.25DAY1T1
    POI2004[MOS] 贪心+DP
  • 原文地址:https://www.cnblogs.com/JerryZao/p/9898587.html
Copyright © 2011-2022 走看看