2016-07-03 2 views
0

Я пытаюсь подключиться к Firebase Cloud Messaging с помощью библиотеки smack. У меня нет большого знания API Smack. После прочтения документов Firebase, я вижу, что соединение должно быть аутентифицировано, и есть серия ответов назад и вперед между «сервером приложений» и серверами облачного подключения. Согласно документам, я должен создать аутентификацию Sasl Plain. Я не знаю, как это реализовать. Но после чтения некоторых сообщений другими пользователями StackOverFlow я вижу, что должен создать класс проверки подлинности. В частности, я просматривал ответы и комментарии на тему «Ошибка аутентификации Gtalk XMPP SASL с использованием механизма X-OAUTH2?» Эти ответы, которые находятся между CCS и «сервером приложений», заключены в теги и теги. Я не знаю, как использовать Smack для получения или создания этих ответов. У меня есть соединение, настроенное с помощью XMPP, и я попытался установить addAsyncStanzListener в мое соединение XMPP с надеждой получить некоторые из этих ответов от CCS. Но ничего не приходит. Ответы между «сервером приложений» и CCS можно найти по этой ссылке https://firebase.google.com/docs/cloud-messaging/server#connectingсоздание, проверка подлинности и разбор с библиотекой Smack

Здесь кто-нибудь знает, как это исходит. Я думаю, что Smack недостаточно хорошо документирован и мало осведомлен о XMPP, это еще хуже. Существуют все эти классы: пакеты, расширения, класс IQ, синтаксические анализаторы XML и т. Д.

Любая грубая структура на основе была бы идеальной.

Благодаря

ответ

1

Вот отрывок из моего кода сервера, который я использую с Firebase. Если вы хотите более подробно разобраться в этом сообщении в блоге (именно так я понял, как получить все, чтобы работать в конце): http://www.grokkingandroid.com/xmpp-server-google-cloud-messaging/

Для того, чтобы получить код ниже для работы, вам нужно будет перейти к Firebase консоль и перейти к проекту настройки

enter image description here
Отсюда вам нужно будет принять к сведению ваш ключ сервера и идентификатор отправителя и заменить его на фрагменте кода ниже (он находится вблизи нижней части кода) и сохраните его на своем пути как SmackCcsClient.class enter image description here

После этого, что вы можете скомпилировать и запустить сервер через командную ПРОМТ:

// Set up java file (replace PATH_TO_WHERE_YOUR_CLASS_IS with your own path and make sure to put the json and smack JARs there as well 
javac -d PATH_TO_WHERE_YOUR_CLASS_IS -sourcepath src -cp PATH_TO_WHERE_YOUR_CLASS_IS\json-simple-1.1.1.jar;PATH_TO_WHERE_YOUR_CLASS_IS\smack-3.4.1-0cec571.jar PATH_TO_WHERE_YOUR_CLASS_IS\SmackCcsClient.java 

// Run 
java -cp PATH_TO_WHERE_YOUR_CLASS_IS;PATH_TO_WHERE_YOUR_CLASS_IS\json-simple-1.1.1.jar;PATH_TO_WHERE_YOUR_CLASS_IS\smack-3.4.1-0cec571.jar SmackCcsClient 

