zoukankan      html  css  js  c++  java
  • 基于TCP/IP的文件服务器编程一例

    来源,华清远见嵌入式学院实验手册,代码来源:华清远见曾宏安

    实现的功能:

    编写TCP文件服务器和客户端。客户端可以上传和下载文件

    客户端支持功能如下:

    1.支持一下命令

    help 显示客户端所有命令和说明

    list  显示服务器端可以下载的文件列表

    get <filename> 下载文件

    put <filename> 上传文件

    quit 退出客户端

    服务器端功能(单进程)

    解析客户端的命令并提供相应的服务

    服务器流程:

    服务器端的代码:

       1:  #include <stdio.h>
       2:  #include <stdlib.h>
       3:  #include <unistd.h>
       4:  #include <string.h>
       5:  #include <fcntl.h>
       6:  #include <dirent.h>
       7:  #include <sys/socket.h>
       8:  #include <netinet/in.h>
       9:  #include <arpa/inet.h>
      10:   
      11:  #define N  256
      12:   
      13:  typedef struct sockaddr SA;  //
      14:   
      15:  void do_list(int connfd)
      16:  {
      17:      DIR *mydir;
      18:      struct dirent *dp;
      19:   
      20:      if ((mydir = opendir(".")) == NULL)   //打开当前目录
      21:      {
      22:          perror("fail to opendir");
      23:          return;
      24:      }
      25:      while ((dp = readdir(mydir)) != NULL)  //读目录,每次返回一项, 读完时,返回空
      26:      {
      27:          if (dp->d_name[0] != '.')  //将.和..以及隐藏文件跳过
      28:          {
      29:              send(connfd, dp->d_name, N, 0);  //为了便于客户端将每次发送的目录项区分开,服务器每次发N个,相应的客户端也受N个
      30:          }
      31:      }
      32:      closedir(mydir);   //关闭目录
      33:  }
      34:   
      35:  void do_get(int connfd, char fname[]) //下载请求处理函数
      36:  {
      37:      int fd, nbyte;
      38:      char buf[N];
      39:   
      40:      if ((fd = open(fname, O_RDONLY)) < 0)  //以只读方式打开,假如不存在,报错
      41:      {
      42:          perror("fail to open");
      43:          send(connfd, "N", 1, 0);  //向客户端发送‘N',表示文件不存在,发送1个,客户端也收1个
      44:          return; ////退出下载请求处理函数,注:服务器不能退出
      45:      }
      46:      send(connfd, "Y", 1, 0);  //向客户端发送Y,表示文件存在
      47:      while ((nbyte = read(fd, buf, N)) > 0)  //读文件,读完了返回空
      48:      {
      49:          send(connfd, buf, nbyte, 0);  //发送文件原则:读多少,就发多少!
      50:      }
      51:      close(fd);
      52:  }
      53:   
      54:  void do_put(int connfd, char fname[])  //上传请求处理函数
      55:  {
      56:      int fd, nbyte;
      57:      char buf[N];
      58:   
      59:      if ((fd = open(fname, O_WRONLY | O_CREAT | O_EXCL, 0666)) < 0)  //不存在创建,存在报错
      60:      {
      61:          perror("fail to open");
      62:          send(connfd, "N", 1, 0);  //向客户端发送‘N',表示文件已存在,发送1个,客户端也收1个
      63:          return;
      64:      }
      65:      send(connfd, "Y", 1, 0);//向客户端发送‘N',表示文件不存在,可以上传。发送1个,客户端也收1个
      66:      while ((nbyte = recv(connfd, buf, N, 0)) > 0)
      67:      {
      68:          write(fd, buf, nbyte);  //写文件原则:接收多少,就写多少
      69:      }
      70:      close(fd);
      71:  }
      72:   
      73:  int main(int argc, char *argv[])
      74:  {
      75:      int listenfd, connfd;
      76:      char command[N];
      77:      struct sockaddr_in myaddr, peeraddr;
      78:      socklen_t peerlen = sizeof(peeraddr);
      79:   
      80:      if (argc < 3)
      81:      {
      82:          printf("Usage : %s <ip> <port>\n", argv[0]);
      83:          return -1;
      84:      }
      85:   
      86:      // XXX int socket(int domain, int type, int protocol);
      87:      if ((listenfd = socket(PF_INET, SOCK_STREAM, 0)) < 0)
      88:      {
      89:          perror("fail to socket");
      90:          exit(-1);
      91:      }
      92:   
      93:      bzero(&myaddr, sizeof(myaddr));
      94:      myaddr.sin_family = PF_INET;
      95:      myaddr.sin_port = htons(atoi(argv[2])); 
      96:      myaddr.sin_addr.s_addr = inet_addr(argv[1]);
      97:      // XXX int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
      98:      if (bind(listenfd, (SA *)&myaddr, sizeof(myaddr)) < 0)
      99:      {
     100:          perror("fail to bind");
     101:          exit(-1);
     102:      }
     103:   
     104:      if (listen(listenfd, 5) < 0)
     105:      {
     106:          perror("fail to listen");
     107:          exit(-1);
     108:      }
     109:   
     110:      while ( 1 )
     111:      {
     112:          // XXX int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
     113:          if ((connfd = accept(listenfd, (SA *)&peeraddr, &peerlen)) < 0)
     114:          {
     115:              perror("fail to accept");
     116:              exit(-1);
     117:          }
     118:          printf("connection from [%s:%d]\n", inet_ntoa(peeraddr.sin_addr), ntohs(peeraddr.sin_port));
     119:          recv(connfd, command, N, 0);  // recv command from client,简单处理,客户端发N个,服务器也应该收N个
     120:          switch ( command[0] )  //因为客户端发来的命令第一个字符就是命令类型
     121:          {
     122:          case 'L':  //客户端的列表请求
     123:              printf("do_list\n");
     124:              do_list(connfd);
     125:              break;
     126:          case 'G':  //客户端的下载请求
     127:              do_get(connfd, command+1);
     128:              break;
     129:          case 'P': //客户端的上传请求
     130:              do_put(connfd, command+1);
     131:              break;
     132:          }
     133:          close(connfd);  //关闭本次的连接套接字
     134:      }
     135:   
     136:      return 0;
     137:  }

     客户端流程:

    客户端的代码:

       1:  #include <stdio.h>
       2:  #include <stdlib.h>
       3:  #include <unistd.h>
       4:  #include <string.h>
       5:  #include <fcntl.h>
       6:  #include <sys/socket.h>
       7:  #include <netinet/in.h>
       8:  #include <arpa/inet.h>
       9:   
      10:  #define  N  256
      11:   
      12:  typedef struct sockaddr SA;
      13:   
      14:  void do_help()
      15:  {
      16:      printf("      help : display help info\n");
      17:      printf("      list : get file list from server\n");
      18:      printf("get <file> : download <file> from server\n");
      19:      printf("put <file> : upload <file> to server\n");
      20:      printf("      quit : exit\n");
      21:  }
      22:   
      23:  void do_list(struct sockaddr_in servaddr)      //list命令处理函数
      24:  {
      25:      int sockfd;
      26:      char buf[N];
      27:   
      28:      if ((sockfd = socket(PF_INET, SOCK_STREAM, 0)) < 0)
      29:      {
      30:          perror("fail to socket");
      31:          exit(-1);
      32:      }
      33:   
      34:          // XXX int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
      35:      if (connect(sockfd, (SA *)&servaddr, sizeof(servaddr)) < 0)
      36:      {
      37:          perror("fail to connect");
      38:          exit(-1);
      39:      }
      40:      
      41:      buf[0] = 'L';         //向服务器发送字符'L',表示list命令
      42:      send(sockfd, buf, N, 0);  // send command to server,发送字节数N最好与服务器那边对应起来,发多少,收多少
      43:      while (recv(sockfd, buf, N, 0) > 0) //简单处理,服务器每次发N个,那么客户端每次也应该收N个,这样就把每个目录项区分开了
      44:      {
      45:          printf(" %s\n", buf);
      46:      }
      47:      close(sockfd); //关闭套接字
      48:  }
      49:   
      50:  void do_get(struct sockaddr_in servaddr, char fname[])  //下载处理函数
      51:  {
      52:      int sockfd, fd, nbyte;
      53:      char buf[N];
      54:   
      55:      if ((sockfd = socket(PF_INET, SOCK_STREAM, 0)) < 0)
      56:      {
      57:          perror("fail to socket");
      58:          exit(-1);
      59:      }
      60:   
      61:          // XXX int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
      62:      if (connect(sockfd, (SA *)&servaddr, sizeof(servaddr)) < 0)
      63:      {
      64:          perror("fail to connect");
      65:          exit(-1);
      66:      }
      67:   
      68:      sprintf(buf, "G%s", fname);  //将上传标识符'G'和文件名格式化输出到缓冲区buf中,想法很好!
      69:      send(sockfd, buf, N, 0);  // send command to server  //将客户端的上传命令连同文件名一块发给服务器。
      70:      //等待服务器的确认回复,因为要下载的文件名可能在服务器上不存在,服务器发送了1个,所以客户端也应该接收至少1个。
      71:      recv(sockfd, buf, 1, 0);  // recv reply from server  
      72:      if (buf[0] == 'N')  //服务器返回N,说明服务器上没有客户端要下载的文件
      73:      {
      74:          printf("can't open %s on server\n", fname);
      75:          close(sockfd);  //关闭套接字
      76:          return;  //退出下载处理函数
      77:      }
      78:      if ((fd = open(fname, O_WRONLY|O_CREAT|O_TRUNC, 0666)) < 0) //不存在创建,存在清除
      79:      {
      80:          perror("fail to open");
      81:          close(sockfd);
      82:          return;
      83:      }
      84:      while ((nbyte = recv(sockfd, buf, N, 0)) > 0)  //接收服务器发送的文件内容 
      85:      {
      86:          write(fd, buf, nbyte);  //写文件原则:收多少,就写到少!
      87:      }
      88:      close(fd);   //关闭文件描述符
      89:      close(sockfd);
      90:  }
      91:   
      92:  void do_put(struct sockaddr_in servaddr, char fname[]) //上传处理函数
      93:  {
      94:      int sockfd, fd, nbyte;
      95:      char buf[N];
      96:   
      97:      if ((sockfd = socket(PF_INET, SOCK_STREAM, 0)) < 0)
      98:      {
      99:          perror("fail to socket");
     100:          exit(-1);
     101:      }
     102:   
     103:          // XXX int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
     104:      if (connect(sockfd, (SA *)&servaddr, sizeof(servaddr)) < 0)
     105:      {
     106:          perror("fail to connect");
     107:          exit(-1);
     108:      }
     109:   
     110:      sprintf(buf, "P%s", fname);//将下载标识符'P'和文件名格式化输出到缓冲区buf中,想法很好!
     111:      send(sockfd, buf, N, 0);  // send command to server//将客户端的下载命令连同文件名一块发给服务器。
     112:      //等待服务器的确认回复,因为要上传的文件名可能在服务器上已经存在。服务器发送了1个,所以客户端也应该接收至少1个。
     113:      recv(sockfd, buf, 1, 0);  // recv reply from server
     114:      if (buf[0] == 'N') //服务器返回N,说明服务器上有客户端要上传的文件
     115:      {
     116:          printf("%s exsit on server,\n", fname);
     117:          close(sockfd);
     118:          return;
     119:      }
     120:      if ((fd = open(fname, O_RDONLY)) < 0)
     121:      {
     122:          perror("fail to open");
     123:          close(sockfd);
     124:          return;
     125:      }
     126:      while ((nbyte = read(fd, buf, N)) > 0) 
     127:      {
     128:          send(sockfd, buf, nbyte,0);  //发送文件原则:从文件流中读多少,就向服务器发多少!
     129:      }
     130:      close(fd);
     131:      close(sockfd);
     132:  }
     133:   
     134:  int main(int argc, char *argv[])
     135:  {
     136:      int sockfd;
     137:      char command[N];
     138:      struct sockaddr_in servaddr;
     139:   
     140:      if (argc < 3)
     141:      {
     142:          printf("Usage : %s <ip> <port>\n", argv[0]);
     143:          return -1;
     144:      }
     145:   
     146:      bzero(&servaddr, sizeof(servaddr));
     147:      servaddr.sin_family = PF_INET;
     148:      servaddr.sin_port = htons(atoi(argv[2]));
     149:      servaddr.sin_addr.s_addr = inet_addr(argv[1]);
     150:   
     151:  #if 0
     152:      // XXX int socket(int domain, int type, int protocol);
     153:      #endif
     154:      while ( 1 )
     155:      {
     156:          printf("client > ");
     157:          fgets(command, N, stdin);
     158:          command[strlen(command)-1] = '\0';  //将'\0'前面的'\n'用'\0'覆盖。
     159:          if (strncmp(command, "help", 4) == 0)
     160:          {
     161:              do_help();
     162:          }
     163:          else if (strncmp(command, "list", 4) == 0)
     164:          {
     165:              do_list(servaddr);
     166:          }
     167:          else if (strncmp(command, "get", 3) == 0)
     168:          {
     169:              do_get(servaddr, command+4);
     170:          }
     171:          else if (strncmp(command, "put", 3) == 0)
     172:          {
     173:              do_put(servaddr, command+4);
     174:          }
     175:          else if (strncmp(command, "quit", 4) == 0)
     176:          {
     177:              printf("bye\n");
     178:              exit(0);
     179:          }
     180:          else
     181:          {
     182:              printf(" invalid command %s\n", command);
     183:              do_help();
     184:          }
     185:      }
     186:   
     187:      return 0;
     188:  }
  • 相关阅读:
    eclipse快捷键
    NetBeans无法使用svn相关功能
    sqlplus执行startup出现ORA-00119,ORA-00132错误
    Oracle中对number类型数据to_char()出现各位少0,或者值为###的处理
    服务器启动报mybatis配置错误
    Java Thread interrupt
    nginx backend 健康检查
    SSH 安全建议
    ARP
    Postgresql 锁查看
  • 原文地址:https://www.cnblogs.com/pengdonglin137/p/2958192.html
Copyright © 2011-2022 走看看