Accepting multiple clients in python socket server - python

I'm trying to build a simple socket server in python:
import socket
import threading
import time
def handle(conn_addr):
print("Someone Connected")
time.sleep(4)
print("And now I die")
host = ''
port = 5000
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
s.bind((host,port))
except socket.error as e:
print(str(e))
s.listen(2)
while True:
threading.Thread(handle(s.accept())).start()
print("Should never be reached")
The socket server should accept multiple clients at the same time. I tried to test its functionality by calling telnet localhost 5000 from multiple tabs from my shell however the pattern that i get is
Someone Connected
And now I die
Someone Connected
And now I die
Instead of
Someone Connected
Someone Connected
Someone Connected
I call the telnet command within 4 seconds of each other so it should have 2 messages of connected in a row however the message is only returned after the previous socket is disconnected. Why is that and how could I go round fixing this?

Its a classic mistake. You called handle() (which slept for 4 seconds) and then tried to create a thread from its result. The target should be a function reference and args should be passed separately.
threading.Thread(target=handle, args=(s.accept(),)).start()
In this version, the main thread waits for an accept and then creates the thread that runs handle.

Related

Handling multiple connections in python with sockets

I have a code which works perfectly for one connection. I have seen two options for multi-client handling but I don't really understand it.
Here is the server socket code:
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as listening_sock:
listening_sock.bind(('', port))
listening_sock.listen()
client_soc, client_address = listening_sock.accept()
client_soc.sendall('200#Welcome to my server!'.encode())
print(f'Address {client_soc.getsockname()[0]} connected with port {client_soc.getsockname()[1]}')
while True:
# get message
msg = client_soc.recv(1024).decode()
# receive log print:
print(f'"{msg}" sent from {client_soc.getsockname()[0]}')
if 'Quit' in msg:
client_soc.sendall('200#Thanks for using my server!'.encode())
client_soc.close()
elif '0' < msg.split('#')[0] <= '9': # one of the valid actions
answer = call_action(msg.split('#')[0], db, msg.split('#')[1]) # the answer for given parameter
client_soc.sendall("200#".encode() + answer.encode())
If I have only one connection it works good and last thing I need to add is option for multiple-client handling. What is the shortest and easiest way to do it?
The code only calls accept once. Instead, call accept in a while loop and create a thread for each client connection so they are handled in parallel. Use the following pattern as an example:
import socket
import threading
# Thread to handle each "client_soc" connection
def handler(client_soc):
...
client_soc.close()
with socket.socket() as listening_sock:
listening_sock.bind(('', 8000))
listening_sock.listen()
while True:
client_soc, client_address = listening_sock.accept()
# Send each "client_soc" connection as a parameter to a thread.
threading.Thread(target=handler,args=(client_soc,), daemon=True).start()
There is also a built-in socket server that simplifies this process. Here's a tested example echo server that echoes back newline-terminated data:
from socketserver import ThreadingTCPServer,StreamRequestHandler
class echohandler(StreamRequestHandler):
def handle(self):
print(f'Connected: {self.client_address[0]}:{self.client_address[1]}')
while True:
# get message
msg = self.rfile.readline()
if not msg:
print(f'Disconnected: {self.client_address[0]}:{self.client_address[1]}')
break # exits handler, framework closes socket
print(f'Received: {msg}')
self.wfile.write(msg)
self.wfile.flush()
server = ThreadingTCPServer(('',8000),echohandler)
server.serve_forever()
Your code blocks itself.
For instance: client_soc, client_address = listening_sock.accept()
Accepts client, then while True: runs forever, so you can work with 1 connection only, because socket.accept() is called once. You should learn some of these to solve your problem: asyncio, threading, multiprocessing. These libraries will help your code to accept and work with clients concurrently. Sockets can use every, but often they are paired with asyncio: https://asyncio.readthedocs.io/

Troubleshooting hanged UDP socket in a Python server application code

