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