Python聊天室程序-----美化

对前边的工作Python聊天室程序和Python聊天室程序---客户端的改进的进一步更新。

客户端和服务器之间的消息传递格式为

struct.Struct('I 50s 100s')

‘I’带表消息类型,在Struct中表示整型数据;‘50s’代表发送消息的用户名,'s'代表字符,用户名最多为50个字符;‘100s’代表所发送的消息,最多为100个字符。

如果需要对发送信息的字体进行一些设置,或者想发送一些表情,可以在消息格式中添加一些新的内容即可。

struct的更详细的信息可以查询《Python标准库》一书。另外json据说也能做相关工作,这个以后再进行尝试。

在客户端里,从接收到的内容中提取具体信息方法为:

data = self.sock.recv(200)
s = struct.Struct('I 50s 100s')
unpacked_data = s.unpack(data)extra = b'\x00'infoType = unpacked_data[0]
username = (unpacked_data[1].decode()).strip(extra.decode())
info = (unpacked_data[2].decode()).strip(extra.decode())

extra是客户端接收到的空字符,比如用户名有50个字符,我们只占用了30个,剩下的20个空的。

unpacked_data是二进制数据,所以转换为字符串时需要使用decode()。strip(rm)是将字符串开头和结尾的rm字符都去除掉。

infoType,username,info就是从提取出的消息类型,用户名,信息。

下面是客户端的示意图,略丑,勿拍。

客户端代码:

# Filename: socket_client.py    
from tkinter import *    
import Pmw    
import threading    
import socket
import structMESSAGE             = 1
NEWPARTICIPANT      = 2
PARTICIPANTLEFT     = 3
REFUSE              = 4class ChatFrame:  def __init__(self, master=None):  # Create a TCP/IP socket And connect the socket to the port where    # the server is listening    self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)    self.server_address = ('localhost', 10000)    print (sys.stderr, 'connecting to %s port %s' % self.server_address)      self.sock.connect(self.server_address)# 将主机hostname当做用户名self.username = socket.gethostname()# Client GUI  self.master = master  ## Base frame for the widgets  self.frame = Frame(master)# 界面左区域的Frameself.leftFrame = Frame(self.frame)## Message Display widget  self.textDisplay = Text(self.leftFrame,height=20,width=40)## Text Input widget  self.textInput = Text(self.leftFrame,height=10,width=40)## 发送消息按钮self.sendBtn = Button(self.leftFrame, text='发送')  self.sendBtn.bind('<Button-1>', self.sendMsg)## 界面右区域的Frameself.rightFrame = Frame(self.frame)## 在线人数标签self.onlineNumLabel = Label(self.rightFrame,text='在线用户:x人',bg='gray')## 在线用户列表self.userList = Listbox(self.rightFrame)## 退出按钮self.exitBtn = Button(self.rightFrame, text='退出')# 接受消息 receiveThread = threading.Thread(name='waitForMSG', target=self.receiveMsg)  receiveThread.start()# 上线通知self.sendInfo(NEWPARTICIPANT)# 发送消息def sendInfo(self,infoType):# 消息类型为新用户上线if infoType == NEWPARTICIPANT:info = 'online\n'## 信息格式为(消息类型,用户名,信息)values = (NEWPARTICIPANT,self.username.encode(),info.encode())## 用户名最多为50个字符,信息最多为100个字符packer = struct.Struct('I 50s 100s')packed_data = packer.pack(*values)# 消息类型为文本输入信息elif infoType == MESSAGE:info = self.textInput.get(1.0,END) print('sending message is %s' % info)## 信息格式为(消息类型,用户名,信息)values = (MESSAGE,self.username.encode(),info.encode())packer = struct.Struct('I 50s 100s')packed_data = packer.pack(*values)self.textInput.delete(1.0,END)elif infoType == PARTICIPANTLEFT:passself.sock.sendall(packed_data)# 接受消息并处理def receiveMsg(self):    while True:    data = self.sock.recv(200)s = struct.Struct('I 50s 100s')unpacked_data = s.unpack(data)extra = b'\x00'## 从接受到的信息中提取具体的信息infoType = unpacked_data[0]username = (unpacked_data[1].decode()).strip(extra.decode())info = (unpacked_data[2].decode()).strip(extra.decode())print('Infomation Type is')print(infoType)print('client received "%s"' % info)if infoType == MESSAGE:message = username + ':' + infoself.textDisplay.insert(END, message)elif infoType == NEWPARTICIPANT:message = username + 'online\n'self.textDisplay.insert(END, message)self.newParticipant(username)# 发送消息按钮事件处理def sendMsg(self,event):self.sendInfo(MESSAGE)# 处理新用户加入def newParticipant(self,username):self.userList.insert(END,username)if __name__ == '__main__':  root = Tk()root.title('Chat Room')  tt = ChatFrame(root)# left framett.textDisplay.pack(fill=X,expand=1,padx=3,pady=3)tt.textInput.pack(fill=X,expand=1,padx=3,pady=3)tt.sendBtn.pack(side=LEFT)tt.leftFrame.pack(side=LEFT,fill=BOTH,padx=3,pady=3,expand=1)  # right framett.onlineNumLabel.pack(fill=X)tt.userList.pack(fill=Y,expand=1)tt.exitBtn.pack(side=RIGHT,anchor=CENTER)tt.rightFrame.pack(side=LEFT,fill=BOTH,pady=3)tt.frame.pack(fill=BOTH, padx=3, pady=3,expand=1)  root.mainloop()

服务器代码:

# Filename: socketServer.py  import socket  
import sys
import struct# Create a TCP/IP socket  
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)  # Bind the socket to the port  
server_address = ('localhost', 10000)  
print (sys.stderr, 'starting up on %s port %s' % server_address)  
sock.bind(server_address)  # Listen for incoming connections  
sock.listen(1)# 消息格式
unpacker = struct.Struct('I 50s 100s')while True:  # Wait for a connection  print (sys.stderr, 'waiting for a connection')  connection, client_address = sock.accept()  try:  print (sys.stderr, 'connection from', client_address)  # Receive the data in small chunks and retransmit it  while True:  data = connection.recv(1000)  print (sys.stderr, 'received "%s"' % data)  if data:  print (sys.stderr, 'sending data back to the client')  connection.sendall(data)  else:  print (sys.stderr, 'no data from', client_address)  break  finally:  # Clean up the connection  connection.close()