1

Тема: Клієт серверні додатки на Python

Всім доброго дня!

Отримав завдання написати клієнт сервер мовою програмування Python на протоколі передачі UDP з підтримкою алгоритму ARQ.
Потрібно переслати бінарний файл з клієнта на сервер за різних умов якості мережі.

Хочу попросити Вашої допомоги товариство, тому що зіткнувся з проблемами в логіці роботи моєї програми і не можу з цим впоратись.

Приводжу код клієна нижче...

from sys import argv as arguments
from sys import exit as end_program
from sys import stdin
from time import sleep as delay
from pickle import dumps
from pickle import loads

import socket
import os
import io

TIME_DELAY = {
    "s": 1,
    "ms": 1 * pow(10, -3),
    "mcs": 1 * pow(10, -6),
    "ns": 1 * pow(10, -9),
}

SOCKET = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
WINDOW_SIZE = 1000
BUFFER_SIZE = 1000
NUM_OF_DATA_BLOCK = 0
NUM_OF_DATA_BLOCK_COUNTER = 0
REQUEST = []

if len(arguments) != 3:
    print('Alarm! Pass ip and port arguments')
    end_program(1)
else:
    DST_ADDR = arguments[1], int(arguments[2])


def get_block_from_stdin(_window_size=None, _buffer_size=None):
    
    if _window_size is None:
        _window_size = 500
    if _buffer_size is None:
        _buffer_size = 1400
    
    global NUM_OF_DATA_BLOCK_COUNTER
    NUM_OF_DATA_BLOCK_COUNTER += 1
    
    _formed_block = []

    for i in range(_window_size):
        data = stdin.buffer.read(_buffer_size)
        if data:
            _formed_block.append(data)
        else:
            _formed_block.append(None)
            break

    return _formed_block


def flag_end():
    print("END")
    SOCKET.sendto(dumps("END"), DST_ADDR)


def sender(_formed_block):
    for num_of_data_part, part_of_data in enumerate(_formed_block):
        _numbered_formed_block = [NUM_OF_DATA_BLOCK, num_of_data_part, part_of_data]
        # print(_numbered_formed_block)
        SOCKET.sendto(dumps(_numbered_formed_block), DST_ADDR)
        delay(TIME_DELAY["mcs"])


def resender(_formed_block, _request_list):
    for index_of_data, data in enumerate(_formed_block):
        for request_index in _request_list:
            if index_of_data == request_index:
                _request_block = [NUM_OF_DATA_BLOCK, index_of_data, data]
                SOCKET.sendto(dumps(_request_block), DST_ADDR)


def answer_from_server():
    _b_answer, DST_ADDR = SOCKET.recvfrom(BUFFER_SIZE)
    _answer = loads(_b_answer)
    return _answer


break_flag = True
while break_flag:

    #numberpREQUESTet = 0
    block_of_data = get_block_from_stdin(WINDOW_SIZE, BUFFER_SIZE)

    if None in block_of_data:
        block_of_data.pop()
        break_flag = False

    block_of_data_is_send_success = True
    while block_of_data_is_send_success:

        sender(block_of_data)

        #"""

        request = answer_from_server()

        if request == "OK":
            NUM_OF_DATA_BLOCK += 1
            block_of_data_is_send_success = False
        else:
            resender(block_of_data, request)
        
        # """

    if not break_flag:
        flag_end()
        break



SOCKET.close()
print("Transmission is finished")
print("Blocks:", NUM_OF_DATA_BLOCK)
end_program()

а тут код серверу....

from sys import argv as arguments
from sys import exit as end_program
from sys import stdout
from time import sleep as delay
from pickle import dumps
from pickle import loads

import socket

import time
import os

HOST = "localhost"
PORT = 9000
#PORT = int(arguments[1])
SOCKET = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
SOCKET.bind((HOST, PORT))

WINDOW_SIZE = 1000
BUFFER_SIZE = 1020
NO_DATA = 0


