Отправителю не приходит ответ от получателя после приема сообщения по UDP (winsock)
Имеется академическое задание - программа гарантированной доставки файла по ненадежному (UDP) каналу связи.
Реализовано разбиение файла на части (chunk), вычисление контрольной суммы. Дело остается за отправкой, в ней и возникает проблема. Сам я совсем новичок в C++ и уж тем более в программировании сокетов.
Проблема следующая. При запуске программы-отправителя (назовем M1) она посылает информацию о файле программе-получателю (назовем M2), та, в свою очередь, их принимает. После M1 производит отправку первой части файла, M2 принимает её и вычисляет контрольную сумму. Затем M2 отсылает ответ, сошлась ли контрольная сумма, т.е. был ли принят файл корректно. Именно этот ответ и никак не доходит до M1 по непонятной мне причине, после чего программа виснет в вечном ожидании этих данных.
Предупреждаю сразу, код здоровый и костыльный, но больше обратиться за помощью буквально некуда - перечитал всю документацию и все форумы, не могу понять, в чем дело.
Структура части файла (chunk):
struct Chunk {
unsigned int number;
unsigned long int hash;
char* name;
FILE* file;
char* content;
size_t max_size;
size_t size;
};
Небольшая функция-обертка для создания и заполнения структуры адреса:
sockaddr_in getAddressStruct(const short& address_family, const char*& ip_address, const unsigned int& port) {
sockaddr_in address{};
ZeroMemory(&address, sizeof(address));
address.sin_family = address_family;
address.sin_port = htons(port);
address.sin_addr.s_addr = inet_addr(ip_address);
return address;
}
Функция отправки файла:
void sendFile(const char*& file_path, const unsigned int& chunk_amount, const unsigned int& chunk_size, const char*& sender_ip, const char*& receiver_ip, const unsigned short int& port) {
if (WSAStart() != 0) {
fprintf(stderr, "Winsock startup error has been occurred!\n ");
exit(1);
}
SOCKET input_socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
SOCKET output_socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (input_socket == INVALID_SOCKET || output_socket == INVALID_SOCKET) {
WSACleanup();
fprintf(stderr, "Socket creation error has been occurred!\n ");
exit(1);
}
sockaddr_in sender_address = getAddressStruct(AF_INET, sender_ip, port);
int sender_size = sizeof(sender_address);
sockaddr_in receiver_address = getAddressStruct(AF_INET, receiver_ip, port);
int receiver_size = sizeof(receiver_address);
if (bind(input_socket, (sockaddr*) &sender_address, sender_size) == SOCKET_ERROR) {
closesocket(input_socket);
closesocket(output_socket);
WSACleanup();
fprintf(stderr, "Socket binding error has been occurred!\n ");
exit(1);
}
const char* file_name = getFileName(file_path);
const char* str_chunk_amount = std::to_string(chunk_amount).c_str();
const char* str_chunk_size = std::to_string(chunk_size).c_str();
printf("File info was sent.\n\n");
sendto(output_socket, file_name, strlen(file_name) + 1, 0, (sockaddr*) &receiver_address, receiver_size);
sendto(output_socket, str_chunk_amount, sizeof(str_chunk_amount), 0, (sockaddr*) &receiver_address, receiver_size);
sendto(output_socket, str_chunk_size, sizeof(str_chunk_size), 0, (sockaddr*) &receiver_address, receiver_size);
Chunk chunk_template{};
chunk_template.name = "chunk_";
chunk_template.max_size = chunk_size;
for (unsigned int _ = 0; _ < chunk_amount; _++) {
bool is_received = false;
Chunk chunk = createChunk(chunk_template, _);
chunk.file = fopen(chunk.name, "rb");
chunk.content = new char[chunk.max_size];
chunk.size = fread(chunk.content, 1, chunk.max_size, chunk.file);
chunk.hash = hash(chunk.content);
fclose(chunk.file);
char* str_chunk = chunkToString(chunk);
while (!is_received) {
printf("Sending chunk #%d . . . ", chunk.number);
sendto(output_socket, str_chunk, strlen(str_chunk) + 1, 0, (sockaddr*) &receiver_address, receiver_size);
char* receiver_acknowledgement = new char[64];
recvfrom(input_socket, receiver_acknowledgement, 64, 0, (sockaddr*) &receiver_address, &receiver_size);
printf("Receiver acknowledgement: %s\n", receiver_acknowledgement);
if (!strcmp(receiver_acknowledgement, "SUCCESS")) {
is_received = true;
printf("Success!\n");
}
else
printf("Chunk was corrupted! Resending . . .\n\n");
}
delete[] chunk.content;
}
delete[] file_name;
delete[] str_chunk_amount;
delete[] str_chunk_size;
closesocket(input_socket);
closesocket(output_socket);
WSACleanup();
}
Функция получения файла:
unsigned int receiveFile(const char*& file_path, const char*& receiver_ip, const char*& sender_ip, const unsigned short int& port) {
if (WSAStart() != 0) {
fprintf(stderr, "Winsock startup error has been occurred!\n ");
exit(1);
}
SOCKET input_socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
SOCKET output_socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (input_socket == INVALID_SOCKET || output_socket == INVALID_SOCKET) {
WSACleanup();
fprintf(stderr, "Socket creation error has been occurred!\n ");
exit(1);
}
sockaddr_in receiver_address = getAddressStruct(AF_INET, receiver_ip, port);
int receiver_size = sizeof(receiver_address);
sockaddr_in sender_address = getAddressStruct(AF_INET, sender_ip, port);
int sender_size = sizeof(sender_address);
if (bind(input_socket, (sockaddr*) &receiver_address, receiver_size) == SOCKET_ERROR) {
closesocket(input_socket);
closesocket(output_socket);
WSACleanup();
fprintf(stderr, "Socket binding error has been occurred!\n ");
exit(1);
}
printf("Ready to receive file!\n");
char* received_file_name = new char[256];
recvfrom(input_socket, received_file_name, 256, 0, (sockaddr*) &sender_address, &sender_size);
char* str_chunk_amount = new char[64];
recvfrom(input_socket, str_chunk_amount, 64, 0, (sockaddr*) &sender_address, &sender_size);
unsigned int chunk_amount = std::stoi((std::string) str_chunk_amount);
delete[] str_chunk_amount;
char* str_chunk_size = new char[64];
recvfrom(input_socket, str_chunk_size, 64, 0, (sockaddr*) &sender_address, &sender_size);
unsigned int chunk_size = std::stoi((std::string) str_chunk_size);
delete[] str_chunk_size;
printf("Received file info:\n"
" File name: %s\n"
" Chunks amount: %d\n"
" Chunks size: %d\n\n",
received_file_name, chunk_amount, chunk_size);
char* file_name = new char[strlen(file_path) + strlen(received_file_name) + 1];
strcpy(file_name, file_path);
strcpy(file_name + strlen(file_path), received_file_name);
delete[] received_file_name;
std::set<unsigned int> received_chunks_numbers;
Chunk chunk_template{};
chunk_template.name = "chunk_";
chunk_template.max_size = chunk_size;
while (received_chunks_numbers.size() != chunk_amount) {
char* str_chunk = new char[chunk_size * 2];
recvfrom(input_socket, str_chunk, chunk_size * 2, 0, (sockaddr*) &sender_address, &sender_size);
unsigned int* separator_positions = new unsigned int[2];
unsigned int count = 0;
for (unsigned int _ = 0; _ < strlen(str_chunk); _++) {
if (str_chunk[_] == '|') {
separator_positions[count] = _;
count++;
}
}
if (count < 2) {
sendto(output_socket, "ERROR", sizeof("ERROR"), 0, (sockaddr*) &sender_address, sender_size);
printf("Chunk was corrupted! Resend requested . . .\n");
delete[] str_chunk;
continue;
}
char* str_chunk_number = new char[16];
char* str_chunk_hash = new char[64];
char* chunk_content = new char[chunk_size];
strncpy(str_chunk_number, str_chunk, separator_positions[0]);
strncpy(str_chunk_hash, str_chunk + separator_positions[0] + 1, separator_positions[1] - separator_positions[0] - 1);
strcpy(chunk_content, str_chunk + separator_positions[1] + 1);
unsigned int chunk_number = std::stoi(std::string(str_chunk_number));
unsigned long int chunk_hash = std::stoi(std::string(str_chunk_hash));
printf("Receiving chunk #%d . . . ", chunk_number);
delete[] str_chunk_number;
delete[] str_chunk_hash;
if (hash(chunk_content) == chunk_hash) {
received_chunks_numbers.insert(chunk_number);
Chunk chunk = createChunk(chunk_template, chunk_number);
chunk.content = chunk_content;
delete[] chunk_content;
chunk.file = fopen(chunk.name, "wb");
fwrite(chunk.content, 1, chunk.size, chunk.file);
fclose(chunk.file);
sendto(output_socket, "SUCCESS", sizeof("SUCCESS"), 0, (sockaddr*) &sender_address, sender_size);
printf("Success!\n");
delete[] separator_positions;
delete[] str_chunk;
}
else {
sendto(output_socket, "ERROR", sizeof("ERROR"), 0, (sockaddr*) &sender_address, sender_size);
printf("Chunk was corrupted! Resend requested . . .\n");
delete[] separator_positions;
delete[] str_chunk;
}
}
closesocket(input_socket);
closesocket(output_socket);
WSACleanup();
return chunk_amount;
}
Очень надеюсь на помощь в любом виде - словесный ответ, ссылка на статью с похожей проблемой или код.
UPD:
- Первоначально была проведена отладка, в ходе которой не было выявлено никаких ошибок: программа исправно доходит вплоть до момента вызова
recvfrom(6)
на М1 и уже только там застывает в вечном ожидании данных. - М1 и М2 работают на одном общем порте - 51000.
- Структуры адресов на М1 и М2 корректны, в них лежат одинаковые адреса - это было выяснено в ходе отладки.
- В коде, который я приложил к вопросу, нет анализа результатов вызова функций
sendto(6)
иrecvfrom(6)
, однако в ходе проведения отладки их результаты были учтены - ошибок не выявлено. WSAGetLastError()
не возвращает никакой ошибки.- При использовании для отправления и получения данных одного сокета вместо двух, каждая из машин при вызове
recvfrom(6)
получает какой-то мусор. - При включении тайм-аута для
recvfrom(6)
на М1 программа продолжает свою работу, но так никогда и не получает ответа от М2, из-за чего уже теперь она застревает в бесконечном цикле посылания части №0 и получении сообщения о контрольной сумме от М2. - При помещении
sendto(6)
на М2 даже в бесконечный цикл, М1 никогда не получает ответ о сходимости контрольных сумм. - Вызов
ping
вместоsendto(6)
на М2 иrecvfrom(6)
на М1 не выявляет никаких ошибок, соединение между машинами есть.