Repository: microsoft/Qcodes

Issue:
IPInstrument._send() could not send all its data
I was browsing the instruments code and found out that in the `IPInstrument` class the `socket.send(...)` function is implemented wrongly:

```python
    def _send(self, cmd):
        data = cmd + self._terminator
        self._socket.send(data.encode())
```

This is what the documentation says about `socket.send(...)`:

> socket.send(bytes[, flags])
Send data to the socket. The socket must be connected to a remote socket. The optional flags argument has the same meaning as for recv() above. Returns the number of bytes sent. Applications are responsible for checking that all data has been sent; if only some of the data was transmitted, the application needs to attempt delivery of the remaining data. 
https://docs.python.org/3.6/library/socket.html

At this moment, if `send(...)` fails, only a part of the message is transmitted. Which will create strange bugs.

A better solution is to use `socket.sendall(...)` or as the example shows:
```python
    def mysend(self, msg):
        totalsent = 0
        while totalsent < MSGLEN:
            sent = self.sock.send(msg[totalsent:])
            if sent == 0:
                raise RuntimeError("socket connection broken")
            totalsent = totalsent + sent
```
https://docs.python.org/3.6/howto/sockets.html#socket-howto
IPInstrument._send() could not send all its data
I was browsing the instruments code and found out that in the `IPInstrument` class the `socket.send(...)` function is implemented wrongly:

```python
    def _send(self, cmd):
        data = cmd + self._terminator
        self._socket.send(data.encode())
```

This is what the documentation says about `socket.send(...)`:

> socket.send(bytes[, flags])
Send data to the socket. The socket must be connected to a remote socket. The optional flags argument has the same meaning as for recv() above. Returns the number of bytes sent. Applications are responsible for checking that all data has been sent; if only some of the data was transmitted, the application needs to attempt delivery of the remaining data. 
https://docs.python.org/3.6/library/socket.html

At this moment, if `send(...)` fails, only a part of the message is transmitted. Which will create strange bugs.

A better solution is to use `socket.sendall(...)` or as the example shows:
```python
    def mysend(self, msg):
        totalsent = 0
        while totalsent < MSGLEN:
            sent = self.sock.send(msg[totalsent:])
            if sent == 0:
                raise RuntimeError("socket connection broken")
            totalsent = totalsent + sent
```
https://docs.python.org/3.6/howto/sockets.html#socket-howto


Discussion:

Resolving patch:
diff --git a/qcodes/instrument/ip.py b/qcodes/instrument/ip.py
--- a/qcodes/instrument/ip.py
+++ b/qcodes/instrument/ip.py
@@ -1,8 +1,10 @@
 """Ethernet instrument driver class based on sockets."""
 import socket
+import logging
 
 from .base import Instrument
 
+log = logging.getLogger(__name__)
 
 class IPInstrument(Instrument):
 
@@ -139,10 +141,14 @@ def set_terminator(self, terminator):
 
     def _send(self, cmd):
         data = cmd + self._terminator
-        self._socket.send(data.encode())
+        self._socket.sendall(data.encode())
 
     def _recv(self):
-        return self._socket.recv(self._buffer_size).decode()
+        result = self._socket.recv(self._buffer_size)
+        if result == b'':
+            log.warning("Got empty response from Socket recv() "
+                        "Connection broken.")
+        return result.decode()
 
     def close(self):
         """Disconnect and irreversibly tear down the instrument."""


Test patch: