How to publish a Python based TCP server on internet

BowTiedTechGuy
8 min readJul 8, 2023

--

In this tutorial, I will explain how you can create a simple TCP server, publish it on internet so that you code running on your local machine is available to anyone on the internet. You will not purchase any domain name or any server in the cloud for this.

We will do this in three steps:

1. Create a simple TCP server and client in Python

2. Change the TCP server to handle multiple TCP connections

3. Understand how we can publish the TCP server on the internet.

It is a misconception that you need to buy a domain name to publish anything on the internet. This misconception stems from the fact that your local machine, either the machine name or the IP address of the machine is not accessible from the internet. What is even more baffling for the uninitiated is that you are told that you can run ipconfig command on windows and see your IP address. But when you go to https://whatismyipaddress.com/ and check your IP address, you see an address that you most likely have never seen before. We will get to all those in step 3 but first let's build a server and a client.

This script starts a server that listens for connections on port 8585. When a client connects, the server receives data from the client, prints the data, and sends the data back to the client in uppercase.

# TCP SERVER CODE
import socketserver

class MyTCPHandler(socketserver.BaseRequestHandler):
"""
The RequestHandler class for our server.

It is instantiated once per connection to the server, and overrides the
handle() method to implement communication to the client.
"""
def handle(self):
while True:
# self.request is the TCP socket connected to the client
self.data = self.request.recv(1024).strip()
if not self.data:
break
print(f"{self.client_address[0]} wrote: {self.data.decode('utf-8')}")
# just send back the same data, but upper-cased
self.request.sendall(self.data.upper())

if __name__ == "__main__":
HOST, PORT = "localhost", 8585

# Create the server, binding to localhost on port 8585
with socketserver.TCPServer((HOST, PORT), MyTCPHandler) as server:
# Activate the server; this will keep running until you
# interrupt the program with Ctrl-C
print(f"Server starting on {HOST}:{PORT}")
print(f"Clients should connect to {HOST}:{PORT}")
print(f"This will keep running until you interrupt the program with Ctrl-C")
try:
server.serve_forever()
except KeyboardInterrupt:
print("\nServer is shutting down")

You can run the server first in a terminal and this will be the output:

Launched TCP server

This script is a simple TCP client program in Python that establishes a connection to a specified server, sends user-inputted messages to the server, and displays the server’s responses. It keeps sending and receiving messages until the user types ‘bye’. After that, it closes the connection to the server.

# TCP CLIENT CODE
import socket

def client_program():
host = "localhost" # as both code is running on same pc
port = 8585 # socket server port number

client_socket = socket.socket() # instantiate
client_socket.connect((host, port)) # connect to the server

message = input(" -> ") # take input

while message.lower().strip() != 'bye':
client_socket.send(message.encode()) # send message
data = client_socket.recv(1024).decode() # receive response
print('Received from server: ' + data) # show response

message = input(" -> ") # again take input

client_socket.close() # close the connection

if __name__ == '__main__':
client_program()

When you run the client, you get an output like this. And then you can input some text which will get printed on the server as well as client terminal in CAPS.

Client prompt when it connects to server and receives input

Now we are going to make two small changes in the server. Change “localhost” to the IP address of the machine on which you want to host your server for the internet. Launch command prompt and run “ipconfig” command to get your IP V4 address:

IPV4 address of the server

Second change is to use ThreadedTCPServer so that multiple client can connect to the server. The ThreadedTCPServer class, using socketserver.ThreadingMixIn, is designed to create a new thread for each client connection. This means that each client connection is handled in parallel, and the server can accept new connections while processing existing ones.

With this, the server is in final shape and form and looks like this:

# TCP SERVER CODE
import socketserver

class ThreadedTCPServer(socketserver.ThreadingMixIn, socketserver.TCPServer):
pass

class MyTCPHandler(socketserver.BaseRequestHandler):
"""
The RequestHandler class for our server.

It is instantiated once per connection to the server, and overrides the
handle() method to implement communication to the client.
"""
def handle(self):
while True:
# self.request is the TCP socket connected to the client
self.data = self.request.recv(1024).strip()
if not self.data:
break
print(f"{self.client_address[0]} wrote: {self.data.decode('utf-8')}")
# just send back the same data, but upper-cased
self.request.sendall(self.data.upper())

if __name__ == "__main__":
#HOST, PORT = "localhost", 8585
# To debug the server, replace the IP address with "localhost"
HOST, PORT = "192.168.86.39", 8585

# Create the server, binding to localhost on port 8585
with ThreadedTCPServer((HOST, PORT), MyTCPHandler) as server:
# Activate the server; this will keep running until you
# interrupt the program with Ctrl-C
print(f"Server starting on {HOST}:{PORT}")
print(f"Clients should connect to {HOST}:{PORT}")
print(f"This will keep running until you interrupt the program with Ctrl-C")
try:
server.serve_forever()
except KeyboardInterrupt:
print("\nServer is shutting down")

