我已经为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);
}
}
答案 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秒)或更长时间就可以了。鉴于它在大部分时间都有效,因此很难诊断出这个问题。