Я пытаюсь написать две сетевые программы на C:
i.клиентская программа, которая запрашивает файл с сервера, сначала отправляя информацию о файле для запроса через UDP, а затем открывает сокет TCP, который будет использоваться для получения запрошенного файла.(клиент действует как сервер в соединении TCP для получения файла.)
ii.серверная программа, которая будет получать информацию о запрошенном файле через UDP и соединяться с клиентом через TCP для отправки файла.
Я думаю, что на сервере происходит что-то странное, когда я пытаюсь прочитать запрошенный файл, используяfread: значение дескриптора файла сокета TCP изменяется, и значение указателя потока (FILE *), из которого я пытаюсь прочитать, также изменяется.Я потратил немного времени на его просмотр, но не смог понять.
Я попытался добавить несколько избыточных переменных (сокет, файл, original_fd) для хранения затронутых значенийдо вызова фреда, но они, похоже, также изменились после вызова фреда (вы можете увидеть это на скриншотах, которые я публикую).Я не смог найти пост на S / O с похожей проблемой.(Я создаю и запускаю программы с использованием подсистемы Windows для Linux.)
собственно серверная программа:
#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»