tftp客户端实现(二)-写文件并回发确认消息

本文详细介绍了如何使用fopen创建文件,然后通过TFTP协议接收服务器数据,同时构造并发送ACK确认消息。内容涵盖fopen的使用、sockaddr_in结构变量的定义、数据接收与解析、ACK构造与发送,以及完整代码示例。
摘要由CSDN通过智能技术生成

一、使用fopen来创建文件

当tftp的RRQ消息发送成功后,正常情况下服务器端就会回发第一个Data数据包,这时,客户端需要做的是,在本地新建一个文件,以便将从服务器端接收的数据写入到此文件中。现在,我们暂时使用fopen来创建文件。

对于fopen()函数,简单介绍一下:

函数原型:FILE * fopen(const char * path,const char * mode);
返回值:文件顺利打开后,指向该流的文件指针就会被返回。如果文件打开失败则返回NULL,并把错误代码存在errno中。
一般而言,打开文件后会做一些文件读取或写入的动作,若打开文件失败,接下来的读写动作也无法顺利进行,所以一般在fopen()后作错误判断及处理。
参数说明:
参数path字符串包含欲打开的文件路径及文件名,参数mode字符串则代表着流形态。
mode有下列几种形态字符串,只列举部分 :
“r” 以只读方式打开文件,该文件必须存在。
“r+” 以可读写方式打开文件,该文件必须存在。
“rb+” 读写打开一个二进制文件,允许读写数据,文件必须存在。
“w” 打开只写文件,若文件存在则文件长度清为0,即该文件内容会消失。若文件不存在则建立该文件。
“w+” 打开可读写文件,若文件存在则文件长度清为零,即该文件内容会消失。若文件不存在则建立该文件。
“a” 以附加的方式打开只写文件。若文件不存在,则会建立该文件,如果文件存在,写入的数据会被加到文件尾,即文件原先的内容会被保留。(EOF符保留)
“a+“ 以附加方式打开可读写的文件。若文件不存在,则会建立该文件,如果文件存在,写入的数据会被加到文件尾后,即文件原先的内容会被保留。 (原来的EOF符不保留)
“wb” 只写打开或新建一个二进制文件;只允许写数据。
具体使用
/*生成需要接收的文件*/
FILE *fp = fopen("请写完整文件名","wb");
if(NULL == fp)
{
    perror("fopen()");
    return 1;
}

二、定义sockaddr_in结构变量,用于保存服务端的IP及端口号,以便后续的通信使用。

struct sockaddr_in peeraddr;
memset(&peeraddr, 0, sizeof(peeraddr));

三、进入while循环,准备接收数据:

其中,ioctl的作用为从sockfd对应的连接中读取数据,FIONREAD参数指明获取接收缓存区中的字节数,并把读取的字节数的值赋给bufferlen。

