First, we will make a server-side program that offers a connection to the client and sends a message to the client. Run server1.py:
import socket
host = "192.168.0.1" #Server address
port = 12345 #Port of Server
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind((host,port)) #bind server
s.listen(2)
conn, addr = s.accept()
print addr, "Now Connected"
conn.send("Thank you for connecting")
conn.close()
The preceding code is very simple; it is minimal code on the server side.
First, import the socket module and define the host and port number, 192.168.0.1 is the server's IP address. Socket.AF_INET defines the IPv4 protocol's family. Socket.SOCK_STREAM defines the TCP connection. The s.bind((host,port)) statement takes only one argument. It binds the socket to the host and port number. The s.listen(2) statement listens to the connection and waits for the client. The conn, addr = s.accept() statement returns two values: conn and addr. The conn socket is the client socket, as we discussed earlier. The conn.send() function sends the message to the client. Finally, conn.close() closes the socket. From the following examples and screenshot, you will understand conn better.
This is the output of the server1.py program:
G:PythonNetworking>python server1.py
Now, the server is in the listening mode and is waiting for the client.
Let's see the client-side code. Run client1.py:
import socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
host = "192.168.0.1" # server address
port =12345 #server port
s.connect((host,port))
print s.recv(1024)
s.send("Hello Server")
s.close()
In the preceding code, there are two new methods, s.connect((host,port)), which connects the client to the server, and s.recv(1024), which receives the strings sent by the server.
The output of client.py and the response of the server is shown in the following screenshot:
The preceding screenshot of the output shows that the server accepted the connection from 192.168.0.11. Don't get confused by seeing port 1789; it is the random port of the client. When the server sends a message to the client, it uses the conn socket, as mentioned earlier, and this conn socket contains the client IP address and port number.
The following diagram shows how the client accepts a connection from the server. The server is in listening mode, and the client connects to the server. When you run the server and client program again, the random port gets changed. For the client, the server port, 12345, is the destination port, and for the server, the client random port, 1789, is the destination port:
TCP communication
You can extend the functionality of the server using the while loop, as shown in the following program. Run the server2.py program:
import socket
host = "192.168.0.1"
port = 12345
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind((host,port))
s.listen(2)
while True:
conn, addr = s.accept()
print addr, "Now Connected"
conn.send("Thank you for connecting")
conn.close()
The preceding code is the same as the previous one, except the infinite while loop has been added.
Run the server2.py program, and from the client, run client1.py.
The output of server2.py is shown here:
One server can give service to many clients. The while loop keeps the server program alive and does not allow the code to end. You can set a connection limit to the while loop; for example, set while i>10 and increment i with each connection.
Before proceeding to the next example, the concept of bytearray should be understood. The bytearray array is a mutable sequence of unsigned integers in the range of 0 to 255. You can delete, insert, or replace arbitrary values or slices. The bytearray array's objects can be created by calling the built-in bytearray array.
The general syntax of bytearray is as follows:
bytearray([source[, encoding[, errors]]])
Let's illustrate this with an example:
>>> m = bytearray("Mohit Mohit")
>>> m[1]
111
>>> m[0]
77
>>> m[:5]= "Hello"
>>> m
bytearray(b'Hello Mohit')
>>>
This is an example of slicing the bytearray.
Now, let's look at the split operation on bytearray():
>>> m = bytearray("Hello Mohit")
>>> m
bytearray(b'Hello Mohit')
>>> m.split()
[bytearray(b'Hello'), bytearray(b'Mohit')]
The following is the append operation on bytearray():
>>> m.append(33)
>>> m
bytearray(b'Hello Mohit!')
>>> bytearray(b'Hello World!')
The next example is of s.recv_into(buff). In this example, we will use bytearray() to create a buffer to store data.
First, run the server-side code. Run server3.py:
import socket
host = "192.168.0.1"
port = 12345
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind((host, port))
s.listen(1)
conn, addr = s.accept()
print "connected by", addr
conn.send("Thanks")
conn.close()
The preceding program is the same as the previous one. In this program, the server sends Thanks; six characters.
Let's run the client-side program. Run client3.py:
import socket
host = "192.168.0.1"
port = 12345
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((host, port))
buf = bytearray("-" * 30) # buffer created
print "Number of Bytes ",s.recv_into(buf)
print buf
s.close
In the preceding program, a buf parameter is created using bytearray(). The s.recv_into(buf) statement gives us the number of bytes received. The buf parameter gives us the string received.
The output of client3.py and server3.py is shown in the following screenshot:
Our client program successfully received 6 bytes of the string, Thanks. You must have an idea of bytearray() by now. I hope you will remember it.
This time, I will create a UDP socket.
Run udp1.py, and we will discuss the code line by line:
import socket
host = "192.168.0.1"
port = 12346
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.bind((host,port))
data, addr = s.recvfrom(1024)
print "received from ",addr
print "obtained ", data
s.close()
socket.SOCK_DGRAM creates a UDP socket, and data, addr = s.recvfrom(1024) returns two things, the first is the data and the second is the address of the source.
Now, see the client-side preparations. Run udp2.py:
import socket
host = "192.168.0.1"
port = 12346
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
print s.sendto("hello all",(host,port))
s.close()
Here, I used the UDP socket and the s.sendto() method, as you can see in the definition of socket.sendto(). You will know that UDP is a connectionless protocol, so there is no need to establish a connection here.
The following screenshot shows the output of udp1.py (the UDP server) and udp2.py (the UDP client):
The server program successfully received data.
Let's assume that a server is running and that there is no client start connection, and that the server will have been listening. So, to avoid this situation, use socket.settimeout(value).
Generally, we give a value as an integer; if I give 5 as the value, this would mean wait for five seconds. If the operation doesn't complete within five seconds, then a timeout exception would be raised. You can also provide a non-negative float value.
For example, let's look at the following code:
import socket
host = "192.168.0.1"
port = 12346
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.bind((host,port))
s.settimeout(5)
data, addr = s.recvfrom(1024)
print "recevied from ",addr
print "obtained ", data
s.close()
I added one extra line, that is, s.settimeout(5). The program waits for five seconds; only after that will it give us an error message. Run udptime1.py.
The output is shown in the following screenshot:
The program shows an error; however, it does not look good if it gives an error message. The program should handle the exceptions.