Socket programming in Python
Introduction
Sockets are endpoints in bi-directional communications between two processes.
Sockets allow us to connect, send, and receive messages across a network. The network can be logical, local, or external. A socket connects and then uses the read() and write() commands the same way that file-descriptors use files and pipes.
Python has a library for socket programming that provides an interface to the Berkeley sockets API. You can import the socket library by writing:
import socket
Some fundamental python socket API methods are:
socket()bind()listen()accept()connect()send()recv()close()
TCP and UDP sockets
Before you can use any other socket function, you need to create a socket. To create a socket, we use the socket() method. It is up to the user to create either a TCP socket or a UDP socket.
Compared to a UDP socket, a TCP socket is more reliable and provides in-order data delivery. However, UDP sockets deliver messages faster. It is advised that you use a TCP socket to achieve best-effort delivery from the underlying network.
Creating a TCP socket:
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
Creating a UDP socket:
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
socket.AF_INET is used to designate the type of addresses our socket can communicate with which, in this case, is ipv4 addresses. socket.SOCK_STREAM specifies a TCP socket and socket.SOCK_DGRAM specifies a UDP socket.
Exploring fundamental socket programming methods
Now, we’ll use this illustration to explain the functionality of different socket programming methods.
-
Both the server and client first create a socket to communicate with other processes. To create a socket, we use the
socket()method. -
Server needs to bind its socket to a specific port and IP. This port and IP will be used by the server to send and receive messages. Use the
bind()method to bind the socket. -
After binding to a defined port and IP, the server needs to listen for incoming connections or messages at this port and IP. Use the
listen()method to listen through the socket. -
For a client to communicate with the server, it first needs to connect to it. Use the
connect()method to connect to a port and IP. -
Before proceeding with the exchange of messages, the server needs to accept the request of a client to connect. Use the
accept()method to accept a connection. -
After the connection is established, both the server and client can exchange messages. U se the
send()andrecv()method to exchange messages. -
Both server and client can close a socket and terminate the connection. Use the
close()method to close the socket.
To learn more about socket programming in python, visit the official documentation.
Client-server example
Here we have a naive implementation of a client-server chat app using python socket programming.
client.py
client.py consists of a Client class that takes care of the client-side implementation of the chat app. There are five methods (including the constructor):
-
__init__: Here, the server’s port and address are initialized. Moreover, the client’s socket is initialized and bound to a random port. -
join: This method sends the join-message to the server when the client code is run for the first time. -
send_message: This method sends the message entered by the client to the server – the server then forwards it to the recipient. -
start: This method is called at the start of the code. It calls either thejoinmethod or thesend_messagemethod depending on the input signal. -
receive_handler: This method receives the message from the server and prints it on the terminal. It is called using a thread object in order to run it parallel to thestartmethod.
server.py
server.py consists of a Server class that takes care of the server-side implementation of the chat app. There are three methods (including the constructor):
-
__init__: Here, the server’s socket is initialized and, by default, bound to addresslocalhostand port11000. -
forward_message: This method forwards the message to the recipient that server receives from the sender. -
start: This method is called upon at the start of the code. This method saves the port and address of the client if it receives ajoinsignal, and forwards the message to the recipient if it receives asend_messagesignal from a the sender.
How to run the chat app
- Press the blue button underneath the code written below.
- Once you see the terminal, write the following command and press enter:
python3 server.py
You have successfully started the server.
- Now, to connect a client to the server, open another terminal and write:
cd usercode
then write
python3 client.py -u Sherlock
You have successfully connected a client named Sherlock to the server.
Sherlock is not going to talk to himself, therefore, let’s make another client named Watson:
python3 client.py -u Watson
To send the message from the client terminal, follow this format:
msg {NameOfClient} {ActualMsg}
For example, you can run this command in the terminal of Sherlock:
msg Watson Education never ends, Watson. It is a series of lessons, with the greatest for the last.
Have fun chatting!
import socket
import sys
import random
from threading import Thread
import getopt
class Client:
'''
This is the main Client Class.
'''
def __init__(self, username, dest, port):
self.server_addr = dest
self.server_port = port
self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
self.sock.bind(("", random.randint(10000, 40000)))
self.name = username
def join(self):
join_message = "join" + " " + self.name
self.sock.sendto(join_message.encode("utf-8"),(self.server_addr,self.server_port))
def send_message(self, msg):
actual_message = "send_message" + " " + msg
self.sock.sendto(actual_message.encode("utf-8"),(self.server_addr,self.server_port))
def start(self):
'''
Main Loop is here
Start by sending the server a JOIN message.
Waits for userinput and then process it
'''
self.join()
while True:
userinput = input()
input_recv = userinput.split()
if input_recv[0] == "msg":
self.send_message(" ".join(input_recv[1:]))
else:
print("incorrect userinput format")
continue
def receive_handler(self):
'''
Waits for a message from server and process it accordingly
'''
while True:
server_message, server_addr_port = self.sock.recvfrom(4096)
server_message = server_message.decode("utf-8")
datalist = server_message.split()
if datalist[0] == "forward_message":
msg_recv_list = datalist[1:]
msg_recv = " ".join(msg_recv_list)
print(msg_recv)
if __name__ == "__main__":
DEST = 'localhost'
PORT = 11000
try:
OPTS, ARGS = getopt.getopt(sys.argv[1:],
"u:", ["user="])
except:
print("Wrong command entered.")
exit(1)
USER_NAME = None
for o, a in OPTS:
if o in ("-u", "--user="):
USER_NAME = a
if USER_NAME is None:
print("Missing Username.")
exit(1)
S = Client(USER_NAME, DEST, PORT)
try:
# Start receiving Messages
T = Thread(target=S.receive_handler)
T.daemon = True
T.start()
# Start Client
S.start()
except (KeyboardInterrupt, SystemExit):
sys.exit()
Free Resources