在3G和wifi之间切换时,Android套接字错误

时间:2011-11-10 22:35:33

标签: java android

我的应用在不同时间连接到服务器。如果我在3g和wifi之间切换,反之亦然,我得到套接字超时错误。我该如何规避这个?我在stackoverflow上阅读了一些帖子,其中人们有类似的问题,他们通过使用新套接字来修复它。如何使用新套接字发出我的http请求?

CODE:

class HttpRunnable implements Runnable
{
    private HttpRequestNotification request = null;

    public HttpRunnable(HttpRequestNotification req)
    {
        request = req;
    }

    public void run()
    {
        HttpURLConnection conn = null;
        try {
            System.out.println(request.url);
            conn = (HttpURLConnection)(new URL(request.url)).openConnection();
            conn.setInstanceFollowRedirects(false);
            conn.setRequestMethod(request.verb);
            conn.setReadTimeout(20000);
            conn.setConnectTimeout(20000);

            // If the request contains an explicit auth token, replace our cached copy
            if (request.authToken != null && request.authToken.length() > 0)
                authorizationToken = request.authToken;

            // Add the authorization token, if we have it
            if (authorizationToken != null && authorizationToken.length() > 0)
            {
                conn.setRequestProperty("Authorization", authorizationToken);
            }

            if (request.postData != null && request.postData.length > 0)
            {   // Let the connection know we'll be posting data
                conn.addRequestProperty("Content-Type", "application/xml");
                conn.setDoOutput(true);
            }
            conn.connect();

            if (request.postData != null && request.postData.length > 0)
            {   // Write POST data, if necessary
                OutputStream os = conn.getOutputStream();
                os.write(request.postData);
                os.close();
            }

            // Grab input stream. If it hasn't occurred already, this will send the Http request over the wire.
            InputStream inputStream = conn.getInputStream();

            // Save off statusCode, statusMessage, headers
            request.statusCode = conn.getResponseCode();
            request.statusMessage = conn.getResponseMessage();
            Map hdrs = conn.getHeaderFields();
            Iterator iter = hdrs.keySet().iterator();
            while (iter.hasNext())
            {
                String key = (String) iter.next();
                String val = conn.getHeaderField(key); 
                if (key != null && val != null)
                {
                    if (key.equalsIgnoreCase("location"))
                        request.url = val;
                    request.headers.put(key, val);
                }
            }

            // Extract the message body. We used to extract the content-length header & only
            // read that amount of bytes, but we found that sometimes the header is missing.
            // Specifically, we found that requests lacked a content-length header in these cases:
            // 1) for HTTP 1.1 requests that provide a "transfer-encoding: chunked" header instead
            // 2) When running on certain carriers, we found the content-length header to be unreliable
            //    (DroidX running over Verizon -- full response body was there, but the content-length
            //    field was waaaay too small.
            // To handle all of these cases, we simply read until the end of the stream is reached, then
            // convert the result to a byte array.  See defect 14288 (over WiFi, some android devices
            // return Content-Length; over 3G, they may use transfer-encoding).
            ByteArrayOutputStream baos = new ByteArrayOutputStream(10*1024);    // This size is an initial size, not max size
            while (true)
            {
                int data = inputStream.read();
                if (data < 0)
                    break;
                baos.write(data);
            }
            request.responseData = baos.toByteArray();

            // Extract the auth token, if it's present
            if (authorizationToken == null && request.url.endsWith("authenticate") && 
                    request.statusCode >= 200 && request.statusCode < 300)
            {
                String authToken = conn.getHeaderField("X-Authorization-Token");
                if (authToken != null && authToken.length() > 0)
                    request.authToken = authorizationToken = authToken;
            }

            if (request.statusCode >= 200 && request.statusCode < 400)
            {
                request.onSuccess();
            }
            else
            {
                // Note -- this check is included here as well as below for future-proofing. Note that currently
                // the J2SE implementation of HttpConnection will throw an IOException if the resopnse code
                // is not in the 200-299 range.
                if (request.statusCode == HttpURLConnection.HTTP_UNAUTHORIZED &&
                        !UserAuthService.HttpRequestNotification_Login.class.isInstance(request))
                {   // Got a 401 Unauthorized. Since this is not a login request, we want to transition
                    // the user back to the login screen.
                    LoginOrchestrator.getInstance().logout();
                }
                else
                    request.onError(request.statusCode, "");
            }

        }
        catch (IOException e)
        {
            try
            {
                if (conn != null)
                {
                    request.statusCode = conn.getResponseCode();
                    request.statusMessage = conn.getResponseMessage();
                }
            }
            catch (IOException ex) {}

            // Have to check for this HTTP_UNAUTHRORIZED case here as well -- the J2SE HttpConnection
            // throws an IOException when the status code is not in the 200-299 range.
            if (request.statusCode == HttpURLConnection.HTTP_UNAUTHORIZED &&
                    !UserAuthService.HttpRequestNotification_Login.class.isInstance(request))
            {   // Got a 401 Unauthorized. Since this is not a login request, we want to transition
                // the user back to the login screen.
                LoginOrchestrator.getInstance().logout();
            }
            else
                request.onError(request.statusCode, e.toString() + ": " + e.getMessage());
        }
        System.out.flush();
    }
}

1 个答案:

答案 0 :(得分:1)

只需重新尝试与新套接字的连接并再次发送所有内容(在您的情况下发送新的HTTP请求)。

基本上,当您的Android设备从WiFi切换到3G时,Web服务器看到的IP地址会发生变化(从您的家庭ISP分配的IP地址到移动运营商网关IP地址)。所以旧套接字变得毫无用处......

我没有使用java.net.HttpURLConnection,但我想在超时异常时你需要重做调用serverAddress.openConnection()之后的相同代码。因为这个函数似乎是创建底层TCP连接的函数。