zoukankan      html  css  js  c++  java
  • python UDP多线程通信,以及自己加的花里胡哨、乱七八糟的东西

    简单的通信代码,发送,接收,转发,接收,发送。用python短短几行就可以解决

    服务器:

    import socket
    
    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 
    # 格式 socket.socket([family[, type[, proto]]])
    #     family: 套接字家族可以使 AF_UNIX 或者 AF_INET。
    #     type: 套接字类型可以根据是面向连接的还是非连接分为 SOCK_STREAM 或 SOCK_DGRAM。
    #     SOCK_DGRAM适用于UDP 。SOCK_STREAM适用于TCP
    #     protocol: 一般不填默认为 0。
    
    HOST = "192.168.43.66"
    PORT = 9999 #服务器端口
    
    s.bind((HOST,PORT))  #启动服务器
    
    while 1:
            (data,addr) = s.recvfrom(1024)
            # 返回值是(data,address)。其中 data 是包含接收数据的字符串,address 是发送数据的套接字地址。
            # 1024是缓冲区大小,1024个字节
            print("接收到的数据",data.decode("utf-8"))
            print("数据来源IP",addr)
            s.sendto(data,addr) #把数据都发回

     客户端:

    import socket
    
    s = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
    
    HOST = "192.168.43.66"  #目的IP
    PORT = 9999             #目的端口
    
    s.connect((HOST,PORT))  #连接到服务器
    
    while 1:
            data = input("要发送的数据")
            s.sendto(data.encode("utf-8"),(HOST,PORT))  #发送数据,data进行编码utf-8
            (data,addr) = s.recvfrom(1024)
            print("接收到的信息:",data.decode("utf-8"))

     过程非常简单,开启服务器,然后打开客户端,连接到服务器。客户端发送一段数据给服务器,服务器把数据转发回给客户端,还可以多开几个客户端。

    效果就是这样子

     但是肯定不行,客户端与客户端之间不能通信,然后就有了下面的代码,这是在网上找到的,我自己写的逻辑没这么清晰,所以就借用了一下这哥们的

    博客地址:https://www.cnblogs.com/markjuruo/p/10101625.html

    github:https://github.com/markjuruo/Python-UDP-Chating

    客户端(版权消息也都留着,可以去看看他的,思路清晰)

    print("Hosted By markjuruo(Linzhihan)")
    print("To view more, please visit")
    print("https://github.com/markjuruo/Python-UDP-Chating
    ")
    
    import socket
    import threading, time
    from sys import exit
    
    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    
    HOST = input("Please input IP address: ")
    PORT = 10888
    
    NickName = input("Please input your nick-name : ")
    s.sendto(NickName.encode('utf-8'), (HOST, PORT))
    
    def RECV():
       while True:
          (data, addr) = s.recvfrom(1024)
          print(data.decode('utf-8'))
          time.sleep(1)
          
    def SEND():
       while True:
          data = input("")
          s.sendto(data.encode('utf-8'), (HOST, PORT))
          time.sleep(1)
    
    t1 = threading.Thread(target=RECV)
    t2 = threading.Thread(target=SEND)
    
    t1.start()
    t2.start()
    t1.join()
    t2.join()

     服务器

    print("Hosted By markjuruo(Linzhihan)")
    print("To view more, please visit")
    print("https://github.com/markjuruo/Python-UDP-Chating
    ")
    
    import socket
    from sys import exit
    
    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    
    HOST = input("Please input IP address: ")
    PORT = 10888
    
    s.bind((HOST, PORT))
    
    user = {}
    
    while True:
       (data, addr) = s.recvfrom(1024)
       if user.get(addr, False) == False:
          user[addr] = data.decode('utf-8')
          print("IP(%s) NickName(%s) Join" % (addr, data.decode('utf-8')))
       else:
          print(addr," : ", data.decode('utf-8'))
          data = user[addr] + " : " + data.decode('utf-8')
          for key, value in user.items():
             if key != addr:
                s.sendto(data.encode('utf-8'), key)

     效果挺不错,比上面要好,相当于是一个聊天室一样的结构,但是用着用着发现问题了,退出会卡死,所以就想着自己再改一改,优化一下退出的代码。

    然后就是现在这版的代码,注释写得非常详细了。

    首先,客户端是有两个线程,一个发送SEND,一个接收RECV。要关闭客户端的话,就要让两个线程都正常关闭,起初想用进程间的通信解决,queue队列,写着写着发现好像可以不用这么复杂。

    关闭的主要流程是,用户输入一个特殊代码,告诉客户端准备退出,然后客户端发送一个请求退出的消息给服务器,然后结束掉SEND线程。

    同时服务器接收到请求后,再单独发送一条消息给这个客户端,此时客户端的RECV线程中正在等待的recvfrom函数接收到这条消息。然后关闭RECV线程。

    至此,两个线程都关闭,然后执行s.close(),关闭套接字,客户端程序退出

    服务器的话就比较简单了,在所有的客户端都退出后,再选择是否退出就行了

    代码如下

    服务器:

    # 服务器
    import socket
    from sys import exit, flags
    
    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    
    HOST = input("Please input IP address: ")
    PORT = 10888
    
    s.bind((HOST, PORT))
    
    user_info = {}  #存放用户信息:nickname和ip地址以及端口
    
    while True:
       (data, addr) = s.recvfrom(1024)  #接收到的全部信息
       if user_info.get(addr, False) == False:   #如果addr地址不在用户信息中
          user_info[addr] = data.decode('utf-8')   #把发送过来的作为nickname,存入user_info
          print("IP(%s) NickName(%s) Join" % (addr, data.decode('utf-8')))
       else:    #已经是老用户了,
          print(addr," : ", data.decode('utf-8'))
          data = user_info[addr] + " : " + data.decode('utf-8')
          if data[-5:] == "退出 -1".format(user_info[addr]):  #如果检测到客户端要退出,
                s.sendto("-1".encode('utf-8'),addr)   #发送一条消息给要退出的客户端,方便结束该客户端的RECV函数线程
                user_info.pop(addr)   #清除用户信息
                if not user_info:   #用户信息为空,判断是否需要退出服务器
                   flag = input("所有用户以退出,是否关闭服务器(Y)")
                   if flag == "Y" or flag == "y":
                      break
          for key, value in user_info.items(): #遍历user_info,把接收到的消息发给除了发送方的所有用户
             if key != addr:
                s.sendto(data.encode('utf-8'), key)

     客户端:

    #客户端
    import socket
    import threading, time
    from sys import exit
    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    
    HOST = input("Please input Server IP address: ")
    PORT = 10888
    
    NickName = input("Please input your nick-name : ")
    print("/q退出") #提示
    s.sendto(NickName.encode('utf-8'), (HOST, PORT))
    
    # Flag = 0  #1则关闭socket
    
    def RECV():
       while True:
          (data, addr) = s.recvfrom(1024)
          if data.decode("utf-8") == "-1": #接收到退出请求,结束函数
             return
          print(data.decode('utf-8'))
          time.sleep(1)
    
    
    def SEND():
       while True:
          data = input("")
          if data=="/q":
             print("退出")
             data = "{}退出 -1".format(NickName)  
             s.sendto(data.encode('utf-8'), (HOST, PORT))#向服务器发送退出请求
             return 0
          s.sendto(data.encode('utf-8'), (HOST, PORT))
          time.sleep(1)
    
    
    t1 = threading.Thread(target=RECV)
    t2 = threading.Thread(target=SEND)
    t1.start()
    t2.start()
    t1.join()
    t2.join()
    
    s.close() #两个进程都关闭,退出

     效果如下:

     退出:

  • 相关阅读:
    ASP.NET安全问题-- 创建安全的Web应用程序
    浅谈ASP.NET内部机制(八)
    ASP.NET 配置文件纵横谈(一)
    项目开发-让设计模式成为一种心智
    浅谈ASP.NET内部机制(七)
    ASP.NET 配置文件纵横谈(二)
    GridView的分页是否真的是鸡肋呢?
    SQL开发中容易忽视的一些小地方(四)
    SQL开发中容易忽视的一些小地方( 三)
    怎样才能充分利用SQL索引
  • 原文地址:https://www.cnblogs.com/This-is-Y/p/14526761.html
Copyright © 2011-2022 走看看