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\n")

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\n")

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() #两个进程都关闭,退出

 

 效果如下:

 退出:

 

posted @ 2021-03-12 22:25  This_is_Y  阅读(1205)  评论(0编辑  收藏  举报