运维开发网

python3高级系列05:动手实现一个聊天程序

运维开发网 https://www.qedev.com 2021-02-27 08:28 出处:51CTO 作者:mb5fdb0a4002420
本节我们使用前面的知识动手实现一个聊天程序,不再像系列教程网络编程中那样,客户端发送消息,服务端回复消息,我们要支持多个客户端和客户端聊天通信。按照我们之前学过的面向对象编程的知识,我们将客户端和服务端封装成两个类,它们有自己的处理逻辑,但都是基于socket通信。class ChatClient:    def __init__(self,host,port):        self.host

本节我们使用前面的知识动手实现一个聊天程序,不再像系列教程网络编程中那样,客户端发送消息,服务端回复消息,我们要支持多个客户端和客户端聊天通信。

按照我们之前学过的面向对象编程的知识,我们将客户端和服务端封装成两个类,它们有自己的处理逻辑,但都是基于socket通信。

class ChatClient:    def __init__(self,host,port):        self.host = host        self.port = port        self.user_id = None
class ChatServer:    c_socket_dict = {}    def __init__(self,host,port):        self.host = host        self.port = port

在本节示例中我们使用TCP协议,所以客户端与客户端的通信,实际上是客户端与服务端的通信,即客户端把消息发送给服务端,服务端根据消息的接收账号,将消息转发给对应的接收账号。

class BindMsg:    def __init__(self,user_id):        self.user_id = user_idclass ChatMsg:    def __init__(self,from_user_id,to_user_id,content):        self.from_user_id = from_user_id        self.to_user_id = to_user_id        self.content = content

简单使用两个消息类来标识通信消息对象,BindMsg绑定消息类只含有一个账号属性,客户端连接服务端时需要输入自己的账号信息。ChatMsg聊天消息类包含由谁发起消息,消息内容,发给谁3个属性。

def open(self):    server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)    server.bind((self.host, self.port))    server.listen()    while True:        c_socket,c_addr = server.accept()        client_addr_str = '[%s:%d]'%client_addr        print('客户端%s已连接'%client_addr_str)        t = Thread(target=self.__run,args=(c_socket,c_addr))        t.start()

由于accept接收客户端连接是阻塞的,所以我们使用新的线程来处理客户端连接socket,一个线程维护一个客户端连接,线程只需持有客户端连接socket即可。

def __run(self,*args):    socket_,addr_ = args    recv_msg = pickle.loads(socket_.recv(1024))    while recv_msg:        if isinstance(recv_msg,BindMsg):           ChatServer.conn_dict[recv_msg.user_id] = socket_           ...        elif isinstance(recv_msg,ChatMsg):            to_ = recv_msg.to_user_id            ChatServer.conn_dict[to_].send(pickle.dumps(recv_msg))            recv_msg = pickle.loads(socket_.recv(1024))            ...

这里只展示部分代码片段,当客户端连接上服务端时,我们根据用户号维护一个用户号:用户socket的字典。当客户端发送ChatMsg聊天消息时,我们找到接收帐号的socket,并由服务端将消息发送给指定的接收客户端。

客户端连接服务端时需要输入自己的账号,并且发送BindMsg绑定消息至服务端。客户端发送消息时,需要输入发送的消息内容和对方账号。

def connect(self):    self.user_id = input('请输入您的账号:\r\n')    client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)    client.connect((self.host, self.port))    bind_ = BindMsg(self.user_id)    client.send(pickle.dumps(bind_))    r = Thread(target=self.read,args=(client,))    r.start()    w = Thread(target=self.write,args=(client,))    w.start()

这里我们使用两个线程分别进行读/写数据,读数据是一个阻塞的动作,此外读数据和写数据并没有依赖关系,是一个并行的动作,所以读、写应该互不影响。

def read(self,socket_):    while True:        recv_msg = pickle.loads(socket_.recv(1024))        if recv_msg.content == 'quit':            breakdef write(self,socket_):    while True:        to_user_id = input('请输入对方聊天账号:\r\n')        content = input('请输入发送内容:\r\n')        if content == 'quit':            break        chat_ = ChatMsg(self.user_id, to_user_id, content)        socket_.send(pickle.dumps(chat_))

这里我们贴出部分读、写函数的代码片段,写数据时需要构造指定的ChatMsg聊天对象。注意以上发送接收数据需要序列化通信数据,因为这里发送的是对象,我们直接使用pickle组件进行字节序列化和反序列化。

以上基本就实现了一个客户端与客户端聊天程序,还有很多不完善的地方,等我们学习了GUI(图形用户界面),可以模拟QQ聊天程序那样,接收发送消息。

扫码领视频副本.gif

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号