第一个程序——构建一个ServerUI

news2025/2/23 5:32:34

简介

本次程序设计均使用python实现,使用sql server对聊天室用户的数据进行存储。通过python socket套接字编程,实现了在线聊天室的功能,并使用python tkinter进行UI界面的设计。

思路

由计算机网络的基础知识易知,两个主机之间通信的协议可以是TCP,也可以是UDP。其中TCP需要通过三次握手在两台主机之间建立连接,然后两台主机之间才可以通信;而UDP则是无连接的。显然,对于这样一个简单的程序,使用TCP和UDP作为通信协议均可,本次程序设计选择的是TCP。

  • 首先面对的问题是,我们想要编写的程序,是一个在线聊天室,用户可以在聊天室内广播消息(即群聊),也可以向聊天室中的某人私发消息(即好友小窗聊天)。
  • 那么显然,只通过单纯的TCP连接是无法满足我们的需求的:因为我们是想让客户端与服务器之间构建TCP连接,主机发出请求,服务器接到请求后和它建立连接,客户明确知道这个服务器是在线聊天室,而服务器却无法得知用户是哪位用户,只能通过服务器socket.accept()接受连接请求后,得知客户端的套接字信息和IP地址及端口号。
    在这里插入图片描述
  • 因此,为了解决这个问题,我们因此了本程序在设计时最关键的思路:以服务器作为中继端,它不仅能够接受信息,还能够转发信息。当客户端向服务器发送信息时,把这条信息编码,为它加上我们人为自定义的前缀。我设计的前缀是目标前缀,即“在服务器中广播/向服务器中另一个用户私发信息”( 私发时,前缀即为能唯一标识目标用户的信息;群发时则是BROADCASTING )
    在这里插入图片描述
    这样,服务器变成了一个后台,在用户视图下(ClientUI),用户可以看到其他用户群发的信息,也能够看到谁在给它私发信息。而对于服务器,它的任务是向用户转发信息 (显然,服务器知道每个连接到它的用户的IP和端口,由于用户在发送信息时,其信息被编码,目标地址作为前缀被编码进入了发给服务器的信息,而服务器端解码,将地址和信息拆包,服务器拿到地址后,如果这个地址是群发地址,则将信息再次编码<将发送方的地址编码进这条信息,以让接收方得知是谁在群发消息>,发送给所有用户;否则,服务器将地址编码<将发送方的地址编码进这条信息,以让接收方得知是谁在向它私发消息>,将信息转发给目标用户) ,并在服务器端显示聊天日志(方便维护与程序测试)。 在这里插入图片描述
    👆客户端广播信息“嗡嗡嗡”;
    在这里插入图片描述
    👆客户端广播“嗡嗡嗡”,在服务器端也能看见这条广播信息,并得知是谁在发送广播信息。如果两个客户进行私聊,服务器端仍然可以对私发信息进行监听。
    在这里插入图片描述
    👆现在在两个客户端之间私发消息,由“猪头”向“电棍”发送“喂喂喂”。
    在这里插入图片描述
    👆“电棍”收到消息。
    在这里插入图片描述
    👆由“猪头”发送给“狗狗”,“电棍”收不到消息。同时由于“狗狗”没上线,服务器会反馈。
    在这里插入图片描述
    👆服务器会监听每条聊天记录。

以上便是本次程序设计中对于服务器ServerUI的设计思路。

ServerUI及其相关源码

socket.py

程序清单中的socket.py,定义了两个类及若干程序中必要的编码解码函数。其中包括客户端的Client以及服务器的Server。

import socket
"""
	①Server类中初始化了套接字socket;
	②开设序列socs,用于存放(append)主动连接至服务器端的客户端套接字信息;
	③ip和port为服务器的ip地址和端口,由于本次程序是在本机上进行仿真操作的,因此ip
	默认设置为自检环回地址127.0.0.1,端口可以自定义;
	④bind指将ip和端口与套接字s绑定;
	⑤listen设置为128,即最多接入128个用户。
"""
class Server():
    def __init__(self,ip,port):
        self.s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.socs = []
        self.ip = ip
        self.port = port
        # self.s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        self.s.bind((self.ip, self.port))
        self.s.listen(128)

class Client():
    def __init__(self,ip,port):
        self.s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.ip = ip
        self.port = port
        self.s.bind((self.ip, self.port))

