Encoding and checksum

It's a good practice to set some form of encoding between the clients and the server, and even better practice if the encoding includes a checksum to verify data integrity. We could improve the example from the last section with a custom protocol that does both encoding and checksum. Let's start by defining the encoding function, where a given message will return the following byte sequence:

Function Byte sequence
The first four bytes will follow a sequence 2A 00 2A 00
Two bytes will be the message length stored using Little Endian order (least significant byte first) 08 00
Four bytes for data checksum 00 00 00 00
Followed by the raw message 0F 1D 3A FF ...
Closing with the same starting sequence 2A 00 2A 00

 

The Checksum function will be calculated by summing the message content, using a group of five bytes in Little Endian (least significant byte first), adding any spare byte left one by one, and then taking the first four bytes of the sum as a Little Endian:

func Checksum(b []byte) []byte {
var sum uint64
for len(b) >= 5 {
for i := range b[:5] {
v := uint64(b[i])
for j := 0; j < i; j++ {
v = v * 256
}
sum += v
}
b = b[5:]
}
for _, v := range b {
sum += uint64(v)
}
s := make([]byte, 8)
binary.LittleEndian.PutUint64(s, sum)
return s[:4]
}

Now, let's create a function that will encapsulate the message using the protocol we defined:

var ErrLength = errors.New("message too long")

func CreateMessage(content []byte) ([]byte, error) {
if len(content) > 65535 {
return nil, ErrLength
}
data := make([]byte, 0, len(content)+14)
data = append(data, Sequence...)
data = append(data, byte(len(content)/256), byte(len(content)%256))
data = append(data, Checksum(content)...)
data = append(data, content...)
data = append(data, Sequence...)
return data, nil
}

We also need another function that does the opposite, checking whether a message is valid and extracting its content:

func MessageContent(b []byte) ([]byte, error) {
n := len(b)
if n < 14 {
return nil, fmt.Errorf("Too short")
}
if open := b[:4]; !bytes.Equal(open, Sequence) {
return nil, fmt.Errorf("Wrong opening sequence %x", open)
}
if length := int(b[4])*256 + int(b[5]); n-14 != length {
return nil, fmt.Errorf("Wrong length: %d (expected %d)", length, n-14)
}
if close := b[n-4 : n]; !bytes.Equal(close, Sequence) {
return nil, fmt.Errorf("Wrong closing sequence %x", close)
}
content := b[10 : n-4]
if !bytes.Equal(Checksum(content), b[6:10]) {
return nil, fmt.Errorf("Wrong checksum")
}
return content, nil
}

We can now use them for encoding and decoding messages. For instance, we could improve the UDP client and server from the previous section and we could encode when sending:

// Send
data, err := common.CreateMessage(msg)
if err != nil {
log.Println("->", addr, "Encode error:", err)
continue
}
if _, err := conn.WriteTo(data, addr); err != nil {
log.Println("->", addr, "Send error:", err)
}

And we can decode the bytes received for incoming messages in order to extract the content:

//Receive
n, addr, err := conn.ReadFromUDP(b)
if err != nil {
log.Println("<-", addr, "Message error:", err)
continue
}
msg, err := common.MessageContent(b[:n])
if err != nil {
log.Println("<-", addr, "Decode error:", err)
continue
}
log.Printf("<- %q from %s", msg, addr)

In order to verify that the content we received is valid, we are using the MessageContent utility function defined previously. This will check for headers, length, and checksum. It will only extract the bytes that compose the message.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.145.194.57