对前边的工作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()