The assignment consisted of implementing a simple chat with Sockets based on a client/server communication. We also needed to insert some functionalities into the application, like changing the name of the user, listing the active clients connected to the chat and even to start a private chat with a specific user. There is no direct communication between clients, all messages need to pass through the server to reach the other end.
Now let’s start to talk about the code!
The strategies to create the system involves using threads to communicate the clients and server parallelly with the help of sockets:
# import libraries from socket import * from threading import Thread # thread
Beginning with the server, the following lines were used for the main configuration. It is needed to describe the server IP, port and operating protocol (TCP in our case):
# Server IP serverName = '' # Server port to connect serverPort = 12000 # TCP protocol serverSocket = socket(AF_INET,SOCK_STREAM) # Bind the Server IP with its port serverSocket.bind((serverName,serverPort)) # Ready to receive connections serverSocket.listen(1)
Next, a loop is started and it creates a new thread each time a client connects to the server:
counter = 0 while 1: # Accept clients connections # This line waits until a new client is connected connectionSocket, addr = serverSocket.accept() # Information order: Socket object, client Nick, Is connected, IP clients.append([connectionSocket,0,0,addr]) # Call thread to manage client t = Thread(target=clientManager, args=(connectionSocket,counter,)) t.start() counter += 1 # Finish server Socket serverSocket.close() begins
The thread to manage the clients is shown below. It starts by receiving the client nickname at its first message. After the thread enters in a loop to manage the next messages the client will send, if it is not a special command such as “name(bob)” or “list()”, the message will be sent to all other connected clients. The “name()” command changes the current name of the client and “list()” returns all available connections to the client.
# Thread to manage each client separetly def clientManager(connectionSocket,t_id): # Receive the clients nick sentence = connectionSocket.recv(1024) # Nick stays at position 1 clients[t_id] = sentence identification = "Cliente %s has logged in." % (sentence) print identification connectionSocket.send(identification) while 1: message = connectionSocket.recv(1024) serv_response = "%s sent: %s" % (clients[t_id],message) print message # Change nickname if "name(" in message: new_name = message.split('name(') new_name = new_name.split(')') new_name = new_name nick_change = "%s changed to: %s"%(clients[t_id],new_name) clients[t_id] = new_name print "New nick is: %s"%new_name for i in range(0, len(clients)): if clients[i]==0: clients[i].send(nick_change) # Send list of connected clients elif "list()" in message: for i in range(0, len(clients)): if clients[i]==0: send_list = "name: %s ip: %s port: 12000\n"%(clients[i],clients[i]) clients[t_id].send(send_list) #Send the message to all clients for a global chat else: for i in range(0, len(clients)): if clients[i]==0: clients[i].send(serv_response) if message == "close()": clients[t_id]=-1 print "Client %s left the room."%clients[t_id] break connectionSocket.close() # Finishes the socket connection<span data-mce-type="bookmark" id="mce_SELREST_start" data-mce-style="overflow:hidden;line-height:0" style="overflow:hidden;line-height:0" ></span>
Subsequently, the client code configuration is very similar to the server as below:
serverName = 'localhost' serverPort = 12000 clientSocket = socket(AF_INET,SOCK_STREAM) # Main configuration difference between client and server clientSocket.connect((serverName, serverPort)) sentence = raw_input('Type your nickname: ') clientSocket.send(sentence)
A thread is started to wait for the server messages without being stopped by the “raw_input()” function. The loop keeps the connection alive until the client types “close()”.
t = Thread(target=receiveMessage, args=(clientSocket,)) t.start() while 1: # Message input to the server sentence = raw_input('') clientSocket.send(sentence) if sentence == "close()": break # Finish client Socket clientSocket.close()
The thread to receive messages parallelly from the server:
# Print the received messages from the server independently def receiveMessage(clientSocket): while 1: msgRecebida = clientSocket.recv(1024) print '%s' % (msgRecebida)
The following images show a demonstration of the chat communication working. The image below starts the server at the port 12000.
After, a client connects and types his nickname.
Another client connects to start a conversation. When the conversation is finished, they both exit the chat.
The next image shows the server terminal, it also receives the ongoing chat between the two users (or more). It also lists the available connections with “list()”. The whole system finishes with a “close()” at the clients and then at the server.
The source code can be found on Github:
There is also an enhanced implementation of the socket chat available on:
In the improved version, two users are able to start a private chat. Both users on a private conversation can change messages that are not going to appear on the global chat to all users connected. As before, the server is always between the exchange of messages.
The next images quickly exemplify the private chat function. Below, user “yang” requests “lenildo” to a private chat:
“lenildo” is asked if he wants to start a private chat with “yang”.
It is important to mention that the implementations described in this post may have some bugs, but they serve the purpose of the proof of concept.