谷歌云消息传递 - 使用 smack 的 xmpp 服务器端不起作用

Posted

技术标签:

【中文标题】谷歌云消息传递 - 使用 smack 的 xmpp 服务器端不起作用【英文标题】:google cloud messaging - xmpp server side using smack not working 【发布时间】:2013-08-13 14:43:01 【问题描述】:

当使用以下代码向使用 smack libary 3.3.0 的设备发送消息时

遇到错误

SASL authentication PLAIN failed: text: 
    at org.jivesoftware.smack.SASLAuthentication.authenticate(SASLAuthentication.java:342)
    at org.jivesoftware.smack.XMPPConnection.login(XMPPConnection.java:221)
    at org.jivesoftware.smack.Connection.login(Connection.java:366)
    at com.test.xmpp.SmackCcsClient.connect(SmackCcsClient.java:335)
    at com.test.xmpp.SmackCcsClient.main(SmackCcsClient.java:345)

当切换到 HTTP 服务器端时,消息正在工作

/**
 * Sample Smack implementation of a client for GCM Cloud Connection Server.
 *
 * <p>For illustration purposes only.
 */
public class SmackCcsClient 

  Logger logger = Logger.getLogger("SmackCcsClient");

  public static final String GCM_SERVER = "gcm.googleapis.com";
  public static final int GCM_PORT = 5235;

  public static final String GCM_ELEMENT_NAME = "gcm";
  public static final String GCM_NAMESPACE = "google:mobile:data";

  static Random random = new Random();
  XMPPConnection connection;
  ConnectionConfiguration config;

  /**
   * XMPP Packet Extension for GCM Cloud Connection Server.
   */
  class GcmPacketExtension extends DefaultPacketExtension 
    String json;

    public GcmPacketExtension(String json) 
      super(GCM_ELEMENT_NAME, GCM_NAMESPACE);
      this.json = json;
    

    public String getJson() 
      return json;
    

    @Override
    public String toXML() 
      return String.format("<%s xmlns=\"%s\">%s</%s>", GCM_ELEMENT_NAME,
          GCM_NAMESPACE, json, GCM_ELEMENT_NAME);
    

    @SuppressWarnings("unused")
    public Packet toPacket() 
      return new Message() 
        // Must override toXML() because it includes a <body>
        @Override
        public String toXML() 

          StringBuilder buf = new StringBuilder();
          buf.append("<message");
          if (getXmlns() != null) 
            buf.append(" xmlns=\"").append(getXmlns()).append("\"");
          
          if (getLanguage() != null) 
            buf.append(" xml:lang=\"").append(getLanguage()).append("\"");
          
          if (getPacketID() != null) 
            buf.append(" id=\"").append(getPacketID()).append("\"");
          
          if (getTo() != null) 
            buf.append(" to=\"").append(StringUtils.escapeForXML(getTo())).append("\"");
          
          if (getFrom() != null) 
            buf.append(" from=\"").append(StringUtils.escapeForXML(getFrom())).append("\"");
          
          buf.append(">");
          buf.append(GcmPacketExtension.this.toXML());
          buf.append("</message>");
          return buf.toString();
        
      ;
    
  

  public SmackCcsClient() 
    // Add GcmPacketExtension
    ProviderManager.getInstance().addExtensionProvider(GCM_ELEMENT_NAME,
        GCM_NAMESPACE, new PacketExtensionProvider() 

      @Override
      public PacketExtension parseExtension(XmlPullParser parser)
          throws Exception 
        String json = parser.nextText();
        GcmPacketExtension packet = new GcmPacketExtension(json);
        return packet;
      
    );
  

  /**
   * Returns a random message id to uniquely identify a message.
   *
   * <p>Note:
   * This is generated by a pseudo random number generator for illustration purpose,
   * and is not guaranteed to be unique.
   *
   */
  public String getRandomMessageId() 
    return "m-" + Long.toString(random.nextLong());
  

  /**
   * Sends a downstream GCM message.
   */
  public void send(String jsonRequest) 
    Packet request = new GcmPacketExtension(jsonRequest).toPacket();
    connection.sendPacket(request);
  

  /**
   * Handles an upstream data message from a device application.
   *
   * <p>This sample echo server sends an echo message back to the device.
   * Subclasses should override this method to process an upstream message.
   */
  public void handleIncomingDataMessage(Map<String, Object> jsonObject) 
    String from = jsonObject.get("from").toString();

    // PackageName of the application that sent this message.
    String category = jsonObject.get("category").toString();

    // Use the packageName as the collapseKey in the echo packet
    String collapseKey = "echo:CollapseKey";
    @SuppressWarnings("unchecked")
    Map<String, String> payload = (Map<String, String>) jsonObject.get("data");
    payload.put("ECHO", "Application: " + category);

    // Send an ECHO response back
    String echo = createJsonMessage(from, getRandomMessageId(), payload, collapseKey, null, false);
    send(echo);
  

  /**
   * Handles an ACK.
   *
   * <p>By default, it only logs a INFO message, but subclasses could override it to
   * properly handle ACKS.
   */
  public void handleAckReceipt(Map<String, Object> jsonObject) 
    String messageId = jsonObject.get("message_id").toString();
    String from = jsonObject.get("from").toString();
    logger.log(Level.INFO, "handleAckReceipt() from: " + from + ", messageId: " + messageId);
  

  /**
   * Handles a NACK.
   *
   * <p>By default, it only logs a INFO message, but subclasses could override it to
   * properly handle NACKS.
   */
  public void handleNackReceipt(Map<String, Object> jsonObject) 
    String messageId = jsonObject.get("message_id").toString();
    String from = jsonObject.get("from").toString();
    logger.log(Level.INFO, "handleNackReceipt() from: " + from + ", messageId: " + messageId);
  

  /**
   * Creates a JSON encoded GCM message.
   *
   * @param to RegistrationId of the target device (Required).
   * @param messageId Unique messageId for which CCS will send an "ack/nack" (Required).
   * @param payload Message content intended for the application. (Optional).
   * @param collapseKey GCM collapse_key parameter (Optional).
   * @param timeToLive GCM time_to_live parameter (Optional).
   * @param delayWhileIdle GCM delay_while_idle parameter (Optional).
   * @return JSON encoded GCM message.
   */
  public static String createJsonMessage(String to, String messageId, Map<String, String> payload,
      String collapseKey, Long timeToLive, Boolean delayWhileIdle) 
    Map<String, Object> message = new HashMap<String, Object>();
    message.put("to", to);
    if (collapseKey != null) 
      message.put("collapse_key", collapseKey);
    
    if (timeToLive != null) 
      message.put("time_to_live", timeToLive);
    
    if (delayWhileIdle != null && delayWhileIdle) 
      message.put("delay_while_idle", true);
    
    message.put("message_id", messageId);
    message.put("data", payload);
    return JSONValue.toJSONString(message);
  

  /**
   * Creates a JSON encoded ACK message for an upstream message received from an application.
   *
   * @param to RegistrationId of the device who sent the upstream message.
   * @param messageId messageId of the upstream message to be acknowledged to CCS.
   * @return JSON encoded ack.
   */
  public static String createJsonAck(String to, String messageId) 
    Map<String, Object> message = new HashMap<String, Object>();
    message.put("message_type", "ack");
    message.put("to", to);
    message.put("message_id", messageId);
    return JSONValue.toJSONString(message);
  

  /**
   * Connects to GCM Cloud Connection Server using the supplied credentials.
   *
   * @param username GCM_SENDER_ID@gcm.googleapis.com
   * @param password API Key
   * @throws XMPPException
   */
  public void connect(String username, String password) throws XMPPException 
    config = new ConnectionConfiguration(GCM_SERVER, GCM_PORT);
    config.setSecurityMode(SecurityMode.enabled);
    config.setReconnectionAllowed(true);
    config.setRosterLoadedAtLogin(false);
    config.setSendPresence(false);
    config.setSocketFactory(SSLSocketFactory.getDefault());

    // NOTE: Set to true to launch a window with information about packets sent and received
    config.setDebuggerEnabled(true);

    // -Dsmack.debugEnabled=true
    XMPPConnection.DEBUG_ENABLED = true;

    connection = new XMPPConnection(config);
    connection.connect();

    connection.addConnectionListener(new ConnectionListener() 

      @Override
      public void reconnectionSuccessful() 
        logger.info("Reconnecting..");
      

      @Override
      public void reconnectionFailed(Exception e) 
        logger.log(Level.INFO, "Reconnection failed.. ", e);
      

      @Override
      public void reconnectingIn(int seconds) 
        logger.log(Level.INFO, "Reconnecting in %d secs", seconds);
      

      @Override
      public void connectionClosedOnError(Exception e) 
        logger.log(Level.INFO, "Connection closed on error.");
      

      @Override
      public void connectionClosed() 
        logger.info("Connection closed.");
      
    );

    // Handle incoming packets
    connection.addPacketListener(new PacketListener() 

      @Override
      public void processPacket(Packet packet) 
        logger.log(Level.INFO, "Received: " + packet.toXML());
        Message incomingMessage = (Message) packet;
        GcmPacketExtension gcmPacket =
            (GcmPacketExtension) incomingMessage.getExtension(GCM_NAMESPACE);
        String json = gcmPacket.getJson();
        try 
          @SuppressWarnings("unchecked")
          Map<String, Object> jsonObject =
              (Map<String, Object>) JSONValue.parseWithException(json);

          // present for "ack"/"nack", null otherwise
          Object messageType = jsonObject.get("message_type");

          if (messageType == null) 
            // Normal upstream data message
            handleIncomingDataMessage(jsonObject);

            // Send ACK to CCS
            String messageId = jsonObject.get("message_id").toString();
            String from = jsonObject.get("from").toString();
            String ack = createJsonAck(from, messageId);
            send(ack);
           else if ("ack".equals(messageType.toString())) 
            // Process Ack
            handleAckReceipt(jsonObject);
           else if ("nack".equals(messageType.toString())) 
            // Process Nack
            handleNackReceipt(jsonObject);
           else 
            logger.log(Level.WARNING, "Unrecognized message type (%s)",
                messageType.toString());
          
         catch (ParseException e) 
          logger.log(Level.SEVERE, "Error parsing JSON " + json, e);
         catch (Exception e) 
          logger.log(Level.SEVERE, "Couldn't send echo.", e);
        
      
    , new PacketTypeFilter(Message.class));


    // Log all outgoing packets
    connection.addPacketInterceptor(new PacketInterceptor() 
      @Override
      public void interceptPacket(Packet packet) 
        logger.log(Level.INFO, "Sent: 0",  packet.toXML());
      
    , new PacketTypeFilter(Message.class));

    connection.login(username, password);
  

  public static void main(String [] args) 
    final String userName = "124079202908" + "@gcm.googleapis.com";
    final String password = "AIzaSyAMt1y_xILk72wiQybuqVEdiq7uGXBEUhQ";

    SmackCcsClient ccsClient = new SmackCcsClient();

    try 
      ccsClient.connect(userName, password);
     catch (XMPPException e) 
      e.printStackTrace();
    
  

