Implementasi server GCM CCS tidak menerima pesan upstream

Saya telah menerapkan GCM CCS baru untuk pesan dua arah antara aplikasi Android dan server web. Pesan hilir (perangkat web) berfungsi dengan sempurna. Sayangnya, pesan upstream (web perangkat) tidak diterima di server. Mereka tampaknya dikirim pada sisi klien (lihat pesan log aplikasi Android di bawah), namun tidak ada yang diterima di server.

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

Saya kira tidak ada yang salah di sisi Android, melainkan di sisi server. Masalahnya adalah, saya tidak dapat mencari tahu apa yang salah, karena koneksi masih aktif dan saya menerima beberapa pesan dari server GCM, seperti ACK. Jadi mengapa pesan normal tidak diterima? Apakah ada yang tahu?

Beberapa detail lain yang perlu disebutkan adalah bahwa server web yang digunakan adalah Glassfish dan saya memulai koneksi XMPP di dalam Servlet. Beberapa cuplikan di bawah ini.

EDIT: Seperti yang telah saya nyatakan dalam jawaban, masalah besar yang mencegah setiap pesan diterima di server telah teratasi. Namun cukup banyak pesan yang masih belum diterima (sekitar 50%).

Misalnya, saya mengirim 2 pesan sekaligus satu demi satu di thread latar belakang, setiap kali pengguna membuat perubahan di aplikasi Android (menekan tombol, jadi ada jeda minimal beberapa detik di antara setiap kumpulan 2 pesan). Terkadang saya menerima kedua pesan di server, terkadang saya hanya menerima 1 pesan, terkadang bahkan tidak terjadi apa-apa... Ini adalah masalah serius, terutama untuk aplikasi yang mengandalkan teknologi ini pada intinya. Adakah yang bisa membantu lebih lanjut untuk memecahkan masalah ini?

Info lebih lanjut: Saya cukup yakin ini tidak terkait dengan klien, karena setiap pesan dikirim, seperti yang Anda lihat di log logcat di atas dan saya juga menerima "peristiwa : terkirim" GCM disiarkan setelah beberapa saat (meskipun tidak segera, mungkin sekitar 5 menit). Jadi itu harus berbasis GCM atau berbasis server.

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 sumber
comment
Mencoba di beberapa perangkat, masalah yang sama.   -  person Bogdan Zurac    schedule 30.04.2014
comment
Saya juga menggunakan GF, memanggil CcsClient dari dalam servlet. Saya harus memeriksa apakah pengaturan saya masih berfungsi dan jika ya, apa perbedaan antara pengaturan Anda dan milik saya. Satu pertanyaan: Bagaimana Anda mengirimkan id registrasi perangkat ke server Anda?   -  person Wolfram Rittmeyer    schedule 30.04.2014
comment
Oh terima kasih, itu akan sangat membantu! Saya mengirimkan id pendaftaran menggunakan panggilan RESTful API, tidak ada yang luar biasa. Mengapa kamu bertanya?   -  person Bogdan Zurac    schedule 30.04.2014
comment
Saya kira begitu, tetapi saya melakukannya secara berbeda. Saya sudah menerima ID pendaftaran menggunakan pesan upstream. Jadi tanpa langkah itu saya tidak akan dapat mengirim apa pun dari server ke perangkat - karena Saya tidak akan mengetahui nomor registrasinya. Namun menularkannya dengan cara lain juga akan berhasil. Aku hanya penasaran dan bertanya-tanya tentang hal itu.   -  person Wolfram Rittmeyer    schedule 30.04.2014
comment
Hm. Bekerja seperti pesona dengan N5 saya. Saya tidak menggunakan perpustakaan klien Layanan Google play terbaru. Yang mana yang kamu gunakan?   -  person Wolfram Rittmeyer    schedule 30.04.2014
comment
Saya menggunakan ketergantungan gradle terbaru, yang tampaknya 4.3.23. Bisakah Anda memberi saya versi yang Anda gunakan? Saya akan mencoba memeriksanya juga, mungkin itu masalahnya.   -  person Bogdan Zurac    schedule 30.04.2014
comment
mari kita melanjutkan diskusi ini di chat   -  person Wolfram Rittmeyer    schedule 01.05.2014
comment
Anda sadar itu berhasil sekarang?   -  person Bogdan Zurac    schedule 22.07.2014


Jawaban (2)


Tampaknya aplikasi web disebarkan dua kali di server. Hal ini menyebabkan servlet yang membuat koneksi XMPP diinisialisasi satu kali, lalu dimusnahkan, lalu diinisialisasi lagi. Urutan ini mungkin bukan hal yang baik untuk koneksi ke GCM (dho..).

