Codewraith
Codewraith

Reputation: 103

How can I reconnect my Client to my Server if I lost packets in my TCP communication?

I am creating a communication between a server and a client using two different computers (two different IPs), but when I am losing a packet in the middle of the process of sending them to the server, my client is not able to reconnect to the server, so it stops sending packets.

I have tried to use another sock.connect(server_address) but even with that it didn't work.

This is my code (with structure tips from a helpful programmer here in Stack Overflow):

import socket
import threading
import time

class Server(threading.Thread):

    def run(self) -> None:
        MY_IP = "10.0.0.113" # Other computer running this class
        PORT_NUMBER = 13000


        # socket TCP/IP
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)


        server_address = (MY_IP, PORT_NUMBER)
        print('Server starting up on {} port {}'.format(*server_address))
        sock.bind(server_address)

        sock.listen(1)

        while True:

            print('Waiting a connection')
            connection, client_address = sock.accept()
            try:
                print('Connection from ', client_address)

                while True:
                    data = connection.recv(16)
                    #print('received {!r}'.format(data))
                    if data:
                        #print('sending data back to the client')
                        connection.sendall(data)
                    else:
                        print('no data came from ', client_address)
                        break

            finally:
                connection.close()

class Client(threading.Thread):

    def run(self) -> None:
        #MY_IP = "10.0.0.112" won't be used because it will send to the server IP.
        OTHER_IP = "10.0.0.113"
        PORT_NUMBER = 13000

        g_windowTime = 0

        # socket TCP/IP
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

        server_address = (OTHER_IP, PORT_NUMBER)
        sock.connect(server_address)

        def execute():
            global g_windowTime
            g_windowTime = time.time()
            sizeWindow = 1
            id_packet = "1"
            packets_resend = []

            while True:
                packets_sent = []

                # Send data
                sizeWindow, packets_sent, id_packet = send_packets(sizeWindow, packets_sent, packets_resend, id_packet)
                print(f"Send packet returned: {sizeWindow}")

                # Waits for the amount
                amount_received = 0
                amount_expected = len(packets_sent)

                while amount_received < amount_expected:
                    try:
                        sock.settimeout(5.0)
                        data = sock.recv(16)
                        amount_received += len(data)
                    except:
                        print("The packet needs to be resend")
                print(f"execute: {sizeWindow} -> {sizeWindow*2}")
                sizeWindow = sizeWindow * 2
                currentTime = (time.time() - g_windowTime) * 1000
                print(f'{str(round(currentTime, 2)).replace(".", ",")}; {int(sizeWindow)}')
                if currentTime > 10000:
                    exit()


        def send_packets(sizeWindow, packets_sent, packets_resend, id_packet):
            global g_windowTime
            i = 0
            j = 0
            timer = 0

            while i < sizeWindow:

                if packets_resend == []:
                    packet = id_packet.encode('utf-8')
                    id_packet = str(int(id_packet) + 1)
                elif packets_resend != []:
                    packet = packets_resend.pop(0)

                if packet not in packets_sent:
                    packets_sent.append(packet)

                # Send the packet
                try:
                    sock.sendall(packet)
                except:
                    # Here is the problem, it cannot connect to the server if it looses a packet
                connected = False
                sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
                while not connected:
                    try:
                        print("Problem with sendall")
                        connect()
                        connected = True
                        print('re-connection successful')
                    except socket.error:
                        time.sleep(2)

                # Timer
                if (i == 0):
                    timer = time.time()
                elif (i > 0) and (time.time() > (timer + 0.01)):
                    if sizeWindow > 1:
                        j = i + 1
                        while j < sizeWindow:
                            packet = id_packet.encode('utf-8')
                            id_packet = str(int(id_packet) + 1)
                            packets_resend.append(packet)
                            j += 1
                        print(f"send packets: {sizeWindow} -> {sizeWindow/2}")
                        sizeWindow = sizeWindow / 2
                        currentTime = (time.time() - g_windowTime) * 1000
                        print(f'{str(round(currentTime, 2)).replace(".", ",")}; {int(sizeWindow)}')

                        send_packets(sizeWindow, packets_sent, packets_resend, id_packet)

                i += 1

            return sizeWindow, packets_sent, id_packet

        def connect():
            sock.connect(server_address)

        execute()

        sock.close()

if __name__ == '__main__':
    #server = Server() needs to be executed in another computer to see the problem, in the same computer it works fine
    #server.start()
    time.sleep(1)

    client = Client()
    client.start()

I am not able to reconnect my client to the server when it looses a packet, is there another method to reconnect them without loosing the connection?

If anyone could help me with that I really could use a hand...

Thanks.

Upvotes: 1

Views: 371

Answers (1)

AlexisBRENON
AlexisBRENON

Reputation: 3109

As stated by @user207421, as you are still using a TCP connection you normally do not have to handle transmission error yourself, as error-free data transfert is one of the key features of TCP against UDP.

Moreover, I would recommend to use the socketserver Python module to build some kind of client/server application. It saves you from many boilerplate code for connection establishment and management as well as thread management.

Finally, your can connect to a server only when it is in the sock.accept() method. I fear that when you lose your connection the server is blocked in any of connection.recv() or connection.sendall() function, thus not accepting any new connection ; that's why we often delegate server workload to other threads or processes ; from the socketserver documentation:

These four classes process requests synchronously; each request must be completed before the next request can be started. This isn’t suitable if each request takes a long time to complete, because it requires a lot of computation, or because it returns a lot of data which the client is slow to process. The solution is to create a separate process or thread to handle each request; the ForkingMixIn and ThreadingMixIn mix-in classes can be used to support asynchronous behaviour.

So, to sum-up my answer: You are experiencing a behavior that you aren't intended to encounter. This is probably the symptom of a mis-conception. So use the available tools to fix this.

Upvotes: 2

Related Questions