SocketServer Python with multiple requests - python

i'm coding a Python Server (with SocketServer) for my Raspberry Pi.
This server wait for incoming clients and activate any components like Relay and leds from that remote connection.
This is my first project so i'm having some trouble:
in my main i create the object and i send the arraylist of the components i want to control; like led with all his own functions
Server.Server(ComponentList)
while True:
do stuff
in Server.py i launch the server and set some settings
class Server():
def __init__(self, ComponentList):
self.ComponentList = ComponentList
self.current_component = 0
self.server = Socket.ThreadedTCPServer((settings.HOSTNAME,settings.PORT), Socket.ThreadedTCPRequestHandler)
self.server_thread = threading.Thread(target=self.server.serve_forever)
self.server_thread.daemon = True
self.server_thread.start()
self.server.set_componentlist(self.ComponentList)
self.server.set_current_component(self.current_component)
def set_current_component(self, current_component):
self.current_component = current_component
def get_current_component(self):
return self.current_component
def set_componentlist(self, ComponentList):
self.ComponentList = ComponentList
def get_componentlist(self):
return self.ComponentList
finally in Socket.py i coded the real server where are spawning the bugs
class ThreadedTCPRequestHandler(SocketServer.BaseRequestHandler):
def handle(self):
print "Client connected with ", self.client_address
self.request.send("Insert Password\r\n")
if self.request.recv(1024).strip() == settings.PASSWORD:
time.sleep(1)
self.request.send("Correct password\r\n")
try:
while 1:
data = None
self.request.send("---------------------\r\n")
self.request.send("Listening for commands\r\n")
self.request.send("1: Connected devices\r\n")
self.request.send("2: Select devices\r\n")
self.request.send("0: Close connection\r\n")
self.request.send("---------------------\r\n")
data = self.request.recv(1024).strip()
if data is not None:
if data == "1":
self.request.send(str(len(self.server.get_componentlist()))+" Components loaded\r\n")
c=0
for i in self.server.get_componentlist():
self.request.send(str(str(c)+": "+i.get_note()+"\r\n"))
c=c+1
if data == "2":
data = self.request.recv(1024).strip()
self.request.send("Select component id\r\n")
self.server.set_current_component(data)
self.request.send("Selected component: "+data+"\r\n")
if data == "0":
break
finally:
self.request.close()
print "Client exited"
else:
time.sleep(1)
self.request.send("Incorrect password\r\n")
self.request.send("Bye\r\n")
self.request.close()
print "Client unauthorized"
class ThreadedTCPServer(SocketServer.ThreadingMixIn, SocketServer.TCPServer):
allow_reuse_address = True
def set_componentlist(self, ComponentList):
self.ComponentList = ComponentList
def get_componentlist(self):
return self.ComponentList
def set_current_component(self, current_component):
self.current_component = current_component
def get_current_component(self):
return self.current_component
in the loop while, after the password request, i can get the first choice (1,2 or 0) but i can't choose the device, and i can get another value from request.recv(1024).
if data == "2":
data = self.request.recv(1024).strip()
self.request.send("Select component id\r\n")
self.server.set_current_component(data)
self.request.send("Selected component: "+data+"\r\n")
the program skip the data = self.request... keeping data empty
and print the output twice.
i guess i'm missing something and i cannot sending multiple information to the server with one connection.
Thanks

Related

Python: How do I lock an object until all threads have done a thing

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

dynamically adding a resource to a python coap server with coapthon library

