.. include:: ../services/channel.rst API使用手册 ============ Server端API: .. module:: sae.channel .. py:function:: create_channel(name, duration=None) 创建一个channel。 :param name: channel的名称。 :param duration: channel的有效时间,单位为秒,默认为1小时。 .. py:function:: send_message(name, message, async=False) 向名为name的channel里发送一条消息 :param name: channel的名称 :param message: 需要发送的消息内容 :param async: 如果设置为True,服务端会立刻返回 .. warning:: 最大可以发送4k的消息,最好不要直接发送二进制的数据。 Javascript客户端API: 在html页面中使用以下代码引用Channel服务的js库。 :: .. js:class:: sae.Channel(url) :param string url: 服务端create_channel()返回的url地址 .. js:attribute:: onopen 设置客户端连接上服务端时的回调函数。 .. js:attribute:: onmessage 设置客户端收到消息时的回调函数。该函数接受一个参数:一个messagae对象,其中的data字段为服务端send_message接口发送的消息内容。 .. js:attribute:: onerror 设置客户端和服务端连接出现错误时的回调函数。 .. js:attribute:: onclose 设置客户端关闭和服务端的连接时的回调函数。 使用示例 ============== 下面我们使用 `TicTacToe(井字棋)`_ 游戏来示范Channel服务的使用方法: .. _TicTacToe(井字棋): http://zh.wikipedia.org/wiki/%E4%BA%95%E5%AD%97%E6%A3%8B 完整代码:https://github.com/sinacloud/sae-channel-examples/tree/master/python **channel的创建和连接** 首先,当用户A打开TicTacToe游戏的主页时,TicTacToe服务端的程序会: + 调用 `create_channel` 创建为用户A创建一个channel,并将该channel的url嵌入到返回给用户的html页面代码中。 + 生成一个加入游戏的连接,用户通过将此连接发送给其它用户B,其它用户B可以通过此连接加入用户A创建的游戏。 每个页面对应的channel的name应该是独一无二的,比如可以使用用户id的字符串作为channel的name。 游戏的主页的html代码模板大致如下所示,其中 `{{ url }}` 和 `{{ game_link }}` 分别为上面生成的channel url和游戏加入连接。 .. code-block:: html ... ... 游戏的js客户端使用 `sae.Channel` 来创建一条channel连接,并且设置channel的onopen/onmessage/onerror/onclose的callback函数。 **使用channel来推送游戏状态信息** 当用户B点击用户A发过来的连接打开了游戏页面时,游戏的javascript客户端通过 `sendMessage` 函数通知服务端。 .. code-block:: javascript onOpened = function() { connected = true; sendMessage('opened'); updateBoard(); }; sendMessage = function(path, opt_param) { path += '?g=' + state.game_key; if (opt_param) { path += '&' + opt_param; } var xhr = new XMLHttpRequest(); xhr.open('POST', path, true); xhr.send(); }; 服务端更新当前游戏的状态,并且通过channel的 `send_message` 将游戏的新的状态发送给用户A和用户B的channel客户端。客户端接受到消息后更新游戏页面。此后用户A和用户B交替走棋,客户端通过 `sendMessage` 将用户的走法发送给服务端。 .. code-block:: javascript moveInSquare = function(id) { if (isMyMove() && state.board[id] == ' ') { sendMessage('/move', 'i=' + id); } } 服务收到消息后更新游戏的状态,再通过 `send_message` 将更新后的状态发送给用户A和B,如此往复直到游戏结束为止。 .. code-block:: python class MovePage(tornado.web.RequestHandler): def post(self): game_key = self.get_argument('g') game = Game.get_by_key_name(game_key) user = self.get_secure_cookie('u') if game and user: id = int(self.get_argument('i')) GameUpdater(game).make_move(id, user) class GameUpdater(): game = None def __init__(self, game): self.game = game def get_game_message(self): gameUpdate = { 'board': self.game.board, 'userX': self.game.userX, 'userO': '' if not self.game.userO else self.game.userO, 'moveX': self.game.moveX, 'winner': self.game.winner, 'winningBoard': self.game.winning_board } return json.dumps(gameUpdate) def send_update(self): message = self.get_game_message() channel.send_message(self.game.userX + self.game.key_name, message) if self.game.userO: channel.send_message(self.game.userO + self.game.key_name, message) def check_win(self): if self.game.moveX: # O just moved, check for O wins wins = Wins().o_wins potential_winner = self.game.userO else: # X just moved, check for X wins wins = Wins().x_wins potential_winner = self.game.userX for win in wins: if win.match(self.game.board): self.game.winner = potential_winner self.game.winning_board = win.pattern return def make_move(self, position, user): if position >= 0 and user == self.game.userX or user == self.game.userO: if self.game.moveX == (user == self.game.userX): boardList = list(self.game.board) if (boardList[position] == ' '): boardList[position] = 'X' if self.game.moveX else 'O' self.game.board = "".join(boardList) self.game.moveX = not self.game.moveX self.check_win() self.game.put() self.send_update() return GameUpdater类检查move的请求是否合法,如果合法则更新游戏的状态并且通知游戏双方新的游戏状态。