谷歌应用引擎通道API

7
我正在尝试学习GAE的通道API(使用Java),但我无法确定从哪里开始。
我阅读了 Channel API Overview (Java),但那里发布的代码出于简洁起见并不完整。
由于我是新手,如果有完整的示例代码将非常有帮助。
谢谢, Shrey
2个回答

33

您提供的Channel API Overview中的代码非常完整,只是有些混乱。我必须承认,一旦您理解了它,我觉得它比他们描述的要简单得多,但我很高兴他们在提供太多信息方面出错。

要完整地解决这个问题有点困难,因为您将如何使用Channel API有点取决于您现有应用程序的基础设施。因此,我试图仅详细说明AppEngine文档提供的内容,希望您可以更好地理解。如果您有任何问题,请使用评论功能进行提问。

首先,我们来了解一些词汇:

  • 通道消息:您希望发送到客户端的消息(很可能是您首次使用Channel API的原因)。
  • 通道密钥:对于用户和用户尝试发送消息的范围来说是唯一的字符串。
  • 通道令牌:对于任何客户端来说都是唯一的字符串。每个客户端每2小时持有1个通道令牌。
  • 通道服务:提供创建通道并通过它们发送通道消息的AppEngine服务器端类。

在服务器上,您需要执行以下操作:

ChannelService channelService = ChannelServiceFactory.getChannelService();

// The channelKey can be generated in any way that you want, as long as it remains
// unique to the user.
String channelKey = "xyz";
String token = channelService.createChannel(channelKey);

一旦您拥有了令牌,您只需要找到一种方法将其传递到客户端代码中。您链接的AppEngine文档通过从Java servlet提供HTML并调用index.replaceAll("\\{\\{ token \\}\\}", token)来完成此操作。

其原理是在JavaScript代码中放置了文字字符串{{ token }}(如下所示),因此无论JavaScript代码中的哪个位置出现{{ token }},它都将被上面的channelService.createChannel(...)调用生成的实际令牌替换。请注意,您不必在以这种方式提供的客户端代码中注入令牌,但这是一个很好的起点,因为这就是他们所做的(并记录下来的)。


既然您已经在JavaScript中注入了令牌,您需要将带有通道令牌的代码传递给客户端(请注意,正如上面所述,您也可以只将令牌传递给客户端,并以此方式创建通道)。他们的代码如下:

<body>
  <script>
    channel = new goog.appengine.Channel('{{ token }}');
    socket = channel.open();
    socket.onopen = onOpened;
    socket.onmessage = onMessage;
    socket.onerror = onError;
    socket.onclose = onClose;
  </script>
</body>
他们没有详细说明如何从服务器的文件中读取内容,但是你可以按照自己喜欢的任何方式进行操作。你也可以在 JavaServlet 中使用 resp.getWriter().print(index) 直接打印字符串,其中index 是保存上述 HTML/JavaScript 内容的字符串。正如我最初所说的,很多事情都取决于你选择什么最适合你应用程序的现有基础设施。
他们希望你定义自己的 JavaScript 函数 onOpenedonMessageonErroronClose,分别在打开通道、接收消息、遇到错误或关闭时调用。你可能只想创建一些简单的实现来更好地了解正在发生的事情。
function onOpened() {
    alert("Channel opened!");
}

function onMessage(msg) {
    alert(msg.data);
}

function onError(err) {
    alert(err);
}

function onClose() {
    alert("Channel closed!");
}

我仍然建议将它们分开成单独的函数,这样您可以更轻松地扩展它们以便玩耍和找出问题。有关JavaScript API的更多细节,请参见Channel API JavaScript参考


你需要建立一种机制来从客户端到服务器获取要发送的数据。再次强调,您希望如何执行此操作并不重要。 AppEngine文档建议设置XMLHttpRequest来实现这个目的。

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();
};

这里,opt_param只是一个可选参数的字符串,格式为x=1&y=2&z=3。这是他们为其示例井字棋应用程序构建的所有基础设施,不是 Channel API 功能的关键;就像我说的,您可以以任何希望的方式进行此调用。

path是到您的servlet的路径(您需要在web.xml文件中设置),该servlet应处理消息的发送和接收(请参见以下部分)。


