zoukankan      html  css  js  c++  java
  • python实现虚拟茶话会

    这个项目目的是编写一个聊天服务器,该聊天服务器的功能有:

    服务器能同时接收来自不同用户的连接

    允许用户同时操作

    能够解释命令,例如,say或者logout命令

    服务器容易扩展

    这个项目里面我们会使用到的模块式asyncore,使用asyncore框架,程序可以处理同时连接多个用户。asyncore框架基于一些底层的机制,这些机制允许服务器逐个的对于连接上的用户进行服务。在处理下一个连接前,它并不读取当前用户的所有可用数据,而只读取一部分。除此之外,服务器只从那些需要读取数据的套接字中读取。程序就这样一遍遍的循环。写入操作同理。只使用socket和select模块就可以实现,但是asyncore和asynchat提供了一个可以处理所有细节的有用框架。

    首先我们需要一个客户端来测试服务器,在windows下我们可以用telnet服务端,在unix内也可以用telnet。

    初次实现:

    首先我们需要创建两个类:一个作为聊天服务器,一个用于表示每个聊天会话(以连接用户)

    要生成基本的ChatServer类,需要继承asyncore模块中的dispatcher类,dispatcher基本上就是一个套接字对象,但是可以利用它额外的事件处理特性。

    下面是可以接受连接的服务器代码

     1 from asyncore import dispatcher
     2 import socket, asyncore
     3 
     4 class ChatServer(dispatcher):
     5     def handle_accept(self):
     6         conn, addr = self.accept()
     7         print 'Connection attempt from ',addr[0]
     8 
     9 s = ChatServer()
    10 s.create_socket(socket.AF_INET, socket.SOCK_STREAM)
    11 s.bind((''.5005))
    12 s.listen(5)
    13 asyncore.loop()

    handle_accpet方法会调用允许客户端连接的self.accept函数。她会返回一个连接和一个地址。handle_accept方法知识打印有关连接尝试的信息。

    当我们运行这个服务器并且用客户端连接他的时候,我们可以再服务器的控制台上看到返回的连接信息。

    ChatSession类的主要任务是为每一个连接创建一个对象,该对象负责收集来自客户端的数据并且进行响应。我们可以自己继承dispatcher并且重写一些方法来实现这个功能,但是幸运的是已经有现成的模块能够完成绝大多数的工作,asynchat

    下面便是一个简单的聊天服务器

     1 # -*- coding:utf-8 -*-
     2 '''
     3 xianghang
     4 2015.4.9
     5 虚拟聊天室
     6 '''
     7 
     8 from asyncore import dispatcher
     9 from asynchat import async_chat
    10 import socket, asyncore
    11 
    12 PORT = 5005
    13 NAME = 'TestChat'
    14 class ChatSessioin(async_chat):
    15     '''
    16     处理服务器和一个用户之间连接的类
    17     '''
    18     def __init__(self, server, sock):
    19         #标准设置任务
    20         async_chat.__init__(self, sock)
    21         self.server = server
    22         self.set_terminator('
    ')
    23         self.data = []
    24         #问候用户
    25         self.push('Welcome to %s
    ' % self.server.name)
    26 
    27     def collect_incoming_data(self, data):
    28         self.data.append(data)
    29 
    30     def found_terminator(self):
    31         '''
    32         如果发现了一个终止对象,也就意味着读入了一个完整的行,将广播给所有人
    33         '''
    34         line = ''.join(self.data)
    35         self.data = []
    36         self.server.broadcast(line)
    37 
    38     def handle_close(self):
    39         async_chat.handle_close(self)
    40         self.server.disconnect(self)
    41 
    42 class ChatServer(dispatcher):
    43     '''
    44     接收连接并且产生单个会话的类。他还会处理到其他会话的广播
    45     '''
    46     def __init__(self, port, name):
    47         #标准设置任务
    48         dispatcher.__init__(self)
    49         self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
    50         self.set_reuse_addr()
    51         self.bind(('', port))
    52         self.listen(5)
    53         self.name = name
    54         self.sessions = []
    55     def disconnect(self, session):
    56         self.sessions.remove(session)
    57 
    58     def broadcast(self, line):
    59         for session in self.sessions:
    60             session.push(line+'
    ')
    61 
    62     def handle_accept(self):
    63         conn, addr = self.accept()
    64         self.sessions.append(ChatSessioin(self, conn))
    65 
    66 if __name__ == '__main__':
    67     s = ChatServer(PORT, NAME)
    68     try: asyncore.loop()
    69     except KeyboardInterrupt: print

    运行这个程序后会发现,客户端虽然能够显示其它用户发送的信息,但不能识别是哪一个用户,而且整个程序没有登录登出等功能,下面我们就来扩展这些功能。

    再次实现:

    首先我们把服务器分为三个房间,登录房间,聊天室,登出房间,三个房间分别为三个类,这些类的父类拥有添加和删除会话 的功能。代码如下

     1 class Room(CommandHandler):
     2     '''
     3     包括一个或多个用户(会话)的泛型环境,他负责基本的命令处理和广播
     4     '''
     5 
     6     def __init__(self, server):
     7         self.server = server
     8         self.sessions = []
     9 
    10     def add(self, session):
    11         '一个会话用户已进入房间'
    12         self.sessions.append(session)
    13 
    14     def remove(self, session):
    15         '一个会话用户已离开房间'
    16         self.sessions.remove(session)
    17 
    18     def broadcast(self, line):
    19         '向房间中的所有会话发送一行'
    20         for session in self.sessions:
    21             session.push(line)
    22 
    23     def do_logout(self, session, line):
    24         '响应logout命令'
    25         raise EndSession

    登录房间继承room父类

     1 class LoginRoom(Room):
     2     '''
     3     为刚刚连接上的用户准备的房间
     4     '''
     5     def add(self, session):
     6         Room.add(self, session)
     7         #当用户进入时,问候他
     8         self.broadcast('Welcome to %s 
    ' % self.server.name)
     9 
    10     def unknown(self, session, cmd):
    11         #所有未知命令(除了login或者logout除外)
    12         #会导致一个警告
    13         session.push('Please log in 
    Use "login <nick>"
    ')
    14 
    15     def do_login(self, session, line):
    16         name = line.strip()
    17         #确保用户输入了名字
    18         if not  name:
    19             session.push('Please enter a name
    ')
    20         #确保用户名没有被使用
    21         elif name in self.server.users:
    22             session.push('The name "%s" is taken.
    ' % name)
    23             session.push('Please try again.
    ')
    24         else:
    25             #名字没问题,所以存储在会话中
    26             #将用户移动到主聊天室
    27             session.name = name
    28             session.enter(self.server.main_room)

      登录房间是让用户登录时所在的房间,在该房间内接收用户的登录命令,登陆成功后进入聊天房间。同样的,也有登出房间登出房间同理。

      聊天室房间负责处理say,look命令,say命令可以让用户在该房间内发言,并显示在其他的客户端上;look命令可以让用户查看当前聊天室所存在的用户。

     1 class ChatRoom(Room):
     2     '''
     3     为多用户相互聊天准备房间
     4     '''
     5     def add(self, session):
     6         #告诉所有人有新用户进入:
     7         self.broadcast(session.name + ' has entered the room
    ')
     8         self.server.users[session.name] = session
     9         Room.add(self, session)
    10 
    11     def remove(self, session):
    12         Room.remove(self, session)
    13         #告诉所有人有用户离开
    14         self.broadcast(session.name + ' has left the room.
    ')
    15 
    16     def do_say(self, session, line):
    17         self.broadcast(session.name +':' + line + '
    ')
    18 
    19     def do_look(self, session, line):
    20         '处理look命令,该命令用于查看谁在房间'
    21         session.push('The following are in this room:
    ')
    22         for other in self.sessions:
    23             session.push(other.name + '
    ')

    整个服务器程序的结构如下图:

  • 相关阅读:
    css hack
    在前端页面开发中所遇到的问题总结
    Animate.css_css3动画库介绍
    响应式内容滑动插件bxSlider
    Combination Sum II
    Subsets
    Combination Sum
    Unique Binary Search Trees II
    Find Median from Data Stream
    Kth Largest Element in an Array
  • 原文地址:https://www.cnblogs.com/Blaxon/p/4411474.html
Copyright © 2011-2022 走看看