Cannot connect to multi-threaded TCP server - python

I'm trying to setup a threaded server where multiple clients can connect at the same time. This is a bit long, please do bear with me. I've already read this helpful realpython article on sockets as well as the socket and socketserver docs.
Python provides facilities to create a server and the socketserver documentation even shows us:
import socket
import threading
import socketserver
class ThreadedTCPRequestHandler(socketserver.BaseRequestHandler):
def handle(self):
data = str(self.request.recv(1024), 'ascii')
cur_thread = threading.current_thread()
response = bytes("{}: {}".format(cur_thread.name, data), 'ascii')
self.request.sendall(response)
class ThreadedTCPServer(socketserver.ThreadingMixIn, socketserver.TCPServer):
pass
def client(ip, port, message):
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
sock.connect((ip, port))
sock.sendall(bytes(message, 'ascii'))
response = str(sock.recv(1024), 'ascii')
print("Received: {}".format(response))
if __name__ == "__main__":
# Port 0 means to select an arbitrary unused port
HOST, PORT = "localhost", 0
server = ThreadedTCPServer((HOST, PORT), ThreadedTCPRequestHandler)
with server:
ip, port = server.server_address
# Start a thread with the server -- that thread will then start one
# more thread for each request
server_thread = threading.Thread(target=server.serve_forever)
# Exit the server thread when the main thread terminates
server_thread.daemon = True
server_thread.start()
print("Server loop running in thread:", server_thread.name)
client(ip, port, "Hello World 1")
client(ip, port, "Hello World 2")
client(ip, port, "Hello World 3")
server.shutdown()
This works fine on my machine but what I'd like to de-couple the client implementation from the server. So I split the implementations and I have.
server.py:
import socketserver
import threading
class ThreadedTCPRequestHandler(socketserver.BaseRequestHandler):
def handle(self):
data = str(self.request.recv(1024), 'ascii')
cur_thread = threading.current_thread()
response = bytes("{}: {}".format(cur_thread.name, data), 'ascii')
self.request.sendall(response)
class ThreadedTCPServer(socketserver.ThreadingMixIn, socketserver.TCPServer):
pass
def main():
HOST, PORT = "localhost", 8080 # localhost aka 127.0.0.1
server = ThreadedTCPServer((HOST, PORT), ThreadedTCPRequestHandler)
with server:
server_thread = threading.Thread(target=server.serve_forever)
server_thread.daemon = False # Do not stop and keep serving
server_thread.start()
print("Server loop running in thread: ", server_thread.name)
if __name__ == '__main__':
main()
and client.py
import socket
class Client:
def __init__(self, ip, port):
self.ip = ip
self.port = port
def start(self, message):
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
sock.connect((self.ip, self.port))
sock.sendall(bytes(message, "ascii"))
response = str(sock.recv(1024), "ascii")
print("Received: {}".format(response))
def main():
client = Client("localhost", 8080)
client.start("Hello World!")
if __name__ == '__main__':
main()
Then I open two different shell occurences and I do:
$ python3 -m server
The server outputs its message and the shell hangs, which is the expected behavior since I turned daemon mode off and it is supposed to serve forever.
Pinging localhost works fine, all packages are received with no loss. On the other hand, using netstat, I can't seem to find the port I'm opening (8080).
and in the other:
$ python3 -m client
Fails with a ConnectionRefusedError which is the typical error when there is nothing to connect to.
My conclusion for now is that the server is closing the port at some point, I suppose when the main thread dies?
I'm not sure how to fix it though? What's wrong with the current implementation.
Thanks for the help.

Related

Docker Sockets and Python

