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
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.
I'm working on a tcp server and a tcp client application developed in Python 3.6.
Once connection has been established, the server sends data to the client and receive data from the client itself.
The server should accept a maximum number of clients. What i'd like is that when the maximum number of connected clients is reached, the server does not accept any other connections and the client is notified and aborted.
Here the server code:
class ThreadedServer(object):
def __init__(self, host, port, max_clients):
self.host = host
self.port = port
self.max_clients = max_clients
self.connected_clients = 0
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:
if self.connected_clients >= self.max_clients:
print("Maximum number of clients reached")
continue
client, address = self.sock.accept()
# keep track connected clients
self.connected_clients += 1
# start a new thread to send data to the connected client
# start a new thread to receive data to the connected client
if __name__ == "__main__":
HOST = "xxx.xxx.xxx.xxx"
PORT = xxxx
MAX_CLIENTS = x
ThreadedServer(HOST, PORT, MAX_CLIENTS).listen()
The client code is the following:
class ThreadedClient(object):
def __init__(self, host, port):
self.host = host
self.port = port
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
def send(self):
self.sock.connect((self.host, self.port))
# start a new thread to receive data to the server
# start a new thread to send data to the server
if __name__ == "__main__":
HOST = "xxx.xxx.xxx.xxx"
PORT = xxxx
ThreadedClient(HOST, PORT).send()
Everything works fine until the maximum number of connected clients is reached.
When an "extra" client is launched, it does not (correctly) receive anything from the server but it starts to try to send data. Data are not received because the server did not accept the connection.
What I'd like is find a way to understand when the server did not accept the client connection before starting new threads in order to manage this scenario correctly.
You're calling client.close() before actually retrieving the client. This will mean that the last client that was accepted will still be in the client variable. This connection will be closed, not the new one.
def listen(self):
self.sock.listen(5)
while True:
client, address = self.sock.accept() # this line needs to be before the if
if self.connected_clients >= self.max_clients:
print("Maximum number of clients reached")
client.close()
continue
# keep track connected clients
self.connected_clients += 1
# start a new thread to send data to the connected client
# start a new thread to receive data to the connected client
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 :) .
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!")
I have a server running which spawns new TCP ports implementing the portforward proxy. I need to be able to remove a port and disconnect all clients ASAP.
factory = ProxyFactory(host, port)
port = reactor.listenTCP(0, factory)
then later
port.loseConnection()
This will close the port, but active connections are not closed! How can I close the port and kill all connections?
I solved this using the following code. RPC is the manager server to add new proxies, and ProxyClient overrides twisted.protocols.portforward.ProxyClient
Basically, I had to keep track of clients on my own and call abortConnection when I wanted to kill their transports.
from twisted.internet import protocol, reactor, error
from twisted.web import xmlrpc, server
from twisted.python import log, failure
import socket
class RPC(xmlrpc.XMLRPC):
proxies = {} # (host, port): tcp.Port()
clients = [] # list of active client transports
def __get(self, host, port):
if (host, port) in self.proxies.keys():
return self.proxies.get((host, port))
return self.__new(host, port)
def __new(self, host, port):
factory = ProxyFactory(host, port)
tcp_port = reactor.listenTCP(0, factory)
self.proxies[(host, port)] = tcp_port
return tcp_port
def xmlrpc_get(self, host, port):
log.msg('get {}'.format(host, port))
port = self.__get(host, port)
return port.getHost().port
def xmlrpc_kill(self, host, port):
log.msg('kill {}'.format(host, port))
tcp_port = self.proxies.pop((host, port), None)
if not tcp_port:
return False
tcp_port.loseConnection() # don't listen anymore
try:
ip = socket.gethostbyname(host)
except:
return False
for client in list(self.clients):
# kill connections now because we're anxious
peer = client.getPeer()
if (ip, port) == (peer.host, peer.port):
log.msg('abort {}'.format(client))
client.abortConnection()
self.clients.remove(client)
return True
class ProxyClient(Proxy):
def connectionMade(self):
RPC.clients.append(self.transport)
self.peer.setPeer(self)
# Wire this and the peer transport together to enable
# flow control (this stops connections from filling
# this proxy memory when one side produces data at a
# higher rate than the other can consume).
self.transport.registerProducer(self.peer.transport, True)
self.peer.transport.registerProducer(self.transport, True)
# We're connected, everybody can read to their hearts content.
self.peer.transport.resumeProducing()
def connectionLost(self, reason):
if self.transport in RPC.clients:
RPC.clients.remove(self.transport)