0


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

简介

本次程序设计均使用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个用户。
"""classServer():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)classClient():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编码信息解码的解码器。
"""defwencoding(addr,data):
    res = addr[0]
    res +='|'
    res +=str(addr[1])
    res +='|'
    res += data
    return res
defw_oriencoding(data):
    res ="1|2|"
    res += data
    return res

defwdecoding(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)ifnot 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")defstop_thread(thread):
    _async_raise(thread.ident, SystemExit)

界面设计ServerUI类

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

classServerUI(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类的实现

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

classApplication_ServerUI(ServerUI):def__init__(self):
        ServerUI.__init__(self)

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

完整实现如下:

classApplication_ServerUI(ServerUI):def__init__(self):
        ServerUI.__init__(self)defconnection_accepted(self):whileTrue:
            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()defrecv_msg(self,soc):
        client_soc = soc[0]
        addr = soc[1]whileTrue: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:continueif 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()defbroadcasting(self,ori_soc,data):#服务端进行广播
        ori_addr = ori_soc[1]for soc_addr inenumerate(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]]defsend2dest(self,ori_soc,dest_addr,data):#单独转发,根据发方的目的地址
        ori_socket = ori_soc[0]
        ori_addr = ori_soc[1]
        found =Falsefor soc_addr inenumerate(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 =Truebreakifnot 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"))defconnection_accepted_threading(self):
        self.t = Thread(target=self.connection_accepted,args=())
        self.t.setDaemon(True)
        self.t.start()defon_or_off(self):ifnot self.isOn:iflen(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

,因此先来看这个函数:

defon_or_off(self):ifnot self.isOn:#实际上,此处设置为异常处理(try && except)更合理iflen(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()

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

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

defconnection_accepted(self):whileTrue:
            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,我们来看这个函数。

③接受客户端信息:

defrecv_msg(self,soc):"首先👇,将soc拆包为用户套接字和地址。client_soc = soc[0]"
        addr = soc[1]whileTrue: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:continueif 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线程广播(我们不希望发送消息这一行为对主进程产生阻塞)。

④私聊信息的转发:

defsend2dest(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 inenumerate(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 =Truebreakifnot 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"))

⑤广播:

defbroadcasting(self,ori_soc,data):#服务端进行广播
        ori_addr = ori_soc[1]for soc_addr inenumerate(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)ifnot 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")defstop_thread(thread):
    _async_raise(thread.ident, SystemExit)classServerUI(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()classApplication_ServerUI(ServerUI):def__init__(self):
        ServerUI.__init__(self)defconnection_accepted(self):whileTrue:
            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()defrecv_msg(self,soc):
        client_soc = soc[0]
        addr = soc[1]whileTrue: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:continueif 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()defbroadcasting(self,ori_soc,data):#服务端进行广播
        ori_addr = ori_soc[1]for soc_addr inenumerate(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]]defsend2dest(self,ori_soc,dest_addr,data):#单独转发,根据发方的目的地址
        ori_socket = ori_soc[0]
        ori_addr = ori_soc[1]
        found =Falsefor soc_addr inenumerate(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 =Truebreakifnot 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"))defconnection_accepted_threading(self):
        self.t = Thread(target=self.connection_accepted,args=())
        self.t.setDaemon(True)
        self.t.start()defon_or_off(self):ifnot self.isOn:iflen(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客户端的设计进行详解。

标签: python socket

本文转载自: https://blog.csdn.net/fatfairyyy/article/details/128671764
版权归原作者 perSistence92 所有, 如有侵权,请联系我们删除。

“第一个程序——构建一个ServerUI”的评论:

还没有评论