来源,华清远见嵌入式学院实验手册,代码来源:华清远见曾宏安
实现的功能:
编写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:  }