Я пишу tftp-сервер на C и тестирую его с помощью команды tftp на терминале. Однако в большинстве случаев я получаю следующее, когда пытаюсь отправить RRQ:
tftp> get a.txt
sent RRQ <file=a.txt, mode=netascii>
received ERROR <code=4, msg=>
Error code 1024:
Другой случай, когда произошло:
tftp> get a.txt
sent RRQ <file=a.txt, mode=netascii>
received DATA <block=20334, 512 bytes>
discarded 4 packets
и этот: это может выглядеть правильно, но это почти не произошло. Текстовый файл, который я использую для проверки, имеет размер 857 байт.
sent ACK <block=1>
received DATA <block=1, 512 bytes>
sent ACK <block=1>
received DATA <block=2, 345 bytes>
Received 857 bytes in 0.0 seconds
и вот часть моего кода
Здесь буфер представляет собой массив символов размера 512, чтобы упростить код, я удалил часть кода обработки ошибок
Спасибо, кто может помочь
#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <sys/select.h>
#include <sys/stat.h>
#include <dirent.h>
#include <fcntl.h>
#include <string.h>
#include <signal.h>
#include <sys/wait.h>
#include <cerrno>//define opcode for later use
enum opcode {
RRQ = 1,
WRQ,
DATA,
ACK,
ERROR
};void handle_write(struct sockaddr_in * sock_info, char* filename, int BUF_LEN){
//printf("Received write request + %d\n", WRQ);
return;
}
void handle_error(unsigned short int * opcode_ptr, char* buffer, socklen_t sockaddr_len, int server_socket, struct sockaddr_in * sock_info, const char* errormsg){
ssize_t n;
*opcode_ptr = htons(ERROR);
*(opcode_ptr + 1) = htons(1);
*(buffer + 4) = 0;
//memcpy(buffer+4, errormsg, strlen(errormsg));
intr_send:
n = sendto(server_socket, buffer, 5, 0,
(struct sockaddr *)sock_info, sockaddr_len);
if(n < 0) {
if(errno == EINTR) goto intr_send;
perror(errormsg);
exit(-1);
}
return;
}
int main() {
int BUF_LEN = 516;
ssize_t n;
char buffer[BUF_LEN];
socklen_t sockaddr_len;
int server_socket;
struct sigaction act;
unsigned short int opcode;
unsigned short int * opcode_ptr;
struct sockaddr_in sock_info;
memset(&sock_info, 0, sockaddr_len);
//----------setup the server----------------//
sock_info.sin_addr.s_addr = htonl(INADDR_ANY);
sock_info.sin_port = htons(5743);
sock_info.sin_family = PF_INET;
if((server_socket = socket(PF_INET, SOCK_DGRAM, 0)) < 0) {
perror("socket");
exit(-1);
}
sockaddr_len = sizeof(sock_info);
if(bind(server_socket, (struct sockaddr *)&sock_info, sockaddr_len) < 0) {
perror("bind");
exit(-1);
}
getsockname(server_socket, (struct sockaddr *)&sock_info, &sockaddr_len);
printf("UDP server listening on port: %d\n", ntohs(sock_info.sin_port));
//----------setup the server----------------//
while(1){
intr_recv:
n = recvfrom(server_socket, buffer, BUF_LEN, 0, (struct sockaddr *)&sock_info, &sockaddr_len);
if(n < 0) {
if(errno == EINTR) goto intr_recv;
perror("recvfrom()");
exit(-1);
}
opcode_ptr = (unsigned short int *)buffer;
opcode = ntohs(*opcode_ptr); //the opcode will be either RRQ or WRQ according to the test
if(opcode != RRQ && opcode != WRQ) {
/* Illegal TFTP Operation */
handle_error(opcode_ptr, buffer, sockaddr_len, server_socket, &sock_info, "invalid command");
}
else {
if(fork() == 0) {
/* Child - handle the request */
FILE* fd;
char* filename;
filename = strdup(buffer+2); //this is the filename to be read (i.e. a.txt)
printf("request received\n");
char data[512];
//----------------------------------handle read request-------------------------------------//
if(opcode == RRQ){
int blocknumber = 0;
int i = 0; //counter for loop
fd = fopen(filename, "r");
free(filename);
//uint8_t data[512];
ssize_t datalen, n;
int done = 0; //this is a boolean indicator that indicates whether the packet transfering process is done or not.
while(!done){
datalen = fread(data, 1, 512, fd);
blocknumber++;
if(datalen < 512){
done = 1; //according to rfc 1350, the last packet will have a data length less than 512 bytes.
}
//for(i = 5; i > 0; i--){
*opcode_ptr = htons(DATA);
opcode = ntohs(*opcode_ptr);
*(opcode_ptr + 1) = htons(blocknumber);
memcpy(buffer + 4, data, datalen);
buffer[datalen + 4] = '\0';
//*(buffer + 4) = 0;
//printf("%d %s\n", datalen, buffer+2);
n = sendto(server_socket, buffer, 4 + datalen, 0, (struct sockaddr *)&sock_info, sockaddr_len);
if(n < 0){
perror("sendto() failed");
exit(-1);
}
//printf("done %d\n", done);
//char buffer[512];
n = recvfrom(server_socket, buffer, sizeof(buffer), 0, (struct sockaddr *)&sock_info, &sockaddr_len);
opcode_ptr = (unsigned short int *)buffer;
opcode = ntohs(*opcode_ptr);
if(n >= 0 && n < 4){
//handle_error(opcode_ptr, buffer, sockaddr_len, server_socket, &sock_info, "invalid request size");
}
if(n > 4){
break;
}
//}
//if(i != 0){
// printf("Transfer timeout!\n");
// exit(1);
//}
//printf("opcode is %d\n", opcode);
if(opcode == ERROR){
printf("Error received\n");
exit(1);
}
if(opcode != ACK){
printf("Invalid message received\n");
exit(1);
}
}
}
//----------------------------------handle read request-------------------------------------////----------------------------------handle write request------------------------------------//
//----------------------------------handle write request------------------------------------//
close(server_socket);
break;
}
else {
/* Parent - continue to wait */
}
}
}
return EXIT_SUCCESS;
}
Я прочитал ваш код, но не проверен выполнением.
Сравнивая с RFC 1350, что я нашел
buffer
недостаточно, потому что нет места для заголовка (код операции и блок #). Вам нужно как минимум еще 4 байта.buffer + 2
с помощью memcpy()
, Это должно уничтожить номер блока. Похоже на то buffer + 4
следует использовать вместоbuffer[datalen + 2] = '\0';
не должно быть нужно Я думаю, что вы должны удалить его, потому что это уничтожит данные или вызовет переполнение буфера.Других решений пока нет …