"""
	👇三个编码解码函数
	①wencoding是将目标地址信息编码进发送信息的编码器函数;
	②w_oriencoding (Short for "w Original encoding") 是用于“当发送方向用户私发信息,
	而该用户不在线”时的编码器。
	③wdecoding是将wencoding编码信息解码的解码器。
"""

def wencoding(addr,data):
    res = addr[0]
    res += '|'
    res += str(addr[1])
    res += '|'
    res += data
    return res
def w_oriencoding(data):
    res = "1|2|"
    res += data
    return res

def wdecoding(data):
    data = tuple(data.split('|'))
    addr = (data[0], int(data[1]))
    data = data[2]
    return addr, data

ServerUI

调用库

from tkinter import *
from tkinter import messagebox
from Socketer import *
from threading import Thread
from tkinter import scrolledtext
import datetime
"""
	inspect和ctypes用于终止进程。
"""
import inspect
import ctypes

终止进程函数_async_raise(tid, exctype) && stop_thread(thread)

当服务器下线时,由于程序仍然没有结束,因为我们可以让服务器重新上线,因此在程序中我们需要终止服务器相关进程(显然需要终止的至少有连接监听进程)。

def _async_raise(tid, exctype):
    """raises the exception, performs cleanup if needed"""
    tid = ctypes.c_long(tid)
    if not inspect.isclass(exctype):
        exctype = type(exctype)
    res = ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, ctypes.py_object(exctype))
    if res == 0:
        raise ValueError("invalid thread id")
    elif res != 1:
        # """if it returns a number greater than one, you're in trouble,
        # and you should call it again with exc=NULL to revert the effect"""
        ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, None)
        raise SystemError("PyThreadState_SetAsyncExc failed")

def stop_thread(thread):
    _async_raise(thread.ident, SystemExit)

界面设计ServerUI类

没什么好说的,ServerUI类使用tkinter进行了UI界面的设计,可以调整UI界面中按钮、输入框、文本显示框及文本的位置与格式。

class ServerUI(object):
    GUI = None
    server_soc = None
    port = None
    text = None
    isOn = False
    host_ip = "127.0.0.1"
    def __init__(self):
        self.GUI = Tk()
        self.GUI.title("Server")
        self.GUI.geometry('500x460')
        self.GUI.wm_resizable(False,False)
        Label(self.GUI, text='服务端IP地址:' + self.host_ip,font=(20)).place(relx=.5, y=15, anchor="center")
        Label(self.GUI, text='服务端端口号:' ,font=(20)).place(relx=.3, y=50, anchor="center")
        self.port = Entry(self.GUI, width=10)
        self.port.place(relx=.5, y=50, anchor="center")
        Button(self.GUI,width=10,text='上线/下线',command=self.on_or_off).place(relx=.7, y=50, anchor="center")
        self.text = scrolledtext.ScrolledText(self.GUI, width=78, height=22, font=('Times New Roman', 10))
        self.text.place(relx=.5, y=240, anchor="center")
        btn = Button(self.GUI, text='清空', font=('黑体', 14), height=1, command=lambda: self.text.delete("1.0", "end"))
        btn.place(relx=.5, y=440, anchor="center")
        self.GUI.mainloop()

在这里插入图片描述
实际上,以上程序控制的是这个👆UI界面的布局。

重头戏:Application_ServerUI类的实现

先看下这个类的定义以及初始化函数👇。

class Application_ServerUI(ServerUI):
    def __init__(self):
        ServerUI.__init__(self)

实际上,ServerUI是Application_ServerUI类的子类👇。
在这里插入图片描述
在这里插入图片描述
在ServerUI中给出了使用tkinter设计UI布局的定义,而在Application_ServerUI中,我则给出了每个定义下的实现(如,某个按钮应该与哪些行为绑定。可以在ServerUI中预设接口,然后在Application_ServerUI中定义函数来实现)。

完整实现如下:

