Test a pair of network sockets at the same time - python

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.

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/

Python , communication through two sockets

I am working on a server that continuously send data to a client. This client may also interact punctuality with the server sending a specific request. I wrote a daemon to do this. Note that this daemon works in a thread. For now, the script is structured as follows :
class MyDaemon(threading.Thread):
def __init__(self):
# Init Stream socket (output)
self.MainSock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.MainSock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
self.MainSock.bind(('', 15555))
self.MainSock.listen(5)
self.MainSock.setblocking(0)
# Init Request socket (input)
self.RequestSock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.RequestSock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
self.RequestSock.bind(('', 15556))
self.RequestSock.listen(5)
self.RequestSock.setblocking(0)
def run(self):
while True:
# Listen to connection on MainSock
try:
self.stream, address = self.MainSock.accept()
except:
pass
# Listen to connection on RequestSock
try:
self.request, address = self.RequestSock.accept()
except:
pass
if self.stream:
send_message_continuously() # it is a stream
if self.request:
recv_a_message_from_client()
do_whatever_action_the client_request()
The problem is that :
using only the steamer, all works fine.
using only the requester, all works fine.
using the two sockets at the same time blocks the streamer.
I read that a single thread cannot connect (or be connected) to two sockets at the same time. I also read that the use of the select module may help to handle this kind of problem, but I never used it and I am a little bit lost about its use on my particular case.
What is the more efficient way to handle this problem ?
How to set up selectin my particular case ?
Wouldn't it be more efficent/simple to send stream to a sub-thread and request to another ?
EDIT : Finally, I used a sub-thread for the stream
When using select you have to test, which of your two sockets is ready to accept:
def run(self):
while True:
ready, _, _ = select.select([self.MainSock, self.RequestSock],[],[])
for sock in ready:
if sock is self.MainSock:
send_message_continuously(sock.accept())
elif sock is self.RequestSock:
recv_a_message_from_client(sock.accept())
I suggest to you to try gevent, simple to understant the api, its good to go if you want to overcome the problem, there is a section about servers
to understand about tcp communication & rethink your current solution.
a code snap -
def handle(socket, address):
print('new connection!')
server = StreamServer(('127.0.0.1', 1234), handle) # creates a new server
server.start() # start accepting new connections
Hope you can spend more time on making the application without making skelts. :)

Asynchronous receiving TCP packets in python

I have an instant messaging app, with client written in python.
Code that connects to a server:
def connect(self, host, port, name):
host = str(host)
port = int(port)
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((host, port))
s.send('CONNECT:' + name)
print s.recv(1024)
return s
Then s will be stored in a self.socket.
Here is function that receives tcp packets from server and prints it to command line.
def chat(self):
while True:
data = self.socket.recv(4096)
if not data:
pass
else:
print data
So in my mind it should receive everything server sends and prints out, but it isn't happens. Anyone knows how to bring it to life?
There is a way with select function to monitor multiple streams, make a list of all the streams you need to handle and use the select function for it, for the user input use sys.stdin and all the sockets that you expect to chat with.
check this: https://docs.python.org/2/library/select.htmlBut still the best way to do asynchronous chat will be with udp, it will work really fine

Accepting multiple clients in python socket server

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.

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

Categories

Resources