另外,我在一个多星期前提交了使用 API 的请求,但尚未收到回复。

【问题讨论】:

你改变了这些行吗? ` final String userName = "你的 GCM 发件人 ID" + "@gcm.googleapis.com";最终字符串密码 = "API 密钥";` 是的,这两个值都已更新,当将它们与 HTTP 实现一起使用时,会收到消息。这是调试窗口中的错误输出 不要只指向外部源 - 在此处发布代码。链接中断,我们不希望因此而提出没有任何意义的问题。 它和链接中的代码相同,只是我替换了用户名和密码,我还是添加了它,任何帮助都会得到帮助。 【参考方案1】:

问题已解决。问题确实是该项目没有注册。在要求 API 接收确认后,我收到了来自 Google 2 周的电子邮件。 这种情况下的错误确实应该得到改进,如果谷歌写下大约多久可以收到电子邮件,那就太好了。 只是想更新遇到问题的其他人。

【讨论】:

【参考方案2】:

@user2679041 我猜你说得对。我今天刚刚注册了我的 gcm 项目 ID,但我还没有收到任何确认电子邮件。这真的很烦人,还有谁要等两个星期?我在@*** 看到很少有人说在3 months too. 之后有请求被谷歌批准的查询 我已经关注了这个link

它有一个注释说:

在 API 控制台中创建启用 GCM 的项目后,您必须填写此表单并成为试用合作伙伴,才能通过 CCS 使用上游消息传递和用户通知。访问仅限于填写表格的人。您将收到一封来自 Google 的电子邮件,通知您现在可以访问; Google 还会向您发送回显服务器的地址,您可以使用该地址将消息退回到您的应用程序。

所以我们可以说谷歌发布了数量有限的测试版。访问权限。

【讨论】:

以上是关于谷歌云消息传递 - 使用 smack 的 xmpp 服务器端不起作用的主要内容,如果未能解决你的问题,请参考以下文章

连接到谷歌云消息云连接服务器的身份验证错误

使用 Smack API (xmpp) 从 Java 回调 JavaFX

谷歌云消息传递必须使用 gmail 帐户吗

用户之间的 Android 谷歌云消息传递

谷歌云消息传递 GCM 可靠吗?

Android:我应该使用啥来进行 android 推送通知?谷歌云消息传递或 Parse.com