I am trying to build a coap server, in which I can add a new resource without the need to stop the server, recode it and restart .my server is suppossed to host two types of resources, "sensors(Sens-Me)" and "Actuators(Act-Me)" . I want that if I press the A key, a new instance of actuator should be added to the server, likewise If i Press S for Sensor .Below is my code :
from coapthon.resources.resource import Resource
from coapthon.server.coap import CoAP
class Sensor(Resource):
def __init__(self,name="Sensor",coap_server=None):
super(Sensor,self).__init__(name,coap_server,visible=True,observable=True,allow_children=True)
self.payload = "This is a new sensor"
self.resource_type = "rt1"
self.content_type = "application/json"
self.interface_type = "if1"
self.var = 0
def render_GET(self,request):
self.payload = "new sensor value ::{}".format(str(int(self.var+1)))
self.var +=1
return self
class Actuator(Resource):
def __init__(self,name="Actuator",coap_server=None):
super(Actuator,self).__init__(name,coap_server,visible=True,observable=True)
self.payload="This is an actuator"
self.resource_type="rt1"
def render_GET(self,request):
return self
class CoAPServer(CoAP):
def __init__(self, host, port, multicast=False):
CoAP.__init__(self,(host,port),multicast)
self.add_resource('sens-Me/',Sensor())
self.add_resource('act-Me/',Actuator())
print "CoAP server started on {}:{}".format(str(host),str(port))
print self.root.dump()
def main():
ip = "0.0.0.0"
port = 5683
multicast=False
server = CoAPServer(ip,port,multicast)
try:
server.listen(10)
print "executed after listen"
except KeyboardInterrupt:
server.close()
if __name__=="__main__":
main()
I am not sure what exactly do you want to do.
Is it just to replace a resource on the same route or add a new one?
Replace a resource
It is not possible according to the current coapthon version source:
https://github.com/Tanganelli/CoAPthon/blob/b6983fbf48399bc5687656be55ac5b9cce4f4718/coapthon/server/coap.py#L279
try:
res = self.root[actual_path]
except KeyError:
res = None
if res is None:
if len(paths) != i:
return False
resource.path = actual_path
self.root[actual_path] = resource
Alternatively, you can solve it in scope of request.
Say, have a registry of handlers which are used by resources and can be changed on a user input event. Well, you'll not be able to add new routes.
If you absolutely need that feature, you may request it from a developer or contribute to that project.
Add a new resource
I have extended your snippet a little bit.
I have a little experience in Python so I an not sure I've made everything properly, but it works.
There is a separate thread polling the user input and adding the same resource. Add the needed code there.
from coapthon.resources.resource import Resource
from coapthon.server.coap import CoAP
from threading import Thread
import sys
class Sensor(Resource):
def __init__(self,name="Sensor",coap_server=None):
super(Sensor,self).__init__(name,coap_server,visible=True,observable=True,allow_children=True)
self.payload = "This is a new sensor"
self.resource_type = "rt1"
self.content_type = "application/json"
self.interface_type = "if1"
self.var = 0
def render_GET(self,request):
self.payload = "new sensor value ::{}".format(str(int(self.var+1)))
self.var +=1
return self
class Actuator(Resource):
def __init__(self,name="Actuator",coap_server=None):
super(Actuator,self).__init__(name,coap_server,visible=True,observable=True)
self.payload="This is an actuator"
self.resource_type="rt1"
def render_GET(self,request):
return self
class CoAPServer(CoAP):
def __init__(self, host, port, multicast=False):
CoAP.__init__(self,(host,port),multicast)
self.add_resource('sens-Me/',Sensor())
self.add_resource('act-Me/',Actuator())
print "CoAP server started on {}:{}".format(str(host),str(port))
print self.root.dump()
def pollUserInput(server):
while 1:
user_input = raw_input("Some input please: ")
print user_input
server.add_resource('sens-Me2/', Sensor())
def main():
ip = "0.0.0.0"
port = 5683
multicast=False
server = CoAPServer(ip,port,multicast)
thread = Thread(target = pollUserInput, args=(server,))
thread.setDaemon(True)
thread.start()
try:
server.listen(10)
print "executed after listen"
except KeyboardInterrupt:
print server.root.dump()
server.close()
sys.exit()
if __name__=="__main__":
main()

Python socket programming with multiple threads

