TFTP文件传输代码注解

三)关键代码段及注释:

-----------------先从客户端主函数看:

int main(int argc, char *argv[])

{

   int sockfd, fd, nbyte;

   char command[32];    //定义一个字符数组,用于接受命令

   struct sockaddr_in server_addr;//定义结构体,存储套接字信息

   if (argc < 3)  //判断接收的参数是否小于3

   {

      printf("Usage : %s <server_ip> : <port>\n", argv[0]);

      exit(-1);   //判断条件成立,则非正常退出

   } 

   if ((sockfd = socket(PF_INET, SOCK_STREAM, 0)) < 0)

   {//创建套接字,返回一个可用的套接字描述符,若创建失败将错误信息在终端显示

      fprintf(stderr, "fail to socket : %s\n", strerror(errno));

      exit(-1);

   }

#ifdef _DEBUG_

   printf("socket is %d\n", sockfd);

#endif 

   memset(&server_addr, 0, sizeof(server_addr));

 //对结构体进行清零操作

   server_addr.sin_family = PF_INET; //指定地址协议簇

   server_addr.sin_port = htons(atoi(argv[2]));// /*  你需要绑定的端口号   */ //若改为htons(0)则表示可自动获取一个可用端口

   server_addr.sin_addr.s_addr = inet_addr(argv[1]);//本机IP,转换为网络字节序

   //server_addr.sin_addr.s_addr = htonl(INADDR_ANY);

 //可以自动获取主机IP

if (connect(sockfd, (SA *)&server_addr, sizeof(server_addr)) < 0)

   {//判断连接服务器是否成功

      printf("fail to connect server\n");

      goto ERROR;

   }

   while ( 1 )

   {

      printf("<client> ");

      fgets(command, 32, stdin); //从键盘接受命令

      command[strlen(command)-1] = '\0'; 

// 将'\n' 用’\0’替换掉,是后面可以正确读取文件名

 

//判断相应的命令,执行对应的函数

      if (strcmp(command, "help") == 0)

      {

         PrintHelp();

      }

      else if (strcmp(command, "list") == 0)

      {

        ProcessList(server_addr);

      }

      else if (strncmp(command, "get ", 4) == 0)

      {

        ProcessGet(server_addr, command);

      }

      else if (strncmp(command, "put ", 4) == 0)

      {

        ProcessPut(server_addr, command);

      }

      else if (strcmp(command, "quit") == 0)

      {

        printf("Bye\n");

        break;

      }

      else

{

        printf("wrong command, 'help' for command list\n");

}

   }

ERROR:

   close(sockfd);//连接失败则关闭套接字

   return 0;

}

1. 获取所有命令的帮助信息

void PrintHelp()

{

   printf("help : display help info\n");

   printf("list : get file list of server\n");

   printf("get  : get <file>\n");

   printf("put  : put <file>\n");

   printf("quit : quit the client\n");

   return;

}。

2.查看服务器所在目录下文件

void ProcessList(struct sockaddr_in server_addr)

{

   int sockfd, nbyte;

   char buf[N];

   strcpy(buf, "L");

   send(sockfd, buf, N, 0);//将本地缓存buf中的内容发到服务器,服务器接收到后会执行相应函数查看自己目录下的内容,并发送回客户端

   while ((nbyte = recv(sockfd, buf, N, 0)) != 0)//接收数据并打印到终端

      printf("%s\n", buf);

}

3.下载文件到客户端

void ProcessGet(struct sockaddr_in server_addr, char command[])

{

   int sockfd, nbyte, fd;

   char buf[N];

   sprintf(buf, "G%s", command+4);//将要下载的文件以”G+文件名”的格式写入缓存

   send(sockfd, buf, N, 0);//发送到服务器,让其查看是否有该文件

   recv(sockfd, buf, N, 0);//接收服务器发回的信息

   if (buf[0] == 'N') // 服务器所在目录下没有该文件

   {

      printf("No such file on server\n");

      goto ERROR_2;

   }

 

   if ((fd = open(command+4, O_WRONLY|O_CREAT|O_TRUNC, 0666)) < 0)

   {//打开该文件,若文件存在则清空该文件,若不存在则自动自动建立该文件,并赋文件权限为可读可写;打开文件失败则返回-1

      printf("fail to create local file %s\n", command+4);

      goto ERROR_2;

   }

   while ((nbyte = recv(sockfd, buf, N, 0)) > 0)

   {//接收来自服务器的数据,返回接收的字节数大于0时,将数据写入该文件

      write(fd, buf, nbyte);

   }

   close(fd);//接收完数据后关闭该文件

ERROR_2:

   close(sockfd);

   return;

}

4.上传文件到服务器

void ProcessPut(struct sockaddr_in server_addr, char command[])