I am trying to get this simple python3 code to run in docker using sockets, but I receive the following error. Can someone point me in the right direction. I am just trying to get it to work so i can integrate it into my broader project
File "registrar.py", line 29, in <module>
with socketserver.TCPServer((HOST, PORT), MyTCPHandler) as server:
AttributeError: __exit__
Server
import socketserver
import json
list_of_nodes = []
class MyTCPHandler(socketserver.BaseRequestHandler):
"""
The request handler class for our server.
It is instantiated once per connection to the server, and must
override the handle() method to implement communication to the
client.
"""
def handle(self):
global list_of_nodes
# self.request is the TCP socket connected to the client
self.data = self.request.recv(1024).strip()
print("{} wrote:".format(self.client_address[0]))
print(json.loads(self.data))
# just send back the same data, but upper-cased
list_of_nodes.append(json.loads(self.data))
self.request.sendall(bytes(str(len(list_of_nodes)-1), 'utf-8'))
print(list_of_nodes)
if __name__ == "__main__":
HOST, PORT = "localhost", 58333
# Create the server, binding to localhost on port 9999
with socketserver.TCPServer((HOST, PORT), MyTCPHandler) as server:
# Activate the server; this will keep running until you
# interrupt the program with Ctrl-C
server.serve_forever()
Client
import socket
import sys
import time
import json
HOST, PORT = "localhost", 58333
data = " ".join(sys.argv[1:])
dict_node = {'nVersion': 1,
'nTime': time.time(),
'addrMe': socket.gethostbyname(socket.gethostname())
}
# Create a socket (SOCK_STREAM means a TCP socket)
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
# Connect to server and send data
sock.connect((HOST, PORT))
json_msg = json.dumps(dict_node)
sock.sendall(bytes(json_msg, "utf-8"))
# Receive data from the server and shut down
received = str(sock.recv(1024), "utf-8")
print("Sent: {}".format(json_msg))
print("Received: {}".format(json.loads(received)))

how to maintain connection between several clients and server in a Python Sockets program

