OSDN Git Service

DO NOT MERGE: Apparently our native TCP sockets do not return an error from blocking...
authorAndreas Huber <andih@google.com>
Tue, 18 Jan 2011 23:26:40 +0000 (15:26 -0800)
committerAndreas Huber <andih@google.com>
Tue, 18 Jan 2011 23:52:31 +0000 (15:52 -0800)
if the network interface is shutdown while connecting.

Change-Id: I168c6026de24812efa9b7e607a9eb83efded8c1f
related-to-bug: 3362836

media/libstagefright/HTTPStream.cpp

index ccc6a34..7194614 100644 (file)
 #include <arpa/inet.h>
 #include <ctype.h>
 #include <errno.h>
+#include <fcntl.h>
 #include <netdb.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
 
-#include <media/stagefright/MediaDebug.h>
+#include <media/stagefright/foundation/ADebug.h>
 
 namespace android {
 
@@ -47,6 +48,82 @@ HTTPStream::~HTTPStream() {
     disconnect();
 }
 
+static bool MakeSocketBlocking(int s, bool blocking) {
+    // Make socket non-blocking.
+    int flags = fcntl(s, F_GETFL, 0);
+    if (flags == -1) {
+        return false;
+    }
+
+    if (blocking) {
+        flags &= ~O_NONBLOCK;
+    } else {
+        flags |= O_NONBLOCK;
+    }
+
+    return fcntl(s, F_SETFL, flags) != -1;
+}
+
+static status_t MyConnect(
+        int s, const struct sockaddr *addr, socklen_t addrlen) {
+    status_t result = UNKNOWN_ERROR;
+
+    MakeSocketBlocking(s, false);
+
+    if (connect(s, addr, addrlen) == 0) {
+        result = OK;
+    } else if (errno != EINPROGRESS) {
+        result = -errno;
+    } else {
+        for (;;) {
+            fd_set rs, ws;
+            FD_ZERO(&rs);
+            FD_ZERO(&ws);
+            FD_SET(s, &rs);
+            FD_SET(s, &ws);
+
+            struct timeval tv;
+            tv.tv_sec = 0;
+            tv.tv_usec = 100000ll;
+
+            int nfds = ::select(s + 1, &rs, &ws, NULL, &tv);
+
+            if (nfds < 0) {
+                if (errno == EINTR) {
+                    continue;
+                }
+
+                result = -errno;
+                break;
+            }
+
+            if (FD_ISSET(s, &ws) && !FD_ISSET(s, &rs)) {
+                result = OK;
+                break;
+            }
+
+            if (FD_ISSET(s, &rs) || FD_ISSET(s, &ws)) {
+                // Get the pending error.
+                int error = 0;
+                socklen_t errorLen = sizeof(error);
+                if (getsockopt(s, SOL_SOCKET, SO_ERROR, &error, &errorLen) == -1) {
+                    // Couldn't get the real error, so report why not.
+                    result = -errno;
+                } else {
+                    result = -error;
+                }
+                break;
+            }
+
+            // Timeout expired. Try again.
+        }
+    }
+
+    MakeSocketBlocking(s, true);
+
+    return result;
+}
+
 status_t HTTPStream::connect(const char *server, int port) {
     Mutex::Autolock autoLock(mLock);
 
@@ -82,7 +159,7 @@ status_t HTTPStream::connect(const char *server, int port) {
     addr.sin_addr.s_addr = *(in_addr_t *)ent->h_addr;
     memset(addr.sin_zero, 0, sizeof(addr.sin_zero));
 
-    int res = ::connect(s, (const struct sockaddr *)&addr, sizeof(addr));
+    status_t res = MyConnect(s, (const struct sockaddr *)&addr, sizeof(addr));
 
     mLock.lock();
 
@@ -90,12 +167,12 @@ status_t HTTPStream::connect(const char *server, int port) {
         return UNKNOWN_ERROR;
     }
 
-    if (res < 0) {
+    if (res != OK) {
         close(mSocket);
         mSocket = -1;
 
         mState = READY;
-        return UNKNOWN_ERROR;
+        return res;
     }
 
     mState = CONNECTED;