在客户端向服务器发送消息后,您需要一个servlet,可以向具有相同通道密钥的所有客户端发送更新

ChannelService channelService = ChannelServiceFactory.getChannelService();

// This channelKey needs to be the same as the one in the first section above.
String channelKey = "xyz"

// This is what actually sends the message.
channelService.sendMessage(new ChannelMessage(channelKey, "Hello World!"));

上面的channelService.sendMessage(...)调用是实际发送消息的操作,以便由您在前一节中定义的onMessage函数接收。


我希望这个答案足够完整(也正确)以帮助您入门。大部分文档中提到的内容(以及我的代码)都可以复制粘贴,只需要进行少量修改。


非常感谢 :) 我之前对这个 index.replaceAll("\{\{ token \}\}", token) 很困惑,现在我可以开始了.. :) - Shrey
它运行得很好... :),我如何进行多播消息?有没有相应的API? - Shrey
5
这个回答值得某种奖励 :) - HaveAGuess
你是说从Java发出的单个sendMessage调用将向多个客户端发送消息,每个客户端都有不同的令牌,共享通道密钥?这似乎与https://developers.google.com/appengine/docs/java/channel/overview#Caveats中的文档相矛盾,该文档指出:“每次只能有一个客户端使用给定的客户端ID连接到通道,因此应用程序不能使用客户端ID进行扇出。”如果它按照您所说的那样工作,那就太好了。但我认为它并没有这样做。 - Chuck
据我所知,所描述的扇出方法在本地工作,但在生产环境中不起作用。您需要为不同的客户端分配不同的通道密钥,并遍历它们,将更新发送到每个客户端。 - Jon Newmuis

13
我刚开始使用StackOverflow,不确定这个问题是否仍然存在,但如果你仍在寻找一个完整的Java示例,使用Google的Channel API,包括ServerSide(Java)和Client(Java),你可以在这里找到我撰写的详细说明:http://masl.cis.gvsu.edu/2012/01/31/java-client-for-appengine-channels/ 其中包括从创建通道(客户端和服务器),发送通道消息(客户端和服务器),以及Java客户端可以使用的简单框架。我也曾经很难理解Google的文档并把它们融合在一起。希望这些信息对您有用。
完整的源代码和聊天示例可以在GitHub上找到:https://github.com/gvsumasl/jacc 以下是一些示例代码,希望能够帮助您:
Java客户端通道创建(使用ChannelAPI框架:Jacc)
ChatListener chatListener = new ChatListener();
ChannelAPI channel = new ChannelAPI("http://localhost:8888", "key", chatListener);
channel.open();

在Java中创建服务器端通道:

public class ChatChannelServlet extends HttpServlet {
  @Override
  public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {    
    String channelKey = req.getParameter("c");

    //Create a Channel using the 'channelKey' we received from the client
    ChannelService channelService = ChannelServiceFactory.getChannelService();
    String token = channelService.createChannel(channelKey);

    //Send the client the 'token' + the 'channelKey' this way the client can start using the new channel
    resp.setContentType("text/html");
    StringBuffer sb = new StringBuffer();
    sb.append("{ \"channelKey\":\"" + channelKey + "\",\"token\":\"" + token + "\"}");

    resp.getWriter().write(sb.toString());
  }
}

Java客户端消息发送(使用ChannelAPI框架:Jacc)

/***
* Sends your message on the open channel
* @param message
*/
public void sendMessage(String message){
try {
        channel.send(message, "/chat");
    } catch (IOException e) {
        System.out.println("Problem Sending the Message");
    }
}

Java服务器端消息发送:

public class ChatServlet extends HttpServlet {
  @Override
  public void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {
    String channelKey = req.getParameter("channelKey");
    String message = req.getParameter("message");

    //Send a message based on the 'channelKey' any channel with this key will receive the message
    ChannelService channelService = ChannelServiceFactory.getChannelService();
    channelService.sendMessage(new ChannelMessage(channelKey, message));
  }
}

我用一个拉取请求修复了它。希望他们采纳。 - JohnyTex
Jacc 是什么许可证下发布的? - JohnyTex

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接