TFTP 中的超时和重新传输问题
Issue With Timeout and Re-transmission in TFTP
我编写了一个简单的 TFTP 服务器,它只处理读取请求 (RRQ),并且工作正常。如果在 5 秒内没有收到 ACK,服务器应该再次重新传输当前数据包。服务器还应该在放弃之前重新传输数据包 3 次。我试图在传输会话中间暂停客户端,以查看服务器是否会再次重新传输数据包,但它没有。问题似乎是服务器没有在 while
循环中继续。我试图测试它是否逃脱了循环,但没有。实在想不通为什么不循环一遍
这是我到目前为止编写的代码...
#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <time.h>
#define TIMEOUT 5000
#define RETRIES 3
void sendFile (char *Filename, char *mode, struct sockaddr_in client, int tid)
{
struct timeval tv;
tv.tv_sec = 5;
char path[70] = "tmp/";
char filebuf [1024];
int count = 0, i; // Number of data portions sent
unsigned char packetbuf[1024];
char recvbuf[1024];
socklen_t recv_size;
int sock = socket(PF_INET, SOCK_DGRAM, 0);
socklen_t optionslength = sizeof(tv);
setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &tv, optionslength);
FILE *fp;
char fullpath[200];
strcpy(fullpath, path);
strncat(fullpath, Filename, sizeof(fullpath) -1);
fp = fopen(fullpath, "r");
if (fp == NULL)
perror("");
memset(filebuf, 0, sizeof(filebuf));
while (1)
{
int acked = 0;
int ssize = fread(filebuf, 1 , 512, fp);
count++;
sprintf((char *) packetbuf, "%c%c%c%c", 0x00, 0x03, 0x00, 0x00);
memcpy((char *) packetbuf + 4, filebuf, ssize);
packetbuf[2] = (count & 0xFF00) >> 8;
packetbuf[3] = (count & 0x00FF);
int len = 4 + ssize;
memset(recvbuf, 0, 1024);
printf("\nSending Packet #%d", count);
sendto(sock, packetbuf, len, 0, (struct sockaddr *) &client, sizeof(client));
for (i=0; i<3; i++)
{
int result = recvfrom(sock, recvbuf, 1024, 0, (struct sockaddr *) &client, &recv_size);
if ((result == -1) && ((errno == EAGAIN) || (errno == EWOULDBLOCK)))
{
printf("\nRetransmitting Packet #%d",count);
sendto(sock, packetbuf, len, 0, (struct sockaddr *) &client, sizeof(client));
}
else if (result == -1)
{
}
else
{
if (tid == ntohs(client.sin_port))
{
printf("\nReceived Ack #%d",count);
acked++;
break;
}
else
continue;
}
}
if (acked!=1)
{
puts("\nGave Up Transmission");
break;
}
if (ssize != 512)
{
break;
}
}
}
int main()
{
int udpSocket, nBytes, tid, pid, status;
char buffer[1024], filename[200], mode[20], *bufindex, opcode;
struct sockaddr_in serverAddr, client;
struct sockaddr_storage serverStorage;
socklen_t addr_size;
udpSocket = socket(AF_INET, SOCK_DGRAM, 0);
serverAddr.sin_family = AF_INET;
serverAddr.sin_port = htons(69);
serverAddr.sin_addr.s_addr = inet_addr("127.0.0.1");
memset(serverAddr.sin_zero, '[=10=]', sizeof serverAddr.sin_zero);
bind(udpSocket, (struct sockaddr *) &serverAddr, sizeof(serverAddr));
pid = fork();
while(1)
{
int client_len = sizeof(client);
memset (buffer, 0, 1024);
nBytes = 0;
while (errno == EAGAIN || nBytes == 0)
{
waitpid(-1, &status, WNOHANG);
nBytes = recvfrom(udpSocket,buffer,1024,0,(struct sockaddr *)&client, &client_len);
}
bufindex = buffer;
bufindex++;
// Record the client port...
tid = ntohs(client.sin_port);
// Extracting the opcode from the packet...
opcode = *bufindex++;
// Extracting the filename from the packet...
strncpy(filename, bufindex, sizeof(filename)-1);
bufindex += strlen(filename) + 1;
// Extracting the mode from the packet...
strncpy(mode, bufindex, sizeof(mode)-1);
// If we received an RRQ...
if (opcode == 1)
{
puts("Received RRQ Packet");
pid = fork();
if (pid == 0)
{
sendFile(filename, mode, client, tid);
exit(1);
}
}
}
return 0;
}
注意:您可以使用linux自带的标准TFTP客户端来测试服务器。
提前致谢:)
可能的原因是:
struct timeval tv;
tv.tv_sec = 5;
tv 分配在堆栈上。堆栈内存未初始化,因此其中包含随机值。所以你需要明确设置 tv.tv_usec 为 0.
OP 的测试方法是杀死客户端。但是,这会杀死套接字。不是一个好的测试方法。
getaddrinfo 的 linux 手册页包含服务器代码示例和回显服务的客户端代码示例。基本逻辑可以应用于tftp服务。
请记住 tftp 有几种状态。这些状态应该反映在代码逻辑中。
我编写了一个简单的 TFTP 服务器,它只处理读取请求 (RRQ),并且工作正常。如果在 5 秒内没有收到 ACK,服务器应该再次重新传输当前数据包。服务器还应该在放弃之前重新传输数据包 3 次。我试图在传输会话中间暂停客户端,以查看服务器是否会再次重新传输数据包,但它没有。问题似乎是服务器没有在 while
循环中继续。我试图测试它是否逃脱了循环,但没有。实在想不通为什么不循环一遍
这是我到目前为止编写的代码...
#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <time.h>
#define TIMEOUT 5000
#define RETRIES 3
void sendFile (char *Filename, char *mode, struct sockaddr_in client, int tid)
{
struct timeval tv;
tv.tv_sec = 5;
char path[70] = "tmp/";
char filebuf [1024];
int count = 0, i; // Number of data portions sent
unsigned char packetbuf[1024];
char recvbuf[1024];
socklen_t recv_size;
int sock = socket(PF_INET, SOCK_DGRAM, 0);
socklen_t optionslength = sizeof(tv);
setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &tv, optionslength);
FILE *fp;
char fullpath[200];
strcpy(fullpath, path);
strncat(fullpath, Filename, sizeof(fullpath) -1);
fp = fopen(fullpath, "r");
if (fp == NULL)
perror("");
memset(filebuf, 0, sizeof(filebuf));
while (1)
{
int acked = 0;
int ssize = fread(filebuf, 1 , 512, fp);
count++;
sprintf((char *) packetbuf, "%c%c%c%c", 0x00, 0x03, 0x00, 0x00);
memcpy((char *) packetbuf + 4, filebuf, ssize);
packetbuf[2] = (count & 0xFF00) >> 8;
packetbuf[3] = (count & 0x00FF);
int len = 4 + ssize;
memset(recvbuf, 0, 1024);
printf("\nSending Packet #%d", count);
sendto(sock, packetbuf, len, 0, (struct sockaddr *) &client, sizeof(client));
for (i=0; i<3; i++)
{
int result = recvfrom(sock, recvbuf, 1024, 0, (struct sockaddr *) &client, &recv_size);
if ((result == -1) && ((errno == EAGAIN) || (errno == EWOULDBLOCK)))
{
printf("\nRetransmitting Packet #%d",count);
sendto(sock, packetbuf, len, 0, (struct sockaddr *) &client, sizeof(client));
}
else if (result == -1)
{
}
else
{
if (tid == ntohs(client.sin_port))
{
printf("\nReceived Ack #%d",count);
acked++;
break;
}
else
continue;
}
}
if (acked!=1)
{
puts("\nGave Up Transmission");
break;
}
if (ssize != 512)
{
break;
}
}
}
int main()
{
int udpSocket, nBytes, tid, pid, status;
char buffer[1024], filename[200], mode[20], *bufindex, opcode;
struct sockaddr_in serverAddr, client;
struct sockaddr_storage serverStorage;
socklen_t addr_size;
udpSocket = socket(AF_INET, SOCK_DGRAM, 0);
serverAddr.sin_family = AF_INET;
serverAddr.sin_port = htons(69);
serverAddr.sin_addr.s_addr = inet_addr("127.0.0.1");
memset(serverAddr.sin_zero, '[=10=]', sizeof serverAddr.sin_zero);
bind(udpSocket, (struct sockaddr *) &serverAddr, sizeof(serverAddr));
pid = fork();
while(1)
{
int client_len = sizeof(client);
memset (buffer, 0, 1024);
nBytes = 0;
while (errno == EAGAIN || nBytes == 0)
{
waitpid(-1, &status, WNOHANG);
nBytes = recvfrom(udpSocket,buffer,1024,0,(struct sockaddr *)&client, &client_len);
}
bufindex = buffer;
bufindex++;
// Record the client port...
tid = ntohs(client.sin_port);
// Extracting the opcode from the packet...
opcode = *bufindex++;
// Extracting the filename from the packet...
strncpy(filename, bufindex, sizeof(filename)-1);
bufindex += strlen(filename) + 1;
// Extracting the mode from the packet...
strncpy(mode, bufindex, sizeof(mode)-1);
// If we received an RRQ...
if (opcode == 1)
{
puts("Received RRQ Packet");
pid = fork();
if (pid == 0)
{
sendFile(filename, mode, client, tid);
exit(1);
}
}
}
return 0;
}
注意:您可以使用linux自带的标准TFTP客户端来测试服务器。
提前致谢:)
可能的原因是:
struct timeval tv;
tv.tv_sec = 5;
tv 分配在堆栈上。堆栈内存未初始化,因此其中包含随机值。所以你需要明确设置 tv.tv_usec 为 0.
OP 的测试方法是杀死客户端。但是,这会杀死套接字。不是一个好的测试方法。
getaddrinfo 的 linux 手册页包含服务器代码示例和回显服务的客户端代码示例。基本逻辑可以应用于tftp服务。
请记住 tftp 有几种状态。这些状态应该反映在代码逻辑中。