I'm in the middle of a project that uses peer-to-peer and server-client architecture. I have a server that communicates with another device through sockets (I have three sockets for both server and device). Each socket is responsible for a certain type of information. I also need the tasks associated with each socket to work in parallel.
Let me explain further: I have a device (GPU) that is processing images. This GPU will need to send to server three different things in parallel. So I'm using three threads and three sockets. On the server side I have three threads("listeners") that will need to connect to the three GPU "clients". After there are three connections, three more threads are open one for each socket and task. Basically the gpu tries to connect to the server (3 sockets) and when the connections are made, each listener creates another thread for the task. When I open the Flask application everything works fine, the info is driven from server to browser without problems. But when I open the app on another tab or browser or computer, the server part hangs. At this point only one task is working on server side.
I would like to give you some code, but this is company property so I can't.
I'll give you a pseudo-code.
if __name__=="__main__"
threading.Thread(target=app.listen1).start()
app.run(threaded)
def listen1():
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s1, socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s2, socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s3:
s1.bind((HOST, PORT))
s1.listen()
s2.bind((HOST, PORT2))
s2.listen()
s3.bind((HOST, PORT3))
s3.listen()
while True:
conn1, addr1 = s1.accept()
conn2, addr2 = s2.accept()
conn3, addr3 = s3.accept()
ident = json.loads(conn1.recv(1024))
if db.existStream(ident['id'])==True:
conn1.send(b'True')
threading.Thread(target=client1, args=(conn1, addr1)).start()
conn2.send(b'True')
threading.Thread(target=client2, args=(conn2, addr2)).start()
conn3.send(b'True')
threading.Thread(target=client3, args=(conn3, addr3)).start()
else:
conn1.send(b'False')
conn2.send(b'False')
conn3.send(b'False')
def client1(conn, addr):
buffer1=b''
while True:
length= int.from_bytes(conn.recv(1024), 'big')
if length==0:
break;
conn.send(b"ACK")
while len(buffer1)<length:
data = conn.recv(262144)
buffer1 += data
buffer2=json.loads(buffer1.decode('utf-8'))
overlay(buffer2['frame'], buffer2['sectorX'])
if 'confidence' in buffer2:
db.guardaFrame(buffer2['frame'], buffer2['sectorX'], buffer2['sectorY'], buffer2['timestamp'], buffer2['azimuth'], buffer2['elevation'])
db.guardaAlerta(buffer2['frame'], buffer2['sectorX'], buffer2['sectorY'], buffer2['timestamp'], buffer2['azimuth'], buffer2['elevation'], buffer2['confidence'])
else:
db.guardaFrame(buffer2['frame'], buffer2['sectorX'], buffer2['sectorY'], buffer2['timestamp'], buffer2['azimuth'], buffer2['elevation'])
buffer1=b''
def client2(conn, addr):
buffer1=b''
while True:
length= int.from_bytes(conn.recv(1024), 'big')
if length==0:
break;
conn.send(b"ACK")
while len(buffer1)<length:
data = conn.recv(262144)
buffer1 += data
global Frame
Frame= b'--frame\r\n' b'Content-Type: image/jpeg\r\n\r\n' + buffer1 + b'\r\n'
buffer1=b''
def client3(conn, addr):
buffer1=b''
while True:
length= int.from_bytes(conn.recv(1024), 'big')
if length==0:
break;
conn.send(b"ACK")
while len(buffer1)<length:
data = conn.recv(1024)
buffer1 += data
buffer2=json.loads(buffer1.decode('utf-8'))
global azimuth
azimuth=buffer2['azimuth']
So, in conclusion, everything works fine when the user only opens a browser window. When the user opens on another computer (of the same network), on in another tab, the clients hang and don't do their tasks (except one). I hope this is enough for you to understand.
Thanks
EDIT: Forgot to mention, the code should be able to accept more equal devices. I mean I need to "listen" for more connections.
EDIT2: So I decided to show the code for better understanding. The "If ..." is on another file.
Wrapping your threading in a class is a bad idea, since the instantiation of the class will force it to be single threaded.
class Server:
def __init__(self, port):
self.port = port
def client(self):
task
def listen(self):
socket created
while true:
do stuff
break
return
if __name__=="__main__"
ports = [123,124,125]
threads = [for p in ports in threading.Thread(Server(p).listen)]
threads.join()
Here is a non functioning example of how gevent works to make this async:
import gevent
from gevent import monkey, spawn
monkey.patch_all()
def listener(HOST, PORT, client):
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s
s.bind((HOST, PORT))
s.listen()
while True:
conn, addr = s.accept()
ident = json.loads(conn.recv(1024))
if db.existStream(ident['id']) == True:
conn.send(b'True')
spawn(client,conn, addr)
else:
conn.send(b'False')
def client1(conn, addr):
buffer1 = b''
while True:
length = int.from_bytes(conn.recv(1024), 'big')
if length == 0:
break;
conn.send(b"ACK")
while len(buffer1) < length:
data = conn.recv(262144)
buffer1 += data
buffer2 = json.loads(buffer1.decode('utf-8'))
overlay(buffer2['frame'], buffer2['sectorX'])
if 'confidence' in buffer2:
db.guardaFrame(buffer2['frame'], buffer2['sectorX'], buffer2['sectorY'], buffer2['timestamp'],
buffer2['azimuth'], buffer2['elevation'])
db.guardaAlerta(buffer2['frame'], buffer2['sectorX'], buffer2['sectorY'], buffer2['timestamp'],
buffer2['azimuth'], buffer2['elevation'], buffer2['confidence'])
else:
db.guardaFrame(buffer2['frame'], buffer2['sectorX'], buffer2['sectorY'], buffer2['timestamp'],
buffer2['azimuth'], buffer2['elevation'])
buffer1 = b''
def client2(conn, addr):
buffer1 = b''
while True:
length = int.from_bytes(conn.recv(1024), 'big')
if length == 0:
break;
conn.send(b"ACK")
while len(buffer1) < length:
data = conn.recv(262144)
buffer1 += data
global Frame
Frame = b'--frame\r\n' b'Content-Type: image/jpeg\r\n\r\n' + buffer1 + b'\r\n'
buffer1 = b''
def client3(conn, addr):
buffer1 = b''
while True:
length = int.from_bytes(conn.recv(1024), 'big')
if length == 0:
break;
conn.send(b"ACK")
while len(buffer1) < length:
data = conn.recv(1024)
buffer1 += data
buffer2 = json.loads(buffer1.decode('utf-8'))
global azimuth
azimuth = buffer2['azimuth']
if __name__ == "__main__":
hosts = (('host1','80',client1),('host2','80',client2),('host3','80',client3))
threads = [spawn(listener, h,p,c) for h,p,c in hosts]
gevent.joinall(threads)
Related
my server sends objects much faster than the client receives. That's why the client receives the objects that were sent 10 seconds ago because the client has to receive every object that is in the queue. So my question is: How do I slow the server down or make my client receiving faster?
Edit: I added time.sleep(0.1) to the server but is there a more efficient way?
This is my Network class:
import socket
import pickle
class Network:
def __init__(self):
self.client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.server = "192.168.1.15"
self.port = 5555
self.addr = (self.server, self.port)
self.player = self.connect()
def get_player(self):
return self.player
def connect(self):
try:
self.client.connect(self.addr)
except:
pass
def send_object(self, data):
self.client.send(pickle.dumps(data))
def get_object(self):
return pickle.loads(self.client.recv(1024))
def send_msg(self, data):
self.client.send(bytes(data, "utf-8"))
def get_msg(self):
return self.client.recv(1024).decode("utf-8")
and my server when it's sending:
p_count = conn.recv(1024).decode("utf-8")
if p_count:
games.append(Game(game_id, int(p_count), ip))
game_id += 1
while True:
conn.send(pickle.dumps(games[-1]))
and my client when it's receiving:
def update(self):
global bg_surface
try:
self.game = n.get_object()
self.player_in = self.game.players_ready
bg_surface = pygame.image.load('./bilder/background3.jpg').convert()
bg_surface = pygame.transform.scale(bg_surface, (screen_width, screen_height))
self.warte_text = Label(f"Warte auf Spieler {self.player_in}/{self.player_amount}", 20, (screen_width // 2, screen_height // 2 * 0.25), (255, 0, 0))
self.warte_text.update(bg_surface)
except:
pass
The efficient way to handle this is to have your server call select() or poll() on the socket to find out when the socket has room in its outgoing-data-buffer (and optionally block until there is room) and only generate and send() data to the socket when the socket has indicated it is ready-for-write. That way your server-thread doesn't get blocked waiting for the client to read data, no matter how slow the client is at reading.
im not too bad at python, and ive tried to make a threaded socket client, but ive hit a problem in my code that i cant solve. Here is the code:
import socket
import threading
class ThreadedServer(object):
def __init__(self, host, port, num):
self.num = num
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(1)
while True:
client, address = self.sock.accept()
client.settimeout(600)
threading.Thread(target = self.listenToClient,args = (client,address)).start()
def listenToClient(self, client, address):
size = 1024
# fix this, please
if self.num == 1:
print("Client 1 connected")
if self.num == 2:
print("Client 2 connected")
if self.num == 3:
print("Client 3 connected")
while True:
data = client.recv(size)
data = data.decode("utf-8")
print("Client", self.num, ": ", data)
# Reply
if data == "lad":
response = "nice"
else:
response = data
client.send(response.encode('utf-8'))
if __name__ == "__main__":
while True:
host = input("Host? ")
port_num = input("Port? ")
try:
port_num = int(port_num)
print("Please connect client")
break
except ValueError:
pass
ThreadedServer(host,port_num, 1).listen()
ThreadedServer(host,port_num, 2).listen()
ThreadedServer(host,port_num, 3).listen()
ThreadedServer.listenToClient()
You see, when i run this, i also connect it to a rather simple client, which sends me data.
In this code, the data i receive is amply called 'data'.
The problem i have in this code is that in the section where i repeat 'if self.num:', i plan for it to give me the following code when i connect 3 client:
Client 1 connected
Client 2 connected
Client 3 connected
but instead it simply repeats 'Client 1 connected' 3 times.
It would be great if anyone could try to solve this problem for me, thanks in advance
Ps: If anyone would like the client too, just ask for it, and i will edit this post to add it in.
You get correct output according to the code you run. Your code has two problems. The first problem is in the main suite. Look at the line ThreadedServer(host,port_num, 1).listen(). It says: create object of class ThreadServer and call method listen() which runs forever (listens to connections forever). You will never start 2 more servers ThreadedServer(host,port_num, 2).listen() and ThreadedServer(host,port_num, 3).listen(). This is good but should be fixed. So you have 1 server which can accept multiple connections. But you count servers instead of clients (this is the second problem which gives you the output you do not expect).
The solution is the following.
Run only 1 server (1 object of class ThreadedServer).
Add counter to the method listen():
def listen(self):
counter = 0
self.sock.listen(1)
while True:
client, address = self.sock.accept()
client.settimeout(600)
counter += 1
threading.Thread(
target=self.listenToClient,
args=(client, address, counter)).start()
Change method listenToClient() a little bit:
def listenToClient(self, client, address, client_id):
size = 1024
print("Client {} connected".format(client_id))
while True:
data = client.recv(size)
data = data.decode("utf-8")
print("Client", self.num, ": ", data)
# Reply
if data == "lad":
response = "nice"
else:
response = data
client.send(response.encode('utf-8'))
And you will get output:
Client 1 connected
Client 2 connected
Client 3 connected
I am writing a simple threaded server that will send a message to all clients. I have an object that is reset after posting the change message, however I am having a hard time figuring out how to reset that object only after all threads have posted the change message.
To add some context to the problem. I am building a multi user Tkinter python app which connects to a remote database to retrieve information and the application needs to know when data changes so that when a user updates data, all other running instances of the app will get the update. From what I understand, MySQL does not support asynchronous application updates. Instead of running a query every 5 seconds on the database to see if there is a change, I am putting this code server side so that it will send a message to a socket on the client that a change has occurred on the database.
The main loop is just a dummy that will simulate a change
Here is my code:
import socket, threading, time, select, os
class dbMonitor:
isDBAltered = False
def postChange(self):
self.isDBAltered = True
def __str__(self):
return str(self.isDBAltered)
class ThreadedServer(object):
def __init__(self, port,dbMon):
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.setblocking(0)
self.sock.bind((socket.gethostname(), self.port))
self.dbMon = dbMon
def listen(self):
self.sock.listen(100)
read_list = [self.sock]
while True:
read,write,error = select.select(read_list,[],[],1)
for s in read:
if s is self.sock:
client, address = self.sock.accept()
client.settimeout(60)
threading.Thread(target = self.listenToClient, args = (client,address)).start()
def listenToClient(self, client, address):
read_list = [client]
size = 1024
while True:
response = b'Ack'
if self.dbMon.isDBAltered:
response = b'CHANGE'
try:
client.send(response)
except:
client.close()
return False
self.dbMon.isDBAltered = False
read,write,error = select.select(read_list,[],[],1)
for s in read:
if s is client:
try:
data = client.recv(size)
print(data)
if data:
client.send(response)
else:
raise error('Client disconnected')
except:
client.close()
return False
def mainLoop():
while True:
time.sleep(15)
print(dbMon)
dbMon.postChange()
dbMon = dbMonitor()
server = ThreadedServer(5005,dbMon)
threading.Thread(target = mainLoop, args=()).start()
threading.Thread(target = server.listen(), args=()).start()
How do I get self.dbMon.isDBAltered = False to execute only after all threads have executed:
response = b'CHANGE'
try:
client.send(response)
You're trying to synchronize something that's asynchronous... This is massively more complicated than it should be. Your dbmon is only storing a boolean flag... why not just asynchronously modify the "database" instead? For example, if the "database" was a thread-safe buffer, you could just append to that buffer or modify that buffer without synchronizing each thread individually, pull the information written to that buffer and write it to the client socket they belong to in another event loop (this is pretty much what asyncore does)
That said, I have some (probably nonworking, but I hope you get the idea) reference modified code for you to go off of if you want to continue pursing this avenue.
Basically, dbmon will keep a mapping of thread ids to [creation time, modified flag]
Our predicate returns true iff all threads created before a certain threshold have ALL set the modified flag. We set the modified flag when we send the response in the data = client.recv(size) portion of your code. And then we wait on that condition in the server send. We keep notifying all waiting threads on each client receive so that when the condition is finally met, our waiting server threads will all unblock and send the subsequent response.
import socket, threading, time, select, os
import collections
class dbMonitor:
def __init__(self):
self.isDBAltered = {}
self.lock = threading.Lock()
def newThread(self, tid):
self.lock.acquire()
# time of creation, boolean whether that thread has sent response
self.isDBAltered[tid] = [time.time(), False]
self.lock.release()
def threadDone(self, tid):
self.lock.acquire()
self.isDBAltered.pop(tid, None)
self.lock.release()
def altered(self, tid):
self.lock.acquire()
self.isDBAltered[tid][1] = True
self.lock.release()
def reset(self, tid):
self.lock.acquire()
self.isDBAltered(tid)[1] = False
self.lock.release()
def __str__(self):
return str(self.isDBAltered)
class ThreadedServer(object):
def __init__(self, port,dbMon):
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.setblocking(0)
self.sock.bind((socket.gethostname(), self.port))
self.dbMon = dbMon
self.lock = threading.lock()
self.cv = threading.Condition()
self.thresh = 2000
def condition_pred(self):
# unblock if threads currently running for longer than self.thresh have all set their flags
return all([timecreate[1] if time.time() - timecreate[0] > self.thresh else True for tid,timecreate in self.dbMon.isDBAltered])
def listen(self):
self.sock.listen(100)
read_list = [self.sock]
while True:
read,write,error = select.select(read_list,[],[],1)
for s in read:
if s is self.sock:
self.lock.acquire()
client, address = self.sock.accept()
client.settimeout(60)
T = threading.Thread(target = self.listenToClient, args = (client,address)).start()
self.dbmon.newThread(T.ident)
self.lock.release()
def listenToClient(self, client, address):
read_list = [client]
size = 1024
while True:
response = b'Ack'
with self.cv:
self.cv.wait_for(self.condition_pred)
self.dbMon.reset(threading.get_ident())
response = b'CHANGE'
try:
client.send(response)
except:
client.close()
self.dbmon.threadDone(threading.get_ident())
return False
read,write,error = select.select(read_list,[],[],1)
for s in read:
if s is client:
with self.cv:
try:
data = client.recv(size)
print(data)
if data:
client.send(response)
self.dbMon.altered(threading.get_ident())
self.cv.notifyAll()
else:
raise error('Client disconnected')
except:
client.close()
self.dbmon.threadDone(threading.get_ident())
return False
I'm trying to run several sockets on different ports as the following:
Socket:
import socket
class Receiver:
TCP_IP = '127.0.0.1' # by default
TCP_PORT = 1999 # by default
BUFFER_SIZE = 1024
def __init__(self, TCP_IP, TCP_PORT):
self.TCP_IP = TCP_IP
self.TCP_PORT = TCP_PORT
def initialize(self):
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind((self.TCP_IP, self.TCP_PORT))
s.listen(1)
conn, addr = s.accept()
print('Connection address:', addr)
while 1:
data = conn.recv(self.BUFFER_SIZE)
if not data: break
rdata = 'U'.encode() + data
print("received data:", data[1:5])
conn.send(rdata[0:5]) # echo
conn.close()
And Runner:
from NVGEmulator.Receiver import Receiver
import threading
class FireStarter:
def starter(self):
nvgEmu1 = Receiver('127.0.0.1', 2999)
print("FIRST INITIALIZED")
nvgEmu1.initialize()
nvgEmu2 = Receiver('127.0.0.1', 2998)
nvgEmu2.initialize()
print("SECOND INITIALIZED")
def starter_second(self):
nvgEmu2 = Receiver('127.0.0.1', 2998)
print("SECOND INITIALIZED")
nvgEmu2.initialize()
if __name__ == '__main__':
print("Receiver has been started")
fs = FireStarter()
thr = threading.Thread(target=fs.starter())
thr.start()
thr.join()
thr2 = threading.Thread(target=fs.starter_second())
thr2.start()
When I run FireStarter, it runs only the first instance of socket. I've read that there is "threading" library which can run several processes in async, but anyway there is no result, cause in console I see that "FIRST INITIALIZED". How to run the second or the third socket listener? May be there is another approach to do this.
You have two problems in that code.
The first one: here the second socket is waiting for the first one to end as it is trapped in the while loop:
def starter(self):
nvgEmu1 = Receiver('127.0.0.1', 2999)
print("FIRST INITIALIZED")
nvgEmu1.initialize()
nvgEmu2 = Receiver('127.0.0.1', 2998)
nvgEmu2.initialize()
print("SECOND INITIALIZED")
The second one is this join sentence thr.join(), with that you are forcing the second thread to wait for the first one, avoiding to run it in parallel.
Probably the approach I would follow is spawning a thread within the initialize function inside your Receiver class and manage the thread there (maybe extending the Thread class), with that you avoid to spawn by yourself a new thread each time and you have your code more encapsulated.
I have the following python script (a bit inefficient, I know) for a P2P chat program using sockets:
#!usr/bin/env python
import socket import threading import select import time import datetime
def main():
class Chat_Server(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
self.running = 1
self.conn = None
self.addr = None
def run(self):
HOST = ''
PORT = 23647
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind((HOST,PORT))
s.listen(1)
self.conn, self.addr = s.accept()
# Select loop for listen
while self.running == True:
inputready,outputready,exceptready \
= select.select ([self.conn],[self.conn],[])
for input_item in inputready:
# Handle sockets
message = self.conn.recv(1024)
if message:
print "Daniel: " + message + ' (' + datetime.datetime.now().strftime('%H:%M:%S') + ')'
else:
break
time.sleep(0)
def kill(self):
self.running = 0
class Chat_Client(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
self.host = None
self.sock = None
self.running = 1
def run(self):
PORT = 23647
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.sock.connect((self.host, PORT))
# Select loop for listen
while self.running == True:
inputready,outputready,exceptready \
= select.select ([self.sock],[self.sock],[])
for input_item in inputready:
# Handle sockets
message = self.sock.recv(1024)
if message:
print "Daniel: " + message + ' (' + datetime.datetime.now().strftime('%H:%M:%S') + ')'
else:
break
time.sleep(0)
def kill(self):
self.running = 0
class Text_Input(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
self.running = 1
def run(self):
while self.running == True:
text = raw_input('')
try:
chat_client.sock.sendall(text)
except:
Exception
try:
chat_server.conn.sendall(text)
except:
Exception
time.sleep(0)
def kill(self):
self.running = 0
# Prompt, object instantiation, and threads start here.
ip_addr = raw_input('Type IP address or press enter: ')
if ip_addr == '':
chat_server = Chat_Server()
chat_client = Chat_Client()
chat_server.start()
text_input = Text_Input()
text_input.start()
else:
chat_server = Chat_Server()
chat_client = Chat_Client()
chat_client.host = ip_addr
text_input = Text_Input()
chat_client.start()
text_input.start()
if __name__ == "__main__":
main()
This script works absolutely fine across a home network, with 192.168... internal IP addresses.
On a school network, with 172... IP addresses, it doesn't seem to work. There is no connection error, but messages are not sent or received, with the exception of if there are two instances of the application being run on the same computer with the same internal IP address, in which case the program works flawlessly.
I am very new to sockets and this sort of networking, so I am wondering if anyone can point out to me why this is the case. Might it be the difference in IP addresses, for example?
Thank you in advance.
I see you're connecting on port 23647 - you may not have access to this on your school network. Check if this port has traffic enabled. see: Port Forwarding for details.
Something like: this site/tool may allow you to check quickly.