การใช้งานเซิร์ฟเวอร์ GCM CCS ไม่ได้รับข้อความอัปสตรีม

ฉันได้ติดตั้ง GCM CCS ใหม่สำหรับข้อความแบบสองทิศทางระหว่างแอปพลิเคชัน Android และเว็บเซิร์ฟเวอร์ ข้อความดาวน์สตรีม (อุปกรณ์เว็บ) ทำงานได้อย่างสมบูรณ์ ขออภัย ไม่ได้รับข้อความอัปสตรีม (อุปกรณ์-เว็บ) บนเซิร์ฟเวอร์ ดูเหมือนว่าจะถูกส่งทางฝั่งไคลเอ็นต์ (ดูข้อความบันทึกแอป 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 เหตุใดจึงไม่ได้รับข้อความปกติ ไม่มีใครมีความคิดใด ๆ ?

รายละเอียดอื่น ๆ ที่ควรกล่าวถึงคือเว็บเซิร์ฟเวอร์ที่ใช้คือ Glassfish และฉันเริ่มการเชื่อมต่อ XMPP ภายใน Servlet ตัวอย่างบางส่วนด้านล่าง

แก้ไข: ดังที่ฉันได้ระบุไว้ในคำตอบ ปัญหาหลักที่ทำให้ไม่สามารถรับข้อความ ใด ๆ บนเซิร์ฟเวอร์ได้รับการแก้ไขแล้ว อย่างไรก็ตาม ยังไม่ได้รับข้อความจำนวนมาก (ประมาณ 50%)

ตัวอย่างเช่น ฉันกำลังส่งข้อความ 2 ข้อความติดต่อกันบนเธรดพื้นหลัง ทุกครั้งที่ผู้ใช้ทำการเปลี่ยนแปลงในแอป Android (กดปุ่ม ดังนั้นจะมีเวลาสองสามวินาทีขั้นต่ำระหว่างทุก ๆ 2 ชุด) บางครั้งฉันได้รับข้อความทั้งสองบนเซิร์ฟเวอร์ บางครั้งฉันได้รับเพียงข้อความเดียว บางครั้งไม่มีอะไรเกิดขึ้น... นี่เป็นปัญหาร้ายแรง โดยเฉพาะอย่างยิ่งสำหรับแอปที่ใช้เทคโนโลยีนี้เป็นหลัก ใครสามารถให้ความช่วยเหลือเพิ่มเติมเพื่อแก้ไขปัญหานี้ได้หรือไม่

ข้อมูลเพิ่มเติม: ฉันค่อนข้างแน่ใจว่านี่ ไม่ เกี่ยวข้องกับลูกค้า เนื่องจากทุกข้อความถูกส่งไป เหมือนที่คุณเห็นในบันทึก logcat ด้านบน และฉันยังได้รับ "เหตุการณ์" :sent" ออกอากาศ 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);
}
    }

person Bogdan Zurac    schedule 29.04.2014    source แหล่งที่มา
comment
ลองบนอุปกรณ์หลายเครื่อง ปัญหาเดียวกัน   -  person Bogdan Zurac    schedule 30.04.2014
comment
ฉันยังใช้ GF โดยเรียก CcsClient จากภายในเซิร์ฟเล็ต ฉันต้องตรวจสอบว่าการตั้งค่าของฉันยังใช้งานได้หรือไม่ และถ้าเป็นเช่นนั้น มีอะไรแตกต่างระหว่างคุณและฉัน คำถามหนึ่งข้อ: คุณส่งรหัสการลงทะเบียนของอุปกรณ์ไปยังเซิร์ฟเวอร์ของคุณอย่างไร   -  person Wolfram Rittmeyer    schedule 30.04.2014
comment
โอ้ ขอบคุณ นั่นคงจะมีประโยชน์มากกว่านะ! ฉันส่งรหัสการลงทะเบียนโดยใช้การเรียก RESTful API ซึ่งไม่มีอะไรผิดปกติ คุณถามทำไม?   -  person Bogdan Zurac    schedule 30.04.2014
comment
ฉันเดาอย่างนั้น แต่ฉันทำมันแตกต่างออกไป ฉันได้รับรหัสการลงทะเบียนโดยใช้ข้อความอัปสตรีมแล้ว ดังนั้นหากไม่มีขั้นตอนนั้น ฉันจะไม่สามารถส่งอะไรจากเซิร์ฟเวอร์ไปยังอุปกรณ์ได้ - เพราะฉันจะไม่ทราบรหัสการลงทะเบียนเป็นอย่างอื่น แต่การส่งสัญญาณด้วยวิธีอื่นก็ควรได้ผลเช่นกัน ฉันแค่สงสัยและสงสัยเกี่ยวกับเรื่องนั้น   -  person Wolfram Rittmeyer    schedule 30.04.2014
comment
อืม ใช้งานได้อย่างมีเสน่ห์กับ N5 ของฉัน ฉันไม่ได้ใช้ไลบรารีไคลเอนต์บริการ Google play ล่าสุด คุณใช้อันไหนอยู่?   -  person Wolfram Rittmeyer    schedule 30.04.2014
comment
ฉันใช้การพึ่งพาการไล่ระดับล่าสุดซึ่งดูเหมือนจะเป็น 4.3.23 ขอทราบรุ่นที่คุณใช้ได้ไหมครับ? ฉันจะลองตรวจสอบด้วยบางทีนั่นอาจเป็นปัญหา   -  person Bogdan Zurac    schedule 30.04.2014
comment
ให้เราสนทนาต่อในการแชท   -  person Wolfram Rittmeyer    schedule 01.05.2014
comment
คุณรู้ไหมว่ามันใช้งานได้แล้ว?   -  person Bogdan Zurac    schedule 22.07.2014


