0

I am writing a TCP server using python and socketserver and using threads. TCP server should send commands to device and read the response from the device. The device sends a initial message to server after connection and following commands are sent after processing initial message. Server is setup to handle multiple connections. The response received by the server contains CRLF \r\n in long string. Now, I want to parse this response and separate messages and store them. And also each starts with "$" and message length is not fixed. Example response from client:

b'$<MSG>\r\n$Name=TEST-Device\r\n$Ext=Test connection\r\n$Security=0\r\n$Software=xxx\r\n$Hardware=xxx\r\n$SUCCESS\r\n$<end>\r\n'

Next step is to parse this message and check if Name=TEST-Device (only example) and then send next commands (shown in pseudo code below).

Server code:

class TCPServerMultiClient:
    ...

    def handle_client(self, client_sock, client_address):

        try:
            time.sleep(5)
            print("- Waiting for initial message -")
            data = client_sock.recv(1024)
            print(data)
            while data:
                # parse the message from client
                data = data.decode('utf-8')
                i = data.rfind("\r\n")
                data, buf = data[:i + 2], data[i + 2:]
                lines = data.split("\r\n")
                lines = filter(None, lines)
                print(lines)
                for line in lines:
                    if line.startswith("$"):
                        symbol = str(line.split(",")[])
                # pseudo code! send next commands only if Name matches (from previous response)
                if Name="TEST-Deivce":

                    print("- Sending first command")
                    cmdToSend=b"first_command"
                    client_sock.sendall(cmdToSend)
                
                    time.sleep(2)            
                    print("- Recieving response")
                    data1 = client_sock.recv(1024)
                    print(data1)

                # check if client closed the connection
                data = client_sock.recv(4024)
            print(f'Connection closed by {client_address}')

        except socket.error as e:
            if e.errno == errno.ECONNABORTED:
                print(os.strerror(e.errno))
            else:
                print(str(e))
                raise

        finally:
            # close socket
            print(f'Closing client socket for {client_address}...')
            client_sock.close()
            print(f'Client socket closed for {client_address}')

    def shutdown_server(self):
        """ Shutdown the server """

        print('Shutting down server...')
        self.sock.close()

But I am not able to split the message and store it. Could anyone please let me know how to properly handle it? And also should I use buffer and BytesIO or asyncio? My goal is to have a "command queue" to send commands from server to devices and "response queue" to store all responses. Maybe I need to do it in a separate class (like Message)

Thanks in advance.

P.S: Please let me know if any info is missing here.

Preeti
  • 535
  • 1
  • 6
  • 30

1 Answers1

1

One way to parse newline-terminated messages is to wrap the socket in a file-like object using socket.makefile and use .readline():

server.py:

import socket
import threading

def handle(client, addr):
    print(f'{addr}: connected')
    with client, client.makefile('r', encoding='utf8') as file:
        while True:
            line = file.readline()
            if not line: break
            print(f'{addr}: {line!r}')
    print(f'{addr}: disconnected')

s = socket.socket()
s.bind(('', 5000))
s.listen()
while True:
    c, a = s.accept()
    threading.Thread(target=handle, args=(c, a), daemon=True).start()

client.py

import socket

def client():
    s = socket.socket()
    s.connect(('localhost', 5000))
    with s:
        s.sendall(b'$<MSG>\r\n$Name=TEST-Device\r\n$Ext=Test connection\r\n$Security=0\r\n$Software=xxx\r\n$Hardware=xxx\r\n$SUCCESS\r\n$<end>\r\n')

# demo multi-client handling
for _ in range(3):
    client()

Output:

('127.0.0.1', 2482): connected
('127.0.0.1', 2482): '$<MSG>\n'
('127.0.0.1', 2483): connected
('127.0.0.1', 2482): '$Name=TEST-Device\n'
('127.0.0.1', 2484): connected
('127.0.0.1', 2482): '$Ext=Test connection\n'
('127.0.0.1', 2483): '$<MSG>\n'
('127.0.0.1', 2482): '$Security=0\n'
('127.0.0.1', 2483): '$Name=TEST-Device\n'
('127.0.0.1', 2482): '$Software=xxx\n'
('127.0.0.1', 2483): '$Ext=Test connection\n'
('127.0.0.1', 2482): '$Hardware=xxx\n'
('127.0.0.1', 2483): '$Security=0\n'
('127.0.0.1', 2482): '$SUCCESS\n'
('127.0.0.1', 2483): '$Software=xxx\n'
('127.0.0.1', 2482): '$<end>\n'
('127.0.0.1', 2483): '$Hardware=xxx\n'
('127.0.0.1', 2484): '$<MSG>\n'
('127.0.0.1', 2483): '$SUCCESS\n'
('127.0.0.1', 2484): '$Name=TEST-Device\n'
('127.0.0.1', 2483): '$<end>\n'
('127.0.0.1', 2484): '$Ext=Test connection\n'
('127.0.0.1', 2482): disconnected
('127.0.0.1', 2484): '$Security=0\n'
('127.0.0.1', 2483): disconnected
('127.0.0.1', 2484): '$Software=xxx\n'
('127.0.0.1', 2484): '$Hardware=xxx\n'
('127.0.0.1', 2484): '$SUCCESS\n'
('127.0.0.1', 2484): '$<end>\n'
('127.0.0.1', 2484): disconnected
Mark Tolonen
  • 166,664
  • 26
  • 169
  • 251