Ping and Pong in a threaded WebSocket server (Python) - python

I've written a threaded websocket server in Python, using the lastest websocket spec and I'm trying to make it send a ping request to every client every x seconds. The only way I came up with to do this is overriding BaseServer.server_forever() like this:
# override BaseServer's serve_forever to send a ping request every now and then
class ModTCPServer(SocketServer.TCPServer):
def __init__(self, server_address, RequestHandlerClass, bind_and_activate=True):
SocketServer.TCPServer.__init__(self, server_address, RequestHandlerClass, bind_and_activate)
self.__is_shut_down = threading.Event()
self.__shutdown_request = False
def serve_forever(self, poll_interval=0.5):
###
global PING_EVERY_SEC
self.lastPing = int(time())
###
self.__is_shut_down.clear()
try:
while not self.__shutdown_request:
r, w, e = select.select([self], [], [], poll_interval)
if self in r:
self._handle_request_noblock()
###
now = int(time())
if (now - self.lastPing) >= PING_EVERY_SEC:
self.socket.send(WebSocketPing(['0x89','0x21','0xa7','0x4b'], now)) # arbitrary key
self.lastPing = now
###
finally:
self.__shutdown_request = False
self.__is_shut_down.set()
class LoginServer(SocketServer.ThreadingMixIn, ModTCPServer):
pass
server = LoginServer(("", PORT), ApplicationHandler)
print "serving at port", PORT
server_thread = threading.Thread(target=server.serve_forever)
server_thread.daemon = True
server_thread.start()
while server_thread.isAlive():
pass
server.shutdown()
Here is the function that constructs the Ping frame, it just puts the timestamp in the contents:
def WebSocketPing(key, timestamp=False):
data = ['0x89','0x8a'] # 0x89 = fin,ping 0x8a = masked,len=10
data.extend(key)
if timestamp:
t = str(timestamp)
else:
t = str(int(time()))
for i in range(10):
masking_byte = int(key[i%4],16)
masked = ord(t[i])
data.append(hex(masked ^ masking_byte))
frame = ''
for i in range(len(data)):
frame += chr(int(data[i],16))
return frame
Bad things happen when I run this
Traceback (most recent call last):
File "LoginServer.py", line 91, in <module>
server = LoginServer(("", PORT), ApplicationHandler)
File "LoginServer.py", line 63, in __init__
SocketServer.TCPServer.__init__(self, server_address, RequestHandlerClass, bind_and_activate)
File "/usr/lib/python2.6/SocketServer.py", line 400, in __init__
self.server_bind()
File "/usr/lib/python2.6/SocketServer.py", line 411, in server_bind
self.socket.bind(self.server_address)
File "<string>", line 1, in bind
socket.error: [Errno 112] Address already in use
I assume this is down to my lack of understanding of how overriding works in Python or to a fundamentally wrong approach to this problem. Is there a better way to do this or a way to make this code work?

The codes does not set the properties __is_shut_down and __shutdown_request anywhere. Therefore, trying to access them fails. Create them in the constructor, like this:
class ModTCPServer(SocketServer.TCPServer):
def __init__(self, *args, **kwargs):
SocketServer.TCPServer.__init__(self, *args, **kwargs)
self.__is_shut_down = threading.Event()
self.__shutdown_request = threading.Event()
In response to the update:
socket.error: [Errno 112] Address already in use
means that another process has already bound to the specified port. On Unix, you can find that process with sudo netstat -ltpn. Alternatively, choose a different port.

Related

Python test http-server: what am I doing wrong?