I am writing a UDP server application that serves as a back end to Teltonika FMB630 car mounted devices.
I already took care of the protocol specifics and decoding, the problem I am facing relates to the UDP socket used.
My UDP server has to send an acknowledgement to the client device upon receiving a message (that is the protocol), however, if I send those ACKs, the server socket stops receiving data after a while.
The server's UDP socket object is passed to an concurrent.futures.ThreadPoolExecutor that fires a function (send_ack) that sends the ACK, however this is not the issue because I tried calling send_ack in the main thread, after receiving data and the same issue occurs.
I suspect the problem is the remote device somehow breaks the connection or the ISP or MNO doesn't route the reply packet (this is a GPRS device) and then the socket.send() method that is used to send the acknowledge, somehow freezes other socket operations, specifically recvfrom_into called in the main thread loop.
I wrote two scripts to illustrate the situation:
udp_test_echo.py :
#!/usr/env/bin python
import socket
import concurrent.futures
def send_ack(sock, addr, ack):
print("Sending ACK to {}".format(addr))
sock.connect(addr)
print("connected to {}".format(addr))
sock.send(ack)
print("ACK sent to {}".format(addr))
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.bind(("127.0.0.1", 1337))
data = bytearray([0] * 10)
executor = concurrent.futures.ThreadPoolExecutor(max_workers=4)
while True:
print("listening")
nbytes, address = s.recvfrom_into(data)
print("Socket Data received {} bytes Address {}".format(nbytes, address))
print("Data received: ", data, " Echoing back to client")
executor.submit(send_ack, s, address, data[:nbytes])
udp_test_client.py:
#!/usr/env/bin python
import socket
import time
import random
def get_random_bytes():
return bytearray([random.randint(0,255) for b in range(10)])
ip = "127.0.0.1"
port = 1337
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.connect((ip, port))
while True:
stuff_to_send = get_random_bytes()
print("Sending stuff", stuff_to_send)
s.sendall(stuff_to_send)
print("reply: ", s.recvfrom(10))
time.sleep(0.1)
Running udp_test_echo.py in one terminal and udp_test_client.py in another, we see normal operation but if you Ctrl+C the test client and re run it, you will see that the server doesn't respond until it is restarted.
Is there a way to timeout a specific sending operation from a specific call to socket.send() method without affecting other calls ? (I want my socket.recvfrom_into call to block on the main thread)
If I settimeout on the entire socket object, I am going to have to deal with many exceptions while waiting for data in the main thread and I don't like to have to rely on exceptions for proper program operation.
The culprit was the socket.connect() call in send_ack, when being called on the server's socket object it causes the socket to no longer be bound and listen on the port specified in the start of the program.
Instead the send_ack function was changed to be:
def send_ack(sock, addr, ack):
print("Sending ACK to {}".format(addr))
sock.sendto(ack, addr)
print("ACK sent to {}".format(addr))
socket.sendto(data, address) uses the existing connection instead of starting a new one.

Problems with sockets

