How can you load test gevent sockets?
I've written a simple server and I want to load test this before I tweak the code to find out what improvements can be done.
Are there any specifics to load testing a socket server?
How would I go about load testing this code so I can compare the results after applying tweaks?
from gevent import server
from gevent.monkey import patch_all; patch_all()
import gevent
class Client(object):
def __init__(self, socket, address, server_handler):
self.socket = socket
self.address = address
self.server_handler = server_handler
gevent.spawn(self.listen)
def listen(self):
f = self.socket.makefile()
while True:
line = f.readline()
if not line:
print 'client died'
break
line = line.rstrip()
for addr, c in self.server_handler.users.iteritems():
msg = '<%s> says: %s' % (self.address, line)
c.send(msg)
print '<%s>: %s' % (self.address, line)
def send(self, msg):
f = self.socket.makefile()
f.write('%s\r\n' % msg)
f.flush()
class ServerHandler(object):
def __init__(self):
self.users = {}
def __call__(self, socket, address):
client = Client(socket, address, self)
self.users[address] = client
self.socket = socket
self.address = address
print 'connection made'
if __name__ == '__main__':
server = server.StreamServer(('0.0.0.0', 5000), ServerHandler())
server.serve_forever()
Related
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))
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
I wanted to write a server which will be able to handle multiple clients sing select and threads.
I know how to write a server only with threads, or only with select. Now, I wanted to mix them together. But it seems I failed.
Server:
import threading
import socket
import select
class Client(threading.Thread):
'''
Multi-threading clients.
'''
def __init__(self):
threading.Thread.__init__(self)
pass
def setup(self, __client, __address):
self.client = __client
self.address = __address
def connect(self):
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
def run(self):
print self.client.__class__
data = self.client.recv(1024)
if data:
self.client.sendall(data)
else:
print "Error:", self.address
class Server(object):
'''
Threading server
'''
def __init__(self):
'''
Constructor
'''
self.server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.server.bind(('127.0.0.1', 9876))
def run(self):
self.server.listen(10)
inputs = [self.server]
while 1:
inready, outready, excready = select.select(inputs, [], []);
for s in inready:
if s == self.server:
sock, address = self.server.accept();
client = Client()
client.setup(sock, address)
client.start()
self.server.close()
if __name__ == '__main__':
server = Server()
server.run()
Client:
import socket
import sys
port = 9876
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()
Client sends and receives only one message and then Im not able to send any more messages. Why? How to improve this? Thank you.
Edit: One error found. There was a loop missing in Client's run method:
import threading import socket import select
class Client(threading.Thread):
'''
Multi-threading clients.
'''
def __init__(self):
threading.Thread.__init__(self)
pass
def setup(self, __client, __address):
self.client = __client
self.address = __address
def connect(self):
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
def run(self):
while True:
print self.client.__class__
data = self.client.recv(1024)
if data:
self.client.sendall(data)
else:
print "Error:", self.address
self.client.close()
class Server(object):
'''
Threading server
'''
def __init__(self):
'''
Constructor
'''
self.server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.server.bind(('127.0.0.1', 9876))
def run(self):
self.server.listen(10)
inputs = [self.server]
while 1:
inready, outready, excready = select.select(inputs, [], []);
for s in inready:
if s == self.server:
sock, address = self.server.accept();
client = Client()
client.setup(sock, address)
client.start()
self.server.close()
if __name__ == '__main__':
server = Server()
server.run()
But now, when client disconnects, I have an error on server's side. How to handle this error?
Exception in thread Thread-4:
Traceback (most recent call last):
File "/usr/lib/python2.7/threading.py", line 801, in __bootstrap_inner
self.run()
File "/home/testserver.py", line 25, in run
data = self.client.recv(1024)
File "/usr/lib/python2.7/socket.py", line 174, in _dummy
raise error(EBADF, 'Bad file descriptor')
error: [Errno 9] Bad file descriptor
iv created a simple async client and server but im unable to get the client to reply after receiving the first time. It seems the server can send back a reply after receiving from the client but the client cant:
here is the client's session:
[mike#mike Public]$ python cl.py
buf got your stuff
dded callback ## this is a log addded to see if execution got where i wanted
and here is the server's log:
[mike#mike Public]$ python that.py
buf ehlo localhost
i was expecting some sort of ping pong effect where one send then the other then rinse lather repeat.
here is the client's code:
import socket
import fcntl, os, io, time, functools
from tornado import ioloop
class Punk(object):
def __init__(self):
self.loop = ioloop.IOLoop.instance()
self.address = 'blah.sock'
self.authkey = "none"
self.sock = socket.socket(socket.AF_UNIX)
def setup(self):
self.sock.connect(self.address)
fcntl.fcntl(self.sock, fcntl.F_SETFL, os.O_NONBLOCK)
self.sock.sendall("ehlo localhost")
self.fd = self.sock.fileno()
self.loop.add_handler(self.fd,self.reader,self.loop.READ)
self.loop.start()
def reader(self,fd,event):
result = b""
if event == self.loop.READ:
try:
while True:
servrep = self.sock.recv(1024)
if not servrep:
break
result += servrep
self.prnt(result)
break
except Exception as e:
print "this %s happend"%e
return
def prnt(self,buf):
print "buf %s"%buf
tim = time.time() + 2
self.loop.instance().add_timeout(tim, self.reply)
#callbac = functools.partial(self.loop.add_timeout,tim,self.reply)
#self.loop.add_callback(self.reply) ### i tried this too
print "added callback"
def reply(self):
self.sock.sendall(" clent got your stuff")
if __name__ == "__main__":
bob = Punk()
bob.setup()
and here is the server:
import socket
import fcntl, os, io, time, functools
from array import array
from tornado import ioloop
class Player(object):
def __init__(self):
self.loop = ioloop.IOLoop.instance()
self.address = 'blah.sock'
self.authkey = "none"
self.sock = socket.socket(socket.AF_UNIX)
def setup(self):
self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR,1)
self.sock.bind(self.address)
fcntl.fcntl(self.sock, fcntl.F_SETFL, os.O_NONBLOCK)
self.sock.listen(1)
self.fd = self.sock.fileno()
self.loop.add_handler(self.fd,self.reader,self.loop.READ)
self.loop.start()
def reader(self,fd,event):
result = b""
if event == self.loop.READ:
self.conn, self.addr = self.sock.accept()
try:
while True:
maxrep = self.conn.recv(1024)
if not maxrep:
break
result += maxrep
self.prnt(result)
break
except Exception as e:
print "this %s happend"%e
return
def prnt(self,buf):
print "buf %s"%buf
tim = time.time() + 2
self.loop.instance().add_timeout(tim, self.reply)
#callbac = functools.partial(self.loop.add_timeout,tim,self.reply)
#self.loop.add_callback(callbac)
def reply(self):
self.conn.sendall("got your stuff")
if __name__ == "__main__":
bob = Player()
bob.setup()
i had set my sockets to nonblock mode, but i did not catch an error when accepting from
a nonblock state when there is no connection:
here:
def reader(self,fd,event):
result = b""
if event == self.loop.READ:
self.conn, self.addr = self.sock.accept()
should be
def reader(self,fd,event):
result = b""
if event == self.loop.READ:
try:
self.conn, self.addr = self.sock.accept() # we get stuck here
self.connl.append(self.conn)
except Exception as e:
pass
I use unittests to check correctness of TCP client communication, so I need some TCP server that I can control it and that it sends to client (and when).
A simple test should look like:
server = Server("localhost", 5555)
server.start()
client.connect()
self.assertTrue("login_message" in server.received_data)
server.send_to_client(reject_messages)
self.assertTrue("login_again" in server.received_data)
time.sleep(10)
self.assertTrue("login_again_and_again" in server.newest_received_data)
server.stop()
self.assertTrue("login failed" in client.logs)
I need the full flow control, what could be used for implementing Server?
Now I try to use threaded SocketServer, but I don't have an access neither to data, nor to controlling it..
I don't know if anybody needs this, but anyway:
I used gevent server
from gevent import sleep, socket
from gevent.server import StreamServer
class Connection:
def __init__(self, host, port):
self.server = StreamServer((host, port), self.handle)
self.data = []
self.socks = []
self.pointer = 0
def handle(self, sock, address):
self.socks.append(sock)
while True:
line = sock.recv(1024)
if line:
self.data += [line]
else:
break
sock.close()
self.socks.remove(sock)
def send(self, msg):
if self.socks:
sock2send = self.socks[-1]
try:
sock2send.send(msg)
except IOError, e:
print "Can't send message '%s'! Exception:" % msg, e
else:
print "No sockets to send the message to"
def start(self):
self.server.start()
def serve_forever(self):
self.server.serve_forever()
def close(self):
self.server.stop()
for sock in self.socks:
sock.close()
def new_data(self):
newest = self.data[self.pointer:]
self.pointer = len(self.data)
return newest
And then unittest looks like this:
def testTCPClient(self):
j = lambda x: "".join(x)
server = Connection("", 5555)
server.start()
client.run()
sleep(3)
data = j(server.new_data())
self.assertTrue("login" in data)
sleep(2)
server.send("login approve")
sleep(2)
data = j(server.new_data())
self.assertTrue("after_login" in data)
server.send("logout")
sleep(2)
data = j(server.new_data())
self.assertTrue("received_logout" in data)
server.close()
self.assertTrue("disconnected" in client.logs)