{

   int sockfd, fd, nbyte;

   char buf[N];

   if ((fd = open(command+4, O_RDONLY)) < 0)

   {//以只读方式打开要上传的文件,打开失败返回-1

      printf("fail to open %s\n", command+4);

      goto ERROR_3;

   }   

   sprintf(buf, "P%s", command+4);

//将要上传的文件名以”P%s”格式写入本地缓存,供发送

   send(sockfd, buf, N, 0);

   while ((nbyte = read(fd, buf, N)) > 0)

   {//读取文件,返回读取的字节数,大于0时向服务器发送数据

      send(sockfd, buf, nbyte, 0);

   }

   close(fd);//读取文件结束,关闭该文件

ERROR_3:

   close(sockfd);

   return;

}

-----------------再来看一下服务器端:

int main(int argc, char *argv[])

{

   int listenfd, connfd;

   char buf[N];//定义服务器端的缓存

   struct sockaddr_in server_addr;

   if ((listenfd = socket(PF_INET, SOCK_STREAM, 0)) < 0)

   {//创建一个数据流套接字,创建失败则将错误信息在终端显示,并退出程序

      fprintf(stderr, "fail to socket : %s\n", strerror(errno));

      exit(-1);

   }

#ifdef _DEBUG_

   printf("socket is %d\n", listenfd);

#endif 

/*进入绑定阶段*/

   memset(&server_addr, 0, sizeof(server_addr));

//对结构体进行清零操作

   server_addr.sin_family = PF_INET;

   server_addr.sin_port = htons(8888);

//指定服务器端口号,并转为网络字节序

   server_addr.sin_addr.s_addr = htonl(INADDR_ANY);//自动获取本机IP

   if (bind(listenfd, (SA *)&server_addr, sizeof(server_addr)) < 0)

   {

      perror("fail to bind");

      exit(-1);

   }

 

   listen(listenfd, 5);// 使创建并且绑定的套接字处于一个监听的状态

   while ( 1 )

   {

      if ((connfd = accept(listenfd, NULL, NULL)) < 0)

      {//接受客户端的连接请求,并返回新的套接字描述符用于处理C/S间的通信

        perror("fail to accept");

        break;

      }

      recv(connfd, buf, N, 0);//接收客户端发来的数据并存入本地缓存

      switch (buf[0])//根据buf[0]执行相应的函数

      {

        case 'L' :

                    ProcessList(connfd);

                    break;

        case 'G' :

                    ProcessGet(connfd, buf);

                    break;

        case 'P' :

                    ProcessPut(connfd, buf);

                    break;

      }

      close(connfd);

   }

 

   return 0;

}

--1.客户端发出list命令时,服务器执行

 void ProcessList(int connfd)

{

   char buf[N];

   DIR *mydir;//定义一个目录描述符

   struct dirent *myitem;//定义变量存储目录的文件信息

 

   mydir = opendir(".");//打开当前目录,打开成功返回一组目录流

   while ((myitem = readdir(mydir)) != NULL)

   {//读取目录信息,返回一个dirent的结构体指针

      if ((strcmp(myitem->d_name, ".") == 0) || (strcmp(myitem->d_name, "..") == 0)) continue;//判断是否为隐藏文件,若是则结束本次循环,若不是则将文件名存入本地缓存发送到客户端

      strcpy(buf, myitem->d_name);

      send(connfd, buf, N, 0);

   }

   closedir(mydir);//读完关闭目录

 

   return;

}

--2.客户端发出get命令时,执行

void ProcessGet(int connfd, char buf[])

{

   int fd, nbyte;

 

   if ((fd = open(buf+1, O_RDONLY)) < 0)

   {//打开客户端要求下载的文件,打开失败将错误信息在终端显示,并发送信息告知客户端

      fprintf(stderr, "fail to open %s : %s\n", buf+1, strerror(errno));

      buf[0] = 'N';

      send(connfd, buf, N, 0);

      return;

   }

     

   buf[0] = 'Y';

   send(connfd, buf, N, 0);

   while ((nbyte = read(fd, buf, N)) > 0)

   {//打开文件成功后,读取文件返回读取字节数,发送到客户端

      send(connfd, buf, nbyte, 0);

   }

   close(fd);

 

   return;

}

--3.客户端发出put命令时,执行

void ProcessPut(int connfd, char buf[])

{

   int fd, nbyte;

 

   if ((fd = open(buf+1, O_WRONLY|O_CREAT|O_TRUNC, 0666)) < 0)

   {//打开文件,若文件存在则清空该文件,若不存在则自动自动建立该文件,并赋文件权限为可读可写;打开文件失败则返回-1

      printf("fail to create %s on server\n", buf+1);

      return;

   }

 

   while ((nbyte = recv(connfd, buf, N, 0)) > 0)

   {//接收客户端传来的数据,写入文件中

      write(fd, buf, nbyte);

   }

   close(fd);

 

   return;

}

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值