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()
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.
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()
and recv()
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.
Here we have a naive implementation of a client-server chat app using python socket programming.
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 the join
method or the send_message
method 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 the start
method.
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 address localhost
and port 11000
.
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 a join
signal, and forwards the message to the recipient if it receives a send_message
signal from a the sender.
python3 server.py
You have successfully started the server.
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()