--> Skip to main content

Featured

Steps to Create a Project on GitHub

Steps to create a project on GitHub:  1.   Start a repo on GitHub 2.   Make a README.md file 3.   Open vs code – new folder – new terminal – git clone http:…. (from the repo). 4.   In terminal – cd theprojectname   à move into the project file 5.   Ls -la is for showing hidden file à not working for me ???? 6.   Make some changes to README file, in terminal git status à shows all the changes on the file 7.   To add all changes update to the folder à git add . (the dot . means all changes to be added) 8.   Add a new file index.html, in terminal à git commit -m “title of commit” -m “description of commit” 9.   Then git push origin master 10.                 ****Initial a repo in local text editor à git init 11.                 After use git add . etc, when pus...

Python Networking Socket Programming - Create a Server, Send Quotes to Clients

 What we will do today is to create a server and a client architecture with Python. The client can send out requests (messages) to the server, the server then give a response to the client, the client  will receive the response. This is a very basic server that doesn't use threading to handle multiple requests from different clients. 

In this project, we use socket interface to create both the server and the client, and establish a connection between them. I borrowed this diagram to show the exact flow of connections between a server and a client. 

Flow Diagram for BSD Sockets Communication using TCP

The socket module in Python is a low-level interface, it can be used for both client and server sides. There is another higher level framework called SocketServer, which is built on top of socket module. It can only be used for servers, but is able to handle multiple clients at a time.

without further ado, let's work on this projectd now. we first need to create a server. It is called 'server.py'. A pair of host IP address and port number is required for binding the server host to the socket. Choose a port number larger than 1023 because most of the smaller numbers are preserved already. For this project, a pair (host, port) is used for the AF_INET address family, where host is a string representing either a hostname like this blog  'algorithmsinmylife.blogspot.com' or an IPv4 address like '100.50.200.5', and port is an integer. 

I use the local computer as the server, use socket.gethostname() to get the host of local machine, then use gethostbyname() to translate it into a suitable constant for passing to bind() call.

A server is ready for listening for any incoming requests after bind() and listen() calls. In an infinite loop, use accept() to accept connections constantly from clients. Once any requests come in, it will return a pair (connection, address); The connection is a new socket object usable to send and receive data on the connection, the IP address is the client that bound to the socket on the other end this connection. We can receive messages from the client and send out responses to the client. The server will stay active for receiving any connection requests from clients until close() is called.

'''
    This is the server, wait for receiving requests from clients
'''
import socket

# Use this local computer as the server, 
# To manually get the ip address: cmd->ipconfig->ipv4 address.
host = socket.gethostbyname(socket.gethostname())
# use a port number outside the 'well-known's ( >1023 )
port = 8989

# create a socket for connecting to clients, 
# This is not for talking to clients yet. 
socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # tcp
# bind to a tuple
socket.bind((host, port))
# queue up max 5 connect requests b4 refusing outside connections.
socket.listen(5)

# run server infinitely untill close() is called
while True:
    # To wait and accept requests from clients, a new socket is created
    # for talking to the client with the defined ip address
    (newlycreatedsocketforclient, clientIPaddress) = socket.accept()
    
    # receive message in bytes from client
    msg = newlycreatedsocketforclient.recv(1024) # bytes
    # decode msg for human
    decoded_msg = msg.decode('utf-8')
    # to send message in bytes to the client, use encode() 
    newlycreatedsocketforclient.send(
    	f"Thank you for your info: {decoded_msg}".encode())

    # server can be closed if needed
    # newlycreatedsocketforclient.close()

Next, we need to create a client that can connect to this specific server. This client needs to know the server's IP address and the port number. With the help of the socket module, a client can ask for a connection to the server by calling connect(). After a connection is established, the client can send requests to the server and receive responses from it. This client resides in client.py

''' This is the client, send out request to a server '''

import socket

# the server is the current computer
server_host = socket.gethostbyname(socket.gethostname())
# port number has to be the same as the server
port = 8989

socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# use a tuple of (host, port), can use higher level function create_connection() instead 
socket.connect((server_host, port))
# for sending requests to server
# the String message can be an http GET request 'GET http://website.com/page.html HTTP/1.0\r\n\r\n'
socket.send(f'Good Day! from a host client.'.encode())
# for receiving response from server
message = socket.recv(1024)
print(message)

For testing this server / client pair connection, use cmd. Open two seperate cmd interfaces, run server.py on one cmd, and client.py on another. Since server.py doesn't activate close() call, we can keep on sending several messages from client.py and receive the returned responses from the server.

For source code, please visit github page


NOTE: that a server must perform the sequence socket(), bind(), listen(), accept() (possibly repeating the accept() to service more than one client), while a client only needs the sequence socket(), connect().

To make the server more close to realistic, I mimic a quote app to offer clients with quotes for each request. By engineering a random quote into the sending message from the server, I updated the server.py to server_for_quotes.py, and here is the new code. The Python random module is used for generating a random index for the quote database.

'''
    This is the server, wait for receiving requests from clients
'''
import socket
import random

quote_db = [
    '''The greatest glory in living lies not in never falling, 
    but in rising every time we fall. -Nelson Mandel''', 
    '''The way to get started is to quit talking and begin 
    doing. -Walt Disney''',
    '''Your time is limited, so don't waste it living someone 
    else's life. Don't be trapped by dogma – which is living 
    with the results of other people's thinking. -Steve Jobs''',
    '''If life were predictable it would cease to be life, and 
    be without flavor. -Eleanor Roosevelt''',
    '''If you look at what you have in life, you'll always have 
    more. If you look at what you don't have in life, you'll 
    never have enough. -Oprah Winfrey'''
]


# use this local computer as the server, 
# cmd->ipconfig->ipv4 address can also mannually get the address
host = socket.gethostbyname(socket.gethostname())
# use a port number outside the 'well-known's
port = 8989

# create a socket for connecting to clients, 
# not for talking to clients. 
socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # tcp
# bind to a tuple
socket.bind((host, port))
# queue up max 5 connect requests b4 refusing outside connections.
socket.listen(5)

# run server infitely
while True:
    # wait and accept requests from clients, a new socket is created
    # for talking to this client with the ip address
    (newlycreatedsocketforclient, clientIPaddress) = socket.accept()
    # receive message from client
    msg = newlycreatedsocketforclient.recv(1024) # bytes
    # decode msg for human
    decoded_msg = msg.decode('utf-8')
    print(decoded_msg)
    # to send message to the client, use encode() 
    index = random.randint(0, 4)
    print(index)
    quote_to_send = quote_db[index]
    newlycreatedsocketforclient.send(
    	f"Thank you! Your quote today is: {quote_to_send}".encode())

    # server can be closed if needed
    # newlycreatedsocketforclient.close()

The updated source code is here.

Popular Posts