Skip to main content

Client and server for TCP message passing

Project description

pytcp_message

View on PyPI

Read the docs

Coverage Status Documentation Status

Sends/receives messages in the following format:

| 1 byte         | 8 bytes        |  ... |
| is compressed? | content length | data |

Messages over 575 bytes are automatically compressed/decompressed with zlib

The main classes are TcpServer and TcpClient, which use the above message format to communicate with each other.

TCP response messages are implemented in TcpMessages. TcpMessage has one member, content, which is the bytes content of the message that will be sent. It also includes convenience methods for writing that content to and from a TCP socket using the above format.

TcpServer opens a connection when a request comes in, then listens on that connection until either the client closes the connection or timeout seconds, passed to the constructor, have elapsed. The default is 30. This allows multiple messages to be sent without re-creating new connections.

TCP request messages are implemented using TcpRequests, which inherit from TcpMessage. TcpRequests include an additional member, client_addr, the requester's address.

When a request comes in, TcpServer creates a TcpRequest object with the client's address and content of the incoming message, along with an empty TcpMessage response. These objects are passed to a list of request handlers attached to the server. After all handlers have run, TcpServer sends the TcpMessage response object to the client.

This allows the server to implement a Chain of Responsibility similar to Express/NodeJS. By passing objects to the various handlers, requests and responses can be modified by each method in the chain.

Example:

import time

from pytcp_message import TcpServer, TcpClient
from datetime import datetime

ADDR = "127.0.0.1"
PORT = 8080


def log_request(req, _):
    now = datetime.now().strftime("%F %T")
    print(
        "[{}] {} <-- {}".format(
            now,
            req.get_client_address()[0],
            req.get_content().decode("utf-8")
        )
    )


def send_response(req, res):
    res.set_content("You are {}:{}".format(
        *req.get_client_address()
    ).encode("utf-8"))


def log_response(req, res):
    now = datetime.now().strftime("%F %T")
    print("[{}] {} --> {}".format(
        now,
        res.get_content().decode("utf-8"),
        req.get_client_address()[0]
    ))


def main():
    send_message = "Hello{}"

    tcpd = TcpServer(address=ADDR, port=PORT, timeout=5)
    tcpd.add_request_handler(log_request)
    tcpd.add_request_handler(send_response)
    tcpd.add_request_handler(log_response)

    tcpd.start()
    print("Server running on tcp://{}:{}...".format(ADDR, PORT))

    tcpc = TcpClient((ADDR, PORT))

    for i in range(0, 10):
        print("Client sending '{}'...".format(send_message.format(i)))
        tcpc.send(send_message.format(i).encode("utf-8"))
        response = tcpc.receive()
        print("Client Received: '{}'\n".format(response.decode("utf-8")))

    tcpc.stop()
    print("Client closed connection.")

    tcpd.stop()
    time.sleep(5)
    print("Server stopped.\n")


if __name__ == "__main__":
    main()

Output:

Server running on tcp://127.0.0.1:8080...
Client sending 'Hello0'...
[2020-06-19 21:01:28] 127.0.0.1 <-- Hello0
[2020-06-19 21:01:28] You are 127.0.0.1:34360 --> 127.0.0.1
Client Received: 'You are 127.0.0.1:34360'

Client sending 'Hello1'...
[2020-06-19 21:01:28] 127.0.0.1 <-- Hello1
[2020-06-19 21:01:28] You are 127.0.0.1:34360 --> 127.0.0.1
Client Received: 'You are 127.0.0.1:34360'

Client sending 'Hello2'...
[2020-06-19 21:01:28] 127.0.0.1 <-- Hello2
[2020-06-19 21:01:28] You are 127.0.0.1:34360 --> 127.0.0.1
Client Received: 'You are 127.0.0.1:34360'

Client sending 'Hello3'...
[2020-06-19 21:01:28] 127.0.0.1 <-- Hello3
[2020-06-19 21:01:28] You are 127.0.0.1:34360 --> 127.0.0.1
Client Received: 'You are 127.0.0.1:34360'

Client sending 'Hello4'...
[2020-06-19 21:01:28] 127.0.0.1 <-- Hello4
[2020-06-19 21:01:28] You are 127.0.0.1:34360 --> 127.0.0.1
Client Received: 'You are 127.0.0.1:34360'

Client sending 'Hello5'...
[2020-06-19 21:01:28] 127.0.0.1 <-- Hello5
[2020-06-19 21:01:28] You are 127.0.0.1:34360 --> 127.0.0.1
Client Received: 'You are 127.0.0.1:34360'

Client sending 'Hello6'...
[2020-06-19 21:01:28] 127.0.0.1 <-- Hello6
[2020-06-19 21:01:28] You are 127.0.0.1:34360 --> 127.0.0.1
Client Received: 'You are 127.0.0.1:34360'

Client sending 'Hello7'...
[2020-06-19 21:01:28] 127.0.0.1 <-- Hello7
[2020-06-19 21:01:28] You are 127.0.0.1:34360 --> 127.0.0.1
Client Received: 'You are 127.0.0.1:34360'

Client sending 'Hello8'...
[2020-06-19 21:01:28] 127.0.0.1 <-- Hello8
[2020-06-19 21:01:28] You are 127.0.0.1:34360 --> 127.0.0.1
Client Received: 'You are 127.0.0.1:34360'

Client sending 'Hello9'...
[2020-06-19 21:01:28] 127.0.0.1 <-- Hello9
[2020-06-19 21:01:28] You are 127.0.0.1:34360 --> 127.0.0.1
Client Received: 'You are 127.0.0.1:34360'

Client closed connection.
Server stopped.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distributions

No source distribution files available for this release.See tutorial on generating distribution archives.

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

pytcp_message-0.1.9-py3-none-any.whl (9.2 kB view details)

Uploaded Python 3

File details

Details for the file pytcp_message-0.1.9-py3-none-any.whl.

File metadata

  • Download URL: pytcp_message-0.1.9-py3-none-any.whl
  • Upload date:
  • Size: 9.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.2.0 pkginfo/1.5.0.1 requests/2.24.0 setuptools/41.2.0 requests-toolbelt/0.9.1 tqdm/4.47.0 CPython/3.8.3

File hashes

Hashes for pytcp_message-0.1.9-py3-none-any.whl
Algorithm Hash digest
SHA256 575da1185975d54ccbb1c3f04509687f5d5b8b9841809c53e29272abbbc90a72
MD5 05069b80e1e43bb8d32b7614471d77eb
BLAKE2b-256 4e6c234ddcd6c1cc9acaa33cdec9eff40e37ddb4da43cc093d9e84609c987bf4

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page