while(1)
{
    int bufferlen = 0;
    /*在此阻塞,等待数据的到来,使用此函数可以知道有多少数据到达接收缓冲器区,
      然后根据接收的数据长度使用malloc()函数来进行动态内存分配*/
    while(bufferlen == 0)
    {
        ioctl(sockfd, FIONREAD, &bufferlen);
    }
    printf("DBG:bufferlen = %d\n", bufferlen);

    void *buffer = malloc(bufferlen);  /*根据到达的数据的大小,动态分配内存*/

    int recvLen = sizeof(peeraddr);
    int recvlen 
  • 1
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
TFTP一个基于UDP协议的简单文件传输协议,操作码用于区分不同的TFTP操作。以下是TFTP操作码的定义: 1. RRQ(读请求) 2. WRQ(请求) 3. DATA(数据) 4. ACK(确认) 5. ERROR(错误) 下面分别介绍TFTP客户端和服务端的实现TFTP客户端: ```c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <unistd.h> #define MAX_BUF_SIZE 1024 void error(char *msg) { perror(msg); exit(1); } int main(int argc, char *argv[]) { if (argc != 4) { fprintf(stderr, "Usage: %s <server_ip> <filename> <mode>\n", argv[0]); exit(1); } int sockfd = socket(AF_INET, SOCK_DGRAM, 0); if (sockfd < 0) { error("Error opening socket"); } struct sockaddr_in serv_addr; bzero(&serv_addr, sizeof(serv_addr)); serv_addr.sin_family = AF_INET; serv_addr.sin_addr.s_addr = inet_addr(argv[1]); serv_addr.sin_port = htons(69); char buf[MAX_BUF_SIZE]; char *mode = "octet"; sprintf(buf, "%c%c%s%c%s%c", 0x00, 0x01, argv[2], 0x00, mode, 0x00); int n = sendto(sockfd, buf, strlen(buf), 0, (struct sockaddr *)&serv_addr, sizeof(serv_addr)); if (n < 0) { error("Error sending request"); } FILE *fp = fopen(argv[2], "wb"); if (!fp) { error("Error opening file for writing"); } int block_num = 0; int recv_len = 0; struct sockaddr_in cli_addr; socklen_t cli_len = sizeof(cli_addr); while (1) { bzero(buf, MAX_BUF_SIZE); n = recvfrom(sockfd, buf, MAX_BUF_SIZE, 0, (struct sockaddr *)&cli_addr, &cli_len); if (n < 0) { error("Error receiving data"); } int opcode = ntohs(*(unsigned short *)buf); if (opcode == 5) { char *error_msg = buf + 4; fprintf(stderr, "Error: %s\n", error_msg); exit(1); } else if (opcode == 3) { int block = ntohs(*(unsigned short *)(buf + 2)); if (block == block_num + 1) { fwrite(buf + 4, 1, n - 4, fp); block_num++; } sprintf(buf, "%c%c%c%c", 0x00, 0x04, block_num >> 8, block_num & 0xff); n = sendto(sockfd, buf, 4, 0, (struct sockaddr *)&cli_addr, cli_len); if (n < 0) { error("Error sending ACK"); } if (n < MAX_BUF_SIZE - 4) { break; } } } fclose(fp); close(sockfd); return 0; } ``` TFTP服务端: ```c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <unistd.h> #include <fcntl.h> #include <errno.h> #define MAX_BUF_SIZE 1024 void error(char *msg) { perror(msg); exit(1); } int main(int argc, char *argv[]) { if (argc != 2) { fprintf(stderr, "Usage: %s <directory>\n", argv[0]); exit(1); } int sockfd = socket(AF_INET, SOCK_DGRAM, 0); if (sockfd < 0) { error("Error opening socket"); } struct sockaddr_in serv_addr; bzero(&serv_addr, sizeof(serv_addr)); serv_addr.sin_family = AF_INET; serv_addr.sin_addr.s_addr = INADDR_ANY; serv_addr.sin_port = htons(69); if (bind(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) { error("Error on binding"); } char buf[MAX_BUF_SIZE]; struct sockaddr_in cli_addr; socklen_t cli_len = sizeof(cli_addr); while (1) { bzero(buf, MAX_BUF_SIZE); int n = recvfrom(sockfd, buf, MAX_BUF_SIZE, 0, (struct sockaddr *)&cli_addr, &cli_len); if (n < 0) { error("Error receiving request"); } int opcode = ntohs(*(unsigned short *)buf); if (opcode == 1) { char *filename = buf + 2; char *mode = buf + 2 + strlen(filename) + 1; if (strcmp(mode, "octet") != 0) { char *error_msg = "Unsupported mode"; sprintf(buf, "%c%c%c%c%s%c", 0x00, 0x05, 0x00, 0x00, error_msg, 0x00); sendto(sockfd, buf, strlen(buf), 0, (struct sockaddr *)&cli_addr, cli_len); continue; } char path[1024]; sprintf(path, "%s/%s", argv[1], filename); int fd = open(path, O_RDONLY); if (fd < 0) { char *error_msg = strerror(errno); sprintf(buf, "%c%c%c%c%s%c", 0x00, 0x05, 0x00, 0x00, error_msg, 0x00); sendto(sockfd, buf, strlen(buf), 0, (struct sockaddr *)&cli_addr, cli_len); continue; } int block_num = 0; while (1) { bzero(buf, MAX_BUF_SIZE); int n = read(fd, buf + 4, MAX_BUF_SIZE - 4); if (n < 0) { error("Error reading file"); } if (n == 0) { break; } *(unsigned short *)buf = htons(3); *(unsigned short *)(buf + 2) = htons(block_num + 1); sendto(sockfd, buf, n + 4, 0, (struct sockaddr *)&cli_addr, cli_len); while (1) { bzero(buf, MAX_BUF_SIZE); n = recvfrom(sockfd, buf, MAX_BUF_SIZE, 0, (struct sockaddr *)&cli_addr, &cli_len); if (n < 0) { error("Error receiving ACK"); } opcode = ntohs(*(unsigned short *)buf); if (opcode == 4) { block_num = ntohs(*(unsigned short *)(buf + 2)); if (block_num == (int)(*(unsigned char *)(buf + 1))) { break; } } } } close(fd); } } close(sockfd); return 0; } ```
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值