发送GCM消息(服务器端)经常失败 - 但远非总是。

9

我正在为我的Android应用使用Google云消息传递(GCM)服务。 我已经按照所有规则来实现它,并且它能够工作。好吧,几乎是这样。

通常情况下,我可以在60-70%的情况下成功从我的服务器发送GCM消息,使用Google网页上讨论的Web服务。

通常情况下,我会从Web服务获得以下回复,表明我成功发送了GCM消息:

{
    "multicast_id":8378088572050307085,
    "success":1,
    "failure":0,
    "canonical_ids":0,
    "results":
    [
        {
            "message_id":"0:1363080282442710%7c4250c100000031"
        }
    ]
}

这句话的意思是:“一切正常,消息已发送。”
然而,在很多情况下,当调用webservice时,我会收到HTTP错误,错误信息如下:
“无法从传输连接中读取数据:已在主机计算机上由软件中止了一个已建立的连接。”
这是.NET消息,告诉我使用HttpWebRequest和POST调用webservice失败了。
以下是一些显示问题的日志消息:
这是我用于调用WS的代码:
public static string SendMessage(string registrationId, string command, string extra, bool retry)
{
    try
    {
        HttpWebRequest request = (HttpWebRequest)WebRequest.Create("https://android.googleapis.com/gcm/send");
        request.Method = PostWebRequest;
        request.KeepAlive = false;

        GCMPostPacket json = new GCMPostPacket()
        {
            collapse_key = "1",
            time_to_live = 60,
            registration_ids = new List<string>(new string[] { registrationId }),
            data = new GcmData()
            {
                message = command,
                misc = extra
            }
        };
        // Converting to JSON string
        string jsonString = SICJsonProtocol.JSONHelper.Serialize<GCMPostPacket>(json);
        byte[] byteArray = Encoding.UTF8.GetBytes(jsonString);

        request.ContentType = "application/json";
        request.ContentLength = byteArray.Length;
        request.ProtocolVersion = HttpVersion.Version10;

        request.Headers.Add(HttpRequestHeader.Authorization, "key=" + "MyVerySecretKey");

        Stream dataStream = request.GetRequestStream();
        dataStream.Write(byteArray, 0, byteArray.Length);
        dataStream.Close();

        using (WebResponse response = request.GetResponse())
        {
            HttpStatusCode responseCode = ((HttpWebResponse)response).StatusCode;
            if (responseCode.Equals(HttpStatusCode.Unauthorized) || responseCode.Equals(HttpStatusCode.Forbidden))
            {
                Console.WriteLine("Unauthorized - need new token");
            }
            else if (!responseCode.Equals(HttpStatusCode.OK))
            {
                Console.WriteLine("Response from web service not OK :");
                Console.WriteLine(((HttpWebResponse)response).StatusDescription);
            }

            StreamReader reader = new StreamReader(response.GetResponseStream());
            string responseLine = reader.ReadLine();
            Console.WriteLine("************************");
            Console.WriteLine("GCM send: " + responseCode + " | " + responseLine);
            // This is the log shown in the image above
            SRef.main.gui.ServiceUpdate("GCM send: " + responseCode + " | " + responseLine);
            reader.Close();
            response.Close();
            return responseLine;
        }

    }
    catch (Exception e)
    {
        // This is the log shown in the image above
        SRef.main.gui.ServiceUpdate("Failed send GCM, " + (retry ? "retrying in 20 sec" : "not retrying") + ". Error=" + e.Message);
        if (retry)
        {
            System.Threading.ThreadPool.QueueUserWorkItem(delegate(object obj)
            {
                try
                {
                    System.Threading.Thread.Sleep(20000);
                    SendMessage(registrationId, command, extra, false);
                }
                catch (Exception ex)
                {
                }
            });
        }
        return null;
    }
}

有没有人能看出我做错了什么,或者我总体上漏掉了什么?

这段内容涉及IT技术领域,您需要检查自己的操作是否正确,或者是否遗漏了某些步骤。

2
在外部catch块中记录e的值是否可能?它可能包含有用的信息。 - Nachi
好的,我会添加它,并查看是否有帮助。 - Ted
实际上,它已经在那里了。e.Message被写入输出中,那就是你看到的错误信息... + ". 错误=" + e.Message); - Ted
1个回答

3

无法从传输连接读取数据:已建立的连接被主机中的软件终止。

这个错误与GCM API无关。它意味着您的客户端尝试联系Web服务,但建立的连接已在其中一个网络层中断开。根据您得到此错误的位置和错误消息,可能意味着几件事情。

  1. 由于套接字超时,您的客户端决定中止连接。增加客户端读取/套接字超时以改善情况。

  2. 位于客户端和服务器之间的负载均衡器丢弃了连接。每个参与者都认为对方终止了连接。

网络超时通常非常麻烦,因为不清楚连接丢失的位置和谁丢失了它。如果增加超时没有帮助,建议通过能够嗅探HTTPS流量(Charles / TCPMON)的代理发送请求,或使用Wireshark查看哪些数据包被丢弃。

您的Android应用程序还具有可以在统计选项卡上启用的GCM监视。检查该图表上的GCM API是否报告了除200 OK之外的状态消息。这将有助于进一步缩小问题范围。如果没有报告除200之外的状态码,则意味着GCM从一开始就没有收到您的API请求。


我知道这是一个网络问题,但它不是一般的网络问题,因为我始终连接到互联网(否则我就无法与外界建立连接等)。这是我的服务器和gcm服务器之间特定的问题。如果我在另一台计算机上或本地运行它,问题仍然存在。 - Ted
统计选项卡?它在哪里? - Ted
一旦您在控制台上绑定了您的发件人 ID,您应该开始看到 GCM 响应代码状态。进入您的应用 -> 统计信息 -> 从下拉菜单中选择 GCM 消息 -> 点击“GCM 响应代码”选项卡。 - Deepak Bala
始终连接互联网并不能消除连接中断的问题。我同意这与您的服务器和GCM服务器之间的连接有关。如果有帮助的话,我曾经看到过这样的连接由于以下原因而中断:|1| -> 负载均衡器(LB)内存不足 |2| -> LB随意丢弃连接 |3| -> 超时设置不当。您仍需要进行网络跟踪以确定问题所在。 - Deepak Bala
我也遇到了同样的问题。GCM 运行良好,但我的客户端应用程序请求失败了。我使用 volley 库发送请求,但它会出现 TimeOutError。我尝试使用 volley 的 setRetryPolicy 方法,但请求仍然失败。你能告诉我有什么更有效的方法吗?WhatsApp 是如何管理所有这些事情的?他们的请求怎么从不超时? - pathe.kiran

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接