.. 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库。 :: <script type="text/javascript" src="/_sae/channel/api.js"></script> .. 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 <head> ... <script src="/_sae/channel/api.js"></script> </head> <body> <script> socket = new sae.Channel('{{ url }}'); socket.onopen = onOpened; socket.onmessage = onMessage; socket.onerror = onError; socket.onclose = onClose; </script> ... <div id='other-player' style='display:none'> Waiting for another player to join.<br> Send them this link to play:<br> <div id='game-link'><a href='{{ game_link }}'>{{ game_link }}</a></div> </div> </body> 游æˆçš„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的请求是å¦åˆæ³•ï¼Œå¦‚æžœåˆæ³•则更新游æˆçš„状æ€å¹¶ä¸”通知游æˆåŒæ–¹æ–°çš„æ¸¸æˆçжæ€ã€‚