I'm trying to write a test http-server for a personal IoT project I"ve got going. My project has its own web-server that responds to certain 'page load' requests. Now I'm trying to expand the web-page itself. Now I could develop the actual server itself simultaneously but downloading it to the IoT device every iteration is extraordinarily slow so I've decided to write a test-server on my machine instead.
The test-server is written in python. The code below is the test-server in its entirety:
import http.server
import socketserver
import string
PORT = 8000
class MyHandler(http.server.SimpleHTTPRequestHandler):
def __init__(self):
self.pageFuncs = { "/Lights": self.doLightsPage,
"/Show": self.doShowPage,
"/Static": self.doShowTypePage,
"/Effects": self.doShowTypePage,
"/Playlist": self.doShowTypePage,
"/Select": self.doControlTypePage,
"/Controls": self.doControlTypePage,
"/query": self.doQueryResultsPage }
self.STATIC_SHOW_TYPE = 0
self.EFFECTS_SHOW_TYPE = 1
self.PLAYLIST_SHOW_TYPE = 2
self.SELECT_CTRL_TYPE = 0
self.CONTROL_CTRL_TYPE = 1
self.LightsOn = 0
self.LightShowOn = 0
self.showType = STATIC_SHOW_TYPE
self.ctrlType = SELECT_CTRL_TYPE
def do_GET(self):
try:
page_name = self.path
if (page_name in pageFuncs):
print (page_name)
self.send_response(200)
self.send_header("Content-type", "text/json")
self.end_headers()
self.pageFuncs[page_name](self)
return
else:
super().do_GET()
except IOError:
self.send_error(404,'File Not Found: %s' % self.path)
except :
pass
def doLightsPage(self):
self.bLightsOn ^= 1
return doQueryResultsPage()
def doShowPage(self):
self.bLightShowOn ^= 1
return doQueryResultsPage()
def doShowTypePage(self):
if (self.path == '/Static') :
self.showType = STATIC_SHOW_TYPE
elif (self.path == '/Effects') :
self.showType = EFFECTS_SHOW_TYPE
else :
self.showType = PLAYLIST_SHOW_TYPE
return doQueryResultsPage()
def doControlTypePage(self):
if (self.path == '/Select') :
self.ctrlType = SELECT_CTRL_TYPE
else :
self.ctrlType = CONTROL_CTRL_TYPE
return doQueryResultsPage()
def doQueryResultsPage(self):
json_data = '{ \"power\": {}, \"show\": {},'
json_data += '\"show_type\": {}, \"control_type\": {} }'
json_data = format(json_data, bLightsOn, bLightShowOn, showType, ctrlType);
self.write(json_data)
def main():
try:
server = socketserver.TCPServer(("", PORT), MyHandler)
print ('started httpserver...')
server.serve_forever()
except KeyboardInterrupt:
print ('^C received, shutting down server')
server.socket.close()
if __name__ == '__main__':
main()
The point of the test is to receive a valid request; change a value and return a json string back reflecting the internal state of the server.
However, as soon as I try to load any of the pages, I get the following error message:
Exception happened during processing of request from ('127.0.0.1', 50149)
Traceback (most recent call last):
File "C:\Program Files\WindowsApps\PythonSoftwareFoundation.Python.3.8_3.8.752.0_x64__qbz5n2kfra8p0\lib\socketserver.py", line 316, in _handle_request_noblock
self.process_request(request, client_address)
File "C:\Program Files\WindowsApps\PythonSoftwareFoundation.Python.3.8_3.8.752.0_x64__qbz5n2kfra8p0\lib\socketserver.py", line 347, in process_request
self.finish_request(request, client_address)
File "C:\Program Files\WindowsApps\PythonSoftwareFoundation.Python.3.8_3.8.752.0_x64__qbz5n2kfra8p0\lib\socketserver.py", line 360, in finish_request
self.RequestHandlerClass(request, client_address, self)
TypeError: __init__() takes 0 positional arguments but 4 were given
So my question is, what am I doing wrong?
I've taken the code format from the following stackoverflow topic which appears to be trying to do the same things, maybe I'm misunderstanding something?

Thread missing 1 required positional argument

Just to give some context: I'm currently learning Python and to do so I started a little Project.
I wrote two Python Scripts: a Host and a Client script.
I'm already at the point where multiple Clients can connect to one "Host" and the Host can send a random String to all Clients.
Now I wanted to solve the problem, that if a Client disconnects from the Host nobody knows until the CLIENTSOCKET is called again.
So I wrote the checkConnection method to ping all Clients every 5 sec, for now.
class Connection():
def __init__(self):
self.s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
def bound(self,RHOST,PORT):
self.s.bind((RHOST,PORT))
self.s.listen(1000)
def connect(self):
((CLIENTSOCKET,ADRESS)) = self.s.accept()
return (CLIENTSOCKET,ADRESS)
def newConnection(Connection,CLIENTS):
con = Connection
cli = CLIENTS
while True:
c = con.connect()
if c != None:
if len(cli) != 0:
for x in range (len(cli)):
if c[1][0] == cli[x][1][0]:
pass
else:
cli.append(c)
else:
cli.append(c)
def checkConnection(CLIENTS):
cli = CLIENTS
while True:
if(len(cli)!=0):
for x in range(len(cli)):
try:
cli[x][0].sendall(b'')
except:
del cli[x]
time.sleep(5)
I'm creating to Threads with each method as a target and give them the needed parameters. The first thread starts like it should, but the second doesn't.
I really don't know why?
t = threading.Thread(target = newConnection,name = "newConnection",args = (g,CLIENTS))
t2 = threading.Thread(target = checkConnection,name = "checkConnection",args = (CLIENTS))
t.start()
t2.start()
Exception in thread checkConnection:
Traceback (most recent call last):
File "C:\Users\User\AppData\Local\Programs\Python\Python35-32\lib\threading.py", line 914, in _bootstrap_inner
self.run()
File "C:\Users\User\AppData\Local\Programs\Python\Python35-32\lib\threading.py", line 862, in run
self._target(*self._args, **self._kwargs)
TypeError: checkConnection() missing 1 required positional argument: 'CLIENTS'