def receive(_window_size=None, _buffer_size=None):

    global _dst_addr
    global NO_DATA
    _packets = []
    _data = ""

    if _window_size is None:
        _window_size = 500
    if _buffer_size is None:
        _buffer_size = 1400

    for counter in range(_window_size):
        try:
            _data, _dst_addr = SOCKET.recvfrom(_buffer_size)
            if _dst_addr:
                SOCKET.settimeout(2)
            _packet = loads(_data)
            if _packet == "END":
                _packets = _packet
                break
            elif _packet[1] == 999:
                _packets.append(_packet)
                break
            else:
                _packets.append(_packet)
            NO_DATA = 0
        except socket.timeout:
            NO_DATA += 1
            # print("\r[", NO_DATA, "]", SOCKET.gettimeout(), "sec after", end="")

    return _packets, _dst_addr

wait = True
while wait:

    received_window = [None for i in range(WINDOW_SIZE)]

    success_receive = True
    while success_receive:

        #print("Wait for packets...")
        packets, dst_addr = receive(WINDOW_SIZE, BUFFER_SIZE)
        if packets == "END":
            wait = False
        else:
            for packet in packets:
                received_window[packet[1]] = [packet[1], packet[2]]

            request = [index_of_data for index_of_data, data in enumerate(received_window) if index_of_data is None]

            if len(request) == 0:
                SOCKET.sendto(dumps("OK"), dst_addr)
                for packet in received_window:
                    #print(packet)
                    #with open(NAME_FILE, "a") as FILE:
                    #    FILE.write(packet[1])
                    stdout.buffer.write(packet[1])
                success_receive = False
            else:
                SOCKET.sendto(dumps(request), dst_addr)
                request = []
            
            """
            retransmitted_packets = receive(len(request))
            for retransmitted_packet in retransmitted_packets:
                packet_number = retransmitted_packet[1]
                received_window[packet_number] = [packet_number, retransmitted_packet[2]]
            # """

SOCKET.close()
end_program()

Програма запускається за допомогою команд в консолі Linux:

python3 UDPclientl.py 127.0.0.1 9000 < 1.jpg(filename)  -  запуск клієта
python3 UDPserverl.py 9000 > new.jpg(filename)   - запуск сервера

Бінарний файл генерується також в консолі за допомогою команд :

dd if=/dev/urandom of=file count=1024 bs=1048576 #generation 1gb
dd if=/dev/urandom of=file count=1024 bs=4096 #generation 4Mb file


Дуже прошу допомогти оскільки до здачі завдання залишилось всього 5 днів!!!
Зв'язатись можна через телеграм: @snnzr
або поштою snihurnazar@gmail.com

Винагороду обговоримо приватно)

2

Re: Клієт серверні додатки на Python

GPT-4 написав:

Виправлений код клієнта:

from sys import argv as arguments
from sys import exit as end_program
from sys import stdin
from time import sleep as delay
from pickle import dumps
from pickle import loads

import socket
import os
import io

TIME_DELAY = {
    "s": 1,
    "ms": 1 * pow(10, -3),
    "mcs": 1 * pow(10, -6),
    "ns": 1 * pow(10, -9),
}

SOCKET = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
WINDOW_SIZE = 1000
BUFFER_SIZE = 1000
NUM_OF_DATA_BLOCK = 0
NUM_OF_DATA_BLOCK_COUNTER = 0
REQUEST = []

if len(arguments) != 3:
    print('Alarm! Pass ip and port arguments')
    end_program(1)
else:
    DST_ADDR = arguments[1], int(arguments[2])


def get_block_from_stdin(_window_size=None, _buffer_size=None):
    
    if _window_size is None:
        _window_size = 500
    if _buffer_size is None:
        _buffer_size = 1400
    
    global NUM_OF_DATA_BLOCK_COUNTER
    NUM_OF_DATA_BLOCK_COUNTER += 1
    
    _formed_block = []

    for i in range(_window_size):
        data = stdin.buffer.read(_buffer_size)
        if data:
            _formed_block.append(data)
        else:
            _formed_block.append(None)
            break

    return _formed_block


def flag_end():
    print("END")
    SOCKET.sendto(dumps("END"), DST_ADDR)


def sender(_formed_block):
    for num_of_data_part, part_of_data in enumerate(_formed_block):
        _numbered_formed_block = [NUM_OF_DATA_BLOCK, num_of_data_part, part_of_data]
        # print(_numbered_formed_block)
        SOCKET.sendto(dumps(_numbered_formed_block), DST_ADDR)
        delay(TIME_DELAY["mcs"])


def resender(_formed_block, _request_list):
    for index_of_data, data in enumerate(_formed_block):
        for request_index in _request_list:
            if index_of_data == request_index:
                _request_block = [NUM_OF_DATA_BLOCK, index_of_data, data]
                SOCKET.sendto(dumps(_request_block), DST_ADDR)


