In the world of networking and data communication, the TCP (Transmission Control Protocol) plays a vital role in ensuring reliable and orderly exchange of information. In this article, I will help you how to create a TCP client using Python and how to use it effectively for smooth data communication.

The TCP Client Class

The TCPlient class encapsulates the functionality required to establish and manage a TCP socket connection. This class provides an organized way to handle socket operations while ensuring graceful handling of errors.

import socket
import select
import time
import sys

class TCPlient():

    def __init__(self, host, port, retryAttempts = 10 ):

        self.time_connect = 0
        self.host = host
        self.port = port
        self.retryAttempts = retryAttempts
        self.attempt = 0
        self.tcp_socket = None
        self.connectionFailed = True
        self.datasend = None
        self.incomingData = False
        
    def TCP_Connection(self,host, port, retryAttempts):
        self.attempt = 0
        self.retryAttempts = retryAttempts
        print("tcp is connecting.......")
        while self.attempt < self.retryAttempts:
            self.host = host
            self.port = port
            self.connect()
            if self.connectionFailed:   
                self.attempt = self.attempt + 1
                print('attempt to reconnecting ' + str(self.attempt))
                time.sleep(5)
                self.time_connect = time.time()
                self.connect()
            else:
                print('TCP connected ' + str(self.attempt))
                self.send_to_Server('Send data to server to check OK \r\n OK', 2)
                self.retryAttempts = 0
                break
               
    def connect(self):
        try:
            self.tcp_socket = socket.socket()
            self.tcp_socket.settimeout(10)
        except socket.error as e:
            print(e)
            self.tcp_socket = None
            pass
            
        try:
            self.tcp_socket.connect((self.host, self.port))
            print("TCP opened")
            self.connectionFailed = False
        except socket.error as e:
            print(e)
            self.tcp_socket.close()
            self.tcp_socket = None
            self.connectionFailed = True
            pass
    

    def disconnect(self):
        if self.tcp_socket:
            try:
                self.tcp_socket.close()
                self.tcp_socket = None
                self.connectionFailed = True
            except socket.error as e:
                print(e)
                pass
        else:
            self.connectionFailed = True
        

    def send_to_Server(self, buf_data, is_byte):
        while self.connectionFailed == False:
            try:

                if is_byte == 1:
                    data_byte = buf_data
                    self.tcp_socket.sendall(data_byte)
                    print("sent byte data to server succeeded!")
                    break
                else:
                    data_enc = (buf_data).encode()
                    self.tcp_socket.sendall(data_enc)    
                    print("sent string data to server succeeded!")
                    break    

            except socket.error:   
                pass
    
    
    def read(self, timeout = 0.1):
        while self.connectionFailed == False:
            rlist, wlist, xlist = select.select([self.tcp_socket], [],
                                                        [],timeout)
            if not (rlist or wlist or xlist):
                break
            else:
                if len(rlist) != 0:
                    try:
                        data = self.tcp_socket.recv(1024)
                    except socket.error as e:
                        print(e)
                        self.tcp_socket.close()
                        self.tcp_socket = None
                        self.connectionFailed = True
                        sys.exit(1)
                        break
                    else:
                        if len(data) == 0:
                            print('orderly shutdown on server end')
                            self.tcp_socket.close()
                            self.tcp_socket = None
                            self.connectionFailed = True
                            self.attempt = 0
                            break
                        else:
                            # got a message do something :)  
                            dec_data = data.decode()   
                            self.datasend = dec_data
                            self.incomingData = True
                            break
                else:
                    break
    
    
    def reConnection(self, host, port):    
        while True:
            print("reconnection.......")
            self.TCP_Connection(host, port)
            if self.connectionFailed == False:
                break
            

The class offers the following essential methods:

  • __init__(self, host, port, retryAttempts=10): Initializes the class with necessary parameters such as the host and port of the server, along with the maximum number of retry attempts for connection.
  • TCP_Connection(self, host, port, retryAttempts): Attempts to establish a TCP connection. It retries until successful or until the specified number of attempts is exhausted.
  • connect(self): Creates a socket connection and handles exceptions gracefully.
  • disconnect(self): Safely closes the socket connection and updates the connection status.
  • send_to_Server(self, buf_data, is_byte): Sends data to the server using either byte or string format.
  • read(self, timeout=0.1): Reads incoming data from the server, managing socket state and potential errors.
  • reConnection(self, host, port): Handles reconnection attempts to maintain a continuous connection.

The Main Program

import TCPsocket
import time


#Parameters for TCP connection
retryAttempts = 5
remote_IP = "your remote IP"
remote_port = "your port" #it should be a number such as 2000
tcp_time_start = 0
time_start = 0
time_send_bit = 60
tcp_outdata = None
tcp_senddata = None


if __name__ == '__main__':
    
    try:

        # server
        remote_IP = remote_IP
        remote_port = remote_port
        
        # Setting TCP Socket
        tcp = TCPsocket.TCPlient(remote_IP, remote_port, retryAttempts)
        tcp.TCP_Connection(remote_IP, remote_port, retryAttempts)
        
        tcp_time_start = time.time() 
        time_start = time.time() 
        lora_time_start = time.time() 
        
        while True:
            #Listening commands from server
            tcp.read()
            if tcp.incomingData:
                   
                tcp_outdata = tcp.datasend
                
                print(tcp_outdata)
                tcp.send_to_Server(tcp_outdata, 2)
                tcp.incomingData = False
                
# =============================================================================
#           Put your other program here, for example listing for Endnode device
# =============================================================================
            

            #Remember always keeping your tcp connection staying alive
            if time.time() - tcp_time_start > time_send_bit:
                print("disconnect tcp service")
                tcp.disconnect()
                tcp_time_start = time.time()
            
            if tcp.connectionFailed:
                #Here you can update IP and port every time from external program
                try:
                    remote_IP =  "your new IP"
                    remote_port = "your new port"
      
                except Exception as e:
                    print("Error:")
                    print(e)
                    pass  
                        
                if tcp.attempt < retryAttempts:
                    print("attempt to connect again")
                    tcp.TCP_Connection(remote_IP, remote_port, retryAttempts)
                                                        
                #reset attempt to zero to make tcp reconnecting 
                tcp.attempt = 0 

                
# =============================================================================
#           Put other program here, for example, updating configuration parameters
# =============================================================================
                            
    except Exception as e:
        print("write error into log file")
        print(e)
    finally:
        pass

The main program serves as a use-case example for the TCPlient class. Here’s how it works:

  1. The program sets up connection parameters, including the server’s IP address, port, and retry attempts.
  2. It initializes the TCP client instance and establishes a connection to the server using the TCP_Connection method.
  3. The program enters an infinite loop where it listens for commands from the server and processes incoming data.
  4. It keeps the connection alive by periodically checking the connection status and attempting reconnection when needed.

The main program exemplifies the flexibility of the TCPlient class by providing an environment for real-world usage scenarios. This could include parallel execution of other tasks, continuous data monitoring, and more.

Conclusion

To dive deeper, you can explore and contribute to the Project’s source code on GitHub. This repository allows you to integrate the TCP client into your projects and adapt it to your specific needs. As I continue this series, stay tuned for the next article, where I’ll introduce the TCP server. This will complete the communication loop and provide a comprehensive understanding of both sides of the TCP connection.

If you have any questions, feel free to leave comments below or reach out to me here. Thank you, and have a wonderful day!