'Is there a way to send message to a specific client in a multithreaded TCP server?

I have made a tcp multithread server which is always listening to new connections and at the same time handling existing clients. If i have 2 clients, lets say A and B. Is there a way to send a message from the server to a specific client either A or B only? Here is my code

import socket
import threading

HEADER = 80
PORT = 9000
SERVER = socket.gethostbyname(socket.gethostname())
ADDR = (SERVER, PORT)
FORMAT = 'utf-8'
DISCONNECT_MESSAGE = "!DISCONNECT"
VITA_POSITIVE = "0000"

server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind(ADDR)


def handle_client(conn, addr):
    print(f"[NEW CONNECTION] {addr} connected.")
    connected = True
    while connected:
        conn.send("VITA".encode(FORMAT))
        vita_response_iconet = conn.recv(HEADER).decode(FORMAT)
        print(vita_response_iconet)
        if vita_response_iconet == VITA_POSITIVE:
                print("VITA received from Iconet")
        else:
                print("VITA not received from Iconet")

    conn.close()


def start():
    server.listen()
    print(f"[LISTENING] Server is listening on {SERVER}")
    while True:
        conn, addr = server.accept()
        print(conn)
        print(addr)
        thread = threading.Thread(target=handle_client, args=(conn, addr))
        thread.start()
        print(f"[ACTIVE CONNECTIONS] {threading.active_count() - 1}")


print("[STARTING] server is starting...")
start()

here is the modified code where i have put in the ip address of the clients and trying to run but i am encountering key error issues

import socket, threading
import time



HEADER = 80
PORT = 9000
SERVER = socket.gethostbyname(socket.gethostname())
ADDR = (SERVER, PORT)
FORMAT = 'utf-8'
DISCONNECT_MESSAGE = "!DISCONNECT"
VITA_POSITIVE = "0000"
my_timer = 0




class ClientThread(threading.Thread):

def __init__(self, conn: socket.socket, addr: str):
    threading.Thread.__init__(self)
    
    self.conn = conn
    self.addr = addr

def send(self, msg: str):
    self.conn.sendall(msg.encode())

def run(self):
    print(f"[NEW CONNECTION] {self.addr} connected.")
    connected = True
    while connected:
        clients["10.14.0.1"].send("VITA".encode(FORMAT))
        vita_response_iconet = clients["10.14.0.1"].recv(HEADER).decode(FORMAT) 
        
        print(vita_response_iconet)
        if vita_response_iconet == VITA_POSITIVE:

                print("VITA received from Iconet")
                vita_iconet = 1  
        
        else:
                print("VITA not received from Iconet")
                vita_iconet = 0
        
        clients["10.14.0.1"].send("VITA".encode(FORMAT))
        vita_response_robot = clients["10.14.0.1"].recv(HEADER).decode(FORMAT)

        print(vita_response_robot)
        if vita_response_iconet == VITA_POSITIVE:

                print("VITA received from Robot") 
                vita_robot = 1
        
        else:
                print("VITA not received from Robot")
                vita_robot = 0


    if vita_iconet and vita_robot == 1:
        my_timer = 0
    else:
          my_timer = my_timer
    


    


        

    self.conn.close()
    


def countup():
global my_timer  
for x in range(1, my_timer+1):
time.sleep(1)
countup_thread = threading.Thread(target=countup)
countup_thread.start()


def start():
server.listen()
print(f"[LISTENING] Server is listening on {SERVER}")
while True:
    conn, addr = server.accept()
    print('interesting')
    print(conn)
    print(addr)
    thread = ClientThread(conn, addr)
    print ('be ready')
    thread.start()
    clients[addr] = thread
    print(f"[ACTIVE CONNECTIONS] {threading.active_count() - 2}")





server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind(ADDR)
clients = {}

connections = threading.Thread(target=start)
connections.start()

print("[STARTING] server is starting...")
start()


Solution 1:[1]

You can do this by creating a class for clients, like this

import socket, threading



HEADER = 80
PORT = 9000
SERVER = socket.gethostbyname(socket.gethostname())
ADDR = (SERVER, PORT)
FORMAT = 'utf-8'
DISCONNECT_MESSAGE = "!DISCONNECT"
VITA_POSITIVE = "0000"



class ClientThread(threading.Thread):

    def __init__(self, conn: socket.socket, addr: str):
        threading.Thread.__init__(self)
        
        self.conn = conn
        self.addr = addr

    def send(self, msg: str):
        self.conn.sendall(msg.encode())

    def run(self):
        print(f"[NEW CONNECTION] {self.addr} connected.")
        connected = True
        while connected:
            self.conn.send("VITA".encode(FORMAT))
            vita_response_iconet = self.conn.recv(HEADER).decode(FORMAT)
            print(vita_response_iconet)
            if vita_response_iconet == VITA_POSITIVE:

                    print("VITA received from Iconet") 
            
            else:
                    print("VITA not received from Iconet")

            

        self.conn.close()



def start():
    server.listen()
    print(f"[LISTENING] Server is listening on {SERVER}")
    while True:
        conn, addr = server.accept()
        print(conn)
        print(addr)
        thread = ClientThread(conn, addr)
        thread.start()
        clients[addr] = thread
        print(f"[ACTIVE CONNECTIONS] {threading.active_count() - 1}")





server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind(ADDR)
clients = {}

print("[STARTING] server is starting...")
start()

Now, you can use clients[client_address].send(data_you_want_to_send) to send a message to only this client

EDIT This code technically allows you to send a message to a specific client but does not leave too many places to do so. To solve this problem, you can put the task of accepting connections in a threading.Thread like this

import socket, threading, time



HEADER = 80
PORT = 9000
SERVER = socket.gethostbyname(socket.gethostname())
ADDR = (SERVER, PORT)
FORMAT = 'utf-8'
DISCONNECT_MESSAGE = "!DISCONNECT"
VITA_POSITIVE = "0000"



class ClientThread(threading.Thread):

    def __init__(self, conn: socket.socket, addr: str):
        threading.Thread.__init__(self)

        self.conn = conn
        self.addr = addr

    def send(self, msg: str):
        self.conn.sendall(msg.encode())

    def run(self):
        print(f"[NEW CONNECTION] {self.addr} connected.")
        connected = True
        while connected:
            self.conn.send("VITA".encode(FORMAT))
            vita_response_iconet = self.conn.recv(HEADER).decode(FORMAT)
            print(vita_response_iconet)
            if vita_response_iconet == VITA_POSITIVE:

                    print("VITA received from Iconet") 

            else:
                    print("VITA not received from Iconet")



        self.conn.close()



def start():
    server.listen()
    print(f"[LISTENING] Server is listening on {SERVER}")
    while True:
        conn, addr = server.accept()
        print(conn)
        print(addr)
        thread = ClientThread(conn, addr)
        thread.start()
        clients[addr] = thread
        print(f"[ACTIVE CONNECTIONS] {threading.active_count() - 1}")





server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind(ADDR)
clients = {}

connections = threading.Thread(target=start)
connections.start()

print("[STARTING] server is starting...")
time.sleep(0.5)

while True:
    client = input("Client you want to send a message : ") # Enter the address of the client you want to send a message
    
    if client not in clients.keys():
        print("This client is not connected")
        continue

    message = input("Message you want to send : ")

    clients[client].send(message)

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1