Неожиданное изменение значения переменных при чтении из файла с использованием fread - PullRequest
0 голосов
/ 01 июня 2019

Я пытаюсь написать две сетевые программы на C:

i.клиентская программа, которая запрашивает файл с сервера, сначала отправляя информацию о файле для запроса через UDP, а затем открывает сокет TCP, который будет использоваться для получения запрошенного файла.(клиент действует как сервер в соединении TCP для получения файла.)

ii.серверная программа, которая будет получать информацию о запрошенном файле через UDP и соединяться с клиентом через TCP для отправки файла.

Я думаю, что на сервере происходит что-то странное, когда я пытаюсь прочитать запрошенный файл, используяfread: значение дескриптора файла сокета TCP изменяется, и значение указателя потока (FILE *), из которого я пытаюсь прочитать, также изменяется.Я потратил немного времени на его просмотр, но не смог понять.

Я попытался добавить несколько избыточных переменных (сокет, файл, original_fd) для хранения затронутых значенийдо вызова фреда, но они, похоже, также изменились после вызова фреда (вы можете увидеть это на скриншотах, которые я публикую).Я не смог найти пост на S / O с похожей проблемой.(Я создаю и запускаю программы с использованием подсистемы Windows для Linux.)

server (left) and client (right) programs side by side

собственно серверная программа:

#include <sys/socket.h> /*  socket definitions        */
#include <sys/types.h>  /*  socket types              */
#include <arpa/inet.h>  /*  inet (3) funtions         */
#include <unistd.h>         /*  misc. UNIX functions      */
#include <errno.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
#include <ctype.h>
#include "helpers.h"

/*  Global constants  */
#define LISTENQ 1024
#define DEFAULT_UDP_PORT 2002
#define MAX_LINE 1000
#define FILE_REQUEST_SIGNAL_SIZE_BYTES (MAX_FILE_NAME_LENGTH + 5)
#define RECEIVE_SIGNAL_FLAGS 0x0
#define SEND_SIGNAL_FLAGS 0x0

void parse_cmd_line_arguments(int, char* [], short int*);