python error: [Errno 32] Broken pipe in Socket.sendall()

I am writing a multi-threaded client/server program. It splits a large file into smaller files in its client side and sends the smaller files to the server concurrently.
The problem is that in every run, the server can only receive two of the smaller files (the first one and another random one). Meanwhile, I encounter the error: "[Errno 32] Broken pipe" in client side of the program in s.sendall(part). The error arises in every thread that starts to send one of the smaller files before reception of the first file (on the server). In other words, every thread that starts to send after the reception the first file (on the server) can complete its task.
I run each of the client and server codes on different computers (both have the following specification: Ubuntu 14.04 desktop, 64 bit, 16GiB ram)
Client side Error:
Traceback (most recent call last):
File "Desktop/File_transmission/Client.py", line 56, in sendSplittedFile
s.sendall(part)
File "/usr/lib/python2.7/socket.py", line 224, in meth
return getattr(self._sock,name)(*args)
error: [Errno 32] Broken pipe
Client.py:
import random
import socket
import time
import threading
import errno
import select
import File_manipulation
import sys, traceback
class Client:
nodesIpAddr = ["....", "...."] #Server = ....
dataPort = 45678
delay = 2
inputFileAddress = 'tosend.dat'
fileOutputPrefix = 'output'
fileOutputSuffix = ".dat"
bufferSize = 2048
max_size_splitted_file = 10*(2**20) # 10 MiB
def __init__ (self, my_ip):
self.ip = my_ip
def send(self, ip_toSend, dataPort):
print "\tSend function is runing."
totalNumSplittedFile = File_manipulation.split_file(Client.inputFileAddress, Client.fileOutputPrefix, Client.max_size_splitted_file , Client.bufferSize)
for i in range(0, totalNumSplittedFile):
thread_send = threading.Thread(target = self.sendSplittedFile, args = (ip_toSend, dataPort, Client.bufferSize, i, Client.fileOutputPrefix, totalNumSplittedFile))
thread_send.start()
def sendSplittedFile(self, ip_toSend, dataPort, bufferSize, fileNumber, fileNamePrefix, totalNumSplittedFile):
# Create a socket (SOCK_STREAM means a TCP socket)
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
BUFFER_SIZE = bufferSize
try:
s.connect((ip_toSend, dataPort))
f = open(fileNamePrefix + '.%s' % fileNumber,'rb')
s.send(str(fileNumber) + " " + str(totalNumSplittedFile))
part = f.read(BUFFER_SIZE)
while (part):
s.sendall(part)
part = f.read(BUFFER_SIZE)
f.close()
s.sendall(part)
time.sleep(Client.delay)
s.sendall('EOF')
print "Done Sending."
print s.recv(BUFFER_SIZE)
s.close()
print "\tData is sent to ", ip_toSend,
except socket.error, v:
traceback.print_exception(*sys.exc_info())
s.close()
nodeIP = [(s.connect(('8.8.8.8', 80)), s.getsockname()[0], s.close()) for s in [socket.socket(socket.AF_INET, socket.SOCK_DGRAM)]][0][1]
n = Client(nodeIP)
n.send(n.nodesIpAddr[0], n.dataPort)
Server Side Error:
Traceback (most recent call last):
File "/usr/lib/python2.7/SocketServer.py", line 295, in _handle_request_noblock
self.process_request(request, client_address)
File "/usr/lib/python2.7/SocketServer.py", line 321, in process_request
self.finish_request(request, client_address)
File "/usr/lib/python2.7/SocketServer.py", line 334, in finish_request
self.RequestHandlerClass(request, client_address, self)
File "/usr/lib/python2.7/SocketServer.py", line 649, in __init__
self.handle()
File "Desktop/File_transmissionServer.py", line 37, in handle
totalFileNumber = int(details[1])
ValueError: null byte in argument for int()
Server.py
import socket
import time
import threading
import errno
import select
import SocketServer
import File_manipulation
class ServerThreadHandler(SocketServer.BaseRequestHandler):
nodesIpAddr = ["....", "...."] #Server = ....
fileOutputPrefix = 'torec '
fileOutputSuffix = '.dat'
dataPort = 45678
delay = 3
maxNumClientListenedTo = 200
timeout_in_seconds = 5
bufferSize = 2048
totalFileNumber = 0 #Total number of splitted files. It should be set by the incoming packets
def handle(self):
BUFFER_SIZE = ServerThreadHandler.bufferSize # Normally 1024, but we want fast response
# self.request is the TCP socket connected to the client
data = self.request.recv(BUFFER_SIZE)
addr = self.client_address[0]
details = str(data).split()
currentFileNum = int(details[0])
totalFileNumber = int(details[1])
print '\tReceive: Connection address:', addr,'Current File Number: ', currentFileNum, 'Total Number of splitted files: ', totalFileNumber
f = open(ServerThreadHandler.fileOutputPrefix + '_Received.%s' % currentFileNum, 'wb')
data = self.request.recv(BUFFER_SIZE)
while (data and data != 'EOF'):
f.write(data)
data = self.request.recv(BUFFER_SIZE)
f.close()
print "Done Receiving." ," File Number: ", currentFileNum
self.request.sendall('\tThank you for data. File Number: ' + str(currentFileNum))
if __name__ == "__main__":
HOST, PORT = ServerThreadHandler.nodesIpAddr[0], ServerThreadHandler.dataPort # HOST = "localhost"
server = SocketServer.TCPServer((HOST, PORT), ServerThreadHandler)
# Activate the server; this will keep running until you interrupt the program with Ctrl-C
server.serve_forever()