I'm trying to use sockets in python. For now, I'm trying to get it such that if any client sends any message it is received at all clients. However I'm getting very weird results. I think it's because I'm using multiple threads. The output of the program changes every time I run it. Is this a threading problem or is it something else?
import socket
import sys
from thread import *
from server import Server
from client import Client
s = Server()
start_new_thread(s.acceptConnection,())
m = Client("m")
k = Client("k")
start_new_thread(m.recieveData,())
start_new_thread(k.recieveData,())
k.sendData("Hey!")
print "*"*100
print repr(k.data()), repr(m.data())
print "*"*100
m.sendData("okay okay")
print "*"*100
print repr(k.data()), repr(m.data())
print "*"*100
m.client.close()
k.client.close()
s.s.close()
Server Class:
import socket
import sys
from thread import *
class Server(object):
def __init__(self,port = 5555):
self.host = 'localhost' # '' means connect to all hosts
self.port = port
self.text = ""
self.s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
try:
self.s.bind((self.host, self.port))
except socket.error as e:
print(str(e))
self.s.listen(2)
print "Waiting for a connection.\n"
self.connections = []
def threaded_client(self,conn):
# conn.send("Connected to server\n")
while True:
try:
data = conn.recv(2048)
except:
data = ""
if(not data):
break
# conn.sendall(reply)
for c,a in self.connections:
try:
c.sendall(data + "\n")
except:
print "connection lost\n"
self.connections.remove((c,a))
conn.close()
def acceptConnection(self):
while True:
conn, addr = self.s.accept()
self.connections += [(conn,addr)]
start_new_thread(self.threaded_client,(conn,))
Client class:
import socket
import sys
from thread import *
class Client(object):
def __init__(self,name):
self.host = 'localhost'
self.port = 5555
self.name = name
self.client= socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.client.connect((self.host,self.port))
self.text = ""
def sendData(self,data):
self.client.send(data)
def recieveData(self):
while True:
try:
data = self.client.recv(2048)
except:
break
if data:
self.text = data
self.client.close()
def data(self):
return self.text
def closeClient(self):
self.client.close()
Anyway you have no warrants about the data was already come back to the clients when you try to print it. You should introduce some Conditions and use wait() and notifyAll() to make sure the data was arrive.... To check if my guess is correct put some sleep() in your test:
import time
k.sendData("Hey!")
print "*"*100
time.sleep(200)
print repr(k.data()), repr(m.data())
print "*"*100
m.sendData("okay okay")
print "*"*100
time.sleep(200)
print repr(k.data()), repr(m.data())
print "*"*100
If it works you should use conditions and notify to do your tests.
Moreover you must protect data access by a Lock().
def recieveData(self):
while True:
try:
data = self.client.recv(2048)
except:
break
if data:
self.l.acquire()
self.text = data
self.l.release()
self.client.close()
def data(self):
self.l.acquire()
ret = self.text
self.l.release()
return ret
Where attribute l of clients are defined in __init__ by
self.l=threading.Lock()

PySerial presenting strange behavior in Python 3