class Application_ServerUI(ServerUI):
    def __init__(self):
        ServerUI.__init__(self)

    def connection_accepted(self):
        while True:
            client_soc, addr = self.server_soc.s.accept()
            self.text.insert(END,
                '[{}]/连接客户端的IP地址:{}/端口:{}\n'.format(
                datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"), addr[0], addr[1],))
            self.server_soc.socs.append((client_soc,addr))
            client_thread = Thread(target=self.recv_msg, args=([(client_soc, addr)]))            
            client_thread.start()

    def recv_msg(self,soc):
        client_soc = soc[0]
        addr = soc[1]
        while True:
            try:
                data = client_soc.recv(1024)
            except:
                self.text.insert(END,
                                 '[{}]/客户端IP地址:{}/端口:{},{}\n'.format(
                                     datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"), addr[0], addr[1],"已断开连接。" ))
                break
            data = data.decode('utf-8')
            try:
                dest_addr, data = wdecoding(data)
            except:
                continue
            if dest_addr[0] != "BROADCASTING" and dest_addr != self.host_ip:#不是广播,而是私聊
                self.text.insert(END,
                    '[{}]\n发方IP地址:{}/发方端口:{}\n发送数据为:{}\n接收端IP地址:{}/端口:{}\n'.format(datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"), addr[0], addr[1],
                    data,dest_addr[0],dest_addr[1]))
                send2dest_threading = Thread(target=self.send2dest, args=([soc,dest_addr, data]))
                send2dest_threading.start()
            elif dest_addr == self.host_ip:
                self.text.insert(END,
                                 '[{}]\n发方IP地址:{}/发方端口:{}\n{}\n服务器已接收。\n'.format(
                                     datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"), addr[0], addr[1],
                                     data))
                send2dest_threading = Thread(target=self.send2dest, args=([soc,dest_addr, "服务器已接收"]))
                send2dest_threading.start()
            elif dest_addr[0] == "BROADCASTING":
                self.text.insert(END,
                                 '[{}]\n[广播]发方IP地址:{}/发方端口:{}\n发送数据为:{}\n'.format(
                                     datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"), addr[0], addr[1],data))
                broadcasting_threading = Thread(target=self.broadcasting,args=([soc, data]))
                broadcasting_threading.start()

    def broadcasting(self,ori_soc,data):#服务端进行广播
        ori_addr = ori_soc[1]
        for soc_addr in enumerate(self.server_soc.socs):
            #遍历当前每个与服务端建立TCP连接的客户端套接字
            soc = soc_addr[1][0]
            addr = soc_addr[1][1]
            sd_data = wencoding(ori_addr, data)
            if addr != ori_addr:
                try:
                    soc.send(sd_data.encode('utf-8'))
                except:
                    soc.close()
                    del self.server_soc.socs[soc_addr[0]]

    def send2dest(self,ori_soc,dest_addr,data):#单独转发,根据发方的目的地址
        ori_socket = ori_soc[0]
        ori_addr = ori_soc[1]
        found = False
        for soc_addr in enumerate(self.server_soc.socs):
            soc = soc_addr[1][0]
            addr = soc_addr[1][1]
            if addr == dest_addr:
                sd_data = wencoding(ori_addr, data)
                try:
                    soc.send(sd_data.encode('utf-8'))
                except:
                    soc.close()
                    del self.server_soc.socs[soc_addr[0]]
                found = True
                break
        if not found:
            self.text.insert(END,
                             '[{}]发方IP地址:{}/发方端口:{},{}\n'.format(
                                 datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"), ori_addr[0], ori_addr[1],
                                 "收方未连接。"))
            send_msg = w_oriencoding("{}".format("收方未连接。"))
            ori_socket.send(send_msg.encode("utf-8"))

    def connection_accepted_threading(self):
        self.t = Thread(target=self.connection_accepted,args=())
        self.t.setDaemon(True)
        self.t.start()

    def on_or_off(self):
        if not self.isOn:
            if len(self.port.get()) == 0:
                messagebox.showerror(title="未输入端口",message="未输入端口")
            else:
                port = self.port.get()
                port = int(port)
                self.server_soc = Server(self.host_ip,port)
                self.isOn = True
                messagebox.showinfo(title="服务器已上线", message="服务器已上线!")
                self.text.insert(END, "[{}]/服务器已上线,IP地址为".format(
                    datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")) + self.server_soc.ip + ",等待客户端连接。\n")
                self.connection_accepted_threading()
        else:
            if self.t.is_alive():
                stop_thread(self.t)
            self.isOn = False
            self.server_soc.s.close()
            messagebox.showinfo(title="服务器已下线", message="服务器已下线!")
            self.text.insert(END, "[{}]/服务器已下线。".format(
                datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"))+"\n")
            self.GUI.destroy()

由于以上代码片段涉及多线程、线程之间的交互以及线程开始的先后顺序,因此我按照线程开始的先后捋顺一下代码。

①首先,当我们设置好端口号,点击ServerUI界面中的“上线”按钮时,服务器上线。在ServerUI类中我们绑定这个按钮的行为是self.on_or_offf,因此先来看这个函数:

    def on_or_off(self):
        if not self.isOn:
        	#实际上,此处设置为异常处理(try && except)更合理
            if len(self.port.get()) == 0:	#检查端口是否填写。
                messagebox.showerror(title="未输入端口",message="未输入端口")
            else:							#正常填写,则上线。
                port = self.port.get()
                port = int(port)
                self.server_soc = Server(self.host_ip,port) #初始化Server类,即Socket.py中定义的Server类
                self.isOn = True
                messagebox.showinfo(title="服务器已上线", message="服务器已上线!")
                self.text.insert(END, "[{}]/服务器已上线,IP地址为".format(
                    datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")) + self.server_soc.ip + ",等待客户端连接。\n")
                self.connection_accepted_threading() #服务器上线,开始监听连接请求
        else:
            if self.t.is_alive():
                stop_thread(self.t)
            self.isOn = False
            self.server_soc.s.close()
            messagebox.showinfo(title="服务器已下线", message="服务器已下线!")
            self.text.insert(END, "[{}]/服务器已下线。".format(
                datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"))+"\n")
            self.GUI.destroy()

👆服务器上线后,开始监听连接请求,通过self.connection_accepted_threading()这个线程。因此我们来关注这个线程及其对应的函数。

②监听连接请求:
其线程如下,对应的函数为self.connection_accepted()

    def connection_accepted_threading(self):
        self.t = Thread(target=self.connection_accepted,args=())
        self.t.setDaemon(True)
        self.t.start()

因此我们再来看这个函数。

def connection_accepted(self):
        while True:
            client_soc, addr = self.server_soc.s.accept()
            self.text.insert(END,
                '[{}]/连接客户端的IP地址:{}/端口:{}\n'.format(
                datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"), addr[0], addr[1],))
            self.server_soc.socs.append((client_soc,addr))
            client_thread = Thread(target=self.recv_msg_threading, args=([(client_soc,addr)]))
            client_thread.start()

显然,需要一直监听连接请求,因此server_soc.s.accept()设置在永真循环中。涉及到了永真循环,必然需要将这个循环开设在一个线程中,否则将导致程序阻塞。接收到了请求后,将其拆包为客户端的socket和地址addr,将客户端套接字append到服务器的socs序列中,以让服务器得知当前在线的用户有哪些;之后,由于客户端连接成功,服务器开始对它发送的消息进行监听与转发,因此开启client_thread进程对这个用户进行持续监听。client_thread对应的函数是recv_msg_threading,我们来看这个函数。

③接受客户端信息:

    def recv_msg(self,soc):
		"首先👇,将soc拆包为用户套接字和地址。client_soc = soc[0]"
        addr = soc[1]
        while True:
            try:
            	"如果正常接收到了信息:"
                data = client_soc.recv(1024)
            except:
                self.text.insert(END,
                                 '[{}]/客户端IP地址:{}/端口:{},{}\n'.format(
                                     datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"), addr[0], addr[1],"已断开连接。" ))
                break
            "👇将收到的信息通过utf-8解码(当然,发送前也需要utf-8编码)。"
            data = data.decode('utf-8')
            try:
            "👇前缀拆包。"
                dest_addr, data = wdecoding(data)
            except:
                continue
            if dest_addr[0] != "BROADCASTING" and dest_addr != self.host_ip:#不是广播,而是私聊
                self.text.insert(END,
                    '[{}]\n发方IP地址:{}/发方端口:{}\n发送数据为:{}\n接收端IP地址:{}/端口:{}\n'.format(datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"), addr[0], addr[1],
                    data,dest_addr[0],dest_addr[1]))
                send2dest_threading = Thread(target=self.send2dest, args=([soc,dest_addr, data]))
                send2dest_threading.start()
            elif dest_addr == self.host_ip:
                self.text.insert(END,
                                 '[{}]\n发方IP地址:{}/发方端口:{}\n{}\n服务器已接收。\n'.format(
                                     datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"), addr[0], addr[1],
                                     data))
                send2dest_threading = Thread(target=self.send2dest, args=([soc,dest_addr, "服务器已接收"]))
                send2dest_threading.start()
            elif dest_addr[0] == "BROADCASTING":
                self.text.insert(END,
                                 '[{}]\n[广播]发方IP地址:{}/发方端口:{}\n发送数据为:{}\n'.format(
                                     datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"), addr[0], addr[1],data))
                broadcasting_threading = Thread(target=self.broadcasting,args=([soc, data]))
                broadcasting_threading.start()

👆显然,发送的信息可以是发送给目标用户或服务器的,也可以是广播的。如果是前者,使用send2dest线程发送给目标,否则使用broadcasting线程广播(我们不希望发送消息这一行为对主进程产生阻塞)。

④私聊信息的转发:

    def send2dest(self,ori_soc,dest_addr,data):#单独转发,根据发方的目的地址
        "ori_socket和addr是发方地址,让收方知道发方是谁。"
        ori_socket = ori_soc[0]
        ori_addr = ori_soc[1]
        found = False
        "从soc_addr序列中寻找收方。"
        for soc_addr in enumerate(self.server_soc.socs):
        	"""
        	由于使用了enumerate,因此soc_addr是一个序列索引元组,其soc_addr[0]是索引
        	号,而soc_addr[1]才是socs序列对应索引号位置的当前连接到服务器的套接字信息。
        	"""
            soc = soc_addr[1][0]
            addr = soc_addr[1][1]
            "👇判断是否找到,找到则发送,否则提示发方收方未连接。"
            if addr == dest_addr:
                sd_data = wencoding(ori_addr, data)
                try:
                    soc.send(sd_data.encode('utf-8'))
                except:
                    soc.close()
                    del self.server_soc.socs[soc_addr[0]]
                found = True
                break
        if not found:
            self.text.insert(END,
                             '[{}]发方IP地址:{}/发方端口:{},{}\n'.format(
                                 datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"), ori_addr[0], ori_addr[1],
                                 "收方未连接。"))
            send_msg = w_oriencoding("{}".format("收方未连接。"))
            ori_socket.send(send_msg.encode("utf-8"))

⑤广播:

    def broadcasting(self,ori_soc,data):#服务端进行广播
        ori_addr = ori_soc[1]
        for soc_addr in enumerate(self.server_soc.socs):
            #遍历当前每个与服务端建立TCP连接的客户端套接字
            soc = soc_addr[1][0]
            addr = soc_addr[1][1]
            sd_data = wencoding(ori_addr, data)
            if addr != ori_addr:
                try:
                    soc.send(sd_data.encode('utf-8'))
                except:
                    soc.close()
                    del self.server_soc.socs[soc_addr[0]]

👆基本上与私发流程相同,不过需要通过for循环遍历每个连接到服务器端的套接字,以发送信息。

启动程序

在main函数中使用Application_ServerUI()即可实例化一个服务器类👇。

if __name__ == '__main__':
    Application_ServerUI()

完整的ServerUI

from tkinter import *
from tkinter import messagebox
from Socketer import *
from threading import Thread
from tkinter import scrolledtext
import datetime
import inspect
import ctypes

def _async_raise(tid, exctype):
    """raises the exception, performs cleanup if needed"""
    tid = ctypes.c_long(tid)
    if not inspect.isclass(exctype):
        exctype = type(exctype)
    res = ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, ctypes.py_object(exctype))
    if res == 0:
        raise ValueError("invalid thread id")
    elif res != 1:
        # """if it returns a number greater than one, you're in trouble,
        # and you should call it again with exc=NULL to revert the effect"""
        ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, None)
        raise SystemError("PyThreadState_SetAsyncExc failed")

def stop_thread(thread):
    _async_raise(thread.ident, SystemExit)

class ServerUI(object):
    GUI = None
    server_soc = None
    port = None
    text = None
    isOn = False
    host_ip = "127.0.0.1"
    def __init__(self):
        self.GUI = Tk()
        self.GUI.title("Server")
        self.GUI.geometry('500x460')
        self.GUI.wm_resizable(False,False)
        Label(self.GUI, text='服务端IP地址:' + self.host_ip,font=(20)).place(relx=.5, y=15, anchor="center")
        Label(self.GUI, text='服务端端口号:' ,font=(20)).place(relx=.3, y=50, anchor="center")
        self.port = Entry(self.GUI, width=10)
        self.port.place(relx=.5, y=50, anchor="center")
        Button(self.GUI,width=10,text='上线/下线',command=self.on_or_off).place(relx=.7, y=50, anchor="center")
        self.text = scrolledtext.ScrolledText(self.GUI, width=78, height=22, font=('Times New Roman', 10))
        self.text.place(relx=.5, y=240, anchor="center")
        btn = Button(self.GUI, text='清空', font=('黑体', 14), height=1, command=lambda: self.text.delete("1.0", "end"))
        btn.place(relx=.5, y=440, anchor="center")
        self.GUI.mainloop()

class Application_ServerUI(ServerUI):
    def __init__(self):
        ServerUI.__init__(self)

    def connection_accepted(self):
        while True:
            client_soc, addr = self.server_soc.s.accept()
            self.text.insert(END,
                '[{}]/连接客户端的IP地址:{}/端口:{}\n'.format(
                datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"), addr[0], addr[1],))
            self.server_soc.socs.append((client_soc,addr))
            client_thread = Thread(target=self.recv_msg, args=([(client_soc, addr)]))
            client_thread.start()

    def recv_msg(self,soc):
        client_soc = soc[0]
        addr = soc[1]
        while True:
            try:
                data = client_soc.recv(1024)
            except:
                self.text.insert(END,
                                 '[{}]/客户端IP地址:{}/端口:{},{}\n'.format(
                                     datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"), addr[0], addr[1],"已断开连接。" ))
                break
            data = data.decode('utf-8')
            try:
                dest_addr, data = wdecoding(data)
            except:
                continue
            if dest_addr[0] != "BROADCASTING" and dest_addr != self.host_ip:#不是广播,而是私聊
                self.text.insert(END,
                    '[{}]\n发方IP地址:{}/发方端口:{}\n发送数据为:{}\n接收端IP地址:{}/端口:{}\n'.format(datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"), addr[0], addr[1],
                    data,dest_addr[0],dest_addr[1]))
                send2dest_threading = Thread(target=self.send2dest, args=([soc,dest_addr, data]))
                send2dest_threading.start()
            elif dest_addr == self.host_ip:
                self.text.insert(END,
                                 '[{}]\n发方IP地址:{}/发方端口:{}\n{}\n服务器已接收。\n'.format(
                                     datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"), addr[0], addr[1],
                                     data))
                send2dest_threading = Thread(target=self.send2dest, args=([soc,dest_addr, "服务器已接收"]))
                send2dest_threading.start()
            elif dest_addr[0] == "BROADCASTING":
                self.text.insert(END,
                                 '[{}]\n[广播]发方IP地址:{}/发方端口:{}\n发送数据为:{}\n'.format(
                                     datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"), addr[0], addr[1],data))
                broadcasting_threading = Thread(target=self.broadcasting,args=([soc, data]))
                broadcasting_threading.start()

    def broadcasting(self,ori_soc,data):#服务端进行广播
        ori_addr = ori_soc[1]
        for soc_addr in enumerate(self.server_soc.socs):
            #遍历当前每个与服务端建立TCP连接的客户端套接字
            soc = soc_addr[1][0]
            addr = soc_addr[1][1]
            sd_data = wencoding(ori_addr, data)
            if addr != ori_addr:
                try:
                    soc.send(sd_data.encode('utf-8'))
                except:
                    soc.close()
                    del self.server_soc.socs[soc_addr[0]]

    def send2dest(self,ori_soc,dest_addr,data):#单独转发,根据发方的目的地址
        ori_socket = ori_soc[0]
        ori_addr = ori_soc[1]
        found = False
        for soc_addr in enumerate(self.server_soc.socs):
            soc = soc_addr[1][0]
            addr = soc_addr[1][1]
            if addr == dest_addr:
                sd_data = wencoding(ori_addr, data)
                try:
                    soc.send(sd_data.encode('utf-8'))
                except:
                    soc.close()
                    del self.server_soc.socs[soc_addr[0]]
                found = True
                break
        if not found:
            self.text.insert(END,
                             '[{}]发方IP地址:{}/发方端口:{},{}\n'.format(
                                 datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"), ori_addr[0], ori_addr[1],
                                 "收方未连接。"))
            send_msg = w_oriencoding("{}".format("收方未连接。"))
            ori_socket.send(send_msg.encode("utf-8"))

    def connection_accepted_threading(self):
        self.t = Thread(target=self.connection_accepted,args=())
        self.t.setDaemon(True)
        self.t.start()

    def on_or_off(self):
        if not self.isOn:
            if len(self.port.get()) == 0:
                messagebox.showerror(title="未输入端口",message="未输入端口")
            else:
                port = self.port.get()
                port = int(port)
                self.server_soc = Server(self.host_ip,port)
                self.isOn = True
                messagebox.showinfo(title="服务器已上线", message="服务器已上线!")
                self.text.insert(END, "[{}]/服务器已上线,IP地址为".format(
                    datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")) + self.server_soc.ip + ",等待客户端连接。\n")
                self.connection_accepted_threading()
        else:
            if self.t.is_alive():
                stop_thread(self.t)
            self.isOn = False
            self.server_soc.s.close()
            messagebox.showinfo(title="服务器已下线", message="服务器已下线!")
            self.text.insert(END, "[{}]/服务器已下线。".format(
                datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"))+"\n")
            self.GUI.destroy()

if __name__ == '__main__':
    Application_ServerUI()

以上便是ServerUI设计的全部内容,稍后对ClientUI客户端的设计进行详解。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/172425.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

基于LSTMGRU的微博突发事件分析与谣言检测(附完整的代码+报告)

问题描述及方法基础 本章主要对课题研究所涉及的机器学习、自然语言处理的原理和方法进行介绍,主要分为四部分,第一部分是将本课谣言检测任务的符号化描述;第二部分是微博数据的预处理,包括语言模型、文本分词等技术;第三部分与第四部分分别是本文搭建的微博谣言检测模型所…

py字符串的格式化笔记

print():和cjava差不多&#xff0c;只是逗号变了&#xff0c;其中 %s 就是模板中的占位符&#xff0c;表示这个位置先占着&#xff0c;待会儿要有数据填入到这里。然后再提供一个元组&#xff0c;里面依次存放需要填入到 %s 位置 的数据。这里是使用变量 (salary,tax,aftertax)…

CSS.前端基础.html

什么是 CSS? CSS 指层叠样式表 (Cascading Style Sheets)样式定义如何显示 HTML 元素样式通常存储在样式表中把样式添加到 HTML 4.0 中&#xff0c;是为了解决内容与表现分离的问题外部样式表可以极大提高工作效率外部样式表通常存储在 CSS 文件中多个样式定义可层叠为一个示…

丝绸之路也可以是科技传播之路

唐宋元海外贸易 618年-1368年 王孝通 生卒年代不详 孙思邈541年—682年 一行 公元683年-公元727年 李淳风 602年&#xff0d;670年 沈括 1031年&#xff0d;1095年 郭守敬 1231年&#xff0d;1316年 扎马鲁丁生卒年代不详 阿拉伯帝国 632年-1258年 阿尔花拉子模 780年&#xf…

【深度强化学习】【论文阅读】【双臂模仿】Deep Imitation Learning for BimanualRobotic Manipulation

title: Deep Imitation Learning for BimanualRobotic Manipulation date: 2023-01-15T20:54:56Z lastmod: 2023-01-19T18:31:57Z Deep Imitation Learning for BimanualRobotic Manipulation 1 Introduction 文中使用的模型是一个深度的、分层的、模块化的架构。与 baselin…

微服务负载均衡器Ribbon学习笔记

目录 1.什么是Ribbon 1.1 客户端的负载均衡 1.2 服务端的负载均衡 1.3 常见负载均衡算法 2. Nacos使用Ribbon 3. Ribbon负载均衡策略 4.修改默认负载均衡策略 方式1&#xff1a;通过自定义配置类来实现 方式2&#xff1a;通过修改配置文件实现&#xff08;推荐&#xf…

9、jQuery

jQuery库&#xff1a;里面存在大量的JavaScript函数 官网&#xff1a;https://jquery.com/ 9.1 获取jQuery jQuery引入 cdn 引入 <script src"https://cdn.bootcss.com/jquery/3.4.1/core.js"></script>本地引入 <script src"lib/jquery-3.6.3.…

C语言练习——3

C语言练习——3一、 操作符练习1.1交换两个变量&#xff08;不创建临时变量&#xff09;1.2 打印整数二进制的奇数位和偶数位1.3[二进制中1的个数](https://www.nowcoder.com/questionTerminal/8ee967e43c2c4ec193b040ea7fbb10b8)1.4[两个整数二进制位不同个数](https://www.no…

【 java 反射下篇】java反射机制不难吧?来看看这篇

&#x1f4cb; 个人简介 &#x1f496; 作者简介&#xff1a;大家好&#xff0c;我是阿牛&#xff0c;全栈领域优质创作者。&#x1f61c;&#x1f4dd; 个人主页&#xff1a;馆主阿牛&#x1f525;&#x1f389; 支持我&#xff1a;点赞&#x1f44d;收藏⭐️留言&#x1f4d…

Element Plus 跟踪表格数据总数,包括查询、筛选等操作

前言 Element Plus的表格组件提供了筛选功能 前端项目中&#xff0c;如果表格使用的是后端分页&#xff0c;使用表格插件及分页器插件就可以了。这种情况下&#xff0c;前端的表格筛选被后端的分页条件查询取代了 另一种情况&#xff1a;不分页&#xff0c;直接查询所有数据…

Win7快速部署weblogic 12c

0x00 前言 需要一个漏洞中等数量的版本。 0x01 安装环境 版本操作系统Windows 7 64bitJDKjdk-8u101-windows-x64weblogic12.2.1.3 0x02 下载地址 JDK 下载地址 https://www.oracle.com/java/technologies/javase/javase8-archive-downloads.html jdk 1.8版本&#xff0c;文…

华为机试题:HJ16 购物单(python)

文章目录知识点详解1、input()&#xff1a;获取控制台&#xff08;任意形式&#xff09;的输入。输出均为字符串类型。2、print() &#xff1a;打印输出。3、strip()&#xff1a;删除字符串&#xff08;开头 / 结尾&#xff09;指定字符&#xff08;默认空格&#xff09;或字符…

两小时上手ActiveMQ

一、消息中间件概述 1.1 消息中间件产生的背景 在客户端与服务器进行通讯时.客户端调用后&#xff0c;必须等待服务对象完成处理返回结果才能继续执行。 客户与服务器对象的生命周期紧密耦合,客户进程和服务对象进程都都必须正常运行;如果由于服务对象崩溃或者网络故障导致用…

ceres学习笔记(四)

前言&#xff1a; 学习了pose_graph_2d部分&#xff0c;因为先学习了3维的pose_graph_3d部分&#xff0c;所以这个就比较容易。简单来说就是se2和se3的区别。整个的运行逻辑和3维部分的pose_graph_3d部分是一样的&#xff0c;概括为&#xff1a; 1.设置好两个type&#xff0c…

7、CenOS6安装Nginx

Nginx的安装与启动 什么是Nginx Nginx 是一款高性能的 http 服务器/反向代理服务器及电子邮件&#xff08;IMAP/POP3&#xff09;代理服务器。由俄罗斯的程序设计师伊戈尔西索夫&#xff08;Igor Sysoev&#xff09;所开发&#xff0c;官方测试 nginx 能够支支撑 5 万并发链接…

《早安隆回》的铁粉,深圳80后男子不计成本,收购袁树雄签名照

谁也没有想到&#xff0c;五十多岁并且离异多年的袁树雄&#xff0c;靠着一首《早安隆回》&#xff0c;一夜之间红遍大江南北。如今《早安隆回》这首歌曲&#xff0c;已经拥有了三百万流量&#xff0c;有人说袁树雄下辈子都吃喝不愁&#xff0c;他的前妻该后悔了。 《早安隆回》…

ue4c++日记4(控制pawn类的运动|创建游戏模式|)

目录 代码速查 调用数学公式 获取位置/设置位置 绑定玩家输入按键&#xff0c;UE4传值给函数进行处理 约束获得的值再输出 创建对象 对象绑定到xxx上 设定默认玩家 实例&#xff1a;sin函数实现往复运动 实例&#xff1a;删除c类 1.删掉cpp和.h文件 2.删编译好的文件B…

缓存Caffeine之W-TinyLFU淘汰测录

我们常见的缓存是基于内存的缓存&#xff0c;但是单机的内存是有限的&#xff0c;不能让缓存数据撑爆内存&#xff0c;所有需要缓存淘汰机制。https://mp.csdn.net/editor/html/115872837 中大概说明了LRU的缓存淘汰机制&#xff0c;以及基于LRU的著名实现guava cache。除了LRU…

Python学习笔记——类(面向对象)

Python中使用类&#xff08;class〕来实现面向对象编程。Python中的类, 具有面向对象编程的所有基本特征&#xff1a;允许多继承、派生类可以重写它父类的任何方法、方法可以调用父类中同名的方法, 对象可以包含任意数量和类型的数据成员。创建类Python中, 使用class语句来创建…

Python 第7章 文件与数据格式化 笔记1

编码&#xff1a;print(云.encode(utf8))print(b\xe4\xba\x91.decode(utf8))要注意代码的编码方式。7.1文件概述windows中一个文件的完标识&#xff1a;D:\Downloads\新建文本文档.txt依次是路径&#xff0c;文件名主干&#xff0c;拓展名。没有包含除了文本字符以外的其他数据…