Multiclient server in Python - how to broadcast? - python

I found online a simple multiclient echo server in Python using threads:
#!/usr/bin/env python
import socket
import sys
import threading
class Client(threading.Thread):
def __init__(self, ip, port, connection):
threading.Thread.__init__(self)
self.connection = connection
self.ip = ip
self.port = port
def run(self):
data = self.connection.recv(1024)
if data :
self.connection.sendall(data)
else :
self.connection.close()
class Server:
def __init__(self, ip, port):
self.ip = ip
self.port = port
self.address = (self.ip, self.port)
self.server = None
self.clients = []
def open_socket(self):
try:
self.server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.server.bind(self.address)
except socket.error, e:
if self.server:
self.server.close()
sys.exit(1)
def run(self):
self.open_socket()
self.server.listen(5)
while True :
connection, (ip, port) = self.server.accept()
c = Client(ip, port, connection)
c.start()
self.clients.append(c)
self.server.close()
if __name__ == '__main__':
s = Server('127.0.0.1', 6666)
s.run()
And I wrote a client:
import socket
import sys
port = 6666
size = 1024
s = None
try:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
host = socket.gethostname()
s.connect(('127.0.0.1', port))
except socket.error, (value, message):
if s:
s.close()
print "Could not open socket: " + message
sys.exit(1)
data = raw_input('> ')
s.sendall(data)
data = s.recv(size)
print "Server sent: %s " % data
s.close()
Everything works great but I wonder how could I add to the server the functionality of broadcast to be able to echo back the message to all connected clients? Or be able to send a message to a specific client?
I have all clients in server's class, but I do not know how to (and where in the server's code, in Client class or Server class place the code to broadcast and send private messages? Thank you.
EDIT:
New server:
#!/usr/bin/env python
import socket
import sys
import threading
class Client(threading.Thread):
def __init__(self, ip, port, connection):
threading.Thread.__init__(self)
self.connection = connection
self.ip = ip
self.port = port
def run(self):
while True:
data = self.connection.recv(1024)
if data :
self.connection.sendall(data)
else :
break
self.connection.close()
class Server:
def __init__(self, ip, port):
self.ip = ip
self.port = port
self.address = (self.ip, self.port)
self.server = None
self.clients = []
def send_to_all_clients(self, msg):
for client in self.clients :
client.connection.send(msg)
def send_to_client(self, ip, port, msg):
for client in self.clients :
if client.ip == ip and client.port == port :
client.connection.send(msg)
def open_socket(self):
try:
self.server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.server.bind(self.address)
except socket.error, e:
if self.server:
self.server.close()
sys.exit(1)
def run(self):
self.open_socket()
self.server.listen(5)
while True :
connection, (ip, port) = self.server.accept()
c = Client(ip, port, connection)
c.start()
self.clients.append(c)
self.server.close()
if __name__ == '__main__':
s = Server('127.0.0.1', 6666)
s.run()
New client:
import socket
import sys
port = 6666
size = 1024
s = None
try:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
host = socket.gethostname()
s.connect(('127.0.0.1', port))
except socket.error, (value, message):
if s:
s.close()
print "Could not open socket: " + message
sys.exit(1)
while True:
data = raw_input('> ')
s.sendall(data)
data = s.recv(size)
print "Server sent: %s " % data
s.close()

Since you already have a list of clients in your Server , you could use it in a function like that :
def send_to_all_clients(self, msg):
for client in self.clients :
client.connection.send(msg)
Then you can make a function to select a specific client :
def send_to_client(self, ip, port, msg):
for client in self.clients :
if client.ip == ip and client.port == port :
client.connection.send(msg)
Note
It's best to add a Send() method to Client and call it to send the msg instead of using client.connection.send(msg)
You could use those functions in the server's run method , if you modify it to handle events
( you could do that with select.select() , or with user input ( KeyboardInterrupt ) , etc , the design is up to you ) .
Also you should modify both client.py and Client , and make them more persistent , because now they close as soon as they sendall or recv .
I hope this gives you some ideas

Related

BlockingIOError: [Errno 11] Resource temporarily unavailable two clients one server sockets python

