Serialisation: Protocol Buffers

Python

Compiling prototype files

The Python files are generated from the prototype by


      protoc personv3.proto --python_out=python
  

When I tried to compile my client (see later), I got the error


      AttributeError: module 'google.protobuf.descriptor' has no attribute '_internal_create_key'
  
I am now using the 3.12.2 version of protoc. It seems that the version installed in Python 3.6 (version 3.11.2) isn't quite compatable with this. I had to do

pip3 uninstall protobuf
pip3 install protobuf
  
to get the versions right.

Structure of files

The compiler just creates one file. However, it isn't nice to look at, as it uses Python meta-calls to generate classes. However, you can write programs assuming that it has generated standard Python classes.

For the example we use, the classes are Person, Person.Name and Person.Email.

Person client

The client creates a Person and then fills in the fields. To add a Name there are two ways: create a Name object, assigns its fields and then copy its value into the Person.Name by CopyFrom(), or assign the fields of Person.Name directly. For the list of emails, add() new emails and fill in the fields of each email.

The client is PersonClient.py:



import socket
import fileinput
import sys

import personv3_pb2

PORT = 2001


if len(sys.argv) < 2:
    print('Usage: comm hostname')
    exit(1)

host = sys.argv[1]

person = personv3_pb2.Person()

#name = person.Name()
#name.family = 'Newmarch'
#name.personal = 'Jan'
#person.name.CopyFrom(name)

person.name.family = 'Newmarch'
person.name.personal = 'Jan'

email1 = person.email.add()
email1.kind = 'private'
email1.address = 'jan@newmarch.name'

email2 = person.email.add()
email2.kind = 'work'
email2.address = 'j.newmarch@boxhill.edu.au'

print(person)

with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
    s.connect((host, PORT))

    s.sendall(person.SerializeToString())

Person server

The server listens for client connections and reads the data. It creates a Person object and then calls the method ParseFromStrin() on the (binary) data. Here the servers just prints the created person and terminates the connection.

The server is PersonServer.py:


# https://realpython.com/python-sockets/#echo-server
# sequential server

import socket
import personv3_pb2

HOST = '' # INADDR_ANY
PORT = 2001

with socket.socket(socket.AF_INET6, socket.SOCK_STREAM) as s:
    s.bind((HOST, PORT))
    s.listen()
    while True:
        conn, addr = s.accept()
        with conn:
            print('Connected by', addr)
            while True:
                data = conn.recv(1024)
                if not data:
                    print('Disconnecting')
                    break
                person = personv3_pb2.Person()
                person.ParseFromString(data)
                print(person)


Copyright © Jan Newmarch, jan@newmarch.name
Creative Commons License
" Network Programming using Java, Go, Python, Rust, JavaScript and Julia" by Jan Newmarch is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License .
Based on a work at https://jan.newmarch.name/NetworkProgramming/ .

If you like this book, please contribute using PayPal