0

I am creating a TCP Server x Client communication but I am suffering from a logic problem that I cannot find out why it is happening. The value of my sizeWindow somehow gets the previous value and breaks the size of my window...

As you can see here:

enter image description here

I need to find out why it goes from 256 to 1024, it should be going to 512 but it isn't...

My guess is that it is not updating the sizeWindow value, but I cannot find out why.

To test my project you will need the Server and the Client classes:

Server Class (you should run this class first):

# ServerTCP
import socket

MY_IP = "127.0.0.1"
PORT_NUMBER = 13000


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


server_address = (MY_IP, PORT_NUMBER)
print('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()

This is my Client class (where the problem is...):

# ClientTCP - 4.0

import socket
import time

MY_IP = "127.0.0.1"
PORT_NUMBER = 13000

g_windowTime = 0

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

server_address = (MY_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)

        # 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")
        sizeWindow = sizeWindow * 2
        tempo = (time.time() - g_windowTime) * 1000
        print(f'{str(round(tempo, 2)).replace(".", ",")}; {int(sizeWindow)}')
        if tempo > 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:
            print("Problem with sendall")
            connect()

        # 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
                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()

My best guess is that the problem is in return sizeWindow, packets_sent, id_packet of the method send_packets, it returns 3 times..., 2 times it returns the correct value of sizeWindow but somehow the third time it changes the value of sizeWindow to the previous value, creating a great problem in my algorithm...

I don't know if I am forgetting something or if it is a syntax error, but I cannot find why it is happening...

I would be very pleased if someone could try to find out why it is happening.

Many many thanks.

Codewraith
  • 103
  • 1
  • 8
  • At first sight, I don't see any problem in the `send_packets` method. However I suppose that you encounter a case where `time.time() > (timer + 0.01)` is `False` and thus not executing your `sizeWindow = sizeWindow / 2`. I'll try to investigate deeper. – AlexisBRENON Nov 27 '19 at 08:42
  • Yes that's it, it ignores that `if` (which it is supposed to do) and goes for the `return sizeWindow, packets_sent, id_packet`. It enters the method `send_packets` three times but the last one it returns the sizeWindow as the previous one. – Codewraith Nov 27 '19 at 08:55

1 Answers1

1

I found your error. In send_packets, you have a recursive call from which you do not get the returned sizeWindow. You should update your function with:

def send_packets(sizeWindow, packets_sent, packets_resend, id_packet):
    # [...]
    while i < sizeWindow:
        # [...]
        # Timer
        if (i == 0):
            timer = time.time()
        elif (i > 0) and (time.time() > (timer + 0.01)):
            if sizeWindow > 1:
                [...]
                sizeWindow, packets_sent, id_packet = send_packets(sizeWindow, packets_sent, packets_resend, id_packet)

        i += 1

    return sizeWindow, packets_sent, id_packet

Below is the code I used to debug your error with some prints.

import socket
import threading
import time


class Server(threading.Thread):

    def run(self) -> None:
        MY_IP = "127.0.0.1"
        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 = "127.0.0.1"
        PORT_NUMBER = 13000

        g_windowTime = 0

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

        server_address = (MY_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
                tempo = (time.time() - g_windowTime) * 1000
                print(f'{str(round(tempo, 2)).replace(".", ",")}; {int(sizeWindow)}')
                if tempo > 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:
                    print("Problem with sendall")
                    connect()

                # 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()
    server.start()
    time.sleep(1)

    client = Client()
    client.start()

And here is the relevant output:

Server starting up on 127.0.0.1 port 13000
Waiting a connection
Connection from  ('127.0.0.1', 53654)
Send packet returned: 1
execute: 1 -> 2
0,36; 2
Send packet returned: 2
execute: 2 -> 4
0,66; 4
Send packet returned: 4
execute: 4 -> 8
0,95; 8
Send packet returned: 8
execute: 8 -> 16
1,28; 16
Send packet returned: 16
execute: 16 -> 32
1,85; 32
Send packet returned: 32
execute: 32 -> 64
3,02; 64
Send packet returned: 64
execute: 64 -> 128
5,35; 128
Send packet returned: 128
execute: 128 -> 256
8,97; 256
Send packet returned: 256
execute: 256 -> 512
18,17; 512
send packets: 512 -> 256.0
28,84; 256
Send packet returned: 256.0
execute: 256.0 -> 512.0
38,54; 512
send packets: 512.0 -> 256.0
48,62; 256
Send packet returned: 256.0
execute: 256.0 -> 512.0
54,73; 512
Send packet returned: 512.0
execute: 512.0 -> 1024.0
61,23; 1024
send packets: 1024.0 -> 512.0
71,58; 512
Send packet returned: 512.0
execute: 512.0 -> 1024.0
81,75; 1024
send packets: 1024.0 -> 512.0
91,94; 512
send packets: 512.0 -> 256.0
102,01; 256
Send packet returned: 512.0
execute: 512.0 -> 1024.0
108,74; 1024

While the send_packets (called twice recursively) divide you sizeWindow by four (from 1024 to 512 to 256) the returned value fetched by the execute method is 512 and not 256.

AlexisBRENON
  • 2,921
  • 2
  • 18
  • 30
  • It worked awesomely well, many thanks AlexisBRENON!!! Just one question, as I am a new Python learner, I would like to ask you if the classes should be created like you did, is it more pythonic to create them like that? Once more many many thanks! – Codewraith Nov 27 '19 at 12:31
  • 1
    @Codewraith Not necessarily. There is a lot of debate about when/where to use classes in Python ([link1](https://stackoverflow.com/questions/33072570/when-should-i-be-using-classes-in-python), [link2](https://devblogs.microsoft.com/python/idiomatic-python-functions-versus-classes/)). With an heavy background of C++/Java/Kotlin programming I often use classes even when not needed. In this particular case, maybe instantiating a [`Thread`](https://docs.python.org/3/library/threading.html#threading.Thread) with a `target` would better suit. All depends on your code organisation. – AlexisBRENON Nov 27 '19 at 13:10
  • Right, I will take a look in this links, thanks once again for the explanation. – Codewraith Nov 27 '19 at 15:32
  • I am facing another problem with the TCP connection, when I try to use two computers 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? I have created another question for this problem, but I couldn't find a solution for this problem yet. – Codewraith Nov 28 '19 at 05:16