ken596 ken596 - 4 years ago 211
Python Question

TypeError: Can't convert 'bytes' object to str implicitly while working with sockets

So I'm trying to convert code from Python 2.7 to Python 3, and it seems as though something has changed. I'm trying to receive binary data over a socket and now it doesn't work. Here's my code.

EDIT: I have added my send code. Also, I don't really like the way it works right now, it's overcomplicated. If you can it would be nice to have a better way of sending/receiving data.

def recv(self):
# Receive the length of the incoming message (unpack the binary data)
dataLength = socket.ntohl(struct.unpack("I", self._recv(4))[0])

# Receive the actual data
return self._recv(dataLength)

def _recv(self, length):
try:
data = ''
recvLen = 0
while recvLen < length:
newData = self.sock.recv(length-recvLen)

if newData == '':
self.isConnected = False
raise exceptions.NetworkError(errors.CLOSE_CONNECTION, errno=errors.ERR_CLOSED_CONNECTION)

data = data + newData # TypeError here
recvLen += len(newData)

return data
except socket.error as se:
raise exceptions.NetworkError(str(se))

def send(self, data):
if type(data) is not str:
raise TypeError()

dataLength = len(data)

# Send the length of the message (int converted to network byte order and packed as binary data)
self._send(struct.pack("I", socket.htonl(dataLength)), 4)

# Send the actual data
self._send(data, dataLength)

def _send(self, data, length):
sentLen = 0
while sentLen < length:
try:
amountSent = self.sock.send(data[sentLen:])
except Exception:
self.isConnected = False
raise exceptions.NetworkError(errors.UNEXPECTED_CLOSE_CONNECTION)

if amountSent == 0:
self.isConnected = False
raise exceptions.NetworkError(errors.UNEXPECTED_CLOSE_CONNECTION)

sentLen += amountSent

Answer Source

Python 3 sends data as bytes so you have to decode to string

 data = data + newData.decode('utf-8')

 # or 

 data = data + newData.decode('ascii')

if you need bytes data then use

 data = b''

and keep without .decode()

 data = data + newData

EDIT: for new code in question.

When you send you have to convert/encode string to bytes and after that get its length. Native chars has length 1 as unicode but they can use 2 bytes (or more).

When you receive you have to work with bytes b'' and at the end convert/decode bytes to string again.

See comments # <-- in code

def send(self, data):
    if not isinstance(data, str): # <-- prefered method
    #if type(data) is not str:
        raise TypeError()

    data = data.encode('utf-8') # <-- convert to bytes

    # get size of bytes
    dataLength = len(data)

    # Send the length of the message (int converted to network byte order and packed as binary data)
    self._send(struct.pack("I", socket.htonl(dataLength)), 4)

    # Send the actual data
    self._send(data, dataLength)


def recv(self):
    # Receive the length of the incoming message (unpack the binary data)
    dataLength = socket.ntohl(struct.unpack("I", self._recv(4))[0])

    # Receive the actual data
    return self._recv(dataLength).decode('utf-8') # <-- convert to string again

def _recv(self, length):
    try:
        data = b'' # <-- use bytes
        recvLen = 0
        while recvLen < length:
            newData = self.sock.recv(length-recvLen)

            #if newData == b'': # <-- use bytes
            if not newData:    # <-- or 
                self.isConnected = False
                raise exceptions.NetworkError(errors.CLOSE_CONNECTION, errno=errors.ERR_CLOSED_CONNECTION)

            data = data + newData # TypeError here
            recvLen += len(newData)

        return data
    except socket.error as se:
        raise exceptions.NetworkError(str(se))
Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download