I have a simple multithreading server, But it creates a new thread for each socket, I don't want to create a lot of threads. My idea is to receive the messages in other way: when the user send a message, it will add the message to a queue of messages and with a threadpool the server will handle these requests.
The simple multithreaded server:
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__":
port_num = input("Port? ")
ThreadedServer('',port_num).listen()
How can I implement my idea or is there better way to do it?
The question seems to be pretty old but i also stumble upon the same issue while working on the socket server, so here is the below code which you can use to make threaded socket server which doesnt spawn new threads on arrival.
Just to give gist ThreadingMixIn classes is overided with threaded pool.
class ThreadPoolMixIn(socketserver.ThreadingMixIn):
'''
use a thread pool instead of a new thread on every request
'''
# numThreads = 50
allow_reuse_address = True # seems to fix socket.error on server restart
def serve_forever(self):
'''
Handle one request at a time until doomsday.
'''
print('[X] Server is Running with No of Threads :- {}'.format(self.numThreads))
# set up the threadpool
self.requests = Queue(self.numThreads)
for x in range(self.numThreads):
t = threading.Thread(target = self.process_request_thread)
t.setDaemon(1)
t.start()
# server main loop
while True:
self.handle_request()
self.server_close()
def process_request_thread(self):
'''
obtain request from queue instead of directly from server socket
'''
while True:
socketserver.ThreadingMixIn.process_request_thread(self, *self.requests.get())
def handle_request(self):
'''
simply collect requests and put them on the queue for the workers.
'''
try:
request, client_address = self.get_request()
except socket.error:
return
if self.verify_request(request, client_address):
self.requests.put((request, client_address))
And then it is called in ThreadedTCPRequest Handler and override the numThreads parameter :
class ThreadedTCPServer(ThreadPoolMixIn, socketserver.TCPServer):
#Extend base class and overide the thread paramter to control the number of threads.
def __init__(self, no_of_threads, server_address, ThreadedTCPRequestHandler):
self.numThreads = no_of_threads
super().__init__(server_address, ThreadedTCPRequestHandler)
Ultimately creating the server which serves forever :
def create_multi_threaded_socket(CONFIG, HandlerClass = ThreadedTCPRequestHandler,
ServerClass = ThreadedTCPServer,
protocol="HTTP/1.0"):
server_address = ('', CONFIG.port)
HandlerClass.protocol_version = protocol
# httpd = ServerClass(server_address, HandlerClass)
server = ThreadedTCPServer(CONFIG.no_of_threads, server_address, ThreadedTCPRequestHandler)
sa = server.socket.getsockname()
print("Serving HTTP on {} port : {}".format(sa[0], sa[1]))
server.serve_forever()
I got the sample code from :
http://code.activestate.com/recipes/574454-thread-pool-mixin-class-for-use-with-socketservert/
Modified bit according to my need.
Hope this helps :) .
Related
I would like to forward data captured on one serial port to a multiclient TCP Server. In short, I need a serial to TCPIP bridge.
import sys
import socket
from threading import Thread
import serial
import serial.threaded
class SerialToNet(serial.threaded.Protocol):
"""serial->socket"""
def __init__(self):
self.sockets: list[socket.socket] = []
def __call__(self):
return self
def data_received(self, data):
"""Forward data from Serial to IP client Sockets"""
for socket in self.sockets:
socket.sendall(data)
class NetToSerial(Thread):
"""socket->serial"""
serial_worker: serial.threaded.ReaderThread
def __init__(self, client_socket):
Thread.__init__(self)
self._socket = client_socket
def run(self):
try:
while True:
data = self._socket.recv(1024)
serial_worker.write(data)
except (ConnectionAbortedError, ConnectionResetError):
print("NetToSerial client disconnection")
return
if __name__ == "__main__":
# Serial connection
SERIAL_COM_PORT = 'COM9'
try:
ser = serial.Serial(SERIAL_COM_PORT, 115200, timeout=2)
except serial.SerialException:
sys.exit(f"Serial port {SERIAL_COM_PORT} it not available")
serial_to_net = SerialToNet()
serial_worker = serial.threaded.ReaderThread(ser, serial_to_net)
serial_worker.start()
# TCP Server
# :todo Use socketserver.TCPServer
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server_socket.bind(('', 3490))
mythreads = []
try:
# Wait new IP clients
while True:
server_socket.listen()
print("Server: waiting TCP client connection")
(client_socket, _) = server_socket.accept()
# New client
net_to_serial_thread = NetToSerial(client_socket)
net_to_serial_thread.serial_worker = serial_worker
serial_to_net.sockets.append(client_socket)
net_to_serial_thread.start()
mythreads.append(net_to_serial_thread)
except KeyboardInterrupt:
pass
for t in mythreads:
t.join()
This implementation is quite working but I don't known how to update sockets in SerialToNet class when a TCP client disconnect.
You need to implement some logic for when a network client disconnects.
You know a client has disconnected because you receive an empty response (b'') from the socket. You're receiving data from network clients in NetToSerial, here:
def run(self):
try:
while True:
data = self._socket.recv(1024)
serial_worker.write(data)
except (ConnectionAbortedError, ConnectionResetError):
print("NetToSerial client disconnection")
return
You need to check the value of data, and if it's empty implement your disconnect logic:
Close the associated socket.
Exit the thread.
That might look like:
class NetToSerial(Thread):
"""socket->serial"""
serial_worker: serial.threaded.ReaderThread
def __init__(self, client_socket):
Thread.__init__(self)
self._socket = client_socket
def run(self):
try:
while True:
data = self._socket.recv(1024)
if not data:
break
serial_worker.write(data)
except (ConnectionAbortedError, ConnectionResetError):
print("NetToSerial client disconnection")
return
finally:
self._socket.close()
But that's only half the solution, because you're writing to this socket in your SerialToNet class. You need to remove the socket from SerialToNet sockets array. You can have the class remove the socket in response to an exception when writing, like this:
class SerialToNet(serial.threaded.Protocol):
"""serial->socket"""
def __init__(self):
self.sockets: list[socket.socket] = []
def __call__(self):
return self
def data_received(self, data):
"""Forward data from Serial to IP client Sockets"""
for socket in self.sockets[:]:
try:
socket.sendall(data)
except OSError:
self.sockets.remove(socket)
Note that because it's not possible to remove an item from a list over which you're currently iterating, we are iterating over a copy of self.sockets in the above code. This means we're free to remove sockets from self.sockets from inside the loop.
With the above changes I believe your code will operate as you intend.
Not directly related to your question, but I'd like to make a comment about your code: as written, it allows multiple network clients to write to the serial port at the same time. That seems like a recipe for disaster and I cannot think of any situation in which that would make sense. You may want to reconsider that aspect of your code.
My Python socket chat with multithreading only accepts one connection. If I try to connect a second client it doesn't work. On the client side it seems like everything is okay but if i try to send a second message from client 2 the message doesn't arrive.
import socket
import threading
class TxtSocket:
def __init__(self, host="127.0.0.1" , port=5555):
self.host = host
self.port = port
self.s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.s.bind((self.host, self.port))
print("Socket was created")
def server(self):
self.s.listen()
print("Server is listening")
conn, addr = self.s.accept()
print(f"{addr} is now connected.")
while True:
data = conn.recv(1024).decode("utf8")
print(data)
if not data:
break
if __name__ == "__main__":
txtsocket = TxtSocket()
for i in range(0, 26):
t = threading.Thread(target=txtsocket.server())
t.start()
# Client
import socket
def Text():
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(("127.0.0.1", 5555))
print("Connected")
while True:
message = input("Deine Nachricht: ")
message = message.encode("utf8")
s.send(message)
Text()
Need couple mods to the server to handle multiple clients.
Need main loop to keep accepting new connections and forking off the processing to a thread
Create a new thread to handle client connection when socket gets a new connection.
The following server code works with multiple running clients as specified above.
# server
import socket
import threading
class TxtSocket:
def __init__(self, host="127.0.0.1", port=5555):
self.host = host
self.port = port
self.thread = 0
self.s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.s.bind((self.host, self.port))
print("Socket was created")
def handle(self, conn, addr):
self.thread += 1
print(f"Thread-{self.thread} {addr} is now connected")
while True:
data = conn.recv(1024)
if not data:
break
print(data.decode("utf8"))
conn.close()
def server(self):
# specify the number of unaccepted connections that the server
# will allow before refusing new connections.
self.s.listen(5)
print(f'Server listening on tcp:{self.host}:{self.port}')
while True:
conn, addr = self.s.accept()
# create new thread to handle the client connection
t = threading.Thread(target=self.handle, args=(conn, addr))
t.start()
if __name__ == "__main__":
txtsocket = TxtSocket()
txtsocket.server()
Note that Python has a SocketServer module that can make some of this easier with a TCPServer that does much of the work. See server example.
you can use thread for close other connections
import socket
from _thread import start_new_thread
server = socket...
first_connection = None
def check_con_isalive():
try:
while True:
first_connection.send(b"\0")
except Exception:
print("connnection was closed")
first_connection.close()
def thread_con(con):
global first_connection
if not first_connection:
first_connection = con
start_new_thread(check_con_isalive, ())
...
else:
print("blocking new connections")
con.close()
if __name__ == '__main__':
while True:
con, adr = server.accept()
start_new_thread(thread_con, (con, ))
I want to write a very basic python application which will listen on a tcp port.
When a client will connect to this server, i want the server to send notifications (a few bytes) to the client and detect if connexion has been closed by the client.
I do not want the client to send data. The client will be on a embedded device (wifi) and i want to preserve battery life.
I want to detect on the server if connexion with client is closed. (I suppose there are some handshakes in protocol stack that can tell me if link is active ?)
I want to be sure that socket.send is thread-safe. The send_notification method can be called by another thread. So if i send PING and NOTIFICATION at the same time, should the client receive something like PINOTIFICATIONNG for example ?
There is my source code:
import socket
import threading
import time
class ClientThread(threading.Thread):
def __init__(self, manager, ip, port, clientsocket):
threading.Thread.__init__(self)
self.manager = manager
self.ip = ip
self.port = port
self.clientsocket = clientsocket
self.clientsocket.settimeout(1.0)
def run(self):
self.manager.clients.append(self)
while True:
try:
self.clientsocket.send("PING")
except:
break
time.sleep(5)
self.manager.clients.remove(self)
def send_notification(self):
self.clientsocket.send("NOTIFICATION")
class Manager:
def __init__(self):
self.clients = []
thread1 = threading.Thread(target=self.bg_task)
thread1.start()
def send_notification(self):
for client in self.clients:
client.send_notification()
def bg_task(self):
tcpsock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
tcpsock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
tcpsock.bind(("",9008))
while True:
tcpsock.listen(10)
(clientsocket, (ip, port)) = tcpsock.accept()
newthread = ClientThread(self, ip, port, clientsocket)
newthread.start()
Thanks
I've planning on designing a UDP server that works as follows: I will be communicating with clients that are behind a firewall and need to be able to send data at any time. Therefore, the client will first initiate a connection to me and periodically keep the connection alive by regularly sending keep alive packets. When I receive one, I need to acknowledge it. At the same time, if I have any data to send, I need to immediately send it. I've put together the following test code:
import threading
import queue
import socket
import time
class SharedAddress():
def __init__(self):
self.lock = threading.Lock()
self.addr = ()
def set_addr(self, addr):
self.lock.acquire()
self.addr = addr
self.lock.release()
def get_addr(self):
self.lock.acquire()
addr = self.addr
self.lock.release()
return addr
class Reader(threading.Thread):
def __init__(self, socket, shared_address):
super().__init__(name='Reader Thread')
self.socket = socket
self.shared_address = shared_address
def run(self):
while True:
# Wait for data from the client
data, addr = self.socket.recvfrom(4096)
#print("Received data from {}".format(addr))
# Echo it back
self.socket.sendto(data, addr)
self.shared_address.set_addr(addr)
class Writer(threading.Thread):
def __init__(self, socket, shared_address):
super().__init__(name='Writer Thread')
self.socket = socket
self.tx_queue = queue.Queue()
self.shared_address = shared_address
def run(self):
while True:
# Wait for data to be received
data = self.tx_queue.get()
# Send it to the client
addr = self.shared_address.get_addr()
if addr:
self.socket.sendto(data, addr)
### Main loop
# Create the socket
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.bind(('', 2000))
sa = SharedAddress()
r = Reader(s, sa)
w = Writer(s, sa)
r.start()
w.start()
while True:
w.tx_queue.put(b'>>Hi this is a message<<\n')
time.sleep(0.1)
r.join()
w.join()
print("Program ended")
Although the code appears to work, I'm concerned about the fact that I'm using the same socket object from two different threads without any sort of lock. I then modified the Writer class to create its own socket object:
class Writer(threading.Thread):
def __init__(self, shared_address):
super().__init__(name='Writer Thread')
self.socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
self.tx_queue = queue.Queue()
self.shared_address = shared_address
This also seems to work just fine. My questions are as follows:
Are socket objects thread safe?
If you create multiple UDP socket objects in Python and use them to send data to same address, do they actually end up referencing the same underlying connection object?
What happens if I call the close command on one of the socket objects? Presumably it will close the underlying OS socket and prevent the other socket objects from then receiving and transmitting.
I got this multi threaded server code and it works but when I type something to send to the client its not sending it the send function only work if I send the data string
anyone knows what's the problem?
#!/usr/bin/env python
import socket, threading
class ClientThread(threading.Thread):
def __init__(self, ip, port, clientsocket):
threading.Thread.__init__(self)
self.ip = ip
self.port = port
self.csocket = clientsocket
print "[+] New thread started for "+ip+":"+str(port)
def run(self):
print "Connection from : "+ip+":"+str(port)
clientsock.send("Welcome to the server ")
data = "dummydata"
while len(data):
data = self.csocket.recv(2048)
print "Client(%s:%s) sent : %s"%(self.ip, str(self.port), data)
userInput = raw_input(">")
self.csocket.send(userInput)
print "Client at "+self.ip+" disconnected..."
host = "0.0.0.0"
port = 4444
tcpsock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
tcpsock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
tcpsock.bind((host, port))
while True:
tcpsock.listen(4)
print "nListening for incoming connections..."
(clientsock, (ip, port)) = tcpsock.accept()
#pass clientsock to the ClientThread thread object being created
newthread = ClientThread(ip, port, clientsock)
newthread.start()
Well, I can see at least one thing that will prevent this from working as intended:
def run(self):
print "Connection from : "+ip+":"+str(port)
clientsock.send("Welcome to the server ")
clientsock is undefined.
My suggestion is don't try to reinvent the wheel (unless you want to understand how the wheel works). There's already the built-in SocketServer but that is synchronous, meaning each request must be completed before the next request can be started.
There are already very easy to use implementations of asynchronous (non-blocking) TCP servers out there. If you want something that doesn't require you to learn a framework and just runs out-of-the-box, I suggest simpleTCP. Here's an example of an echo server:
from simpletcp.tcpserver import TCPServer
def echo(ip, queue, data):
queue.put(data)
server = TCPServer("localhost", 5000, echo)
server.run()
And here's an example of a client connecting to it:
from simpletcp.clientsocket import ClientSocket
s1 = ClientSocket("localhost", 5000)
response = s1.send("Hello, World!")