I have the following class (very basic) for communication with 3G modem through AT commands:
import serial
import time
class ATCommands(object):
def __init__(self, port):
self.ser = None
self.port = port
self.open()
def open(self):
self.ser = serial.Serial('/dev/'+ self.port, 115200, timeout=1)
def sendCommand(self,command):
self.ser.write(command.encode())
data = self.ser.readline().strip()
return data
def getIMEI(self):
IMEI = self.sendCommand("AT+CGSN\r")
IMEI = self.sendCommand("AT+CGSN\r")
return IMEI
def getIMEIErro(self):
IMEI = self.sendCommand("AT+CGSN\r")
return IMEI
def getIMEIErro2(self):
self.ser.write("AT+CGSN\r".encode())
data = self.ser.readline().strip()
return data
def __del__(self):
self.ser.close()
if __name__ == '__main__':
print(ATCommands('ttyUSB1').getIMEI()) #OK
print(ATCommands('ttyUSB1').getIMEIErro()) #erro
print(ATCommands('ttyUSB1').getIMEIErro2()) #erro
In the code above I have two strange things:
getIMEI() only works if I execute the statement self.sendCommand("AT+CGSN\r") twice in a row. getIMEIErro() shows that the IMEI is not returned a single command is sent.
If I run the command self.ser.readline() outside the method sendCommand() the code also does not work. getIMEIErro2() shows this error
Anyone know the reason for the errors?
PS: I'm using python 3 e pySerial 2.7
Try this
clear the buffer
put sleeps to wait the baudrate to apply the commands to the modem
always end your commands with \r\n
Something like (based of in my class in https://github.com/vhpanisa/misc pyatapi.py):
def sendCommand(self,command):
from time import sleep
while self.ser.inWaiting() > 0:
self.ser.read()
sleep(0.1)
# Maybe ascii encode not needed, just bytes convert
self.ser.write(bytes(command+"\r\n", encoding='ascii'))
sleep(0.5)
data = []
while self.ser.inWaiting() > 0:
msg = self.ser.readline().strip()
sleep(0.1) # Wait for buffer
msg = msg.replace(b"\r",b"")
msg = msg.replace(b"\n",b"")
if msg != b"":
data.append(str(msg, encoding='ascii'))
return data

zeromq: why dealer sends one message and receives from one client?

I'm trying to understand my code's behavior.
I'm using zeromq to create a server that sends a "ping" and waits for "pong" responses.
What i'm seeing is that when I send a ping, only one client receives it.
when I run this code and send "ping" for the first time i receive:
pong: A
and when i run it again, i get
pong: B
why is that? I want to send one "ping" and receive two pongs.
here's the code:
from threading import Thread
import zmq
class zmqdealer(object):
def __init__(self, port):
context = zmq.Context()
self.sock = context.socket(zmq.DEALER)
#self.sock.setsockopt(zmq.RCVTIMEO, 1000)
self.sock.bind("tcp://*:%s" % port)
thread = Thread(target=lambda: self.poll())
thread.daemon = True
thread.start()
def poll(self):
while True:
reply = self.sock.recv()
if reply != "":
print(reply)
def ping(self):
self.sock.send_multipart(['', 'ping'])
class zmqrep(object):
def __init__(self, ident,host, port):
context = zmq.Context()
self.sock = context.socket(zmq.REP)
self.sock.connect("tcp://%s:%s" % (host, port))
self.ident = ident
thread = Thread(target=lambda: self.pong())
thread.daemon = True
thread.start()
def pong(self):
while True:
request = self.sock.recv()
if request == "ping":
msg = "pong: %s" % self.ident
self.sock.send(msg)
if __name__ == "__main__":
port = 11112
host = "localhost"
server = zmqdealer(port)
client1 = zmqrep('A',host,port)
client2 = zmqrep('B',host,port)
answer = raw_input('press <ENTER> to exit or type \'ping\' to get a pong\n')
while True:
if answer == "":
break
if answer == "ping":
server.ping()
answer = raw_input()
EDIT
I found a way to make this work. I really hope there is another way because i genuinely hate this one! so it looks like dealer sends to the clients in a round robin fashion. so to make my ping work i had to send it to all the clients. how? i subscribed to the monitor socket and added every connected client to a list. every time i ping, i ping to every client. look:
import threading
import zmq
from zmq.utils import monitor
def threadify(func, daemon=True):
thread = threading.Thread(target=func)
thread.daemon = daemon
thread.start()
class zmqdealer(object):
def __init__(self, port):
context = zmq.Context()
self.sock = context.socket(zmq.DEALER)
self.monitor_sock = self.sock.get_monitor_socket()
self.sock.bind("tcp://*:%s" % port)
self.connected_clients = {}
threadify(func=self.poll)
threadify(func=self.monitor)
def poll(self):
while True:
reply = self.sock.recv()
if reply != "":
print reply
def add_client(self, event):
endpoint = event['endpoint']
value = event['value']
if endpoint in self.connected_clients:
self.connected_clients[endpoint].append(value)
else:
self.connected_clients[endpoint] = [value]
def remove_client(self, event):
endpoint = event['endpoint']
value = event['value']
if endpoint in self.connected_clients \
and value in self.connected_clients[endpoint]:
self.connected_clients[endpoint].remove(value)
def monitor(self):
options = {zmq.EVENT_ACCEPTED: lambda e: self.add_client(e),
zmq.EVENT_DISCONNECTED: lambda e: self.remove_client(e)}
while True:
event = monitor.recv_monitor_message(self.monitor_sock)
event_type = event['event']
if event_type in options:
options[event_type](event)
event['event'] = event_types[event_type]
print event
def ping(self):
connected_clients_amount = sum([len(clients) for clients in self.connected_clients.values()])
for i in xrange(connected_clients_amount):
self.sock.send_multipart(['', 'ping'])
if connected_clients_amount <= 0:
print "there are no connected clients!"
class zmqrep(object):
def __init__(self, ident, host, port):
context = zmq.Context()
self.sock = context.socket(zmq.REP)
self.sock.connect("tcp://%s:%s" % (host, port))
self.identity = ident
self.stopped = threading.Event()
threadify(self.pong)
def pong(self):
while not self.stopped.isSet():
request = self.sock.recv()
if request == "ping":
msg = "pong: %s" % self.identity
self.sock.send(msg)
self.sock.close()
def stop(self):
self.stopped.set()
if __name__ == "__main__":
port = 11112
host = "localhost"
num = 5
server = zmqdealer(port)
clients = [zmqrep(i.__str__(), host, port) for i in xrange(num)]
answer = raw_input('press <ENTER> to exit or type \'ping\' to get a pong\n')
while True:
if answer == "":
break
if answer == "ping":
server.ping()
if answer == "kill":
if len(clients) > 0:
die = clients[0]
clients.remove(die)
die.stop()
else:
print "there are no connected clients!\n"
answer = raw_input()
Router/Dealer sockets are best used for distributing tasks. Say you have 10 tasks and 2 workers, you do not care who does what. Dealer/Router will distribute in a round robin fashion.
Maybe Pub/Sub or Push/Pull sockets would fit your usecase better? They are both broadcast sockets.
Here's an example of Push/Pull used in a similar fashion as what you're doing.
You often end up doing pairs of sockets, one to transmit and one other to receive results. You could for example do a PUSH with a ping message + random identifier, and ask clients to answer on PUB/SUB where you subscribe to this random identifier. This way you can match requests and responses.

Categories