below is a basic program that has the client enter a name, then it connects to the server and gets a response, and the server then appends the name to a list, the problem is when a second client connects to the server the first client loses connection and this happens for every client that connects. how do I solve this?
server.py :
import socket
s = socket.socket()
host = "127.0.0.1"
port = 5409
s.bind((host, port))
names = []
while True:
s.listen(5)
c, addr = s.accept() # Establish connection with client.
print 'Got connection from', addr
while True:
try:
name = c.recv(1024)
print name
except:
print ""
if name not in names:
names.append(name)
message = "Hello " + name
c.sendall(message)
print names
break
client.py :
import socket # Import socket module
s = socket.socket() # Create a socket object
host = "127.0.0.1" # Get local machine name
port = 5409 # Reserve a port for your service.
name = raw_input("What Is your Name? ")
s.connect((host, port))
while True:
try:
s.send(name)
except:
break
try:
print s.recv(1024)
except:
break
You will need to make your server able to handle concurrent connections, either with multithreading, multiprocessing, or select.
The socketserver module provides convenient basic server classes using threading or multiprocessing. The official documentation has some good examples. Here is one using the threading module for concurrency:
import socket
import threading
import socketserver
class ThreadedTCPRequestHandler(socketserver.BaseRequestHandler):
def handle(self):
data = str(self.request.recv(1024), 'ascii')
cur_thread = threading.current_thread()
response = bytes("{}: {}".format(cur_thread.name, data), 'ascii')
self.request.sendall(response)
class ThreadedTCPServer(socketserver.ThreadingMixIn, socketserver.TCPServer):
pass
def client(ip, port, message):
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
sock.connect((ip, port))
sock.sendall(bytes(message, 'ascii'))
response = str(sock.recv(1024), 'ascii')
print("Received: {}".format(response))
if __name__ == "__main__":
# Port 0 means to select an arbitrary unused port
HOST, PORT = "localhost", 0
server = ThreadedTCPServer((HOST, PORT), ThreadedTCPRequestHandler)
ip, port = server.server_address
# Start a thread with the server -- that thread will then start one
# more thread for each request
server_thread = threading.Thread(target=server.serve_forever)
# Exit the server thread when the main thread terminates
server_thread.daemon = True
server_thread.start()
print("Server loop running in thread:", server_thread.name)
client(ip, port, "Hello World 1")
client(ip, port, "Hello World 2")
client(ip, port, "Hello World 3")
server.shutdown()
server.server_close()
If you would like to build your own server without using socketserver, you can look at the source for the socketserver module (it's simple), or there are plenty of examples online of basic TCP/UDP servers using all three concurrency methods.

Multi threaded server send function

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!")

How to make a simple multithreaded socket server in Python that remembers clients

How do I make a simple Python echo server that remembers clients and doesn't create a new socket for each request? Must be able to support concurrent access. I want to be able to connect once and continually send and receive data using this client or similar:
import socket
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
host = raw_input("Server hostname or ip? ")
port = input("Server port? ")
sock.connect((host,port))
while True:
data = raw_input("message: ")
sock.send(data)
print "response: ", sock.recv(1024)
I.e. with the server running on port 50000, using the above client I want to be able to do this:
me#mine:~$ client.py
Server hostname or ip? localhost
Server Port? 50000
message: testa
response: testa
message: testb
response: testb
message: testc
response: testc
You can use a thread per client to avoid the blocking client.recv() then use the main thread just for listening for new clients. When one connects, the main thread creates a new thread that just listens to the new client and ends when it doesn't talk for 60 seconds.
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__":
while True:
port_num = input("Port? ")
try:
port_num = int(port_num)
break
except ValueError:
pass
ThreadedServer('',port_num).listen()
Clients timeout after 60 seconds of inactivity and must reconnect. See the line client.settimeout(60) in the function ThreadedServer.listen()

Connections outlive SocketServer.TCPServer?

How can I make a TCP server properly close sockets?
I wrote a simple TCP server to echo some information back to client. Now I would like to use it on localhost:8088 and stop it using killall and restart at any time as I'm working on the code.
However, I'm having trouble making it close all sockets and "free" the address, so when I quickly make a few tests, fix something in the code and then stop (Ctrl+C) and start the server again, I get socket.error: [Errno 98] Address already in use.
When I sudo netstat -ntap, I can still see few 127.0.0.1:8088 sockets in TIME_WAIT state. So I have to wait until they "die out".
My test case:
#!/usr/bin/python
import SocketServer
class MyEchoHandler(SocketServer.BaseRequestHandler):
def handle(self):
self.data = self.request.recv(1024).strip()
# do something smart with the data, but for now, just say hello.
self.reply = "Content-Type: text/plain\r\n\r\nhello."
self.request.send(self.reply)
self.request.close()
def main():
server = SocketServer.TCPServer((HOST, PORT), MyEchoHandler)
server.serve_forever()
if __name__ == '__main__':
HOST, PORT = "localhost", 8088
main()
What am I doing wrong? Shouldn't self.request.close() be enough?
I'm trying this on Debian with Python 2.7.3, although I need to support Squeeze with Python 2.6 as well.
The TCP stack puts the port into timed-wait for awhile (something like 30 seconds to multiple minutes depending on your system). You can change that behavior with the SO_REUSEADDR socket option. The trick is that the option must be set before the port is bound.
If you have the raw socket, you can:
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
SocketServer lets you set the option globally with the allow_reuse_address attribute:
SocketServer.TCPServer.allow_reuse_address = True
Or you can do it when you create the server:
def main():
server = SocketServer.TCPServer((HOST, PORT), MyEchoHandler, False)
server.allow_reuse_address = True
server.server_bind()
server.serve_forever()
Or even override the bind method:
class MyTCPServer(SocketServer.TCPServer):
def server_bind(self):
self.allow_reuse_address = True
super(MyTCPServer, self).server_bind()
Here are 3 solutions that worked for me on my Windows machine.
(1) set globally
#!/usr/bin/python
import SocketServer
import time
SocketServer.allow_reuse_address = True
class MyEchoHandler(SocketServer.BaseRequestHandler):
def handle(self):
self.data = self.request.recv(1024).strip()
# do something smart with the data, but for now, just say hello.
self.reply = "Content-Type: text/plain\r\n\r\n" + time.asctime()
self.request.send(self.reply)
self.request.close()
def main():
server = SocketServer.TCPServer((HOST, PORT), MyEchoHandler)
server.serve_forever()
if __name__ == '__main__':
HOST, PORT = "localhost", 8088
main()
(2) Set locally
#!/usr/bin/python
import SocketServer
import time
class MyEchoHandler(SocketServer.BaseRequestHandler):
def handle(self):
self.data = self.request.recv(1024).strip()
# do something smart with the data, but for now, just say hello.
self.reply = "Content-Type: text/plain\r\n\r\n" + time.asctime()
self.request.send(self.reply)
self.request.close()
def main():
server = SocketServer.TCPServer((HOST, PORT), MyEchoHandler, False)
server.allow_reuse_address = True
server.server_bind()
server.server_activate()
server.serve_forever()
if __name__ == '__main__':
HOST, PORT = "localhost", 8088
main()
(3) Inherit
#!/usr/bin/python
import SocketServer
import time
class MyEchoHandler(SocketServer.BaseRequestHandler):
def handle(self):
self.data = self.request.recv(1024).strip()
# do something smart with the data, but for now, just say hello.
self.reply = "Content-Type: text/plain\r\n\r\n" + time.asctime()
self.request.send(self.reply)
self.request.close()
class MyTCPServer(SocketServer.TCPServer):
def server_bind(self):
self.allow_reuse_address = True
SocketServer.TCPServer.server_bind(self)
def main():
server = MyTCPServer((HOST, PORT), MyEchoHandler)
server.serve_forever()
if __name__ == '__main__':
HOST, PORT = "localhost", 8088
main()
You can call server.shutdown() but you have to do it from another thread 8-( IMO this is weak area in Python's TCPServer.

Categories