คำตอบ (2)


ปรากฏว่ามีการใช้งานเว็บแอปพลิเคชันสองครั้งบนเซิร์ฟเวอร์ ซึ่งทำให้เซิร์ฟเล็ตที่สร้างการเชื่อมต่อ XMPP ได้รับการเตรียมใช้งานหนึ่งครั้ง จากนั้นถูกทำลาย และเตรียมใช้งานอีกครั้ง ลำดับนี้อาจไม่ใช่สิ่งที่ดีสำหรับการเชื่อมต่อกับ GCM (dho..)

เพียงตรวจสอบให้แน่ใจว่าเซิร์ฟเล็ตเริ่มต้นได้เพียงครั้งเดียวเท่านั้น ตรวจสอบสิ่งนี้โดยใช้ตัวบันทึกภายในวิธีการ init() และ destroy() หากมีการเรียกมากกว่าหนึ่งครั้ง ให้ลองกำหนดเว็บโมดูลให้กับเซิร์ฟเวอร์เสมือนเฉพาะสำหรับการปรับใช้ ฉันเชื่อว่าสิ่งนี้แตกต่างจากเว็บเซิร์ฟเวอร์ไปยังเว็บเซิร์ฟเวอร์ สำหรับ Glassfish คุณต้องดำเนินการจากภายในคอนโซลผู้ดูแลระบบ (localhost:4848)

วิธีนี้จะช่วยแก้ปัญหาที่ใหญ่กว่าได้ ปัญหาอีกประการหนึ่งที่ฉันพบก็คือการส่งข้อความต้นทางไม่น่าเชื่อถือเลย บางครั้งข้อความอัปสตรีมต่อเนื่องกันหลายรายการจากอุปกรณ์เดียวกันก็ทำงานได้อย่างไร้ที่ติ เพียงเพื่อลองข้อความอื่นที่ไม่ได้ส่งไปยังเซิร์ฟเวอร์เลย ยังไม่ทราบรูปแบบของปัญหานี้... ฉันจะกลับมาหากพบสิ่งอื่นใด

แก้ไข: เห็นได้ชัดว่ามีปัญหาเมื่อใช้การใช้งานบนเซิร์ฟเวอร์ภายในเครื่อง ย้ายไปยังเซิร์ฟเวอร์ระยะไกล (Staging VPS) และปัญหาดูเหมือนจะหมดไป ทุกข้อความจะได้รับจากเซิร์ฟเวอร์ ฉันจะกลับมาอีกหากปัญหายังคงมีอยู่ แต่ฉันสงสัย ฉันคิดว่าปัญหาในท้องถิ่นนั้นเกิดจาก ISP ของฉัน หรือแม้แต่การเชื่อมต่อ Wi-Fi ในพื้นที่ของฉัน ฉันไม่มีคำตอบที่สมบูรณ์เกี่ยวกับสาเหตุที่แท้จริง แต่อย่างน้อยมันก็ทำงานได้อย่างสมบูรณ์บนเซิร์ฟเวอร์ชั่วคราว

