socket: use non-blocking socket and new Timeout class
diff --git a/serial/urlhandler/protocol_socket.py b/serial/urlhandler/protocol_socket.py
index a017ee3..80c4ea9 100644
--- a/serial/urlhandler/protocol_socket.py
+++ b/serial/urlhandler/protocol_socket.py
@@ -26,7 +26,8 @@
 except ImportError:
     import urllib.parse as urlparse
 
-from serial.serialutil import SerialBase, SerialException, portNotOpenError, to_bytes
+from serial.serialutil import SerialBase, SerialException, to_bytes, \
+    portNotOpenError, writeTimeoutError, Timeout
 
 # map log level names to constants. used in from_url()
 LOGGER_LEVELS = {
@@ -61,6 +62,8 @@
         except Exception as msg:
             self._socket = None
             raise SerialException("Could not open port {}: {}".format(self.portstr, msg))
+        # after connecting, switch to non-blocking, we're using select
+        self._socket.setblocking(False)
 
         # not that there is anything to configure...
         self._reconfigure_port()
@@ -149,11 +152,10 @@
         if not self.is_open:
             raise portNotOpenError
         read = bytearray()
-        timeout = self._timeout
+        timeout = Timeout(self._timeout)
         while len(read) < size:
             try:
-                start_time = time.time()
-                ready, _, _ = select.select([self._socket], [], [], timeout)
+                ready, _, _ = select.select([self._socket], [], [], timeout.time_left())
                 # If select was used with a timeout, and the timeout occurs, it
                 # returns with empty lists -> thus abort read operation.
                 # For timeout == 0 (non-blocking operation) also abort when
@@ -166,27 +168,19 @@
                 if not buf:
                     raise SerialException('socket disconnected')
                 read.extend(buf)
-                if timeout is not None:
-                    timeout -= time.time() - start_time
-                    if timeout <= 0:
-                        break
-            except socket.timeout:
-                # timeout is used for write support, just go reading again
-                pass
-            except socket.error as e:
-                # connection fails -> terminate loop
-                raise SerialException('connection failed ({})'.format(e))
             except OSError as e:
                 # this is for Python 3.x where select.error is a subclass of
                 # OSError ignore EAGAIN errors. all other errors are shown
                 if e.errno != errno.EAGAIN:
                     raise SerialException('read failed: {}'.format(e))
-            except select.error as e:
+            except (select.error, socket.error) as e:
                 # this is for Python 2.x
                 # ignore EAGAIN errors. all other errors are shown
                 # see also http://www.python.org/dev/peps/pep-3151/#select
                 if e[0] != errno.EAGAIN:
                     raise SerialException('read failed: {}'.format(e))
+            if timeout.expired():
+                break
         return bytes(read)
 
     def write(self, data):
@@ -197,12 +191,42 @@
         """
         if not self.is_open:
             raise portNotOpenError
-        try:
-            self._socket.sendall(to_bytes(data))
-        except socket.error as e:
-            # XXX what exception if socket connection fails
-            raise SerialException("socket connection failed: {}".format(e))
-        return len(data)
+
+        d = to_bytes(data)
+        tx_len = length = len(d)
+        timeout = Timeout(self._write_timeout)
+        while tx_len > 0:
+            try:
+                n = self._socket.send(d)
+                if timeout.is_non_blocking:
+                    # Zero timeout indicates non-blocking - simply return the
+                    # number of bytes of data actually written
+                    return n
+                elif not timeout.is_infinite:
+                    # when timeout is set, use select to wait for being ready
+                    # with the time left as timeout
+                    if timeout.expired():
+                        raise writeTimeoutError
+                    _, ready, _ = select.select([], [self._socket], [], timeout.time_left())
+                    if not ready:
+                        raise writeTimeoutError
+                else:
+                    assert timeout.time_left() is None
+                    # wait for write operation
+                    _, ready, _ = select.select([], [self._socket], [], None)
+                    if not ready:
+                        raise SerialException('write failed (select)')
+                d = d[n:]
+                tx_len -= n
+            except SerialException:
+                raise
+            except OSError as v:
+                if v.errno != errno.EAGAIN:
+                    raise SerialException('write failed: {}'.format(v))
+                # still calculate and check timeout
+                if timeout.expired():
+                    raise writeTimeoutError
+        return length - len(d)
 
     def reset_input_buffer(self):
         """Clear input buffer, discarding all that is in the buffer."""