Pyro: cannot execute callbacks

Using Pyro4 I haven't managed to successfully execute a callback from server to client.
The server script looks as follows:
class RobotController(object):
def __init__(self):
self.robotStates = []
def moveDemo(self, motionTime):
stopTime = datetime.datetime.now() + datetime.timedelta(0,motionTime)
while datetime.datetime.now() < stopTime:
print "Robot is moving..."
time.sleep(1)
print "Robot stopped"
return 0
def doCallback(self, callback):
print("server: doing callback 1 to client")
try:
callback.call1()
except:
print("got an exception from the callback.")
print("".join(Pyro4.util.getPyroTraceback()))
print("server: doing callback 2 to client")
try:
callback.call2()
except:
print("got an exception from the callback.")
print("".join(Pyro4.util.getPyroTraceback()))
print("server: callbacks done")
if __name__ == '__main__':
robotController = RobotController()
if os.name == 'posix':
daemon = Pyro4.Daemon(host="192.168.1.104", port=8000);
else:
daemon = Pyro4.Daemon(host="localhost", port=8000);
Pyro4.Daemon.serveSimple(
{ robotController: "robotController"},
ns=False,
daemon=daemon,
verbose = True
)
and the client looks as follows:
class CallbackHandler(object):
def crash(self):
a=1
b=0
return a//b
def call1(self):
print("callback 1 received from server!")
print("going to crash - you won't see the exception here, only on the server")
return self.crash()
#Pyro4.callback
def call2(self):
print("callback 2 received from server!")
print("going to crash - but you will see the exception here too")
return self.crash()
daemon = Pyro4.core.Daemon()
callback = CallbackHandler()
daemon.register(callback)
#robotController = Pyro4.Proxy("PYRO:robotController#192.168.1.104:8000")
robotController = Pyro4.Proxy("PYRO:robotController#localhost:8000")
robotController._pyroOneway.add("doCallback")
robotController.doCallback(callback)
When executing the command robotController.doCallback(callback), the method doCallback on server is executed, but the server cannot access the client. It returns the following:
Traceback (most recent call last):
File "C:\LASTNO\Python Projects\PiBot\RobotServer\PyroServer\pyroServer.py", line 63, in doCallback
callback.call2()
File "C:\Python27\lib\site-packages\Pyro4\core.py", line 160, in __call__
return self.__send(self.__name, args, kwargs)
File "C:\Python27\lib\site-packages\Pyro4\core.py", line 286, in _pyroInvoke
self.__pyroCreateConnection()
File "C:\Python27\lib\site-packages\Pyro4\core.py", line 371, in __pyroCreateConnection
raise ce
CommunicationError: cannot connect: [Errno 10061] No connection could be made because the target machine actively refused it
Does anyone know what could be the cause of the error and how to fix it? Thank you!
I solved the problem by modifying the client code as follows:
class CallbackHandler(object):
def crash(self):
a=1
b=0
return a//b
def call1(self):
print("callback 1 received from server!")
print("going to crash - you won't see the exception here, only on the server")
return self.crash()
#Pyro4.callback
def call2(self):
print("callback 2 received from server!")
print("going to crash - but you will see the exception here too")
return self.crash()
daemon = Pyro4.core.Daemon()
callback = CallbackHandler()
daemon.register(callback)
with Pyro4.core.Proxy("PYRO:robotController#localhost:8000") as server:
server._pyroOneway.add("doCallback")
server.doCallback(callback)
motion = server.moveDemo(15)
print("waiting for callbacks to arrive...")
print("(ctrl-c/break the program once it's done)\n")
daemon.requestLoop()

