GCM CCS服务器实现未接收上游消息

时间:2014-04-29 13:48:53

标签: java xmpp google-cloud-messaging

我已经为Android应用程序和Web服务器之间的双向消息实施了新的GCM CCS。下游消息(网络设备)完美运行。不幸的是,服务器上没有收到上游消息(device-web)。它们似乎确实在客户端发送(请参阅下面的Android应用程序日志消息),但服务器上没有收到任何内容。

D/GCM﹕ GcmService start Intent { act=com.google.android.gcm.intent.SEND flg=0x10 pkg=com.google.android.gms cmp=com.google.android.gms/.gcm.GcmService (has extras) } com.google.android.gcm.intent.SEND

我认为Android方面没有任何问题,而是在服务器端。问题是,我无法弄清楚出了什么问题,因为连接仍然存在,我确实收到了来自GCM服务器的一些消息,比如ACK。那么为什么没有收到正常的消息?有人有任何想法吗?

值得一提的其他一些细节是正在使用的Web服务器是Glassfish,我在Servlet中启动XMPP连接。下面的一些片段。

编辑:正如我在答案中所述,阻止在服务器上收到任何消息的主要问题已得到解决。但是,仍然没有收到很多消息(大约50%)。

例如,我每次在后台线程上一个接一个地发送2条消息,每次用户在Android应用程序中进行更改时(按一个按钮,所以在那之间至少有几秒钟)每批2)。有时我在服务器上收到这两条消息,有时我只收到其中的一条消息,有时甚至什么都没发生......这是一个严重的问题,特别是对于那些依赖这项技术的应用而言。任何人都可以得到进一步的帮助,以解决这个问题吗?

更多信息:我很确定这是客户端相关,因为正在发送每封邮件,就像您在上面的logcat日志中看到的那样还会收到"事件:发送"一段时间后GCM广播(不是立即,也许是5分钟)。所以它必须是基于GCM或基于服务器的东西。

public class CcsServlet extends HttpServlet
{
    private static Logger logger = Logger.getLogger(CcsServlet.class.getName());


    public void init(ServletConfig config) throws ServletException
    {
        CcsManager ccsManager = CcsManager.getInstance();
        try
        {
            ccsManager.connect();
        }
        catch (Exception e)
        {
            logger.warning("Cannot connect to CCS server.");
            e.printStackTrace();
        }
    }
}


public class CcsManager
{
    private static XMPPConnection connection;
    private static Logger logger = Logger.getLogger(CcsManager.class.getName());


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

    private static CcsManager sInstance = null;
    private static final String USERNAME = "xxxxxxxxxx" + "@gcm.googleapis.com";
    private static final String PASSWORD = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";




        public CcsManager()
        {
            // Add GcmPacketExtension
            ProviderManager.getInstance().addExtensionProvider(
                    GcmPacketExtension.GCM_ELEMENT_NAME,
                    GcmPacketExtension.GCM_NAMESPACE, new PacketExtensionProvider()
                    {
                        public PacketExtension parseExtension(XmlPullParser parser) throws Exception
                        {
                            String json = parser.nextText();
                            return new GcmPacketExtension(json);
                        }
                    });
        }

        public static CcsManager getInstance()
        {
            if (sInstance == null)
                sInstance = new CcsManager();
            return sInstance;
        }

/**
 * Connects to GCM Cloud Connection Server
 */
public void connect() throws IOException, XMPPException
{
    ConnectionConfiguration config = new ConnectionConfiguration(GCM_SERVER, GCM_PORT);
    config.setSecurityMode(ConnectionConfiguration.SecurityMode.enabled);
    config.setReconnectionAllowed(true);
    config.setRosterLoadedAtLogin(false);
    config.setSendPresence(false);
    config.setSocketFactory(SSLSocketFactory.getDefault());
    config.setDebuggerEnabled(false);
    connection = new XMPPConnection(config);
    connection.connect();
    connection.addConnectionListener(new ConnectionListener()
    {
        public void reconnectionSuccessful()
        {
            logger.info("Reconnecting..");
        }

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

        public void reconnectingIn(int seconds)
        {
            logger.log(Level.INFO, "Reconnecting in %s secs", seconds);
        }

        public void connectionClosedOnError(Exception e)
        {
            logger.info("Connection closed on error.");
        }

        public void connectionClosed()
        {
            logger.info("Connection closed.");
        }
    });

    // Handle incoming packets
    connection.addPacketListener(new PacketListener()
    {
        public void processPacket(Packet packet)
        {
            logger.log(Level.INFO, "Received: " + packet.toXML());
            Message incomingMessage = (Message) packet;
            GcmPacketExtension gcmPacket =
                    (GcmPacketExtension) incomingMessage.getExtension(GcmPacketExtension.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()
    {
        public void interceptPacket(Packet packet)
        {
            logger.log(Level.INFO, "Sent: {0}",  packet.toXML());
        }
    }, new PacketTypeFilter(Message.class));

    connection.login(USERNAME, PASSWORD);
}
    }

2 个答案:

答案 0 :(得分:6)

似乎Web应用程序在服务器上部署了两次。这导致创建XMPP连接的servlet初始化一次,然后销毁,然后再次初始化。对于与GCM(dho ..)的连接,这个序列可能不是一件好事。

确保servlet仅初始化一次。通过在其init()和destroy()方法中使用记录器来检查这一点。如果确实多次调用它,请尝试将Web模块分配给特定的虚拟服务器以进行部署。我相信这与Web服务器不同。对于Glassfish,您需要在管理控制台(localhost:4848)中执行此操作。

这解决了更大的问题。我遇到的另一个问题是上游消息传递根本不可靠。有时来自同一设备的多个连续上游消息可以完美地工作,只能尝试另一个未完全推送到服务器的消息。还没有弄清楚这个问题的任何模式......如果我找到其他任何东西,我会回来的。

编辑:在本地服务器上使用该实现时显然存在问题。移动到远程服务器(暂存VPS),问题似乎消失了。服务器接收每条消息。如果问题仍然存在,我会回来的,但我对此表示怀疑。我认为本地问题是由于我的ISP,或者heck,甚至是我本地的Wi-Fi连接。关于究竟是什么造成这种情况,我没有完整的答案,但至少它在登台服务器上运行良好。

答案 1 :(得分:2)

在这里发布另一个解决方案因为我遇到了同样的问题。

我构建了一个简单的Android应用程序,它使用GCM API向服务器发送上游消息,并从服务器接收下游(推送)消息。

对于服务器端,我使用了基于此问题的接受答案的java程序... GCM XMPP Server using Smack 4.1.0,它使用开源Smack Java XMPP library

下游消息传递(服务器到设备)坚如磐石 - 没有任何东西被丢弃。

但并非所有上游消息都在服务器端接收...大约20-30%被丢弃。我还使用Wireshark进行了检查,并且数据包肯定没有进入服务器端的网卡。

我的案例中的问题是在Android应用程序中调用gcm.send() ... 消息ID字符串确实需要对每个上游消息都是唯一的,而我只是传递一个恒定的值。

使用持续的消息ID,每当我尝试将它们彼此靠近发送时(例如相隔250ms),就会导致上游消息失败。如果我发送它们的间隔为1000毫秒(1秒)或更长时间就可以了。鉴于它在大部分时间都有效,因此很难诊断出这个问题。