I am trying to simply send messages from two client sockets to one server socket. When I start up the server socket, it works fine, and then when I start up each of the two client sockets (I'm using 3 different terminal windows) everything works fine. If I start up the server and one client, I can send messages from the client to the server. If I then start up the second client, I can send messages from the second client to the server, however now if I go back and try to send messages from the first client to the server, I get a " BlockingIOError: [Errno 11] Resource temporarily unavailable ". What is the problem?
clientclass.py:
from itertools import count
import select
import random
import threading
import time
class Client(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
self.host = "127.0.0.1"
self.port = 65432
def run(self):
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.id = random.randrange(0, 1000)
print(f"client id {self.id} connected")
self.sock.connect((self.host, self.port))
while True:
text = input('m: ')
self.sock.sendall(text.encode())
server.py:
import socket
from itertools import count
import select
from _thread import start_new_thread
import threading
import time
from queue import Queue
class server(threading.Thread):
def __init__(self):
self.host = "127.0.0.1"
self.port = 65432
threading.Thread.__init__(self)
def run(self):
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
self.sock.bind((self.host, self.port))
self.sock.listen(5)
self.sock.setblocking(0)
print(f"server instantiated")
inputs = [self.sock]
outputs = []
message_queues = {}
while inputs:
inputready,outputready,exceptready = select.select(inputs,outputs,inputs)
for input_item in inputready:
if input_item is self.sock:
conn, addr = self.sock.accept()
print("New connection from: ", addr)
conn.setblocking(0)
inputs.append(conn)
else:
data = conn.recv(1024)
if data:
print("Client: " + data.decode())
#message_queues[input_item].put(data)
if input_item not in outputs:
outputs.append(input_item)
else:
if input_item in outputs:
outputs.remove(input_item)
inputs.remove(input_item)
input_item.close()
class Message_Sender(threading.Thread):
def __init__(self, client):
threading.Thread.__init__(self)
self.client = client
def run(self):
while True:
text = input('m: ')
server.conn.sendall(text.encode())
server = server()
server.start()
msg = Message_Sender(server)
msg.start()
client instances:
import clientclass
client = clientclass.Client()
client.start()
I saw your code and I think I have solved the problem you might have
I changed some code In a way It receives multiple clients at the same time
server.py:
import socket
from itertools import count
import select
from _thread import start_new_thread
import threading
import time
from queue import Queue
class server(threading.Thread):
def __init__(self):
self.host = "127.0.0.1"
self.port = 65432
threading.Thread.__init__(self)
def run(self):
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
self.sock.bind((self.host, self.port))
print(f"server instantiated")
inputs = [self.sock]
outputs = []
message_queues = {}
while True:
self.sock.listen(5)
conn, addr = self.sock.accept()
receive = threading.Thread(target=self.receive_data, args=(conn,))
receive.start()
inputs.append(conn)
def receive_data(self, conn):
while True:
data = conn.recv(1024)
if data:
print("Client: " + data.decode())
# do something
class Message_Sender(threading.Thread):
def __init__(self, client):
threading.Thread.__init__(self)
self.client = client
def run(self):
while True:
text = input('m: ')
server.conn.sendall(text.encode())
server = server()
server.start()
msg = Message_Sender(server)
msg.start()
I have made a loop inside the run function, when someone tries to connect it will make a new thread to receive their messages. it wasn't clear to me what you wanted to do with the variables inputs and input_item, ect. but I think you can get really far with this. If you still have any problems feel free to reach out.

How to make a fast continuous stream of data with socket on Python?

I am writing a code where an arduino reads data from an accelerometer, passes it to a python script and then that computer running python sends the data to another computer over a socket conneciton. I have written code which can send and recieve one message at a time, slowly, but as soon as I do it bascially as fast as I can (about 100Hz) then the server side only prints the first message, but no more.
Here is the code for my server side:
import socket
class server:
def __init__(self, PORT=9077, MAX_CONNECTIONS=1000, BUFF_SIZE=1024):
self.s = socket.socket()
self.HOST = socket.gethostbyname(socket.gethostname())
self.PORT = PORT
self.MAX_CONNECTIONS = MAX_CONNECTIONS
self.BUFF_SIZE = BUFF_SIZE
self.s.bind((self.HOST, self.PORT))
self.s.listen(self.MAX_CONNECTIONS)
self.recievingData = False
print("Starting a server")
print("IP: " + str(self.HOST))
print("Port: " + str(self.PORT))
def recieveData(self):
self.recievingData = True
while self.recievingData:
print("Waiting for data")
c, addr = self.s.accept()
print(addr)
data = b''
part = c.recv(self.BUFF_SIZE)
data += part
while len(part) > self.BUFF_SIZE:
# print("looping")
part = c.recv(self.BUFF_SIZE)
print(len(part))
data += part
print(data)
c.close()
def stopRecieving(self):
self.revievingData = False
new_server = server()
new_server.recieveData()
and the client side:
class client:
def __init__(self, HOST="192.168.0.51", PORT=9077):
self.s = socket.socket()
self.HOST = HOST
self.PORT = PORT
self.s.connect((self.HOST, self.PORT))
def send_message(self, message):
message = message.encode()
sent = self.s.sendall(message)
print(sent)
and I basically just call send_message every time there is new data to send.
Is there a way to increase the speed at which the server can recieve messages from the same client? Do I need to create multiple threads for recieving data?
My solution to this was using DTP protocol, where you send messages without checking if the data has been recieved. This is achieved using socket.socket(type=SOCK_DGRAM) on the client and server side and using self.s.recvfrom(self.BUFF_SIZE) for recieving data from a client and self.s.sendto(str(message).encode(), (host, port)) for sending data. This way, there is no handshake between the client and the server and it runs much faster.
You just need some error checking from your input data.
For reference, this is my full updated code:
import socket
from socket import SOCK_DGRAM, SO_REUSEADDR
import numpy as np
import threading
class client:
def __init__(self, HOST="192.168.0.51", PORT=9077):
self.s = socket.socket(type=SOCK_DGRAM)
self.HOST = HOST
self.PORT = PORT
def send_message(self, message):
self.s.sendto(str(message).encode(), (host, port))
class server:
def __init__(self, PORT=9077, BUFF_SIZE=1024):
self.s = socket.socket(type=SOCK_DGRAM)
self.HOST = socket.gethostbyname(socket.gethostname())
self.PORT = PORT
self.MAX_CONNECTIONS = MAX_CONNECTIONS
self.BUFF_SIZE = BUFF_SIZE
self.s.bind((self.HOST, self.PORT))
# self.s.listen(self.MAX_CONNECTIONS)
self.recievingData = False
self.recievedData = np.zeros((1,4))
self.thread = threading.Thread(target=self.recieveData)
self.thread.start()
# self.s.setblocking(0)
self.startRecieving()
print("Starting a server")
print("IP: " + str(self.HOST))
print("Port: " + str(self.PORT))
def startRecieving(self):
self.recievingData = True
self.recievedData = np.zeros((1,4))
self.thread = threading.Thread(target=self.recieveData)
self.thread.start()
print("Started reading data")
def stopRecieving(self):
self.recievingData = False
self.thread.join()
print("Stopped reading data")
def recieveData(self):
self.recievingData = True
while self.recievingData:
# print("Waiting for data")
part, addr = self.s.recvfrom(self.BUFF_SIZE)
# print(part, addr)
data = b''
data += part
while len(part) > self.BUFF_SIZE:
# print("looping")
part = self.s.recvfrom(self.BUFF_SIZE)
# print(len(part))
data += part
self.lastData = data
print(data)
as_float = np.array([[float(x.strip()) for x in data.decode().split(',')]])
self.recievedData = np.vstack((self.recievedData, as_float))

(CHAT ROOM) Usually only one address is allowed using a socket (protocol / network address / port)

I try to create a chat room in oop style, but socket don't work right.
ERROR:
File "D:/Master/Chat/client_chat.py", line 10, in init
self.client.bind(self.SERVER)
OSError: [WinError 10048] Usually only one address is allowed using a socket (protocol / network address / port)
Server part:
import socket
class Server:
def __init__(self):
self.ADDR = "localhost"
self.PORT = 5454
self.SERVER = (self.ADDR, self.PORT)
self.server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.server.bind(self.SERVER)
self.server.listen()
self.conn, self.addr = self.server.accept()
self.message = self.conn.recv(64).decode('utf-8')
print("[SERVER RUNNING]")
def receive_message(self):
return self.message
def main():
print(Server())
if __name__ == '__main__':
main()
Client part:
import socket
class Client:
def __init__(self):
self.ADDR = "localhost"
self.PORT = 5454
self.SERVER = (self.ADDR, self.PORT)
self.client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.client.bind(self.SERVER)
self.client.connect(self.SERVER)
self.msg_inp = input().encode('utf-8')
def send_message(self):
return self.msg_inp
def main():
print(Client())
if __name__ == '__main__':
main()
Changing ports doesn't help!

Using proxy and multiple sockets in python

I'm writing a proxy with tcp connection that listens to multiple ports from a client and forward it to a server.
The problem is that the software hangs on the sock.accept.
Maybe I'm doing a messy logic here, but I need a client that connects to a PC, and that PC connects to another device. So I wrote that small proxy, and I get INVALID ARGUMENT ERROR in socket.accept()
import select
import socket
import threading
class Proxy(object):
def __init__(self, ip, ports):
self._ip = ip
self._sockets = []
self._proxy = {}
for port in ports:
self._proxy[port] = self.add_socket(port)
def add_socket(self, port=None):
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
if port:
# sock.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
sock.bind(('0.0.0.0',port))
self._sockets.append(sock)
return sock
def get_client(self, src_sock, src_addr):
src_ip, src_port = src_addr
_, dst_port = src_sock.getsockname()
if src_ip == self._ip:
# got packet from device
dst_addr = ("10.8.8.210", dst_port)
else:
# got packet from client
dst_addr = self._ip, dst_port
print(">", src_port, dst_addr)
dst_sock = self._proxy[src_port]
return dst_sock, dst_addr
def run(self):
while True:
read_list, _, _ = select.select(self._sockets, [], [])
if read_list:
for sock in read_list:
try:
conn, addr = sock.accept()
data = conn.recvfrom(16*2024)
# print("got data from {} {}".format(sock, addr))
dst_sock, dst_addr = self.get_client(sock, addr)
# print("forwarding data from {} to {}".format(addr, dst_addr, len(data)))
dst_sock.sendto(data, dst_addr)
except:
raise # pass # print("no recipient for data")
for s in self._sockets:
s.close()
ports = [30001,30002,30003, 30070, 30071,30072,30075]
p = Proxy("192.168.2.10", ports)
p.run()
You have to call listen on the socket before accept:
adding sock.listen(1) to add_socket after bind
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.bind(('0.0.0.0', port))
sock.listen(1)
self._sockets.append(sock)
then allows you to call accept without the error. You may want to set the listen backlog to a greater number.

Python Checking UDP message

this is my client:
import socket
import threading
class ThreadedServer(object):
def __init__(self, host, port):
self.host = host
self.port = port
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
self.sock.bind((self.host, self.port))
def listen(self):
self.sock.listen(5)
while True:
client, address = self.sock.accept()
client.settimeout(60)
threading.Thread(target = self.listenToClient,args = (client,address)).start()
def listenToClient(self, client, address):
size = 1024
while True:
try:
data = client.recv(size)
if data:
# Set the response to echo back the recieved data
response = data
client.send(response)
else:
raise error('Client disconnected')
except:
client.close()
return False
if __name__ == "__main__":
ThreadedServer('',5000).listen()
I'm trying parse the message received. I have tried the following:
if data:
if data == "command":
print("Command Received")
elif data != "command":
response = data
client.send(response)
else:
raise error('Client disconnected')
I have printed data to console and it comes out as b'command', I have tried using that in the equal to string and it still does not print 'Command receieved' when I send it from my client which just connects to the server and sends a message doing the following mySocket.send(message.encode()) I was wondering how I could fix my issue?
Just found the solution: you have to decode the UDP message when printing it.
Example:
print data.decode()

Categories

Resources