I'm trying to set up a small server where when the client logs in gets some messages.
The server code
import socket
#Networking
s = socket.socket()
print("Network successfully created")
port = 3642
s.bind(('',port))
print("Network has been binded to %s" %(port))
s.listen(5)
print("Waiting for connections")
while True:
c, addr = s.accept()
print("Got a connection from",addr)
c.send(bytes("Thank you for connecting to me. Currently we","utf-8"))
c.send(bytes("Working on the server","utf-8"))
c.close()
This is the client code
# Import socket module
import socket
# Create a socket object
s = socket.socket()
# Define the port on which you want to connect
port = 3642
# connect to the server on local computer
s.connect(('MyIp..', port))
# receive data from the server
print(s.recv(1024))
# close the connection
s.close()
Everything works fine such as the connecting and the first message gets printed, however I can't get the second message to get printed. The one that says working on the server. I have just began learning about sockets and barely know anything about them so the solution probably is obvious it's just
I can't seem to figure it out. Thank you for any responses. (I would appreciate thorough responses)
If the two sent buffers happen to not get consolidated into a single buffer in the recv (which can happen based on timing, which OS you're running and other factors), then it makes sense that you would not see the second buffer because you're only making one recv call. If you want to receive everything the server sent, put the recv in a loop until it returns an empty string. (Empty string indicates end-of-file [i.e. socket closed by the other end].) – Gil Hamilton

Python: Notification System?

I am working on a chat server that runs on my local network using socket, and then I have a client program running on all of the computers in my house, and this program allows all of the clients to talk to each other.
The problem is, you have to manually update the chat log by pressing enter.
The way I want it to work, maybe, is to check for a new message every few seconds, and if there is a new one, play a sound. Does anyone know how I can do this, I'll try to figure it out on my own, as I have done with most of this project, but any help is appreciated.
Here is the server:
import socket
import sys
# Create a TCP/IP socket
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_address = ('192.168.1.80', 10000)
print >>sys.stderr, 'starting up on %s port %s' % server_address
sock.bind(server_address)
sock.listen(1)
print 'Waiting for user...'
convo='Welcome!'
while True:
# Find connections
connection, client_address = sock.accept()
try:
data = connection.recv(999)
if data=='EMPTY':
pass
else:
print data
convo=convo+'\n'+data
connection.sendall(convo)
except:
connection.close()
Here is the client:
import socket
import sys,os
name=raw_input("Enter name: ")
# Create a TCP/IP socket
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_address = ('192.168.1.80', 10000)
print >>sys.stderr, 'connecting to %s port %s' % server_address
while True:
message=raw_input('Message: ')
try:
os.system('cls')
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect(server_address)
if message is not '':
sock.sendall(name+": "+message)
else:
sock.sendall('EMPTY')
if message=='quit':
break
x=sock.recv(999)
print x
except:
break
sock.close()
Thanks!
If you need two operations to happen at the same time (the client script needs to read input from the user and read new messages from the server), then you'd need to either use threads (one thread for reading user input, and one for reading messages from the server), or futures (since python3.2).
Here's question for playing audio in python: Play audio with Python
As for your client, why are you reconnecting to your server every single time? Anyway, if I understand the problem correctly you're blocking on user input, but also want to handle messages from the server.
Without getting complicated with threads, I would recommended using a recurring signal, which I believe could handle this. There's a function call setitimer(), which will break what you're doing and call a function every so often then return to where you were (user input). In your timer function, check for server messages, print any received, play your sound and return to user input. There's an setitimer() example enter link description here.
Might be a little ugly with the user typing, so you may need to reprint what they're currently typing, but haven't sent out (using something other than raw_input()).
For a slightly more complicated option, which may help you there's a function call select(), which can block while listening for socket input AND user input. Then you just distinguish which is which and keep it all in one loop.
while True:
# select on server socket and user input (blocks for either one)
# if here, either a message has been received or the user typed something
# if a message from server
...
playSound()
# else
# send to server

Test a pair of network sockets at the same time

I have an app X that can run on either of two computers, but on no more than one at once. I have another app Y, written in Python, that given the two possible ip addresses needs to find out which computer is running app X (if any). I've partially solved this by having a UDP service that listens on a port and responds with a 'Hello' whenever it receives some data. The client can try and send data to the app X port on each address and if it gets a response, I know the application is running on that computer.
My code so far looks like this:
def ipaddress(self):
"""Test which side responds on the status port."""
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
try:
s.settimeout(5)
s.sendto("status", (ADDR_A, PORT))
s.recvfrom(1024)
except socket.timeout:
try:
s.sendto("status", (ADDR_B, PORT))
s.recvfrom(1024)
except:
pass
else:
return ADDR_B
else:
return ADDR_A
finally:
s.close()
return None
The problem with this function is that it's called periodically whenever I want to talk to the computer running app X. It will always test ADDR_A first, and if it's not running app X then I have to wait for the socket to timeout before trying ADDR_B. Although it doesn't happen often app X could have switched computers whenever I come around trying again.
Is there a better way? I'm wondering if it's possible to connect to both computers in parallel and return as soon as one responds? Or should I cache which ip address responded first last time the function was called? How would I code these or other ideas?
Thanks.
EDIT: Here is my revised code using select:
def ipaddress(addr_a, addr_b, timeout=5):
"""Test which side responds on the status port."""
# Create UDP sockets for each address
socks = [ socket.socket(socket.AF_INET, socket.SOCK_DGRAM),
socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
]
# Send some data to each socket
for sock, addr in zip(socks, (addr_a, addr_b)):
sock.connect(addr) # do explicit connect so getpeername works
sock.send("status")
# Wait for the first to respond if any
while socks:
waiting = select.select(socks, [], socks, timeout)[0]
if waiting:
for sock in waiting:
try:
data = sock.recv(1024)
if data:
return sock.getpeername()[0]
except Exception, e:
# Occasionally get [Errno 10054] which means socket isn't really
# available, so see if other responds instead...
socks.remove(sock)
else:
break # timeout occurred
return None
You should look at select.select() which provides exactly the capability you are looking for to look at the two computers in parallel.

Categories

Resources