person Bogdan Zurac    schedule 01.05.2014
comment
คุณใช้โฮสต์เซิร์ฟเวอร์ใด ฉันไม่รู้ว่าโฮสต์เซิร์ฟเวอร์ของฉันรองรับโปรโตคอล XMPP หรือไม่ คุณรู้ได้อย่างไรว่ามันเป็นเช่นนั้น? ขอบคุณ. - person Marco Altran; 25.09.2014
comment
ฉันใช้ DigitalOcean หลังจากที่ฉันได้ค้นหาผู้ให้บริการที่ดีที่สุดพร้อมราคาที่ดีที่สุดมาระยะหนึ่งแล้ว อย่างไรก็ตาม หากคุณมีโฮสต์อื่น เพียงโทรหาพวกเขาหรือส่งอีเมลเกี่ยวกับปัญหานี้ - person Bogdan Zurac; 25.09.2014
comment
ขอบคุณ @Andrew ฉันใช้ DailyRazor กับ Tomcat พวกเขาไม่อนุญาตให้ผู้ฟังหรือการเชื่อมต่อขาเข้า มีเพียงการเชื่อมต่อเอาต์พุตเท่านั้น แต่ใช้งานได้ดีกับข้อความดาวน์สตรีม (ยังไม่ได้ทดสอบกับข้อความอัปสตรีม) สิ่งเดียวที่จับได้คือฉันต้องอัปเกรดเป็น Tomcat 7 และ JVM 7 เนื่องจากไลบรารี Smack ทำให้เกิดปัญหาความเข้ากันได้ - person Marco Altran; 26.09.2014
comment
อาจเกิดจากพารามิเตอร์ Message ID ในการเรียก gcm.send() ของแอป Android ของคุณที่ถูกตั้งค่าเป็นค่าคงที่ นั่นคือสิ่งที่เกิดขึ้นในกรณีของฉัน ฉันส่งรหัสข้อความคงที่แทนที่จะเป็นรหัสข้อความที่ไม่ซ้ำกันทุกครั้ง ส่งผลให้ข้อความอัพสตรีมหลุดทุกครั้งที่ฉันพยายามส่งข้อความใกล้กันเกินไป เช่น 3 ข้อความต่อวินาที ถ้าฉันส่ง 1 ครั้งต่อวินาทีหรือน้อยกว่านั้น มันก็จะไม่ดรอป ยากมากที่จะวินิจฉัยปัญหานี้เนื่องจากใช้งานได้เกือบตลอดเวลา - person dodgy_coder; 29.10.2015
comment
ไม่ นั่นไม่ใช่ปัญหา - person Bogdan Zurac; 29.10.2015

โพสต์วิธีแก้ไขปัญหาอื่นที่นี่ เพราะฉันติดอยู่กับปัญหาเดียวกันนี้

ฉันสร้างแอป Android ง่ายๆ ที่ใช้ GCM API เพื่อส่งข้อความอัปสตรีมไปยังเซิร์ฟเวอร์ และรับข้อความดาวน์สตรีม (พุช) จากเซิร์ฟเวอร์

สำหรับฝั่งเซิร์ฟเวอร์ ฉันใช้โปรแกรมจาวาตามคำตอบที่ยอมรับสำหรับคำถามนี้ ... เซิร์ฟเวอร์ GCM XMPP ที่ใช้ Smack 4.1.0 ซึ่งใช้โอเพ่นซอร์ส Smack ไลบรารี Java XMPP

การส่งข้อความแบบดาวน์สตรีม (เซิร์ฟเวอร์ไปยังอุปกรณ์) นั้นแข็งแกร่งมาก - ไม่มีอะไรตกหล่น

แต่ไม่ใช่ข้อความอัปสตรีมทั้งหมดที่ได้รับทางฝั่งเซิร์ฟเวอร์ ... ประมาณ 20-30% ถูกตัดทิ้ง ฉันยังตรวจสอบโดยใช้ Wireshark และแพ็กเก็ตไม่เข้ามาในการ์ดเครือข่ายทางฝั่งเซิร์ฟเวอร์อย่างแน่นอน

ปัญหาในกรณีของฉันคือการเรียกไปที่ gcm send() ในแอป Android ... สตริงรหัสข้อความจะต้องไม่ซ้ำกันสำหรับข้อความอัปสตรีมทุกข้อความ ในขณะที่ฉันแค่ส่งค่าคงที่

ด้วย ID ข้อความคงที่ ส่งผลให้ข้อความอัปสตรีมล้มเหลวทุกครั้งที่ฉันพยายามส่งข้อความใกล้กันเกินไป เช่น ห่างกัน 250 มิลลิวินาที ถ้าฉันส่งให้พวกเขาโดยเว้นระยะห่าง 1,000 มิลลิวินาที (1 วินาที) หรือมากกว่านั้นก็ใช้งานได้ ยากมากที่จะวินิจฉัยปัญหานี้เนื่องจากใช้งานได้เกือบตลอดเวลา

person dodgy_coder    schedule 28.10.2015