We also need to make one similar change in the TCP client code. We need to find the public IP address of your server and specify it here. Go to a website like https://whatismyipaddress.com/ from the machine and pick your IPV4 address. Put that address in the TCP client code above and it finally looks like this:

# TCP CLIENT CODE
import socket

def client_program():
host = "localhost" # as both code is running on same pc
port = 8585 # socket server port number

# On the machine which is running the TCP server, go to https://whatismyipaddress.com/ and get your public IP address
# To debug the client, replace the IP address with "localhost"
#host, port = "67.168.30.222", 8585

client_socket = socket.socket() # instantiate
client_socket.connect((host, port)) # connect to the server

message = input(" -> ") # take input

while message.lower().strip() != 'bye':
client_socket.send(message.encode()) # send message
data = client_socket.recv(1024).decode() # receive response
print('Received from server: ' + data) # show response

message = input(" -> ") # again take input

client_socket.close() # close the connection

if __name__ == '__main__':
client_program()

You can run the server first and then the client and both the client and server are talking to each other.

Now that we have covered the #1 and #2 in our original goal, let’s start to tackle the last but the most tricky part of it all. How do we publish this server on the internet. You can take your TCP client and run it from any other internet connected machine from your office, or your friend’s laptop. And you will see that it is not able to connect to the server at all. You will keep getting this error:

Error message when client runs on a different machine on internet

There are two things you have to do to get over that problem.

First, you need to let the TCP packet from your friend’s machine get to your internet router and then from your internet router to your own machine or server on which your TCP server is running. This is called Port Forwarding. Network Address Translation (NAT) is a method used by routers to translate the private IP addresses of devices on a local network to a public IP address when communicating over the internet. This allows multiple devices on a local network to share a single public IP address, which is the only address visible to the outside world.

Port forwarding, a subset of NAT, is a technique used to direct incoming internet traffic to a specific device or service on a private local network. When an incoming connection is made to the public IP address on a specified port, the router will redirect this connection to the private IP address and port of the specified device on the local network. This allows services hosted on local devices, like a web server or game server, to be accessible over the internet.

In summary, NAT and port forwarding allow you to expose a service, like your Python TCP server, running on a machine in your local network to the internet. You’d typically configure these settings in your router, setting up a rule to forward incoming connections on a certain port to the local IP address and port of your machine.

Here is what the output looks like on my Google home when I add the setting for port forwarding. Remember that you have to specify both IP address and port where you want the packet forwarded by the router. To the internet, all devices, laptops, phones have a single IP address — that of the router. But the router knows about private IP addresses of all your devices.

Adding port forwarding settings on Google Home

There is one last piece remaining before your TCP client running on your friends machine in New York can connect with your TCP server running on your home machine in San Francisco.

Firewall Rules.

Here are the basic steps you would need to follow:

  1. Configure Port Forwarding on Your Router: You need to configure your router to forward incoming connections on a specific port to the server’s local IP address and port. The process for this varies depending on the make and model of your router, so you may need to consult the router’s manual or do a web search for instructions.
  2. Configure Your Firewall: Next, you need to allow incoming connections to that port on your firewall. Here are the steps for Windows Firewall:
  • Open Windows Firewall.
  • Click on “Advanced Settings”.
  • Click on “Inbound Rules” in the left pane.
  • Click on “New Rule” in the right pane.
  • Choose “Port” and click “Next”.
  • Choose “TCP” (or “UDP” depending on your needs). TCP here.
  • Enter the port number into the “Specific Local Ports” field. 8585 Here.
  • Click “Next” and choose “Allow the Connection”.
  • Click “Next” and choose when the rule applies.
  • Click “Next”, give the rule a name, and click “Finish”.

Outgoing rules control traffic that’s leaving your network, so they typically don’t need to be adjusted for a server that’s accepting incoming connections. However, if your server also needs to make outgoing connections (for example, to retrieve data from another server), you may need to configure outgoing rules as well.

And now your TCP server should be talking to your TCP client over the internet.

Closing thoughts:

You might also want to note that in your TCP server each thread consumes some resources, so creating a large number of threads might slow down your server or even cause it to run out of resources. This is one of the reasons why in a production environment, you might want to use a more robust networking library or a full-featured web server that can manage resources more efficiently.

While Python’s socketserver module is great for creating simple network servers for learning or for internal use, it's not designed for handling heavy traffic in a production environment. For a high-load production server, consider using more advanced frameworks or web servers like Django, Flask, Nginx, etc.

Summary: We create a multi-threaded TCP server, configured firewall on the server and Port Forwarding on the Router to publish the server on internet by running it on a machine at home. Then We used the publich IP address of the router to write a client which will connect o this server over the internet.

Github link for code

--

--

BowTiedTechGuy
BowTiedTechGuy

Written by BowTiedTechGuy

I like to learn and now learning to share

No responses yet