Skip to content
Awesome Sublime edited this page Jun 25, 2020 · 2 revisions

功能需求

使用TCP协议实现人机聊天互动,程序具有服务端和客户端,具体要求:

  1. 要求服务端代码具有一定的智能,能够根据不完整的的问题识别客户端真的要问的问题。如客户端输入how old,服务端能回答年龄。
  2. 服务器客户端之间能简单发送和接收文件。至少应有序列化和反序列化功能。收发双方,应打印显示发送或接收的原始对象的信息。
  3. 服务器于多客户聊天功能。

模块功能

本软件主要由客户端和服务器端两个模块组成。其中,客户端主要面向服务器端进行连接和信息的交互,客户端需向服务器端发送Socket连接请求和连接关闭的指令。当客户端与服务器的连接建立后,即可开始向服务器端发送文字聊天信息和文件。此时,客户端会通过Socket发送打包完成后的聊天信息或文件,客户端也会收到来自服务器端的回复信息和服务器当前状态信息。 其次,服务器端则主要负责处理和回复来自客户端的文字信息和文件。当服务器端检测到由客户端发送而来的文字聊天信息时,将会把收到的信息和本地数据库里的信息进行比对和筛选,提取出相应的文字回复信息,回发送给客户端。当收到文件时,服务器端会自动反序列化和保存接收到的文件,并保存到自己目录的文件中。

模块接口

本软件中,两个模块主要由Socket(套接字)作为中间媒介和接口来传输文字信息和文件。其中,每一个客户端会创建发送数据和接收数据两个线程,用于实现Socket的收发操作。服务器端则会为每一个连接此服务器的客户端创建一个单独的线程,用于接收和处理此客户端发来的文字信息和文件。收发双方均会调用Python提供的标准Socket API接口实现互联。

structure

实现流程

客户端

此部分代码主要实现了通过点击发送按钮,发送文字聊天信息和文件等操作。

    def OnSendBufferBtn(self, event): # 文字发送按钮
        buff = self.sendBufferText.GetValue()
        self.msg = buff
        self.buffBtnClicked = True

    def OnSendFileBtn(self, event): # 文件发送给按钮
        self.filename = self.sendFileName.GetValue()
        self.fileBtnClicked = True

    def recv(self):   # 接收线程
        while True:
            print(self.closeFlag)
            if self.closeFlag == True:
                break
            data_recv = self.tcp_socket_client.recv(1024)
            data=data_recv.decode()
            self.recvBuffer.SetValue(data)
            #print('>服务端:', data)
                
    def send_msg(self):  #发送线程
        self.msg = ''
        self.filename = ''
        self.modeChat = 0
        self.modeFile = 0
        while True:
            isChat = self.checkBoxChat.GetValue()
            isFile = self.checkBoxFile.GetValue()
            if isChat == False and isFile == False:
                if self.closeFlag == True:
                        break
                continue

            if isChat and not isFile:     # 1.聊天模式        
                self.modeChat = 1
                self.modeFile = 0
                self.sendCurrentMode(self.modeChat, self.modeFile)
                while not self.buffBtnClicked:
                    self.statusBar.SetStatusText('正在等待发送')
                    if self.closeFlag == True:
                        self.flag = True
                        break
                    continue

                if self.flag:
                    break
                self.buffBtnClicked = False
                #print(self.closeFlag)

                if self.msg != '':    
                    self.tcp_socket_client.send(self.msg.encode())
                    self.msg = ''
                    self.statusBar.SetStatusText('消息发送成功')
                    time.sleep(2)
                    self.statusBar.SetStatusText('就绪')
                self.exit_and_set_default()

            elif not isChat and isFile:      # 2.文件发送模式       
                self.modeChat = 0
                self.modeFile = 1
                self.sendCurrentMode(self.modeChat, self.modeFile)
                while not self.fileBtnClicked:
                    self.statusBar.SetStatusText('正在等待发送')
                    if self.closeFlag == True:
                        self.flag = True
                        break
                    continue

                if self.flag:
                    break
                self.fileBtnClicked = False
                if self.filename != '':
                    file_size=os.path.getsize(self.filename)
                    fhead = struct.pack('l',file_size)
                    print(fhead)      #打印出发送的原始对象信息
                    self.tcp_socket_client.sendall(fhead)
                    fp=open(self.filename,'rb')
                    while True:      
                        data=fp.read(BUFSIZE)
                        if not data:
                            break
                        self.tcp_socket_client.sendall(data)
                    fp.close()

                    self.filename = ''
                    self.statusBar.SetStatusText('文件发送成功')
                    time.sleep(2)
                    self.statusBar.SetStatusText('就绪')
                    self.exit_and_set_default()

服务器端

此部分代码主要实现了服务器端接收文字信息和文件。接收到文字信息后,发送回复信息到客户端;接收到文件后,解包并保存到本地。

def client(clientSocket,Num):  # 服务器端对应客户端的一个线程
    global clientNum
    mode='00'
    flag2 = False
    while True:
        if flag2 == True:
            clientSocket.sendall('服务器端即将退出...'.encode())
            time.sleep(5)
            print('服务器端已退出。')
            break
        while True:
            mode = clientSocket.recv(BUFSIZE)
            mode = mode.decode()   
            print(mode)
            while not (mode == '10' or mode == '01'):
                pass
            if mode == '10':   # 1.聊天文字接收模式
                data = clientSocket.recv(BUFSIZE)
                data = data.decode() 
                print('received message:{0} from client{1}'.format(data, Num))  
                match = '.*%s.*'%data
                print(match)
                matchKeys = []
                pattern = re.compile(match)
                for key in words.keys():
                    result = pattern.findall(key)
                    print(key)
                    print(result)
                    if result != []:
                        matchKeys.extend(result)
                print(matchKeys)
                if matchKeys == []:
                    matchKeys.extend(['Not Found'])
                for eachkey in matchKeys:
                    print(eachkey)
                    clientSocket.sendall(words.get(eachkey,'Nothing').encode())
                #if data.lower()=='bye':
                #    break
                time.sleep(2)
                break
            elif mode == '01':    # 2.文件接收模式 
                data = clientSocket.recv(BUFSIZE)
                print(data)   #打印出接收的原始对象信息
                f_info=struct.unpack('l',data) 
                file_size=f_info[0]    
                recv_size=0
                while not recv_size==file_size:
                    with open('ClientFile.dat','ab') as fp:  
                        if file_size - recv_size > BUFSIZE:
                            data = clientSocket.recv(BUFSIZE)              
                            recv_size += len(data)
                        else:
                            data= clientSocket.recv(file_size - recv_size) 
                            recv_size = file_size
                        print(data)
                        fp.write(data)
                clientSocket.sendall(words.get(data,'服务器已收到文件。').encode())
                mode = '00'
                break
            else:
                flag2 = True
                break
        
        time.sleep(2)
              
    clientSocket.close()    
    lock.acquire()              
    clientNum-=1
    lock.release()

功能测试

不完整聊天问题回复

经测试,当从客户端GUI界面输入不完整问题时,会自动得到从服务器端获取的完整回复信息。

1 2

文件发送与接收

在测试文件发送接收功能时,使用testfile.txt文件作为发送文件,ClientFile.dat作为接收保存文件。在命令行窗口,可以看见发送和接收的原始对象信息。

3

4

5

多客户聊天

经测试,当打开一个服务器端和两个客户端时,两个客户端可以同时连接服务器端,且互不干扰。两个客户端可以分别与服务器端聊天,得到不同的回复。

6