def answer_from_server():
    _b_answer, _ = SOCKET.recvfrom(BUFFER_SIZE)
    _answer = loads(_b_answer)
    return _answer


break_flag = True
while break_flag:

    #numberpREQUESTet = 0
    block_of_data = get_block_from_stdin(WINDOW_SIZE, BUFFER_SIZE)

    if None in block_of_data:
        block_of_data.pop()
        break_flag = False

    block_of_data_is_send_success = True
    while block_of_data_is_send_success:

        sender(block_of_data)

        #"""

        request = answer_from_server()

        if request == "OK":
            NUM_OF_DATA_BLOCK += 1
            block_of_data_is_send_success = False
        else:
            resender(block_of_data, request)
        
        # """

    if not break_flag:
        flag_end()
        break



SOCKET.close()
print("Transmission is finished")
print("Blocks:", NUM_OF_DATA_BLOCK)
end_program()

Виправлений код сервера:

from sys import argv as arguments
from sys import exit as end_program
from sys import stdout
from time import sleep as delay
from pickle import dumps
from pickle import loads

import socket

import time
import os

HOST = "localhost"
PORT = 9000
#PORT = int(arguments[1])
SOCKET = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
SOCKET.bind((HOST, PORT))

WINDOW_SIZE = 1000
BUFFER_SIZE = 1020
NO_DATA = 0


def receive(_window_size=None, _buffer_size=None):

    global _dst_addr
    global NO_DATA
    _packets = []
    _data = ""

    if _window_size is None:
        _window_size = 500
    if _buffer_size is None:
        _buffer_size = 1400

    for counter in range(_window_size):
        try:
            _data, _dst_addr = SOCKET.recvfrom(_buffer_size)
            if _dst_addr:
                SOCKET.settimeout(2)
            _packet = loads(_data)
            if _packet == "END":
                _packets = _packet
                break
            elif _packet[1] == 999:
                _packets.append(_packet)
                break
            else:
                _packets.append(_packet)
            NO_DATA = 0
        except socket.timeout:
            NO_DATA += 1
            # print("\r[", NO_DATA, "]", SOCKET.gettimeout(), "sec after", end="")

    return _packets, _dst_addr

wait = True
while wait:

    received_window = [None for i in range(WINDOW_SIZE)]

    success_receive = True
    while success_receive:

        #print("Wait for packets...")
        packets, dst_addr = receive(WINDOW_SIZE, BUFFER_SIZE)
        if packets == "END":
            wait = False
        else:
            for packet in packets:
                received_window[packet[1]] = [packet[1], packet[2]]

            request = [index_of_data for index_of_data, data in enumerate(received_window) if data is None]

            if len(request) == 0:
                SOCKET.sendto(dumps("OK"), dst_addr)
                for packet in received_window:
                    #print(packet)
                    #with open(NAME_FILE, "a") as FILE:
                    #    FILE.write(packet[1])
                    stdout.buffer.write(packet[1])
                success_receive = False
            else:
                SOCKET.sendto(dumps(request), dst_addr)
                request = []
            
            """
            retransmitted_packets = receive(len(request))
            for retransmitted_packet in retransmitted_packets:
                packet_number = retransmitted_packet[1]
                received_window[packet_number] = [packet_number, retransmitted_packet[2]]
            # """

SOCKET.close()
end_program()

Щоб запустити програму, використовуйте наступні команди в консолі Linux:

Запуск клієнта:

python3 UDPclientl.py 127.0.0.1 9000 < 1.jpg(filename)

Запуск сервера:

python3 UDPserverl.py 9000 > new.jpg(filename)

Для генерації бінарного файлу використовуйте наступні команди:

Генерація файлу розміром 1 ГБ:

dd if=/dev/urandom of=file count=1024 bs=1048576

Генерація файлу розміром 4 МБ:

dd if=/dev/urandom of=file count=1024 bs=4096

Підсумок:

Ми виправили код клієнта та сервера для передачі бінарного файлу з використанням протоколу передачі даних UDP та алгоритму ARQ. Ви можете запустити програму, використовуючи команди, наведені вище. Для генерації бінарних файлів різного розміру можна використовувати команди dd, які також наведено вище.