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.
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.
here i have server and client that can connect together and send message.but second method*(def run(self):)* of these classes in both server and client did not called why they are send and receive message?(Why these methods working without calling them?)
code of server that uses socket and Thread:
from threading import Thread
import socket
class SendingThread(Thread):
def __init__(self, mySocket):
Thread.__init__(self)
self.mySocket = mySocket
def run(self):
# write code to send data continuously
while True:
data = input()
self.mySocket.send(bytes(data, 'utf-8'))
class ReceivingThread(Thread):
def __init__(self, mySocket):
Thread.__init__(self)
self.mySocket = mySocket
def run(self):
# write code to receive data continuously
while True:
msg = self.mySocket.recv(1024)
print(msg.decode('utf-8'))
# create a socket object
s = socket.socket(
socket.AF_INET, # internet address family => IP v4
socket.SOCK_STREAM # TCP
)
# bind socket with a port number
s.bind(('127.0.0.1', 2010))
# keep System_1 in listening mode
s.listen()
# accept the incoming connection request
mySocket, address = s.accept()
# create a thread to send data
sendThread = SendingThread(mySocket)
# create an another to receive data
receiveThread = ReceivingThread(mySocket)
# start both threads
sendThread.start()
receiveThread.start()
code of client that uses socket and Thread:
from threading import Thread
import socket
class MySendingThread(Thread):
def __init__(self, mySocket):
Thread.__init__(self)
self.mySocket = mySocket
def run(self):
# write code to send data to System_1
while True:
data = input()
self.mySocket.send(bytes(data, 'utf-8'))
class MyReceivingThread(Thread):
def __init__(self, mySocket):
Thread.__init__(self)
self.mySocket = mySocket
def run(self):
# write code to receive data from System_1
while True:
msg = self.mySocket.recv(1024)
print(msg.decode('utf-8'))
# create a socket object
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# send a connection request
s.connect(('127.0.0.1', 2010))
# create a thread to send data => System_1
mySendThread = MySendingThread(s)
# create a thread to receive data from System_1
myReceiveThread = MyReceivingThread(s)
# start both threads
mySendThread.start()
myReceiveThread.start()
SendingThread and ReceivingThread in server and MySendingThread and MyReceivingThread in client inherit from threading.Thread
Have a look at the docs:
The Thread class represents an activity that is run in a separate
thread of control. There are two ways to specify the activity: by
passing a callable object to the constructor, or by overriding the
run() method in a subclass. No other methods (except for the
constructor) should be overridden in a subclass. In other words, only
override the __init__() and run() methods of this class.
Once a thread object is created, its activity must be started by calling the thread’s start() method. This invokes the run() method in
a separate thread of control.
When you call start() it invoke the respective run() method you override in the child class.
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 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'm trying to run new process for each new instance of class Server. Each Server instance should listen on specific port. I have this (simplified) code so far: source
class Server(object):
def handle(connection, address):
print("OK...connected...")
try:
while True:
data = connection.recv(1024)
if data == "":
break
connection.sendall(data)
except Exception as e:
print(e)
finally:
connection.close()
def __init__(self, port, ip):
self.port = port
self.ip = ip
self.socket = socket(AF_INET, SOCK_STREAM)
self.socket.bind((self.ip, self.port))
self.socket.listen(1)
while True:
print("Listening...")
conn, address = self.socket.accept()
process = multiprocessing.Process(target=Pmu.handle, args=(conn, address))
process.daemon = True
process.start()
s1 = Server(9001,"127.0.0.1")
s2 = Server(9002,"127.0.0.1")
But when I run this script only first server s1 is running and waiting for connection. How to make both servers listening at the same time?
Your current server is effectively a SocketServer.ForkingTCPServer that enters a tight loop in its __init__, foerever accepting new connections, and creating a new child process for each incoming connection.
The problem is that __init__ never returns, so only one server gets instantiated, one socket gets bound, and only one port will accept new requests.
A common way of solving this type of problem is to move the accept loop into a worker thread. This code would look something like this:
import multiprocessing
import threading
import socket
class Server(object):
def handle(self, connection, address):
print("OK...connected...")
try:
while True:
data = connection.recv(1024)
if data == "":
break
connection.sendall(data)
except Exception as e:
print(e)
finally:
connection.close()
print("Connection closed")
def accept_forever(self):
while True:
# Accept a connection on the bound socket and fork a child process
# to handle it.
print("Waiting for connection...")
conn, address = self.socket.accept()
process = multiprocessing.Process(
target=self.handle, args=(conn, address))
process.daemon = True
process.start()
# Close the connection fd in the parent, since the child process
# has its own reference.
conn.close()
def __init__(self, port, ip):
self.port = port
self.ip = ip
self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.socket.bind((self.ip, self.port))
self.socket.listen(1)
# Spin up an acceptor thread
self.worker = threading.Thread(target=self.accept_forever)
self.worker.daemon = True
self.worker.start()
def join(self):
# threading.Thread.join() is not interruptible, so tight loop
# in a sleep-based join
while self.worker.is_alive():
self.worker.join(0.5)
# Create two servers that run in the background
s1 = Server(9001,"127.0.0.1")
s2 = Server(9002,"127.0.0.1")
# Wait for servers to shutdown
s1.join()
s2.join()
Note one other change I snuck in here:
# Wait for servers to shutdown
s1.join()
s2.join()
Using the saved reference to the Server's accept worker, we call .join() from the main thread to force things to block while the servers are running. Without this, your main program will exit nearly immediately, due to the workers' .daemon attribute being set.
It's also worth noting that this approach will have some quirks:
Since the handler functions are running in separate processes, you will need to share data structures between them carefully using Queue, Value, Pipe, and other multiprocessing constructs if they depend on each other.
There is no rate limiting of active concurrent connections; creating a new process for every single request can be expensive, and can create a vector for your service being easily DoSed.