/*

SmackCcsClient:

import org.jivesoftware.smack.ConnectionConfiguration; 
import org.jivesoftware.smack.ConnectionConfiguration.SecurityMode; 
import org.jivesoftware.smack.ConnectionListener; 
import org.jivesoftware.smack.PacketInterceptor; 
import org.jivesoftware.smack.PacketListener; 
import org.jivesoftware.smack.XMPPConnection; 
import org.jivesoftware.smack.XMPPException; 
import org.jivesoftware.smack.filter.PacketTypeFilter; 
import org.jivesoftware.smack.packet.DefaultPacketExtension; 
import org.jivesoftware.smack.packet.Message; 
import org.jivesoftware.smack.packet.Packet; 
import org.jivesoftware.smack.packet.PacketExtension; 
import org.jivesoftware.smack.provider.PacketExtensionProvider; 
import org.jivesoftware.smack.provider.ProviderManager; 
import org.jivesoftware.smack.util.StringUtils; 
import org.json.simple.JSONValue; 
import org.json.simple.parser.ParseException; 
import org.xmlpull.v1.XmlPullParser; 

import java.util.HashMap; 
import java.util.Map; 
import java.util.Random; 
import java.util.logging.Level; 
import java.util.logging.Logger; 
import java.io.*; 
import javax.net.ssl.SSLSocketFactory; 

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

    static final String REG_ID_STORE = "gcmchat.txt"; 

    static final String MESSAGE_KEY = "SM"; 
    Logger logger = Logger.getLogger("SmackCcsClient"); 

    public static final String GCM_SERVER = "fcm-xmpp.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"); 



      String messageText = payload.get("message_text"); 
      String messageFrom = payload.get("message_userfrom"); 
      String messageTime = payload.get("message_timestamp"); 
      String toUser = payload.get("message_recipient"); 

      payload.put("message_text", messageText); 
      payload.put("message_userfrom", messageFrom);   
      payload.put("message_timestamp", messageTime); 

      String message = createJsonMessage(toUser, getRandomMessageId(), 
        payload, collapseKey, null, false); 
      send(message); 

    } 

    /** 
    * 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 
    *   [email protected] 
    * @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 void writeToFile(String name, String regId) throws IOException { 
     Map<String, String> regIdMap = readFromFile(); 
     regIdMap.put(name, regId); 
     PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter(
       REG_ID_STORE, false))); 
     for (Map.Entry<String, String> entry : regIdMap.entrySet()) { 
      out.println(entry.getKey() + "," + entry.getValue()); 
     } 
     out.println(name + "," + regId); 
     out.close(); 

    } 

    public Map<String, String> readFromFile() { 
    Map<String, String> regIdMap = null; 
    try { 
     BufferedReader br = new BufferedReader(new FileReader(REG_ID_STORE)); 
     String regIdLine = ""; 
     regIdMap = new HashMap<String, String>(); 
     while ((regIdLine = br.readLine()) != null) { 
      String[] regArr = regIdLine.split(","); 
      regIdMap.put(regArr[0], regArr[1]); 
     } 
     br.close(); 
    } catch(IOException ioe) { 
    } 
     return regIdMap; 
    } 

public static void main(String [] args) { 
    final String userName = "Sender ID" + "@gcm.googleapis.com"; 
    final String password = "Server key"; 

    SmackCcsClient ccsClient = new SmackCcsClient(); 

    try { 
     ccsClient.connect(userName, password); 
    } catch (XMPPException e) { 
     e.printStackTrace(); 
    } 
} 
} 
+0

Спасибо за информацию. Я смотрел этот пример несколько дней назад. В моем коде не так уж плохо реализовать. Тем не менее, я вижу, что нет аутентификации, поскольку соединение идет с Firebase Cloud Messaging. Если вы посмотрите на эту ссылку, https://firebase.google.com/docs/cloud-messaging/server#connecting, она сообщит, что существует некоторая связь между вашим сервером приложений и Firebase CCS для аутентификации соединения. В нем говорится, что для работы должен быть простой механизм SASL. Я не вижу, где это происходит в коде выше. @RileyMacDonald –

+0

Кроме того, Райли. Зачем нам нужно запускать наш сервер через командную строку. Не можете ли вы просто скомпилировать и запустить из своей среды разработки, поскольку код сервера находится в вашей среде IDE. Возможно, это именно так, как вы запускаете свой код через командную строку[email protected] –

+0

Между этим сервером и FCM существует соединение. В конце кода вы увидите, где наконец завершено соединение: ccsClient.connect (имя пользователя, пароль); Также я просто решил запустить сервер через CMD для тестирования, но вы можете его реализовать, но вы хотите, чтобы вы –