IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

    简单聊天室<一>

    Fish (fsh267@gmail.com)发表于 2014-02-18 00:00:00
    love 0

    #背景

    互联网上在线聊天服务的种类很多, 如IRC, 在Python中实现这样一个方法, 可以使用Twisted框架.

    自己写一个的原因:

    学习网络编程基本的知识
    看书笔记总结

    聊天服务器基本的功能如下:

    • 服务器能接受不同客户端的连接
    • 允许用户并行操作
    • 能够解释命令, 如在shell中的操作


    #初次实现

    ###1 迷你服务器

    from asyncore import dispatcher
    import asyncore
    
    class ChatServer(dispatcher):
        pass
    
    s = ChatServer()
    asyncore.loop

    ###2 可以接受连接的服务器

    from asyncore import dispatcher
    import asyncore
    import socket
    
    PORT = 5267
    
    class ChatServer(dispatcher):
        def handle_accept(self):
            conn, addr = self.accept()
            print 'Connection from ', addr[0]
    
    s = ChatServer()
    s.create_socket(socket.AF_INET, socket.SOCK_STREAM)
    s.bind(('', PORT))
    s.listen(5)
    asyncore.loop()

    handle_accept方法会调用允许客户端连接的self.accept函数. 他返回一个连接和一个地址, 初始化的过程, 调用了create_socket创建套接字, bind来绑定端口, listen用来监听. 试着运行下面的语句:

    telnet localhost 5267
    

    会出现如下

    fish@love67:~$telnet localhost 5267
    Connection form 127.0.0.1
    

    ###3 具有一些清理功能的服务器

    上面的程序使用键盘关闭(ctrl + c等)会导致堆栈跟踪, 为了避免这类情况, 可以在 try/except 语句放置loop. 再加上一点清理功能, 如下:

    from asyncore import dispatcher
    import asyncore
    import socket
    
    PORT = 5267
    
    class ChatServer(dispatcher):
        def __init__(self, port):
            # 子类的初始化, 也可以用super
            dispatcher.__init__(self, port)
            self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
            # 清理函数 set_reuse_addr()
            self.set_reuse_addr()
            self.bind(('', port))
            self.listen(5)
        
        def handle_accept(self):
            conn, addr = self.accept()
            print 'Connection from ' , addr[0]
    
    if __name__ == '__main__':
            s = ChatServer(PORT)
            try:
                asyncore.loop()
            except KeyboardInterrupt:
                pass

    ###4 ChatSession类

    下面代码中, async_chat类(位于asynchat中)的好处是他隐藏了大多数基本的套接字读写操作, 为了让他起作用, 只需要覆盖两个函数: collect_incoming_data和found_terminator. 前者每次从套接字中读取一些bit文本时调用, 后者在读取一个结束符是调用.

    from asyncore import dispatcher
    from asynchat import async_chat
    import asyncore, socket
    
    PORT = 5267
    
    class ChatSession(async_chat):
        def __init__(self, sock):
            asyncore.__init__(self, sock)
            self.set_terminator('\r\n')
            self.data = []
        # 函数覆盖
        def collect_incoming_data(self, data):
            self.data.append(data)
        # 函数覆盖  
        def found_terminator(self):
            line = ''.join(self.data)
            self.data = []
            print line
        
    class ChatServer(dispatcher):
        def __init__(self, port):
            dispatcher.__init__(self, port)
            self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
            self.set_reuse_addr()
            self.bind(('', port))
            self.listen(5)
            self.sessions = []
        def handle_accept(self):
            conn, addr = self.accept()
            self.sessions.append(ChatSession(conn))
    if __name__ == '__main__':
        s = ChatServer(PORT)
        try:
            asyncore.loop()
        except KeyboardInterrupt:
            pass

    值得注意的是:

    • code>set_terminator</code>方法用于终止对象, 设定为网络卸协议中经常使用的终止符\r\n
    • ChatSession 对象会将目前读取的数据保存为字符串列表data, 当读取更多数据时, collect_incoming_data会自动调用, 将读取的数据追加到列表中
    • found_terminator方法在读取到终止符时停止, 并将self.data重置为空列表.
    • ChatServer保存会话列表

    #整合

    ##简单的聊天服务器

    from asyncore import dispatcher
    import asyncore, socket
    from asynchat import async_chat
    
    PORT = 5267
    NAME = "ChatRoom"
    
    class ChatSession(async_chat):
        def __init__(self, server, sock):
            async_chat.__init__(self, sock)
            self.server = server
            self.set_terminator('\r\n')
            self.data = []
            self.push('Welcome to %s \r\n', %self.server)
    
        def collect_incoming_data(self, data):
            self.data.append(data) 
        def found_terminator(self):
            "如果发现了一个终止符, 说明读入了完整的一行, 将其广播给所有人"
            line = ''.join(self.data)
            self.data = []
            self.server.broadcast(line)
        def handle_close(self):
            async_chat.handle_close(self)
            self.server.disconnect(self)
    
    class ChatServer(dispatcher):
        "接收连接并产生单个会话的类,  他还会处理到其他会话的广播"
        def __init__(self, port, name):
            dispatcher.__init__(self, port)
            self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
            self.set_reuse_addr()
            self.bind(('', port))
            self.name = name
            self.listen(5)
            self.sessions = []
        def disconnect(self, session):
            self.sessions.remove(session)
        def broadcast(self, session):
            for session in self.sessions:
                session.push(line + '\r\n')
        def handle_accept(self):
            conn, addr = self.accept()
            self.sessions.append(ChatSession(self, conn))
    
    if __name__ == '__main__':
        s = ChatServer(PORT, NAME)
        try:
            asyncore.loop()
        except KeyboardInterrupt:
            pass


沪ICP备19023445号-2号
友情链接