Python Pyro4 nameserver freezes when adding more than two remote objects

Say I want to ping something from different locations, so I wrap ping commandline tool into python and use pyro4 prc library to call it.
I have a python Pyro4 nameserver
import Pyro4
Pyro4.config.COMPRESSION = True
Pyro4.naming.startNSloop("ipofnameserver")
And simple ping server:
class Pinger(object):
def ping(self, host):
return subprocess.check_output(["ping", host, "-c 4"])
pinger = Pinger()
daemon = Pyro4.Daemon() # make a Pyro daemon
ns = Pyro4.locateNS(host = "ipofnameservre", port=9090) # find the name server
uri = daemon.register(pinger) # register the greeting object as a Pyro object
print ns.register("location1", uri)
print "Ready. Object uri =", uri # print the uri so we can use it in the client later
daemon.requestLoop()
As long as I have only two pingservers everything is ok, but after I add third one nameserver stop responding. Every pingserver has unique name of course.
For example, I want to check availability of the servers:
ns = Pyro4.locateNS(host = "nameserverip", port=9090)
names = ns.list().keys()
print names
print ns.list()
for n in names:
if n == 'Pyro.NameServer': continue
proxy = Pyro4.Proxy("PYRONAME:"+n)
try:
print n, proxy._Proxy__pyroCreateConnection()
except:
print "offline"
This works with two pingservers, but with three it just waits for something. Traceback of this script terminated with ctrl+C:
ns = Pyro4.locateNS(host = "nameserverip", port=9090)
File "/usr/local/lib/python2.7/dist-packages/Pyro4/naming.py", line 319, in locateNS
proxy.ping()
File "/usr/local/lib/python2.7/dist-packages/Pyro4/core.py", line 146, in __call__
return self.__send(self.__name, args, kwargs)
File "/usr/local/lib/python2.7/dist-packages/Pyro4/core.py", line 250, in _pyroInvoke
self.__pyroCreateConnection()
File "/usr/local/lib/python2.7/dist-packages/Pyro4/core.py", line 312, in __pyroCreateConnection
msgType, flags, seq, data = MessageFactory.getMessage(conn, None)
File "/usr/local/lib/python2.7/dist-packages/Pyro4/core.py", line 665, in getMessage
headerdata = connection.recv(cls.HEADERSIZE)
File "/usr/local/lib/python2.7/dist-packages/Pyro4/socketutil.py", line 323, in recv
return receiveData(self.sock, size)
File "/usr/local/lib/python2.7/dist-packages/Pyro4/socketutil.py", line 104, in receiveData
data=sock.recv(size, socket.MSG_WAITALL)
strace shows the following:
socket(PF_INET, SOCK_STREAM, IPPROTO_IP) = 3 fcntl(3, F_GETFL)
= 0x2 (flags O_RDWR) fcntl(3, F_SETFL, O_RDWR) = 0 connect(3, {sa_family=AF_INET, sin_port=htons(9090),
sin_addr=inet_addr("ipofnameserver")}, 16) = 0 setsockopt(3,
SOL_SOCKET, SO_KEEPALIVE, [1], 4) = 0 recvfrom(3,
The following example is not working either, as it unable to resolve names into pyro_uri, because it just waits for something like in previous example. Interesting thing about this example that it prints fls, which contains names of all remote pingservers. Then adding fourth pingserver I'm unable even to print names of registered pingservers.
def _ping((host, firing_location)):
pinger = Pyro4.Proxy("PYRONAME:" + firing_location)
return pinger.ping(host)
def ping(host):
ns = Pyro4.locateNS(host = "178.209.52.240", port=9090)
names = ns.list().keys()
fls = []
for name in names:
if name == 'Pyro.NameServer': continue
fls.append(name)
print fls
p = Pool(len(fls))
jobs = p.map(_ping, zip([host]*len(fls), fls) )
for j in jobs:
print j.split("/")[-3], "±", j.split("/")[-1][:-1]
return jobs
I'm struggling with it for two days and have no idea of what's wrong with my code.

Categories

Resources