Python Threaded TCP Socket Server Example

I created this repo recently and thought some of you might find it useful for your projects. There are a lot of ways to create a stand-alone tcp socket server in python and a shitload of examples out there so it can be a bit difficult to figure out which is the best one to use or start from.

https://github.com/matthewwachter/python-tcp-threaded-server

I use this particular example pretty often when we need to communicate to TD from a Flask app or really any python app/code that would be risky (or impossible) to run directly from TD.

If you were to send a message to it from TD you could use the TCP DAT and do something like this from a Text DAT:

from datetime import datetime
import json

msg = {
	'cmd': 'test',
	'data': [str(datetime.now())],
}

msg = json.dumps(msg)

op('tcpip1').send(msg)

And then parse the incoming message in the callback script for the TCP DAT like so:

import json

def onReceive(dat, rowIndex, message, bytes, peer):
	myjson = json.loads(message)
	
	cmd = myjson['cmd']
	data = myjson['data']
	
	print(cmd, data)
	return

One interesting thing I have noticed is that TD tends to add an extra byte on the end of the message (0x00). I’m a little unclear about why this would happen but I added this line in the server to help deal with that issue:

data = loads(data.rstrip('\0'))

This example could be enhanced with the addition of a user provided callback function for the received client messages but I decided that it would be better not to overly complicate things. Later on I may add a different version for that.

Enjoy!

1 Like

Thanks Matthewwatcher, simple and easy to read code.
I d be interesting in seeing the user provided callback functions example if that’s possible. Wonder a few time how to structure those callbacks.

Cheers.

@oliviano something like this

from datetime import datetime
from json import loads, dumps
from pprint import pprint
import socket
from threading import Thread


class ThreadedServer(Thread):
    def __init__(self, host, port, timeout=60, callback=None, debug=False):
        self.host = host
        self.port = port
        self.timeout = timeout
        self.callback = callback
        self.debug = debug
        Thread.__init__(self)

    # run by the Thread object
    def run(self):
        if self.debug:
            print(datetime.now())
            print('SERVER Starting...', '\n')

        self.listen()

    def listen(self):
        # create an instance of socket
        self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

        # bind the socket to its host and port
        self.sock.bind((self.host, self.port))
        if self.debug:
            print(datetime.now())
            print('SERVER Socket Bound', self.host, self.port, '\n')

        # start listening for a client
        self.sock.listen(5)
        if self.debug:
            print(datetime.now())
            print('SERVER Listening...', '\n')
        while True:
            # get the client object and address
            client, address = self.sock.accept()

            # set a timeout
            client.settimeout(self.timeout)

            if self.debug:
                print(datetime.now())
                print('CLIENT Connected:', client, '\n')

            # start a thread to listen to the client
            Thread(
                target=self.listenToClient,
                args=(client, address, self.callback)
            ).start()

            # send the client a connection message
            # res = {
            #     'cmd': 'connected',
            # }
            # response = dumps(res)
            # client.send(response.encode('utf-8'))

    def listenToClient(self, client, address, callback):
        # set a buffer size ( could be 2048 or 4096 / power of 2 )
        size = 1024
        while True:
            try:
                # try to receive data from the client
                data = client.recv(size).decode('utf-8')
                if data:
                    data = loads(data.rstrip('\0'))
                    if self.debug:
                        print(datetime.now())
                        print('CLIENT Data Received', client)
                        print('Data:')
                        pprint(data, width=1)
                        print('\n')

                    if callback is not None:
                        callback(client, address, data)

                else:
                    raise error('Client disconnected')

            except:
                if self.debug:
                    print(datetime.now())
                    print('CLIENT Disconnected:', client, '\n')
                client.close()
                return False


def some_callback(client, address, data):
    print('data received', data)
    # send a response back to the client

    res = {
        'cmd': data['cmd'],
        'data': data['data']
    }
    response = dumps(res)
    client.send(response.encode('utf-8'))


if __name__ == "__main__":
    ThreadedServer('127.0.0.1', 8008, timeout=86400, callback=some_callback, debug=True).start()

Check this simple…Python socket programming