In my view, one of the easiest way to learn anything new is to develop a simple project around that technology or language. Poof! After a small project, you are kickstarted with the technology. Well, that’s what I tried while started learning Socket Programming as part of Internet Protocols class during my Master’s. I am writing this post with a small project, ‘Creating simple chat application over Python’. It’s just socket programming is easy
Now, Starting with the project! In Socket Programming, we program for both Client and Server as they are the ones’ talking over the connection. Technically, it’s not right to say this but well for the sake of simplicity, it’s one way to put it. For a Chat Application, the simple communication between Client and Server is below:
As it is evident from above image, the Client sends some data/message to the server and server reverts with a message after receiving.
The first thing to start in such an application is Server side code as that’s the first thing needs to be setup before coding for the client machine. If you don’t know much about socket programming, please check the Python Socket Programming basics before moving forward with the post.
import socket import time host = '127.0.0.1' # Loopback address for the port port = 5000 # Port assigned after the range of reserved ports i.e. 1025 clients =  # Now clients can be many, so this list maintains the clients. serverSocket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # Using UDP protocol serverSocket.bind((host,port)) serverSocket.setblocking(0) quitting = False print("Server Started.") while not quitting: try: data, addr = serverSocket.recvfrom(1024) # Here, 1024 is the buffer, which can be set to any value if "Quit" in str(data): quitting = True if addr not in clients: clients.append(addr) print(time.ctime(time.time()) + str(addr) + ": :" + str(data)) # Printing Time stamp of messages from each client. for client in clients: serverSocket.sendto(data, client) except: pass serverSocket.close()
In the code above, first, we define the host and port number of the values which are needed for the server to communicate over the network. Now, the steps on how the code above works:
- First, we define the host and port number for the server for communication over the network. The host value here is taken to be ‘127.0.0.1’ which is the loopback address for the local machine. This address can be replaced with a proper IP address of a machine. Port number taken here is value randomly taken after the assigned/restricted ports from the list. The well-defined ports are those ports which are for different purposes like for the web(80), telnet, ssh etc.
- As this is a chat application, we cannot expect only one or two clients. The ‘clients’ list is initialized in order to manage any additional client on the server.
- The next few lines of code are to set up a UDP based socket. I guess that is evident too.
- We are setting quitting flag to check if any client wants to quit the chat in order to quit the server as soon chat is over.
- In the subsequent while loop, we are receiving data and address of each client from the receiving end of the server. Here, the buffer is set to ‘1024’ which can be changed to any buffer size.
- As UDP is an unreliable connection, so, we need to add try and except block in order to handle any exception occurring in the code.
- Whenever there is a new client which in not in the client’s list, it is appended to the list. The client can then communicate with the connected clients.
For creating a simple chat application using UDP, we are just broadcasting a message from one client to other clients connected to the server. In such an application, there is no provision of a secure message. Well, that can be achieved by different protocols but not UDP.
import socket import threading import time tLock = threading.Lock() shutdown = False def receving(name, sock): while not shutdown: try: tLock.acquire() while True: data, addr = sock.recvfrom(1024) print(str(data)) except: pass finally: tLock.release() host = '127.0.0.1' port = 0 server = ('127.0.0.1',5000) s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) s.bind((host, port)) s.setblocking(0) rT = threading.Thread(target=receving, args=("RecvThread",s)) rT.start() alias = input("Name: ") message = input(alias + "-> ") while message != 'q': if message != '': s.sendto(bytes(alias + ": " + message, 'utf-8'), server) tLock.acquire() message = input(alias + "-> ") tLock.release() time.sleep(0.2) shudown = True rT.join() s.close()
In the code above, we are creating threads to manage communication between client and the server in order to update the messages over the channel.
The receiving function above starts a lock through the thread so that there is no resource problem with the process. Till the time function is receiving messages from the server, the message is taken and printed right away. As soon, there is any connection disruption, the while loop execution stops and we just release the lock.
In the subsequent code, we are setting up the socket and a receiving thread for the client. Now, as the thread is set up, we can start sending messages to other connected clients. This process will continue till there is no ‘q’ message from the client which indicates that the client will stop communicating over the channel. After breaking out of the while loop, it is good to close both socket and thread.
I think I have kept things simple! If you have any doubts or would like to add more thought, you are welcome to comment. 🙂
Code & Learning from: https://www.youtube.com/watch?v=PkfwX6RjRaI