Pastikan saja servlet diinisialisasi hanya sekali. Periksa ini dengan menggunakan logger di dalam metode init() dan destroy(). Jika memang dipanggil lebih dari satu kali, coba tetapkan modul web ke server virtual tertentu untuk diterapkan. Saya yakin ini berbeda dari server web ke server web. Untuk Glassfish, Anda perlu melakukan ini dari dalam konsol admin (localhost:4848).

Hal ini memecahkan masalah yang lebih besar. Masalah lain yang saya temui adalah pengiriman pesan upstream tidak dapat diandalkan sama sekali. Terkadang beberapa pesan upstream berturut-turut dari perangkat yang sama berfungsi dengan sempurna, hanya untuk mencoba pesan lain yang tidak dikirim sama sekali ke server. Belum menemukan pola apa pun untuk masalah ini... Saya akan kembali jika menemukan hal lain.

EDIT: Rupanya ada masalah saat menggunakan implementasi di server lokal. Dipindahkan ke server jarak jauh (pementasan VPS) dan masalahnya tampaknya hilang. Setiap pesan diterima oleh server. Saya akan kembali jika masalah berlanjut, tapi saya meragukannya. Saya berasumsi masalah lokal disebabkan oleh ISP saya, atau bahkan koneksi Wi-Fi lokal saya. Saya tidak memiliki jawaban lengkap tentang apa sebenarnya yang menyebabkan ini, tapi setidaknya ini berfungsi dengan baik di server pementasan.

person Bogdan Zurac    schedule 01.05.2014
comment
Host server apa yang Anda gunakan? Saya tidak tahu apakah host server saya mendukung protokol XMPP. Bagaimana Anda mengetahui hal itu? Terima kasih. - person Marco Altran; 25.09.2014
comment
Saya menggunakan DigitalOcean, setelah beberapa lama mencari penyedia terbaik dengan harga terbaik. Bagaimanapun, jika Anda memiliki host lain, cukup hubungi mereka atau kirim email mengenai masalah ini. - person Bogdan Zurac; 25.09.2014
comment
Terima kasih @Andrew Saya menggunakan DailyRazor dengan Tomcat. Mereka tidak mengizinkan pendengar atau koneksi masuk, hanya koneksi output, tetapi berfungsi baik dengan pesan hilir (belum diuji dengan pesan upstream). Satu-satunya kendala adalah saya harus memutakhirkan ke Tomcat 7 dan JVM 7 karena perpustakaan Smack menyebabkan masalah kompatibilitas. - person Marco Altran; 26.09.2014
comment
Mungkinkah ini disebabkan oleh parameter ID Pesan di panggilan gcm.send() aplikasi Android Anda yang disetel ke konstan? Itulah yang menyebabkannya dalam kasus saya. Saya selalu meneruskan ID pesan konstan, bukan ID pesan unik. Hal ini mengakibatkan pesan upstream hilang setiap kali saya mencoba mengirimnya terlalu dekat satu sama lain, misalnya 3 pesan per detik. Jika saya mengirim 1 kali per detik atau kurang, itu tidak akan membatalkannya. Sangat sulit untuk mendiagnosis masalah ini karena masalah ini sering kali berhasil. - person dodgy_coder; 29.10.2015
comment
Tidak, bukan itu masalahnya. - person Bogdan Zurac; 29.10.2015

Posting solusi lain di sini karena saya terjebak pada masalah yang sama.

Saya membuat aplikasi Android sederhana yang menggunakan GCM API untuk mengirim pesan upstream ke server, dan untuk menerima pesan downstream (push) dari server.

Untuk sisi server, saya menggunakan program Java berdasarkan jawaban yang diterima untuk pertanyaan ini ... Server XMPP GCM menggunakan Smack 4.1.0 yang menggunakan sumber terbuka Smack Pustaka Java XMPP.

Perpesanan hilir (server ke perangkat) sangat solid - tidak ada yang terjatuh.

Namun tidak semua pesan upstream diterima di sisi server... sekitar 20-30% dibuang. Saya juga memeriksa menggunakan Wireshark dan paketnya pasti tidak masuk ke kartu jaringan di sisi server.

Masalah dalam kasus saya adalah panggilan ke gcm. send() di Aplikasi Android ... string ID Pesan benar-benar harus unik untuk setiap pesan upstream, sedangkan saya hanya memberikan nilai konstan.

Dengan ID pesan yang konstan, hal ini mengakibatkan pesan upstream gagal setiap kali saya mencoba mengirimnya terlalu dekat satu sama lain, misalnya dengan jarak 250 md. Jika saya mengirimnya dengan jarak 1000 ms (1 detik) atau lebih, itu berhasil. Sangat sulit untuk mendiagnosis masalah ini karena masalah ini sering kali berhasil.

person dodgy_coder    schedule 28.10.2015