Running server and client python scripts from a single python script or GUI button

Viewed 625

I have been working with python server and client communication. In order to establish a connection, usually, the server needs to run 1st and then client from separate python scripts.

What I want now is to make it automated. I want to run both server and client from a single python script or a GUI button. I have been trying different ways like multiprocessing and multithreading but it is not working.

Please advice.

Sample server and client codes below:

Server.py

import socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind(('0.0.0.0', 5005))
s.listen(1)
conn, addr = s.accept()
while 1:
    data = conn.recv(1024)
    print("received data",list(data))
    if not data:
        break
    conn.sendall(data)
conn.close()

Client.py

import socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('0.0.0.0', 5005))
message = bytearray([1])
s.sendall(message)
data = s.recv(1024)
s.close()
print ('Received', repr(data))
2 Answers

We can handle it by assigning two different functions. A few notes:

  • When two different threads are trying to print something in a same stream, they should acquire a common lock first so that they print something in order.
  • Server starts listening on "0.0.0.0" means it is ready to accept a connection from anywhere. On the other hand, when you're running the client side on your machine, you should connect to localhost or 127.0.0.1 instead of connecting to 0.0.0.0.
import threading
import socket

stream_lock = threading.Lock()

def server_func():
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.bind(('0.0.0.0', 5005))
    s.listen(1)
    conn, addr = s.accept()
    while 1:
        data = conn.recv(1024)

        # Getting printing stream lock is important
        stream_lock.acquire()
        print("received data", list(data))
        stream_lock.release()
        if not data:
            break
        conn.sendall(data)
    conn.close()

def client_func():
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.connect(('127.0.0.1', 5005))
    message = bytearray([1, 2, 3])
    s.sendall(message)
    data = s.recv(1024)
    s.close()
    stream_lock.acquire()
    print('Received', repr(data))
    stream_lock.release()

t_server = threading.Thread(target=server_func).start()
t_client = threading.Thread(target=client_func).start()

# Output:
# received data [1, 2, 3]
# Received b'\x01\x02\x03'

i convert @aminrd 's answer to class object and add some code to store received data from client also added some comments for who need to understand each step

import socket
import threading
import json
import time


class Communication:
    stream_lock = threading.Lock()  # creating thread lock object

    def __init__(self):
        self.clientSocket = None
        self.serverSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)  # create socket and store
        self.STORED_DATA = {"function_name": "", "data": ""}  # sample stored data
        self.th = []  # threads list

    def server(self):
        self.serverSocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR,
                                     1)  # make reusing adress with different port
        self.serverSocket.bind(('0.0.0.0', 5005))  # bind socket with port
        self.serverSocket.listen(2)  # permitted client number
        print("Server is listening")
        while True:
            client, address = self.serverSocket.accept()  # welcome new client
            # send client data to new thread and process
            self.th.append(threading.Thread(target=self.listener, daemon=True, args=(client,)).start())

    def listener(self, client):
        while True:
            data = client.recv(2048)  # receive new data to store
            if data:
                # you can get data from any client and puch data to all clients(multiple status monitors)
                data_variable = json.loads(data.decode('utf-8'))  # decode byte to dict
                self.stream_lock.acquire()  # i'm not sure about usage of lock. wrong place ?
                if 'command' in data_variable and data_variable['command'] == 'write':
                    self.STORED_DATA = data_variable
                else:
                    # or you can do some server stuff and manipulate data
                    self.STORED_DATA['function_name'] = 1
                    self.STORED_DATA['data'] = 1
                self.stream_lock.release()
                data_string = json.dumps(self.STORED_DATA).encode('utf-8')  # convert stored data to bytes
                client.sendall(data_string)  # now send stored data to all clients

    def client(self):
        self.clientSocket.connect(('127.0.0.1', 5005))  # connect to server

    # this function is callable from outside of class
    # you can give new data to client to send to server
    def receive(self, new_data_string):
        self.clientSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)  # create socket for client
        self.clientSocket.connect(('127.0.0.1', 5005))  # connect to server
        data_string = json.dumps(new_data_string).encode('utf-8')  # convert data to bytes
        self.clientSocket.send(data_string)  # send to server
        data = self.clientSocket.recv(2048)  # receive server data
        if not data:
            print("no data")
            self.clientSocket.close()
        data_variable = json.loads(data.decode('utf-8'))  # convert data to dict
        self.stream_lock.acquire()
        self.stream_lock.release()
        self.clientSocket.close()
        return data_variable  # return received data


if __name__ == "__main__":
    x = Communication()
    t_server = threading.Thread(target=x.server, daemon=True).start()
    read = {"command": "read", "function_name": "", "data": ""}
    result = x.receive(read)
    print('Client Received', repr(result))
    time.sleep(1)
    write = {"command": "write", "function_name": "2", "data": "2"}
    result = x.receive(write)
    print('Client Received', repr(result))


Related