-
Notifications
You must be signed in to change notification settings - Fork 0
Home
使用TCP协议实现人机聊天互动,程序具有服务端和客户端,具体要求:
- 要求服务端代码具有一定的智能,能够根据不完整的的问题识别客户端真的要问的问题。如客户端输入how old,服务端能回答年龄。
- 服务器客户端之间能简单发送和接收文件。至少应有序列化和反序列化功能。收发双方,应打印显示发送或接收的原始对象的信息。
- 服务器于多客户聊天功能。
本软件主要由客户端和服务器端两个模块组成。其中,客户端主要面向服务器端进行连接和信息的交互,客户端需向服务器端发送Socket连接请求和连接关闭的指令。当客户端与服务器的连接建立后,即可开始向服务器端发送文字聊天信息和文件。此时,客户端会通过Socket发送打包完成后的聊天信息或文件,客户端也会收到来自服务器端的回复信息和服务器当前状态信息。 其次,服务器端则主要负责处理和回复来自客户端的文字信息和文件。当服务器端检测到由客户端发送而来的文字聊天信息时,将会把收到的信息和本地数据库里的信息进行比对和筛选,提取出相应的文字回复信息,回发送给客户端。当收到文件时,服务器端会自动反序列化和保存接收到的文件,并保存到自己目录的文件中。
本软件中,两个模块主要由Socket(套接字)作为中间媒介和接口来传输文字信息和文件。其中,每一个客户端会创建发送数据和接收数据两个线程,用于实现Socket的收发操作。服务器端则会为每一个连接此服务器的客户端创建一个单独的线程,用于接收和处理此客户端发来的文字信息和文件。收发双方均会调用Python提供的标准Socket API接口实现互联。
此部分代码主要实现了通过点击发送按钮,发送文字聊天信息和文件等操作。
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界面输入不完整问题时,会自动得到从服务器端获取的完整回复信息。
在测试文件发送接收功能时,使用testfile.txt文件作为发送文件,ClientFile.dat作为接收保存文件。在命令行窗口,可以看见发送和接收的原始对象信息。
经测试,当打开一个服务器端和两个客户端时,两个客户端可以同时连接服务器端,且互不干扰。两个客户端可以分别与服务器端聊天,得到不同的回复。