Introduction
Python is a great programming language for computer networking. It allows us to create solid applications very fast and easily. In this tutorial, we are going to implement a fully-functioning TCP chat. We will have one server that hosts the chat and multiple clients that connect to it and communicate with each other. At the end, you can also add custom features like chat rooms, commands, roles etc., if you want to.
Client-Server Architecture
For our application, we will use the client-server architecture. This means that we will have multiple clients (the users) and one central server that hosts everything and provides the data for these clients.
Therefore, we will need to write two Python scripts. One will be for starting the server and one will be for the client. We will have to run the server first, so that there is a chat, which the clients can connect to. The clients themselves, are not going to directly communicate to each other but via the central server.
Implementing The Server
Now let’s start by implementing the server first. For this we will need to import two libraries, namely socket and threading. The first one will be used for the network connection and the second one is necessary for performing various tasks at the same time.
import socket
import threading
The next task is to define our connection data and to initialize our socket. We will need an IP-address for the host and a free port number for our server. In this example, we will use the localhost address (127.0.0.1) and the port 55555. The port is actually irrelevant but you have to make sure that the port you are using is free and not reserved. If you are running this script on an actual server, specify the IP-address of the server as the host. Check out this list of reserved port numbers for more information.
# Connection Data
host = '127.0.0.1'
port = 55555
# Starting Server
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind((host, port))
server.listen()
# Lists For Clients and Their Nicknames
clients = []
nicknames = []
When we define our socket, we need to pass two parameters. These define the type of socket we want to use. The first one (AF_INET) indicates that we are using an internet socket rather than an unix socket. The second parameter stands for the protocol we want to use. SOCK_STREAM indicates that we are using TCP and not UDP.
After defining the socket, we bind it to our host and the specified port by passing a tuple that contains both values. We then put our server into listening mode, so that it waits for clients to connect. At the end we create two empty lists, which we will use to store the connected clients and their nicknames later on.
# Sending Messages To All Connected Clients
def broadcast(message):
for client in clients:
client.send(message)
Here we define a little function that is going to help us broadcasting messages and makes the code more readable. What it does is just sending a message to each client that is connected and therefore in the clients list. We will use this method in the other methods.
Now we will start with the implementation of the first major function. This function will be responsible for handling messages from the clients.
# Handling Messages From Clients
def handle(client):
while True:
try:
# Broadcasting Messages
message = client.recv(1024)
broadcast(message)
except:
# Removing And Closing Clients
index = clients.index(client)
clients.remove(client)
client.close()
nickname = nicknames[index]
broadcast('{} left!'.format(nickname).encode('ascii'))
nicknames.remove(nickname)
break
As you can see, this function is running in a while-loop. It won’t stop unless there is an exception because of something that went wrong. The function accepts a client as a parameter. Everytime a client connects to our server we run this function for it and it starts an endless loop.
What it then does is receiving the message from the client (if he sends any) and broadcasting it to all connected clients. So when one client sends a message, everyone else can see this message. Now if for some reason there is an error with the connection to this client, we remove it and its nickname, close the connection and broadcast that this client has left the chat. After that we break the loop and this thread comes to an end. Quite simple. We are almost done with the server but we need one final function.
# Receiving / Listening Function
def receive():
while True:
# Accept Connection
client, address = server.accept()
print("Connected with {}".format(str(address)))
# Request And Store Nickname
client.send('NICK'.encode('ascii'))
nickname = client.recv(1024).decode('ascii')
nicknames.append(nickname)
clients.append(client)
# Print And Broadcast Nickname
print("Nickname is {}".format(nickname))
broadcast("{} joined!".format(nickname).encode('ascii'))
client.send('Connected to server!'.encode('ascii'))
# Start Handling Thread For Client
thread = threading.Thread(target=handle, args=(client,))
thread.start()
When we are ready to run our server, we will execute this receive function. It also starts an endless while-loop which constantly accepts new connections from clients. Once a client is connected it sends the string ‘NICK’ to it, which will tell the client that its nickname is requested. After that it waits for a response (which hopefully contains the nickname) and appends the client with the respective nickname to the lists. After that, we print and broadcast this information. Finally, we start a new thread that runs the previously implemented handling function for this particular client. Now we can just run this function and our server is done.
Notice that we are always encoding and decoding the messages here. The reason for this is that we can only send bytes and not strings. So we always need to encode messages (for example using ASCII), when we send them and decode them, when we receive them.
receive()
Implementing The Client
A server is pretty useless without clients that connect to it. So now we are going to implement our client. For this, we will again need to import the same libraries. Notice that this is now a second separate script.
import socket
import threading
The first steps of the client are to choose a nickname and to connect to our server. We will need to know the exact address and the port at which our server is running.
# Choosing Nickname
nickname = input("Choose your nickname: ")
# Connecting To Server
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client.connect(('127.0.0.1', 55555))
As you can see, we are using a different function here. Instead of binding the data and listening, we are connecting to an existing server.
Now, a client needs to have two threads that are running at the same time. The first one will constantly receive data from the server and the second one will send our own messages to the server. So we will need two functions here. Let’s start with the receiving part.
# Listening to Server and Sending Nickname
def receive():
while True:
try:
# Receive Message From Server
# If 'NICK' Send Nickname
message = client.recv(1024).decode('ascii')
if message == 'NICK':
client.send(nickname.encode('ascii'))
else:
print(message)
except:
# Close Connection When Error
print("An error occured!")
client.close()
break
Again we have an endless while-loop here. It constantly tries to receive messages and to print them onto the screen. If the message is ‘NICK’ however, it doesn’t print it but it sends its nickname to the server. In case there is some error, we close the connection and break the loop. Now we just need a function for sending messages and we are almost done.
# Sending Messages To Server
def write():
while True:
message = '{}: {}'.format(nickname, input(''))
client.send(message.encode('ascii'))
The writing function is quite a short one. It also runs in an endless loop which is always waiting for an input from the user. Once it gets some, it combines it with the nickname and sends it to the server. That’s it. The last thing we need to do is to start two threads that run these two functions.
# Starting Threads For Listening And Writing
receive_thread = threading.Thread(target=receive)
receive_thread.start()
write_thread = threading.Thread(target=write)
write_thread.start()
And now we are done. We have a fully-functioning server and working clients that can connect to it and communicate with each other.
Running The Chat
Let’s go for a test run. Just keep in mind that we always need to start the server first because otherwise the clients can’t connect to a non-existing host. Here you can see an example of a chat with two clients (you can also connect with 20 if you want):
Server Log
Connected with ('127.0.0.1', 4970)
Nickname is Neural
Connected with ('127.0.0.1', 4979)
Nickname is Nine
Client A Log
Choose your nickname: Neural
Neural joined!Connected to server!
Nine joined!
Hello
Neural: Hello
Nine: What's up?
Not much! How about you?
Neural: Not much! How about you?
Nine: Same! Gotta Go! Bye!
Nine left!
Client B Log
Choose your nickname: Nine
Nine joined!
Connected to server!
Neural: Hello
What's up?
Nine: What's up?
Neural: Not much! How about you?
Same! Gotta Go! Bye!
Nine: Same! Gotta Go! Bye!
As you can see, it works very well! You can now modify and customize your chat however you want. Maybe you want to have multiple chat rooms or maybe you want to have different roles like admin and moderator. Be creative!
I hope you enjoyed this tutorial and you learned something. If you are interested in a GUI version of this chat, please leave a comment. It works quite differently, since it works more with events than with endless loops. Also, if you want to tell me something or ask questions, feel free to ask in the comments! Check out my instagram page or the other parts of this website, if you are interested in more! Stay tuned!
an error came
Traceback (most recent call last):
File “server.py”, line 11, in
server.listen()
File “/usr/lib/python2.7/socket.py”, line 228, in meth
return getattr(self._sock,name)(*args)
TypeError: listen() takes exactly one argument (0 given)
yes its working
i have used python2 instead of python3
You can write the no of connections in it like 10 or 100. That’s it.
I put 1 in there.
Your should put listen(5 or how much client you want to connect)
Can you help me tejaswan, i am getting the same error.
If u have the same error as tejaswan, then u should download python3 python3 programs are not suitable for python2
try keeping some value inside the listen function…
Hey!
Just asking out of curiosity, how to go about a client – client version of this?
do you mean peer to peer, multiple clients?
you are right but in perspective the client is also a server bcoz he is accepting or sending the messages, it is same for all.
I would like to know how to do a a visual studio ui version of this
When does the handle function on the server side get called? I read over and over through the code, and I saw that the broadcast function got called in the receive function and the receive function get called at the end of the script, but the handle function was only defined and not called, is there a reason for this?
File “c:\Users\91994\Desktop\RJ networks\python\TCP_server.py”, line 8
server.bind((host,port))
^
SyntaxError: invalid syntax
I just want to clear my doubt your video was awesome…..but, I don’t know why I am getting this error in visual studio
hey, how can I use a public IP in these scripts so that I can run it over the internet?
HI, i tried to run this chatroom. the server runs correctly, but when i open the client, insert the nickname and connect to server, the server automatically get closed and the client starts a loop. how can i solve?
It would be awesome to learn how to make this not only on local but allow people from any router to access and type through it.
how to transfer the file in chat room between clientt
the code is working but i cant type messages.
HI!
Very useful your video!
I’m new programming in Python and take a challenge of programming a multi player game. I implemented your code in my pc and works very good for more than one client. I got a Mac too, and implemented the client on it and I had to do some changes in server code. I had to use host=’0.0.0.0′ to get comunicaton with the mac. Althoug i succeded in comunications, they behave different on the mac. The incoming messages to the mac appears to be enqueued. You have to press enter to get them on the display. Any hint about this ..
had the same problem change python to python3
Maybe just put a 1 in server.listen(1)
That’s,working pretty good thanks Neuralnine!
How do i bring it online? on my wifi that my friends can chat there and more?
Hey!,
On my system the chats are not syncing . What’s the problem?
It is a great project.
I have admin, senior and junior added to this project.
Thank you so much for sharing!!
Is it possible to get it to run on Python 3?
I do get the following error:
14 “stream = p.open(format=FORMAT,
15 channels=CHANNELS,
16 rate=RATE,
17 input=True,
18 frames_per_buffer=CHUNK,”
Exception has occurred: OSError
[Errno -9999] Unanticipated host error
File “G:\Documents\Programmering\Instant-Messaging-master\send_voice.py”, line 18, in
frames_per_buffer=CHUNK,
File “G:\Documents\Programmering\Instant-Messaging-master\Client.py”, line 14, in
from send_voice import send_voice
File “G:\Documents\Programmering\Instant-Messaging-master\IM_client.py”, line 11, in
from Client import Client
how do i remove/stop/kill the thread
host = ‘127.0.0.1’_# localhost
^
SyntaxError: invalid syntax
can anyone help me with this please
When using it on two devices server sends blank messages if message sent from client
how would i use this online? like from computer to computer
Thank you for this code. I need to use this code in order to send notification to thounsands of my clients without discuss with them..
Just send them message when i wish..
How can i modify the client code?
It’s possible?
Need a help please
Thank you
I need Gui version of this project please
Hi, thanks for the article, very helpful and descriptive, I have an issue where I can’t send a message from any client. If type a message and press enter it just goes to the next line and does not appear in the terminal of the other connected client.
I have reviewed my code and it seems fine, any suggestions on what could be causing it are welcome.
Many thanks
Jackjack
can I use this code and connect to another server and port?
Hi NeuralNine , you forgot to attach ” handle function ” of server’s piece of code in this blog post .
Yes plz make a GUI version of this plz….
working just great and learning as we go thanks
I got this error when used it on a old version of python try “server.listen(0)”