# -*- coding: utf-8 -*-
## Copyright 2019 Trevor van Hoof and Jan Pijpers.
## Licensed under the Apache License, Version 2.0
## Downloaded from https://janpijpers.com or https://gumroad.com/janpijpers
## See the license file attached or on https://www.janpijpers.com/script-licenses/
'''
Name: bytesForNetworkTransfer
Description:
byte tests for data transmission across a network
Simple example on how to create a data packet for sending across a tcp network connection.
'''
import sys
import struct
def create_packet( message, type = 100 ):
'''
This packs our string message into a packet that we can send across a network.
The type makes it easier to know how to handle the data on the receiving end.
For example, type 100 means normal text data
but type 200 can mean jpg image data.
the way we construct our packet is as follows.
[size][type][message]
the first part will say how big our message is.
the second part will hold the type number
the third part will be the message.
:param message: a byte string message.
:param type: the message type. This can be any number between 0 and 255.
:return:
'''
if not isinstance(message, basestring):
raise TypeError("The message is not a string. ")
try:
## Lets make sure we can encode the data as utf8 and back
decodedMessage = messageContents.decode("utf8") ## data is now unicode
encodedMessage = decodedMessage.encode("utf8") ## data is now back to binary.
except UnicodeDecodeError as e:
print "This message can not be decoded as UTF8. There are characters in there utf8 doesnt understand."
raise e
## We use the sys getsizeof function to find out how much bytes this string takes in memory.
## using len would give us the number of characters, but not actually the number of bytes.
messageSize = sys.getsizeof(message)
## Lets start packing this.
## For this we use the struct module.
## https://docs.python.org/2/library/struct.html
## First we write the size.
## >I means unsigned int. so a maximum value of 65,535
## if you expect bigger messages you should use >Q
byteString = struct.pack(">I", messageSize)
## Then we write the type
## >B is an unsigned char. it allows for 256 different types.
byteString += struct.pack(">B", type)
## Now we add our contents.
byteString += messageContents
return byteString
def read_packet( incommingMessage ):
'''
Here we decode our message.
There are a few interesting caveats with network data transmission.
In this case I will assume a tcp network connection.
That means that "in theory" all the data comes in, in order.
However you can not always be sure that you received exactly one full message.
So if we do not have enough data, this function returns [None, remaining data]
if we have exactly the right amount of data [extracted message, ""]
if we have more data left [extracted message, remaining data]
Because we know how we wrote our create packet function,
we know that the message size is unsigned int( >I ) which is 4 bytes long
*see the table in the struct python docs
And the same goes for the type which is 1 byte long.
so we use that information here.
:param incommingMessage: the message as we received it from the network.
:return: list [the message content, the remaining data]
'''
if len( incommingMessage ) < 4:
return [None, incommingMessage]
sizeBytes = incommingMessage[:4]
unpackedMessageSize = struct.unpack(">I", sizeBytes)[0]
typeBytes = incommingMessage[4]
unpackedMessageType = struct.unpack(">B", typeBytes)[0]
unpackedMessage = incommingMessage[5:unpackedMessageSize]
## Now we could do something with our data and handle it
## according to the unpackedMessageType.
## i.e.
## if unpackedMessageType == 200:
## unpackedMessage = bytes_to_image(unpackedMessage)
return [unpackedMessage, incommingMessage[unpackedMessageSize+5:]]
## If we do not specify encoding then python will store this string as byte code.
## using japanese characters so we cover some wierd edge cases.
messageContents = "textAnd漢字カタカナ"
## Create the packet ready for sending.
networkPacket = create_packet(messageContents)
## Decode it once we have received it.
decodedMessage, remainingPacketData = read_packet(networkPacket)
print "Original: ", repr(messageContents)
print "Decoded : ", repr(decodedMessage)
assert(messageContents == decodedMessage)
print "All tests passed"