TFTP客户端
#include <head.h>
#define SER_PORT 69
#define SER_IP "192.168.114.219"
struct ack {
short ack;
short bnum;
};
int do_download(int cfd, struct sockaddr_in sin);
int do_upload(int cfd, struct sockaddr_in sin);
int main(int argc, const char* argv[])
{
// 创建套接字
int cfd = socket(AF_INET, SOCK_DGRAM, 0);
if (cfd < 0)
PRINT_ERR("socket");
// 填充服务器IP
struct sockaddr_in sin;
sin.sin_addr.s_addr = inet_addr(SER_IP);
sin.sin_port = htons(SER_PORT);
sin.sin_family = AF_INET;
char mode;
while (1) {
puts("****************************************");
puts("***************1.下载*******************");
puts("***************2.上传*******************");
puts("***************3.退出*******************");
puts("****************************************");
printf("输入>>");
mode = getchar();
while (getchar() != 10)
;
switch (mode) {
case '1':
do_download(cfd, sin);
break;
case '2':
do_upload(cfd, sin);
break;
case '3':
goto END;
}
}
END:
// 关闭套接字
close(cfd);
return 0;
}
int do_download(int cfd, struct sockaddr_in sin)
{
printf("要下载的文件名>>");
char name[64] = "0";
scanf("%s", name);
while (getchar() != 10)
;
// 发送下载请求
char data[516] = "";
short* p = (short*)data;
*p = htons(1);
sprintf(data + 2, "%s%c%s%c", name, 0, "octet", 0);
size_t size = 4 + 2 + strlen(name) + 5;
if (sendto(cfd, &data, size, 0, (struct sockaddr*)&sin, sizeof(sin)) < 0)
PRINT_ERR("sendto");
struct sockaddr_in newsin;
socklen_t addrlen = sizeof(sin);
struct ack ack;
ssize_t res;
// 在本地创建要下载的文件
FILE* fd = fopen(name, "w");
if (fd == NULL)
PRINT_ERR("fopen");
while (1) {
// 读取数据并写入到本地文件
bzero(data, sizeof(data));
res = recvfrom(cfd, data, sizeof(data), 0, (struct sockaddr*)&newsin, &addrlen);
if (res < 0)
PRINT_ERR("recvfrom");
if (5 == ntohs(*(short*)data)) { // 操作码=5时退出并输出错误信息
puts(data + 4);
break;
}
fwrite(data + 4, res - 4, 1, fd);
// 回应
ack.ack = htons(4);
ack.bnum = (*(short*)(data + 2));
if (sendto(cfd, &ack, sizeof(ack), 0, (struct sockaddr*)&newsin, sizeof(sin)) < 0)
PRINT_ERR("sendto");
if (res < 516) {
puts("下载完成!");
break;
}
}
fclose(fd);
return 0;
}
int do_upload(int cfd, struct sockaddr_in sin)
{
printf("要上传的文件名>>");
char name[64] = "";
scanf("%s", name);
while (getchar() != 10)
;
// 发送上传请求
char data[516] = "";
short* p = (short*)data;
*p = htons(2);
sprintf(data + 2, "%s%c%s%c", name, 0, "octet", 0);
size_t size = 4 + 2 + strlen(name) + 5;
if (sendto(cfd, &data, size, 0, (struct sockaddr*)&sin, sizeof(sin)) < 0)
PRINT_ERR("sendto");
struct sockaddr_in newsin;
socklen_t addrlen = sizeof(sin);
short* ack = (short*)data;
short* bnum = ack + 1;
struct ack ACK;
bzero(&ACK, sizeof(ACK));
ssize_t res;
// 接收ACK和新的端口
bzero(data, sizeof(data));
if (recvfrom(cfd, &data, sizeof(data), 0, (struct sockaddr*)&newsin, &addrlen) < 0)
PRINT_ERR("recvfrom");
printf("port=%d\n", ntohs(newsin.sin_port));
if (5 == ntohs(*(short*)data)) { // 操作码=5时退出并输出错误信息
puts(data + 4);
return -1;
}
// 绑定新的端口
if (connect(cfd, (struct sockaddr*)&newsin, addrlen) < 0)
PRINT_ERR("connect");
// 打开本地要上传的文件
FILE* fd = fopen(name, "r");
if (fd == NULL)
PRINT_ERR("fopen");
short i = 0; // 块编号
while (1) {
// 读本地文件并发送给服务器
bzero(data, sizeof(data));
*ack = htons(3);
*bnum = htons(++i);
res = fread(data + 4, 1, sizeof(data) - 4, fd);
if (sendto(cfd, &data, res + 4, 0, NULL, 0) < 0)
PRINT_ERR("sendto");
int j = 0;
while (j < 10) {
// 接收应答
bzero(&ACK, sizeof(ACK));
if (recvfrom(cfd, &ACK, sizeof(ACK), 0, NULL, NULL) < 0)
PRINT_ERR("recvfrom");
// ack验证
if (ntohs(ACK.ack) == 4 && ntohs(ACK.bnum) == i)
break;
else if (5 == ntohs(ACK.ack)) { // 操作码=5时退出并解除端口
printf("差错码:%d\n", ntohs(ACK.bnum));
newsin.sin_family = AF_UNSPEC;
if (connect(cfd, (struct sockaddr*)&newsin, addrlen) < 0)
PRINT_ERR("connect");
fclose(fd);
return -1;
}
if (sendto(cfd, &data, res + 4, 0, NULL, 0) < 0)
PRINT_ERR("sendto");
j++;
}
if (j >= 10) {
puts("超时!");
break;
}
if (res < 512) {
puts("上传完毕!");
break;
}
}
// 解除端口
newsin.sin_family = AF_UNSPEC;
if (connect(cfd, (struct sockaddr*)&newsin, addrlen) < 0)
PRINT_ERR("connect");
fclose(fd);
return 0;
}