-
Notifications
You must be signed in to change notification settings - Fork 7
Session 10: Client_Server
- Time: 2h
- Date: Tuesday, March-03th-2020
- Goals: *
- Introduction
- Introduction to the programming of servers
- Introduction to the programming of servers
- Exercises
- End of the session
- Author
- Credits
- License
We already known the basic communication mechanism between two applications located in different computers. Each of them is identified with the IP and PORT. We use the abstraction of sockets. These are objects that has some methods for allowing the communication. The bytes written to one socket are received on the other side, and vice-versa
The way the twos apps behaves in the communication depends on the model used. In the client-model, the initiative of the communications relies on the client, while the server is just waiting for clients to connect
We saw in the previous sessions that the client uses only one socket to connect to a server
In the case of Servers they need two sockets: One for listenning to the connections from the clients, and the other for transferring the data from/to the client, once the connection has been established
The communication process between the client and server is as follows:
-
Step 1: Initial state
- The client has created a socket
- The sever has created a socket and configured it in listening mode
-
Step 2: The client start the connection (calling the connect() method in the socket)
- Step 3: The server creates another socket for communicating with the client
- Step 4: The client and the server communicates normally, using the "blue" sockets. The "red" socket continues listening for new connection from other clients
What happens if another client tries to connect to the server when it is already connected to the previous client?
The clients connect to the first socket, the one that is in listening mode. Typically, whenever there is a new connection, the server creates a copy of itself (we call it a fork, or a thread) that attends the client using another socket
Depending on the capacity of the server, it could attend more or less clients. If the server is busy and it cannot attend more clients, the listening socket will put the requests in a queue. They will be attended when the server finished with the previous clients
In the servers we are developing in this subject, we will only attend one client at a time, for simplicity
We will learn the basic ideas used for programming a simple server
The steps that should be followed to setup a working server are:
- Step 1: Create the socket (Method socket())
- Step 2: Configure the socket: bind it to the remote IP and PORT (Method bind()
- Step 3: Configure the socket in listening mode (Method listen())
-
Main loop:
- Step 5: Wait for a client to connect (method accept())
- Step 6: When a client connects, the socket library creates a new socket for communicating with the client
- Step 7: Read the client messages. What does the client want? (metho rcv)
- Step 8: Process the request and send a response message (method send)
Let's write our first server. We will call it happy server, as it is always sending us a message no matter what is the request from the client
Create the Session-10 folder in your working repo. Our server will be programed in the happy-server.py file
We will write our server from scratch. Make sure you understand all the parts
This is the version 1. We create the socket, bind it to its IP and PORT parameters and configure it for being a listening socket. Finally we close the socket
import socket
# Configure the Server's IP and PORT
PORT = 8080
IP = "192.168.1.45"
# -- Step 1: create the socket
ls = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# -- Step 2: Bind the socket to server's IP and PORT
ls.bind((IP, PORT))
# -- Step 3: Configure the socket for listening
ls.listen()
print("The server is configured!")
# -- Close the socket
ls.close()
This server will be running in your computer. Makes sure that your IP address is correct.
Run it. You will see the message we have printed and nothing more. It just finished ok
The server is configured!
Process finished with exit code 0
If you have not configure the right IP you will see a message in red like this:
Once the server is correctly configured, we wait for connections from the client. It is done by calling the accept() method on the listening socket (ls)
import socket
# Configure the Server's IP and PORT
PORT = 8080
IP = "192.168.124.179"
# -- Step 1: create the socket
ls = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# -- Step 2: Bind the socket to server's IP and PORT
ls.bind((IP, PORT))
# -- Step 3: Configure the socket for listening
ls.listen()
print("The server is configured!")
# -- Waits for a client to connect
print("Waiting for Clients to connect")
ls.accept()
print("A client has connected to the server!")
# -- Close the socket
ls.close()
Run it. You will see this message on the console:
The server is configured!
Waiting for Clients to connect
The program no longer finish (like before). Now it waits for the clients to connect. You can test it with some of the clients you did in Practice 2, or you can directly use the nc command from the linux console:
$ print "Test" | nc 192.168.124.179 8080
In the server's console you will see new messages and then the server is done:
The server is configured!
Waiting for Clients to connect
A client has connected to the server!
Process finished with exit code 0
Let's debug it. We place a breakpoint after calling the accept() method and then we click on the little green bug
The server starts running
Only when a client is connected, the program reaches the breakpoint and stops
From there, we can execute the program step by step until it is finished. In this animation you can see this process
Sometimes, after executing the server and running it again you will see a red error message on the console that says:
OSError: [Errno 98] Address already in use
This is because the port has not still been freed by the operating system. Soemetimes it takes one minute or two to free it. One solution is just to change the server port to another. For example 8081. Then running it again
Another solution is to configure the socket so that the addresses can be reused. You have to add this line below the socket creations:
# -- Step 1: create the socket
ls = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# -- Optional: This is for avoiding the problem of Port already in use
ls.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
The accept() method returns a pair of values: the new socket used for communicating with the client and the client's ip and port values
We store the socket in the cs object (client-socket). We use the rcv() method of the cs socket for receiving the client's message and printing it on the console
# ....
# ....
# -- Waits for a client to connect
print("Waiting for Clients to connect")
(cs, client_ip_port) = ls.accept()
print("A client has connected to the server!")
# -- Read the message from the client
# -- The received message is in raw bytes
msg_raw = cs.recv(2048)
# -- We decode it for converting it
# -- into a human-redeable string
msg = msg_raw.decode()
# -- Print the received message
print(f"Message received: {msg}")
# -- Close the socket
ls.close()
We run the server and send a message from a client, for example with the nc command:
$ printf "Test" | nc 192.168.124.179 8080
We will see the message on the Server's console:
The server is configured!
Waiting for Clients to connect
A client has connected to the server!
Message received: Test
Process finished with exit code 0
All the exercises and experiments performed during this session should be stored in the Session-10 folder
- Filename: Session-08/IPs.py
Let's play a little bit with the IP address. Try to find the IP address of your mobile phone. If you are connected to the URJC wifi you should have an IP
How to find this information depends on your mobile phone. Usually on Android phones you will find it in Settings/system/About the phone/state/IP address
Get the IP address of your computer at the LAB
Open a new file (IPs.txt) and complete the following information:
Machine | Interface | Date | Place | IP |
---|---|---|---|---|
mobile | Wifi | |||
Lab computer | Wire | |||
..... | ..... | ..... | .......... | .............. |
The first column is for your computer in the lab and/or your mobile. The second is the type of interface. Wifi or wire. The third is the date. The fourth is the place: home/Alcorcon Lab and the last one is the IP address.
You should get the IP of your phone when you reach home. And also, take note of your mobile IP when you reach the LAB tomorrow. Write down all the IPs in the table
- Filename: Session-08/Ex2.txt
The session is finished. Make sure, during this week, that everything in this list is checked!
- You have all the items of the session 7 checked!
- Your working repo contains the Session-08 Folder with the following files:
- IPs.txt
- Ex2.txt
- Ex3.py
- server.py
- All the previous files have been pushed to your remote Github repo
- Juan González-Gómez (Obijuan)
- Alvaro del Castillo. He designed and created the original content of this subject. Thanks a lot :-)
S0: Introduction
S1: Tools I
S2: Tools II
S3: Practicing with the tools
S8: Client-Server-1
S9: Client-Server-2
S10: Client-server-3
S11: Client-server-4
S12: HTTP protocol-1
S13: HTTP protocol-2
S14: HTTP module
S15: HTTP module
S16: HTML forms
S17: HTML forms