自己动手写ftp服务器1(搭建框架)
Tofloor
poster avatar
zhqh100
deepin
2014-04-29 04:51
Author
这几天面试屡屡受挫,不知道为啥我想找个C开发的岗位这么不顺,只好回来继续学习,于是决定学习一下如何实现一个ftp服务器。我也是初学者,能力有限,也希望得到大牛的指点。

原想能在网上搜索到诸多详细的教程,不过搜索后才看到到处都是教如何搭建ftp服务器的,开发的很少,开发之后能够像真正的ftp服务器一样使用的就更少了。与开发相关的更多是理论阐释,代码分析较少。有些是自己的ftp服务器和自己的ftp客户端通信没有问题,但不能用主流的ftp客户端(如FileZilla)获取文件,这显然不是我想要的结果。有些是用Java或C#等其他语言实现的,还是希望能够用C语言实现出来。

在网上搜到一个tinyftp,确实能用,于是决定对其肢解,一步一步拼接出一个ftp服务器。关于ftp协议的理论知识可以自行搜索,这里不再复制了。
大多数时候,我们知道起点,也知道终点,但却不知道中间的路怎么走。
我很喜欢《自己动手写操作系统》这本书:)

这里一步一步实现,今天先实现第一步,搭建框架。这是C语言实现TCP服务器的通行框架,废话少说,直接上代码。

开发语言:C语言
操作系统:LinuxDeepin 2013
  1. #include
  2. #include
  3. #include
  4. #include
  5. #include
  6. #include
  7. #include
  8. #define bool unsigned short
  9. typedef struct cmd_opts {
  10.         bool daemonize;
  11.         bool listen_any;
  12.         char *listen_addr;
  13.         int port;
  14.         int max_conn;
  15.         int userid;
  16.         char *chrootdir;
  17. }cmd_opts;
  18. enum {
  19.         TRUE=1,
  20.         FALSE=0
  21. };
  22. int main(int argc,char *argv[])
  23. {
  24.         //设置一些初始配置
  25.         struct cmd_opts *opts= malloc(sizeof(struct cmd_opts));
  26.         opts->daemonize    = FALSE;
  27.         opts->listen_any   = TRUE;
  28.         opts->port         = 21;
  29.         opts->userid       = 0;
  30.         opts->chrootdir    = "./";
  31.         opts->max_conn     = 5;
  32.         opts->listen_addr  = NULL;
  33.         struct sockaddr_in servaddr;
  34.         memset((char *)&servaddr, 0, sizeof(servaddr));
  35.         //TCP协议
  36.         servaddr.sin_family = PF_INET;
  37.         servaddr.sin_addr.s_addr =  htonl(INADDR_ANY);
  38.         servaddr.sin_port = htons (opts->port);
  39.         int servaddr_len = sizeof(servaddr);
  40.         int sock;
  41.         if ((sock = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) {
  42.                 perror("socket");
  43.                 return 1;
  44.         }
  45.         int flag = 1;
  46.         setsockopt(sock, SOL_SOCKET,SO_REUSEADDR,(char *) &flag, sizeof(int));
  47.         setsockopt(sock, IPPROTO_TCP,TCP_NODELAY,(char *) &flag, sizeof(int));
  48.         //套接字绑定端口和地址
  49.         if(bind (sock, (struct sockaddr *)&servaddr, sizeof(servaddr))<0) {
  50.                 perror("bind");
  51.                 return -1;
  52.         }
  53.         if(listen(sock,opts->max_conn) <0) {
  54.                 perror("listen");
  55.                 return -1;
  56.         }
  57.         int connection;
  58.         //死循环等待客户端连接
  59.         while(1){
  60.                 printf("Begin to accept\n");
  61.                 if ((connection = accept(sock, (struct sockaddr *) &servaddr, &servaddr_len)) < 0) {
  62.                         perror("accept");
  63.                         return -1;
  64.                 }
  65.                 int pid = fork();
  66.                 char msg[]= "220 Service ready for new user.\r\n";
  67.                 char data_buff[1000];
  68.                 char read_buff[1000];
  69.                 int len = strlen(msg);
  70.                 if(pid==0) {
  71.                         printf("main\n");
  72.                         //客户端连接后,服务器先主动发送200 Service ready
  73.                         if (send(connection, msg, len,0) < 0) {
  74.                                 perror("send");
  75.                                 close(connection);
  76.                                 return -1;
  77.                         }
  78.                         while(1){
  79.                                 //初始化读取buffer和发送buffer
  80.                                 memset(data_buff,0,sizeof(data_buff));
  81.                                 memset(read_buff,0,sizeof(read_buff));
  82.                                 //等待客户端发送指令
  83.                                 int recvbuff = recv(connection,read_buff,sizeof(read_buff),0);
  84.                                 if(recvbuff<1) {
  85.                                         return -1;
  86.                                 }
  87.                                 if(recvbuff==sizeof(read_buff)) {
  88.                                         return -2;
  89.                                 }
  90.                                 printf("Received:%s\n",read_buff);
  91.                                 //不做用户检测,一律认为是匿名用户,请求发送密码
  92.                                 if (send(connection, "331 Anonymous login okay, send your complete email as your password.\r\n", sizeof("331 Anonymous login okay, send your complete email as your password.\r\n"),0) < 0) {
  93.                                         perror("send");
  94.                                         close(connection);
  95.                                         return -1;
  96.                                 }
  97.                         }
  98.                 } else if(pid>0) {
  99.                         printf("child\n");
  100.                         close(connection);
  101.                 }
  102.         }
  103.         return 0;
  104. }
Copy the Code

调试客户端建议使用CuteFTP或者SmartFtp,FileZilla打印的信息不够多,用CuteFTP登陆该服务器可以看到如下信息:
  1.                 *** CuteFTP 9.0 - build Jun 25 2013 ***
  2. STATUS:>          [2014/4/28 20:44:08] Getting listing ""...
  3. STATUS:>          [2014/4/28 20:44:08] Connecting to FTP server... 192.168.11.29:21 (ip = 192.168.11.29)...
  4. STATUS:>          [2014/4/28 20:44:08] Socket connected. Waiting for welcome message...
  5.                 [2014/4/28 20:44:08] 220 Service ready for new user.
  6. STATUS:>          [2014/4/28 20:44:08] Connected. Authenticating...
  7. COMMAND:>        [2014/4/28 20:44:08] USER anonymous
  8.                 [2014/4/28 20:44:08] 331 Anonymous login okay, send your complete email as your password.
  9. COMMAND:>        [2014/4/28 20:44:08] PASS *****
Copy the Code

看到客户端已经可以成功登陆了。
Reply Favorite View the author
All Replies
李逍遥mx
deepin
2014-05-01 01:25
#1
虽然有注释,但是看上去还是有点吃力,希望注释能详细一点。
Reply View the author