int main(int argc, char* argv[]) {

  // INET ADDRESS VARIABLES
  int udp_socket;                                                           // the server's udp socket
    int tcp_socket;                                                         // the server's tcp socket (will only be instantiated on file request)
    short int udp_port;                                                 // the server's (udp) port number
    struct sockaddr_in server_address;                          // the address of the server
    struct sockaddr_in client_address;                          // the address of the client (ip address and port)
    socklen_t client_address_length = sizeof(client_address); // length of the client's address struct
  socklen_t client_server_length = sizeof(server_address); // length of the client's address struct

  // DATA TRANSFER VARIABLES
  int number_of_bytes_received, number_of_bytes_read, total_number_of_bytes_read;
  int number_of_bytes_sent;
  int file_size = -1;
  char buffer[MAX_LINE];                                              // reading and writing buffer
  char filename[MAX_FILE_NAME_LENGTH];
  bool file_read_error = false;
  FILE* requested_file_fd;

  parse_cmd_line_arguments(argc, argv, &udp_port);
  printf("Open for UDP transmissions on port: [%i].\n", udp_port);

  if ((udp_socket = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
    perror("Unable to open UDP socket: ");
    exit(EXIT_FAILURE);
  }

  set_inet_address(&server_address, AF_INET, INADDR_ANY, udp_port);

  printf("Server is running at address: [%s]:[%i]\n", inet_ntoa(server_address.sin_addr), server_address.sin_port);

  if (bind(udp_socket, (struct sockaddr*) &server_address, sizeof(server_address)) < 0) {
    perror("Error binding socket to address/port");
    exit(EXIT_FAILURE);
  }

  while(TRUE) {
    memset(buffer, 0, sizeof(buffer));

    if ((number_of_bytes_received = recvfrom(udp_socket,
                                             buffer,
                                             FILE_REQUEST_SIGNAL_SIZE_BYTES,
                                             RECEIVE_SIGNAL_FLAGS,
                                             (struct sockaddr*) &client_address,
                                             &client_address_length)) < 0) {
      perror("Error receiving signal bytes: ");
      exit(EXIT_FAILURE);
    }

    if (number_of_bytes_received == 0) {
      printf("Received 0 signal bytes. Check client?\n");
    } else {
      printf("Signal received: [%s].\n", buffer);
      strcpy(filename, strchr(buffer, '\n') + 1);
      printf("Requested file: [%s]\n", filename);

      memset(buffer, 0, sizeof(buffer));
      // Receive client TCP port number.
      if ((number_of_bytes_received = recvfrom(udp_socket,
                                             buffer,
                                             PORT_NUMBER_SIZE_BYTES,
                                             RECEIVE_SIGNAL_FLAGS,
                                             (struct sockaddr*) &client_address,
                                             &client_address_length)) < 0) {
        perror("Error receiving signal bytes: ");
        exit(EXIT_FAILURE);
      }

      if (number_of_bytes_received == 0) {
        printf("Expected port number but nothing was received. Check client?\n");
      } else {
        printf("Received port nummber: [%s].\n", buffer);
        client_address.sin_port = atoi(buffer);

        // Set up TCP socket.
        if ((tcp_socket = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
          perror("Can't create TCP socket");
          printf("Abandoning File transfer.\n");
          continue;
        }

        if (connect(tcp_socket, (struct sockaddr*) &client_address, client_address_length) < 0) {
          perror("Can't connect to client");
          printf("Abandoning File transfer.\n");
          continue;
        }

        // Open file.
        if((requested_file_fd = fopen(filename, "r")) == NULL) {
          perror("Error opening file");
          exit(EXIT_FAILURE);
        }
        const FILE* original_fd = requested_file_fd;

        get_file_size(requested_file_fd, &file_size);
        // Read from disk to buffer, then from buffer to client TCP socket.
        printf("The size of the desired file is: [%i].\n", file_size);

        memset(buffer, 0, sizeof(buffer));
        number_of_bytes_read = 0, total_number_of_bytes_read = 0;

        while (total_number_of_bytes_read < file_size) {

          // For some reason, requested_file_fd and \
             tcp_socket are altered after fread is called.
          printf("requested_file_fd (before fread): [%x]\n", requested_file_fd);
          printf("tcp_socket (before fread): [%x]\n", tcp_socket);
          int socket = tcp_socket;
          FILE* file = requested_file_fd;
          printf("file (before fread): [%x]\n", file);
          printf("socket (before fread): [%x]\n", socket);
          printf("original_fd (before fread): [%x]\n", original_fd);
          number_of_bytes_read = fread(buffer,
                                      1,
                                      4096,
                                      file);
          // printf("number of bytes read: [%i].\n", number_of_bytes_read);
          printf("requested_file_fd (after fread): [%x]\n", requested_file_fd);
          printf("tcp_socket (after fread): [%x]\n", tcp_socket);
          printf("file (after fread): [%x]\n", file);
          printf("socket (after fread): [%x]\n", socket);
          printf("original_fd (after fread): [%x]\n", original_fd);

          if ((number_of_bytes_read) < 0) {
            perror("Error reading from file");
            file_read_error = true;
            break;
          } else {
            // if ((number_of_bytes_sent = sendto(tcp_socket, buffer, number_of_bytes_read, 0x0, (struct sockaddr*) &client_address, client_address_length)) < 0) {
            if (write(tcp_socket, buffer, number_of_bytes_read) < 0) {
              perror("Error writing file to client");
              break;
            }
            total_number_of_bytes_read += number_of_bytes_read;
          }
        }

        // Close file.
        if (fclose(requested_file_fd) ==  EOF) {
          perror("Error closing file");
          exit(EXIT_FAILURE);
        }

        // Close TCP socket.
        if ((close(tcp_socket) < 0)) {
          perror("Error closing server tcp socket");
          exit(EXIT_FAILURE);
        }
      }
    }
  }

  return EXIT_SUCCESS;
}

void parse_cmd_line_arguments(int argc, char* argv[], short int* udp_port) {
  char* end_ptr;

  if (argc == 1) {
    *udp_port = DEFAULT_UDP_PORT;
  } else if (argc == 2) {
    *udp_port = strtol(argv[1], &end_ptr, 0);

    if (*end_ptr) {
      printf("Invalid port number provided: %s.\n", argv[1]);
      exit(EXIT_SUCCESS);
    }
  } else {
    printf("Invalid arguments provided.\n");
    exit(EXIT_SUCCESS);
  }
}

вспомогательные файлы: helper.h:

#define TRUE 1
#define MAX_FILE_NAME_LENGTH 255
#define PORT_NUMBER_SIZE_BYTES 5
#define FILE_READ_ELEMENT_SIZE_BYTES 1
#define NUMBER_OF_FILE_READ_ELEMENTS_PER_READ 4096
// #define false 0
// #define true 1


void set_inet_address(struct sockaddr_in* server_address_ptr,
                             const int type,
                             const in_addr_t inet_address,
                             const short int port_number);

void get_file_size(FILE* fd, int*);

helper.c:

#include <arpa/inet.h> /*  inet (3) funtions         */
#include <stdio.h>
#include <string.h>
#include "helpers.h"

void set_inet_address(struct sockaddr_in* server_address_ptr,
                             const int inet_family,
                             const in_addr_t inet_address,
                             const short int port_number) {
  memset((char*) server_address_ptr, 0, sizeof(struct sockaddr_in));
  (*server_address_ptr).sin_family = inet_family;
  (*server_address_ptr).sin_addr.s_addr = inet_address;
  (*server_address_ptr).sin_port = port_number;
}

void get_file_size(FILE* fd, int* size) {
  fseek(fd, 0, SEEK_SET);
  fseek(fd, 0, SEEK_END);
  *size = ftell(fd);
  fseek(fd, 0, SEEK_SET);
}

Я ожидал, что сервер просто прочитает содержимое файла и отправит его клиенту, но потому чтоtcp_socket и required_file_fd изменяют вызовы на «write» и fclose «fail»

...