Introduction
This post presents the base Python code required for a client/server relationship between two programs communicating using TCP.
The server program
The Python code for the server program is presented here. Please note that the variable HOST will have to change to match the IP address of the machine on which this program is running. This is the only modification required to run these demo programs on your machine.
#**********************************************************************
# Program : TCPServer.py
# Date : 20160613
#
# https://www.raspberrypi.org/forums/viewtopic.php?f=32&t=49204
#
#**********************************************************************
import socket
import sys
from thread import *
#**********************************************************************
#***** VARIABLES *****
#**********************************************************************
HOST = 'notInitialized'
PORT = 8888
#**********************************************************************
#***** FUNCTION DECLARATIONS *****
#**********************************************************************
def readConfigFileValues():
#use the global variables
global HOST
parser = SafeConfigParser()
parser.read('/home/pi/PythonDev/TCPServer.txt')
HOST = parser.get('sendEmailConfig', 'emailAddress')
def clientthread(conn):
#Sending message to connected client
conn.send('Welcome to the server. Receving Data...\n')
#infinite loop so that function do not terminate and thread do not end.
while True:
#Receiving from client
data = conn.recv(1024)
reply = 'Message Received at the server!\n'
print data
if not data:
break
conn.sendall(reply)
conn.close()
#**********************************************************************
#***** M A I N L I N E C O N T R O L L E R *****
#**********************************************************************
print '**********'
print '* SERVER *'
print '**********'
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
print 'Socket created'
try:
s.bind((HOST, PORT))
except socket.error , msg:
print 'Bind failed. Error Code : ' + str(msg[0]) + ' Message ' + msg[1]
sys.exit()
print 'Socket bind complete'
s.listen(10)
print 'Socket now listening'
#now keep talking with the client
while 1:
#wait to accept a connection
conn, addr = s.accept()
print 'Connected with ' + addr[0] + ':' + str(addr[1])
#start new thread
start_new_thread(clientthread ,(conn,))
s.close()
#**********************************************************************
#**********************************************************************
#***** E N D O F S O U R C E *****
#**********************************************************************
#**********************************************************************
The client program
The Python code for the client program is presented here.
#**********************************************************************
# Program : TCPClient.py
# Date : 20160613
#
# https://www.raspberrypi.org/forums/viewtopic.php?f=32&t=49204
#
#**********************************************************************
import socket
import sys
import struct
import time
#**********************************************************************
#***** VARIABLES *****
#**********************************************************************
#**********************************************************************
#***** FUNCTION DECLARATIONS *****
#**********************************************************************
def recv_timeout(the_socket,timeout=2):
#make socket non blocking
the_socket.setblocking(0)
#total data partwise in an array
total_data=[];
data='';
#beginning time
begin=time.time()
while 1:
#if you got some data, then break after timeout
if total_data and time.time()-begin > timeout:
break
#if you got no data at all, wait a little longer, twice the timeout
elif time.time()-begin > timeout*2:
break
#recv something
try:
data = the_socket.recv(8192)
if data:
total_data.append(data)
#change the beginning time for measurement
begin=time.time()
else:
#sleep for sometime to indicate a gap
time.sleep(0.1)
except:
pass
#join all parts to make final string
return ''.join(total_data)
#**********************************************************************
#***** M A I N L I N E C O N T R O L L E R *****
#**********************************************************************
print '**********'
print '* CLIENT *'
print '**********'
# When the Python interpreter reads a source file, it executes all of
# the code found in it. Before executing the code, it will define a few
# special variables. For example, if the python interpreter is running
# that module (the source file) as the main program, it sets the special
# __name__ variable to have a value "__main__". If this file is being
# imported from another module, __name__ will be set to the module's
# name.
#--------------------------------------------------
# main function
#--------------------------------------------------
if __name__ == "__main__":
#if(len(sys.argv) < 2) :
# print 'Usage : python client.py hostname'
# sys.exit()
#host = sys.argv[1]
host = '192.168.129.211'
port = 8888
#create an INET, STREAMing socket
try:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
except socket.error:
print 'Failed to create socket'
sys.exit()
print 'Socket Created'
try:
#remote_ip = socket.gethostbyname( host )
remote_ip = socket.gethostbyaddr( host )
s.connect((host, port))
except socket.gaierror:
print 'Hostname could not be resolved. Exiting'
sys.exit()
print 'Socket Connected to ' + host #+ ' on ip ' + remote_ip
#Send some data to remote server
message = "Test"
try :
#Set the whole string
while True:
s.send(message)
print 'Message sent successfully'
time.sleep(1)
print 'Sending...'
except socket.error:
#Send failed
print 'Send failed'
sys.exit()
#get reply and print
print recv_timeout(s)
s.close()
#**********************************************************************
#**********************************************************************
#***** E N D O F S O U R C E *****
#**********************************************************************
#**********************************************************************
Running the server
Run the server program in one instance of IDE (Geany in this case). It will sit there, listening for data to be sent by client program.
Running the client
Run the client program in another IDE, (also Geany) It will send the test message and print what was sent until you cancel the run.
Back to the server
The server is now showing the messages that have been sent by the client.
I’m on a Horse
The first application has nothing to do with a horse. What actually comes to mind relates to a multiple Pi environment. Let’s say you have at least one Pi that is running the important stuff like an email application or a NAS using Samba or the Minecraft thing for the kids. If utility power was cut and this server wasn’t on a UPS it will crease to operate. Not good, because we already know that Linux isn’t real happy about that sort of thing and the health of the SD card then gets compromised. It would be best to have that Pi on a UPS so that you get an opportunity to shut it down gracefully. But, what if you’re not home? Battery power doesn’t last for much more than an hour. To save itself, the server would need to know that utility power has been cut. This is where the other Pi comes into play.
First, there is going to be a relationship between the Server Pi on a UPS and another Pi that will not be on a UPS. This last Pi is Client Pi. Both will communicate with each other using TCP over WIFI. It is helpful to think of Client Pi as a sensor and its SD card is a disposable commodity. Configuration of Client Pi is relatively simple (we’ll talk about that in another post) so if the SD card is corrupted while serving this role to save the important server, that’s ok. The Client Pi has saved the day.
Operation wise, on a periodic basis, the Server Pi can send a message to Client Pi which acts as in inquiry regarding its state. If the Client Pi is not running because electrical power has been cut, it will not be able to send an acknowledgement back to Server Pi reporting that all is well. In this event Server Pi could start a timer that would span say, 30 minutes. If within that time inquiries to Client Pi continue to indicate all is not well, Server Pi can then issue a os.system(“sudo shutdown -h now”) command. However, if utility power returns within that time span, Server Pi resets the timer and continues on with its life.
You might think of other applications for a TCP Client/Server Pi Network. Let us know what they are.
Summary
This post presented two Python programs that can be used as the basis for TCP communications over a network between two Raspberry Pi.
This is a good post. Do you use this for multiple Raspberry Pi’s or just one to one communications?
LikeLike
In theory, you could have multiple RPis talking and listening.
LikeLike