Java 连接排空后打开新连接。谷歌云消息
我对谷歌云消息有些陌生。我们已经使用它几个月了,但就在最近,我们收到了“连接耗尽”的消息。发生这种情况时,所有通信都停止 谷歌表示: 当您收到连接消息时,您应该 立即开始向另一个CCS连接发送消息,打开 如有必要,请重新连接。然而,你应该保留原稿 连接打开并继续接收可能通过 连接(并确认)-CCS将处理启动连接 准备好后关闭 我的问题是Java 连接排空后打开新连接。谷歌云消息,java,spring-mvc,google-cloud-messaging,Java,Spring Mvc,Google Cloud Messaging,我对谷歌云消息有些陌生。我们已经使用它几个月了,但就在最近,我们收到了“连接耗尽”的消息。发生这种情况时,所有通信都停止 谷歌表示: 当您收到连接消息时,您应该 立即开始向另一个CCS连接发送消息,打开 如有必要,请重新连接。然而,你应该保留原稿 连接打开并继续接收可能通过 连接(并确认)-CCS将处理启动连接 准备好后关闭 我的问题是 如果我手动打开一个新连接,如果我不关闭现有连接,它如何知道要使用哪个连接 如果同时发送6条消息,如何阻止该方法打开6个连接?还是我对此感到困惑 为什么会发生连接
@PostConstruct
public void init() throws Exception{
try {
smackCcsClient.connect(Long.parseLong(env.getProperty("gcm.api")), env.getProperty("gcm.key"));
}catch (IOException e ){
e.printStackTrace();
}catch(SmackException e){
e.printStackTrace();
}catch(XMPPException e){
e.printStackTrace();
}
}
然而,在这之后,我再也没有接触过连接。我是否处理错了,连接是我应该更频繁接触的还是我需要跟踪的
_______________________在问题后加上_________________________
我在他们的示例代码中添加了一个连接,以尝试重新初始化连接。看起来是这样的:
if ("CONNECTION_DRAINING".equals(controlType)) {
connectionDraining = true;
//Open new connection because old connection will be closing or is already closed.
try {
connect(Long.parseLong(env.getProperty("gcm.api")), env.getProperty("gcm.key"));
} catch (XMPPException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (SmackException e) {
e.printStackTrace();
}
} else {
logger.log(Level.INFO, "Unrecognized control type: %s. This could happen if new features are " + "added to the CCS protocol.",
controlType);
}
我也是GCM新手,面临着同样的问题……我通过在连接消息上创建新的smackcsclient()解决了这个问题。旧连接应仍然存在并接收消息,但不发送,因为: 受保护的易失性布尔连接训练=真 谷歌表示连接将被CCS关闭: CCS将在连接准备就绪时启动连接关闭 在CCS关闭连接之前,您将能够从两个连接接收消息,但只能使用新的连接发送消息。当旧连接被关闭时,它应该被销毁,我不确定是否调用了垃圾收集器…试图解决这个问题
注:我对这个答案不是100%肯定,但也许它会为讨论打开更多的空间。我已经编写了处理此类情况的代码(基本上是将新的下游消息转移到新的连接)。。。没有经过彻底的测试
import java.util.Deque;
导入java.util.HashMap;
导入java.util.Map;
导入java.util.concurrent.concurrentLinkedQue;
导入javax.net.ssl.SSLSocketFactory;
导入org.jivesoftware.smack.ConnectionConfiguration;
导入org.jivesoftware.smack.ConnectionConfiguration.SecurityMode;
导入org.jivesoftware.smack.ConnectionListener;
导入org.jivesoftware.smack.PacketInterceptor;
导入org.jivesoftware.smack.PacketListener;
导入org.jivesoftware.smack.SmackException.NotConnectedException;
导入org.jivesoftware.smack.XMPPConnection;
导入org.jivesoftware.smack.filter.PacketTypeFilter;
导入org.jivesoftware.smack.packet.DefaultPacketExtension;
导入org.jivesoftware.smack.packet.Message;
导入org.jivesoftware.smack.packet.packet;
导入org.jivesoftware.smack.packet.PacketExtension;
导入org.jivesoftware.smack.provider.PacketExtensionProvider;
导入org.jivesoftware.smack.provider.ProviderManager;
导入org.jivesoftware.smack.tcp.XMPPTCPConnection;
导入org.jivesoftware.smack.util.StringUtils;
导入org.slf4j.Logger;
导入org.slf4j.LoggerFactory;
导入org.xmlpull.v1.XmlPullParser;
导入com.fasterxml.jackson.core.type.TypeReference;
/**
*基于https://developer.android.com/google/gcm/ccs.html#smack
*
*@author Abhinav.Dwivedi
*
*/
公共类SmackCcsClient实现CCSCClient{
私有静态最终记录器Logger=LoggerFactory.getLogger(smackcsclient.class);
私有静态最终字符串GCM_SERVER=“GCM.googleapis.com”;
专用静态最终int GCM_端口=5235;
私有静态最终字符串GCM\u元素\u NAME=“GCM”;
私有静态最终字符串GCM_NAMESPACE=“google:mobile:data”;
私有静态客户端实例;
静止的{
addExtensionProvider(GCM_元素_名称、GCM_命名空间、新PacketExtensionProvider(){
@凌驾
公共PacketExtension parseExtension(XmlPullParser parser)引发异常{
字符串json=parser.nextText();
返回新的gcmpacktextension(json);
}
});
}
私人频道;
公共静态SmackCcsClient实例(){
if(实例==null){
已同步(smackcsclient.class){
if(实例==null){
实例=新的SMACKCCClient();
}
}
}
返回实例;
}
私有SmackCcsClient(){
通道=新的ConcurrentLinkedQue();
channels.addFirst(connect());
}
私有类频道{
专用XMPPConnection连接;
/**
*指示连接是否处于排放状态,这意味着它将不接受任何新的下游
*信息。
*/
private volatile boolean connectionTraining=false;
/**
*发送包含所提供内容的数据包。
*/
私有void发送(字符串jsonRequest)抛出NotConnectedException{
数据包请求=新的gcmpacktextension(jsonRequest).toPacket();
发送数据包(请求);
}
私有void handleControlMessage(映射jsonObject){
debug(“handleControlMessage():{}”,jsonObject);
String controlType=(String)jsonObject.get(“控件_
import java.util.Deque;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentLinkedDeque;
import javax.net.ssl.SSLSocketFactory;
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.SmackException.NotConnectedException;
import org.jivesoftware.smack.XMPPConnection;
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.tcp.XMPPTCPConnection;
import org.jivesoftware.smack.util.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xmlpull.v1.XmlPullParser;
import com.fasterxml.jackson.core.type.TypeReference;
/**
* Based on https://developer.android.com/google/gcm/ccs.html#smack
*
* @author Abhinav.Dwivedi
*
*/
public class SmackCcsClient implements CcsClient {
private static final Logger logger = LoggerFactory.getLogger(SmackCcsClient.class);
private static final String GCM_SERVER = "gcm.googleapis.com";
private static final int GCM_PORT = 5235;
private static final String GCM_ELEMENT_NAME = "gcm";
private static final String GCM_NAMESPACE = "google:mobile:data";
private static volatile SmackCcsClient instance;
static {
ProviderManager.addExtensionProvider(GCM_ELEMENT_NAME, GCM_NAMESPACE, new PacketExtensionProvider() {
@Override
public PacketExtension parseExtension(XmlPullParser parser) throws Exception {
String json = parser.nextText();
return new GcmPacketExtension(json);
}
});
}
private final Deque<Channel> channels;
public static SmackCcsClient instance() {
if (instance == null) {
synchronized (SmackCcsClient.class) {
if (instance == null) {
instance = new SmackCcsClient();
}
}
}
return instance;
}
private SmackCcsClient() {
channels = new ConcurrentLinkedDeque<Channel>();
channels.addFirst(connect());
}
private class Channel {
private XMPPConnection connection;
/**
* Indicates whether the connection is in draining state, which means that it will not accept any new downstream
* messages.
*/
private volatile boolean connectionDraining = false;
/**
* Sends a packet with contents provided.
*/
private void send(String jsonRequest) throws NotConnectedException {
Packet request = new GcmPacketExtension(jsonRequest).toPacket();
connection.sendPacket(request);
}
private void handleControlMessage(Map<String, Object> jsonObject) {
logger.debug("handleControlMessage(): {}", jsonObject);
String controlType = (String) jsonObject.get("control_type");
if ("CONNECTION_DRAINING".equals(controlType)) {
connectionDraining = true;
} else {
logger.info("Unrecognized control type: {}. This could happen if new features are "
+ "added to the CCS protocol.", controlType);
}
}
}
/**
* Sends a downstream message to GCM.
*
*/
@Override
public void sendDownstreamMessage(String message) throws Exception {
Channel channel = channels.peekFirst();
if (channel.connectionDraining) {
synchronized (channels) {
channel = channels.peekFirst();
if (channel.connectionDraining) {
channels.addFirst(connect());
channel = channels.peekFirst();
}
}
}
channel.send(message);
logger.debug("Message Sent via CSS: ({})", message);
}
/**
* Handles an upstream data message from a device application.
*
*/
protected void handleUpstreamMessage(Map<String, Object> jsonObject) {
// PackageName of the application that sent this message.
String category = (String) jsonObject.get("category");
String from = (String) jsonObject.get("from");
@SuppressWarnings("unchecked")
Map<String, String> payload = (Map<String, String>) jsonObject.get("data");
logger.info("Message received from device: category ({}), from ({}), payload: ({})", category, from,
JsonUtil.toJson(payload));
}
/**
* Handles an ACK.
*
* <p>
* Logs a INFO message, but subclasses could override it to properly handle ACKs.
*/
public void handleAckReceipt(Map<String, Object> jsonObject) {
String messageId = (String) jsonObject.get("message_id");
String from = (String) jsonObject.get("from");
logger.debug("handleAckReceipt() from: {}, messageId: {}", from, messageId);
}
/**
* Handles a NACK.
*
* <p>
* Logs a INFO message, but subclasses could override it to properly handle NACKs.
*/
protected void handleNackReceipt(Map<String, Object> jsonObject) {
String messageId = (String) jsonObject.get("message_id");
String from = (String) jsonObject.get("from");
logger.debug("handleNackReceipt() from: {}, messageId: ", from, messageId);
}
/**
* 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.
*/
protected 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 JsonUtil.toJson(message);
}
/**
* Connects to GCM Cloud Connection Server using the supplied credentials.
*
* @return
*/
@Override
public Channel connect() {
try {
Channel channel = new Channel();
ConnectionConfiguration config = new ConnectionConfiguration(GCM_SERVER, GCM_PORT);
config.setSecurityMode(SecurityMode.enabled);
config.setReconnectionAllowed(true);
config.setRosterLoadedAtLogin(false);
config.setSendPresence(false);
config.setSocketFactory(SSLSocketFactory.getDefault());
channel.connection = new XMPPTCPConnection(config);
channel.connection.connect();
channel.connection.addConnectionListener(new LoggingConnectionListener());
// Handle incoming packets
channel.connection.addPacketListener(new PacketListener() {
@Override
public void processPacket(Packet packet) {
logger.debug("Received: ({})", packet.toXML());
Message incomingMessage = (Message) packet;
GcmPacketExtension gcmPacket = (GcmPacketExtension) incomingMessage.getExtension(GCM_NAMESPACE);
String json = gcmPacket.getJson();
try {
Map<String, Object> jsonObject = JacksonUtil.DEFAULT.mapper().readValue(json,
new TypeReference<Map<String, Object>>() {});
// present for ack, nack and control, null otherwise
Object messageType = jsonObject.get("message_type");
if (messageType == null) {
// Normal upstream data message
handleUpstreamMessage(jsonObject);
// Send ACK to CCS
String messageId = (String) jsonObject.get("message_id");
String from = (String) jsonObject.get("from");
String ack = createJsonAck(from, messageId);
channel.send(ack);
} else if ("ack".equals(messageType.toString())) {
// Process Ack
handleAckReceipt(jsonObject);
} else if ("nack".equals(messageType.toString())) {
// Process Nack
handleNackReceipt(jsonObject);
} else if ("control".equals(messageType.toString())) {
// Process control message
channel.handleControlMessage(jsonObject);
} else {
logger.error("Unrecognized message type ({})", messageType.toString());
}
} catch (Exception e) {
logger.error("Failed to process packet ({})", packet.toXML(), e);
}
}
}, new PacketTypeFilter(Message.class));
// Log all outgoing packets
channel.connection.addPacketInterceptor(new PacketInterceptor() {
@Override
public void interceptPacket(Packet packet) {
logger.debug("Sent: {}", packet.toXML());
}
}, new PacketTypeFilter(Message.class));
channel.connection.login(ExternalConfig.gcmSenderId() + "@gcm.googleapis.com", ExternalConfig.gcmApiKey());
return channel;
} catch (Exception e) {
logger.error(Logging.FATAL, "Error in creating channel for GCM communication", e);
throw new RuntimeException(e);
}
}
/**
* XMPP Packet Extension for GCM Cloud Connection Server.
*/
private static final class GcmPacketExtension extends DefaultPacketExtension {
private final 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,
StringUtils.escapeForXML(json), GCM_ELEMENT_NAME);
}
public Packet toPacket() {
Message message = new Message();
message.addExtension(this);
return message;
}
}
private static final class LoggingConnectionListener implements ConnectionListener {
@Override
public void connected(XMPPConnection xmppConnection) {
logger.info("Connected.");
}
@Override
public void authenticated(XMPPConnection xmppConnection) {
logger.info("Authenticated.");
}
@Override
public void reconnectionSuccessful() {
logger.info("Reconnecting..");
}
@Override
public void reconnectionFailed(Exception e) {
logger.error("Reconnection failed.. ", e);
}
@Override
public void reconnectingIn(int seconds) {
logger.info("Reconnecting in {} secs", seconds);
}
@Override
public void connectionClosedOnError(Exception e) {
logger.info("Connection closed on error.");
}
@Override
public void connectionClosed() {
logger.info("Connection closed.");
}
}
}