上节中实现了"USER"和"PASS"命令,如下:

事实上FTP是有很多命令组成的,如果就采用上面的这种方法来实现的话,就会有很多if...else if语句,代码显得很臃肿,所以有必要想办法来避免这种写法,所以一个新的方式既将诞生------命令映射,实际上在之前读取配置文件变量时就已经接触到了,下面则看具体的做法:

下面这个表是FTP命令的声明:

函数

说明

static void do_user(session_t *sess);

static void do_pass(session_t *sess);

static void do_cwd(session_t *sess);

static void do_cdup(session_t *sess);

static void do_quit(session_t *sess);

static void do_port(session_t *sess);

static void do_pasv(session_t *sess);

static void do_type(session_t *sess);

static void do_stru(session_t *sess);

static void do_mode(session_t *sess);

static void do_retr(session_t *sess);

static void do_stor(session_t *sess);

static void do_appe(session_t *sess);

static void do_list(session_t *sess);

static void do_nlst(session_t *sess);

static void do_rest(session_t *sess);

static void do_abor(session_t *sess);

static void do_pwd(session_t *sess);

static void do_mkd(session_t *sess);

static void do_rmd(session_t *sess);

static void do_dele(session_t *sess);

static void do_rnfr(session_t *sess);

static void do_rnto(session_t *sess);

static void do_site(session_t *sess);

static void do_syst(session_t *sess);

static void do_feat(session_t *sess);

static void do_size(session_t *sess);

static void do_stat(session_t *sess);

static void do_noop(session_t *sess);

static void do_help(session_t *sess);

所以在程序中先声明:

记得当时实现配置模块时,一个配置项与配置项变量相对应,而这边则应该是一个命令字符串与一个命令处理函数相对应,因而也能定义一个结构体来配置这种对应关系,如下:

FTP命令与命令处理函数对应表

typedef struct ftpcmd

{

const char *cmd;//命令字符串

void (*cmd_handler)(session_t *sess);//函数指针

} ftpcmd_t;

static ftpcmd_t ctrl_cmds[] = {

/* 访问控制命令 */

{"USER",      do_user   },//如果是USER命令,则执行do_user方法

{"PASS",       do_pass   },

{"CWD",              do_cwd   },

{"XCWD",     do_cwd   },

{"CDUP",      do_cdup  },

{"XCUP",      do_cdup  },

{"QUIT",       do_quit   },

{"ACCT",      NULL     },

{"SMNT",      NULL     },

{"REIN",       NULL     },//这种命令是没有执行函数的

/* 传输参数命令 */

{"PORT",       do_port   },

{"PASV",       do_pasv   },

{"TYPE",       do_type   },

{"STRU",       do_stru    },

{"MODE",     do_mode },

/* 服务命令 */

{"RETR",       do_retr    },

{"STOR",       do_stor    },

{"APPE",       do_appe  },

{"LIST", do_list     },

{"NLST",       do_nlst    },

{"REST",       do_rest    },

{"ABOR",      do_abor   },

{"\377\364\377\362ABOR", do_abor},

{"PWD",        do_pwd   },

{"XPWD",     do_pwd   },

{"MKD",              do_mkd   },

{"XMKD",     do_mkd   },

{"RMD",        do_rmd   },

{"XRMD",     do_rmd   },

{"DELE",      do_dele   },

{"RNFR",      do_rnfr   },

{"RNTO",      do_rnto   },

{"SITE", do_site    },

{"SYST",       do_syst    },

{"FEAT",       do_feat },

{"SIZE", do_size    },

{"STAT", do_stat    },

{"NOOP",      do_noop  },

{"HELP",       do_help   },

{"STOU",      NULL     },

{"ALLO",      NULL     }

};

另外发现,之前配置模块的最后是NULL结尾的,而这里并没有,所以这次是用另外一种遍历方法来判断是否遍历完,下面先将上面的映射关系代码拷至代码中:

ftpproto.c:

  1. #include "ftpproto.h"
  2. #include "sysutil.h"
  3. #include "str.h"
  4. #include "ftpcodes.h"
  5.  
  6. static void ftp_reply(session_t *sess, int status, const char *text);
  7. static void do_user(session_t *sess);
  8. static void do_pass(session_t *sess);
  9. static void do_cwd(session_t *sess);
  10. static void do_cdup(session_t *sess);
  11. static void do_quit(session_t *sess);
  12. static void do_port(session_t *sess);
  13. static void do_pasv(session_t *sess);
  14. static void do_type(session_t *sess);
  15. static void do_stru(session_t *sess);
  16. static void do_mode(session_t *sess);
  17. static void do_retr(session_t *sess);
  18. static void do_stor(session_t *sess);
  19. static void do_appe(session_t *sess);
  20. static void do_list(session_t *sess);
  21. static void do_nlst(session_t *sess);
  22. static void do_rest(session_t *sess);
  23. static void do_abor(session_t *sess);
  24. static void do_pwd(session_t *sess);
  25. static void do_mkd(session_t *sess);
  26. static void do_rmd(session_t *sess);
  27. static void do_dele(session_t *sess);
  28. static void do_rnfr(session_t *sess);
  29. static void do_rnto(session_t *sess);
  30. static void do_site(session_t *sess);
  31. static void do_syst(session_t *sess);
  32. static void do_feat(session_t *sess);
  33. static void do_size(session_t *sess);
  34. static void do_stat(session_t *sess);
  35. static void do_noop(session_t *sess);
  36. static void do_help(session_t *sess);
  37.  
  38. typedef struct ftpcmd
  39. {
  40. const char *cmd;
  41. void (*cmd_handler)(session_t *sess);
  42. } ftpcmd_t;
  43. static ftpcmd_t ctrl_cmds[] = {
  44. /* 访问控制命令 */
  45. {"USER", do_user },
  46. {"PASS", do_pass },
  47. {"CWD", do_cwd },
  48. {"XCWD", do_cwd },
  49. {"CDUP", do_cdup },
  50. {"XCUP", do_cdup },
  51. {"QUIT", do_quit },
  52. {"ACCT", NULL },
  53. {"SMNT", NULL },
  54. {"REIN", NULL },
  55. /* 传输参数命令 */
  56. {"PORT", do_port },
  57. {"PASV", do_pasv },
  58. {"TYPE", do_type },
  59. {"STRU", do_stru },
  60. {"MODE", do_mode },
  61. /* 服务命令 */
  62. {"RETR", do_retr },
  63. {"STOR", do_stor },
  64. {"APPE", do_appe },
  65. {"LIST", do_list },
  66. {"NLST", do_nlst },
  67. {"REST", do_rest },
  68. {"ABOR", do_abor },
  69. {"\377\364\377\362ABOR", do_abor},
  70. {"PWD", do_pwd },
  71. {"XPWD", do_pwd },
  72. {"MKD", do_mkd },
  73. {"XMKD", do_mkd },
  74. {"RMD", do_rmd },
  75. {"XRMD", do_rmd },
  76. {"DELE", do_dele },
  77. {"RNFR", do_rnfr },
  78. {"RNTO", do_rnto },
  79. {"SITE", do_site },
  80. {"SYST", do_syst },
  81. {"FEAT", do_feat },
  82. {"SIZE", do_size },
  83. {"STAT", do_stat },
  84. {"NOOP", do_noop },
  85. {"HELP", do_help },
  86. {"STOU", NULL },
  87. {"ALLO", NULL }
  88. };
  89. void handle_child(session_t *sess)
  90. {
  91. writen(sess->ctrl_fd, "220 (miniftpd 0.1)\r\n", strlen("220 (miniftpd 0.1)\r\n"));
  92. int ret;
  93. while ()
  94. {
  95. memset(sess->cmdline, , sizeof(sess->cmdline));
  96. memset(sess->cmd, , sizeof(sess->cmd));
  97. memset(sess->arg, , sizeof(sess->arg));
  98. ret = readline(sess->ctrl_fd, sess->cmdline, MAX_COMMAND_LINE);
  99. if (ret == -)
  100. ERR_EXIT("readline");
  101. else if (ret == )
  102. exit(EXIT_SUCCESS);
  103.  
  104. printf("cmdline=[%s]\n", sess->cmdline);
  105. // 去除\r\n
  106. str_trim_crlf(sess->cmdline);
  107. printf("cmdline=[%s]\n", sess->cmdline);
  108. // 解析FTP命令与参数
  109. str_split(sess->cmdline, sess->cmd, sess->arg, ' ');
  110. printf("cmd=[%s] arg=[%s]\n", sess->cmd, sess->arg);
  111. // 将命令转换为大写
  112. str_upper(sess->cmd);
  113. // 处理FTP命令
  114. if (strcmp("USER", sess->cmd) == )
  115. {
  116. do_user(sess);
  117. }
  118. else if (strcmp("PASS", sess->cmd) == )
  119. {
  120. do_pass(sess);
  121. }
  122. }
  123. }
  124.  
  125. static void do_user(session_t *sess)
  126. {
  127. //USER jjl
  128. struct passwd *pw = getpwnam(sess->arg);
  129. if (pw == NULL)
  130. {
  131. // 用户不存在
  132. ftp_reply(sess, FTP_LOGINERR, "Login incorrect.");
  133. return;
  134. }
  135. sess->uid = pw->pw_uid;
  136. ftp_reply(sess, FTP_GIVEPWORD, "Please specify the password.");
  137. }
  138.  
  139. static void do_pass(session_t *sess)
  140. {
  141. // PASS 123456
  142. struct passwd *pw = getpwuid(sess->uid);
  143. if (pw == NULL)
  144. {
  145. // 用户不存在
  146. ftp_reply(sess, FTP_LOGINERR, "Login incorrect.");
  147. return;
  148. }
  149.  
  150. printf("name=[%s]\n", pw->pw_name);
  151. struct spwd *sp = getspnam(pw->pw_name);
  152. if (sp == NULL)
  153. { // 没有找到影子文件,则代表登录也是失败的
  154. ftp_reply(sess, FTP_LOGINERR, "Login incorrect.");
  155. return;
  156. }
  157.  
  158. // 将明文进行加密
  159. char *encrypted_pass = crypt(sess->arg, sp->sp_pwdp);
  160. // 验证密码
  161. if (strcmp(encrypted_pass, sp->sp_pwdp) != )
  162. {
  163. ftp_reply(sess, FTP_LOGINERR, "Login incorrect.");
  164. return;
  165. }
  166.  
  167. setegid(pw->pw_gid);
  168. seteuid(pw->pw_uid);
  169. chdir(pw->pw_dir);
  170.  
  171. ftp_reply(sess, FTP_LOGINOK, "Login successful.");
  172. }
  173.  
  174. static void ftp_reply(session_t *sess, int status, const char *text)
  175. {
  176. char buf[] = {};
  177. sprintf(buf, "%d %s\r\n", status, text);
  178. writen(sess->ctrl_fd, buf, strlen(buf));
  179. }

而像之前的配置映射的方式代码写法如下:

但是由于这次并没有以NULL结尾,所以说得换一种新的方式,如下:

编译一下:

所以需要实现这些函数:

  1. #include "ftpproto.h"
  2. #include "sysutil.h"
  3. #include "str.h"
  4. #include "ftpcodes.h"
  5.  
  6. static void ftp_reply(session_t *sess, int status, const char *text);
  7. static void do_user(session_t *sess);
  8. static void do_pass(session_t *sess);
  9. static void do_cwd(session_t *sess);
  10. static void do_cdup(session_t *sess);
  11. static void do_quit(session_t *sess);
  12. static void do_port(session_t *sess);
  13. static void do_pasv(session_t *sess);
  14. static void do_type(session_t *sess);
  15. static void do_stru(session_t *sess);
  16. static void do_mode(session_t *sess);
  17. static void do_retr(session_t *sess);
  18. static void do_stor(session_t *sess);
  19. static void do_appe(session_t *sess);
  20. static void do_list(session_t *sess);
  21. static void do_nlst(session_t *sess);
  22. static void do_rest(session_t *sess);
  23. static void do_abor(session_t *sess);
  24. static void do_pwd(session_t *sess);
  25. static void do_mkd(session_t *sess);
  26. static void do_rmd(session_t *sess);
  27. static void do_dele(session_t *sess);
  28. static void do_rnfr(session_t *sess);
  29. static void do_rnto(session_t *sess);
  30. static void do_site(session_t *sess);
  31. static void do_syst(session_t *sess);
  32. static void do_feat(session_t *sess);
  33. static void do_size(session_t *sess);
  34. static void do_stat(session_t *sess);
  35. static void do_noop(session_t *sess);
  36. static void do_help(session_t *sess);
  37.  
  38. typedef struct ftpcmd
  39. {
  40. const char *cmd;
  41. void (*cmd_handler)(session_t *sess);
  42. } ftpcmd_t;
  43.  
  44. static ftpcmd_t ctrl_cmds[] = {
  45. /* 访问控制命令 */
  46. {"USER", do_user },
  47. {"PASS", do_pass },
  48. {"CWD", do_cwd },
  49. {"XCWD", do_cwd },
  50. {"CDUP", do_cdup },
  51. {"XCUP", do_cdup },
  52. {"QUIT", do_quit },
  53. {"ACCT", NULL },
  54. {"SMNT", NULL },
  55. {"REIN", NULL },
  56. /* 传输参数命令 */
  57. {"PORT", do_port },
  58. {"PASV", do_pasv },
  59. {"TYPE", do_type },
  60. {"STRU", do_stru },
  61. {"MODE", do_mode },
  62.  
  63. /* 服务命令 */
  64. {"RETR", do_retr },
  65. {"STOR", do_stor },
  66. {"APPE", do_appe },
  67. {"LIST", do_list },
  68. {"NLST", do_nlst },
  69. {"REST", do_rest },
  70. {"ABOR", do_abor },
  71. {"\377\364\377\362ABOR", do_abor},
  72. {"PWD", do_pwd },
  73. {"XPWD", do_pwd },
  74. {"MKD", do_mkd },
  75. {"XMKD", do_mkd },
  76. {"RMD", do_rmd },
  77. {"XRMD", do_rmd },
  78. {"DELE", do_dele },
  79. {"RNFR", do_rnfr },
  80. {"RNTO", do_rnto },
  81. {"SITE", do_site },
  82. {"SYST", do_syst },
  83. {"FEAT", do_feat },
  84. {"SIZE", do_size },
  85. {"STAT", do_stat },
  86. {"NOOP", do_noop },
  87. {"HELP", do_help },
  88. {"STOU", NULL },
  89. {"ALLO", NULL }
  90. };
  91.  
  92. void handle_child(session_t *sess)
  93. {
  94. writen(sess->ctrl_fd, "220 (miniftpd 0.1)\r\n", strlen("220 (miniftpd 0.1)\r\n"));
  95. int ret;
  96. while ()
  97. {
  98. memset(sess->cmdline, , sizeof(sess->cmdline));
  99. memset(sess->cmd, , sizeof(sess->cmd));
  100. memset(sess->arg, , sizeof(sess->arg));
  101. ret = readline(sess->ctrl_fd, sess->cmdline, MAX_COMMAND_LINE);
  102. if (ret == -)
  103. ERR_EXIT("readline");
  104. else if (ret == )
  105. exit(EXIT_SUCCESS);
  106.  
  107. printf("cmdline=[%s]\n", sess->cmdline);
  108. // 去除\r\n
  109. str_trim_crlf(sess->cmdline);
  110. printf("cmdline=[%s]\n", sess->cmdline);
  111. // 解析FTP命令与参数
  112. str_split(sess->cmdline, sess->cmd, sess->arg, ' ');
  113. printf("cmd=[%s] arg=[%s]\n", sess->cmd, sess->arg);
  114. // 将命令转换为大写
  115. str_upper(sess->cmd);
  116. // 处理FTP命令
  117. /*
  118. if (strcmp("USER", sess->cmd) == 0)
  119. {
  120. do_user(sess);
  121. }
  122. else if (strcmp("PASS", sess->cmd) == 0)
  123. {
  124. do_pass(sess);
  125. }*/
  126. int i;
  127. int size = sizeof(ctrl_cmds) / sizeof(ctrl_cmds[]);
  128. for (i=; i<size; i++)
  129. {
  130. if (strcmp(ctrl_cmds[i].cmd, sess->cmd) == )
  131. {
  132. if (ctrl_cmds[i].cmd_handler != NULL)
  133. {
  134. ctrl_cmds[i].cmd_handler(sess);
  135. }
  136. else
  137. {
  138. ftp_reply(sess, FTP_COMMANDNOTIMPL, "Unimplement command.");
  139. }
  140.  
  141. break;
  142. }
  143. }
  144.  
  145. if (i == size)
  146. {
  147. ftp_reply(sess, FTP_BADCMD, "Unknown command.");
  148. }
  149. }
  150. }
  151.  
  152. static void ftp_reply(session_t *sess, int status, const char *text)
  153. {
  154. char buf[] = {};
  155. sprintf(buf, "%d %s\r\n", status, text);
  156. writen(sess->ctrl_fd, buf, strlen(buf));
  157. }
  158.  
  159. static void do_user(session_t *sess)
  160. {
  161. //USER jjl
  162. struct passwd *pw = getpwnam(sess->arg);
  163. if (pw == NULL)
  164. {
  165. // 用户不存在
  166. ftp_reply(sess, FTP_LOGINERR, "Login incorrect.");
  167. return;
  168. }
  169. sess->uid = pw->pw_uid;
  170. ftp_reply(sess, FTP_GIVEPWORD, "Please specify the password.");
  171. }
  172.  
  173. static void do_pass(session_t *sess)
  174. {
  175. // PASS 123456
  176. struct passwd *pw = getpwuid(sess->uid);
  177. if (pw == NULL)
  178. {
  179. // 用户不存在
  180. ftp_reply(sess, FTP_LOGINERR, "Login incorrect.");
  181. return;
  182. }
  183.  
  184. printf("name=[%s]\n", pw->pw_name);
  185. struct spwd *sp = getspnam(pw->pw_name);
  186. if (sp == NULL)
  187. { // 没有找到影子文件,则代表登录也是失败的
  188. ftp_reply(sess, FTP_LOGINERR, "Login incorrect.");
  189. return;
  190. }
  191.  
  192. // 将明文进行加密
  193. char *encrypted_pass = crypt(sess->arg, sp->sp_pwdp);
  194. // 验证密码
  195. if (strcmp(encrypted_pass, sp->sp_pwdp) != )
  196. {
  197. ftp_reply(sess, FTP_LOGINERR, "Login incorrect.");
  198. return;
  199. }
  200.  
  201. setegid(pw->pw_gid);
  202. seteuid(pw->pw_uid);
  203. chdir(pw->pw_dir);
  204.  
  205. ftp_reply(sess, FTP_LOGINOK, "Login successful.");
  206. }
  207.  
  208. static void do_cwd(session_t *sess)
  209. {
  210. }
  211.  
  212. static void do_cdup(session_t *sess)
  213. {
  214. }
  215.  
  216. static void do_quit(session_t *sess)
  217. {
  218. }
  219.  
  220. static void do_port(session_t *sess)
  221. {
  222. }
  223.  
  224. static void do_pasv(session_t *sess)
  225. {
  226. }
  227.  
  228. static void do_type(session_t *sess)
  229. {
  230. }
  231.  
  232. static void do_stru(session_t *sess)
  233. {
  234. }
  235.  
  236. static void do_mode(session_t *sess)
  237. {
  238. }
  239.  
  240. static void do_retr(session_t *sess)
  241. {
  242. }
  243.  
  244. static void do_stor(session_t *sess)
  245. {
  246. }
  247.  
  248. static void do_appe(session_t *sess)
  249. {
  250. }
  251.  
  252. static void do_list(session_t *sess)
  253. {
  254. }
  255.  
  256. static void do_nlst(session_t *sess)
  257. {
  258. }
  259.  
  260. static void do_rest(session_t *sess)
  261. {
  262. }
  263.  
  264. static void do_abor(session_t *sess)
  265. {
  266. }
  267.  
  268. static void do_pwd(session_t *sess)
  269. {
  270. }
  271.  
  272. static void do_mkd(session_t *sess)
  273. {
  274. }
  275.  
  276. static void do_rmd(session_t *sess)
  277. {
  278. }
  279.  
  280. static void do_dele(session_t *sess)
  281. {
  282. }
  283.  
  284. static void do_rnfr(session_t *sess)
  285. {
  286. }
  287.  
  288. static void do_rnto(session_t *sess)
  289. {
  290. }
  291.  
  292. static void do_site(session_t *sess)
  293. {
  294. }
  295.  
  296. static void do_syst(session_t *sess)
  297. {
  298. }
  299.  
  300. static void do_feat(session_t *sess)
  301. {
  302. }
  303.  
  304. static void do_size(session_t *sess)
  305. {
  306. }
  307.  
  308. static void do_stat(session_t *sess)
  309. {
  310. }
  311.  
  312. static void do_noop(session_t *sess)
  313. {
  314. }
  315.  
  316. static void do_help(session_t *sess)
  317. {
  318. }

再次编译运行:

可见,命令解析一切OK,接下来就一个个命令来实现,首先是"SYST"命令,对照着vsftpd来做:

所以对应的函数中给出如下输出:

编译运行:

其中"FEAT"表示Feature,表示服务端的特性,实际上这条命令不实现也没有关系,如果暂且不想实现它,则可以给它对应的处理函数配置成NULL,如下:

编译运行:

紧着着发送了CLNT命令,这个命令没有,所以就提示无效的命令:

【注意】:当无效的命令时,我们也必须给它响应,否则客户端就会阻塞。

当响应了CLNT命令之后,最后发送了REST 100命令,这表示断点续传,对比着vsftpd输出结果来看:

所以,我们也来先实现FEAT命令,实现完之后,看是否最后还会发送RESET 100命令,将FEAT的命令注释还原:

然后处理它对应的函数:

然后接vsftpd的响应格式来输出:

再次编译运行:

接着来实现PWD命令,先来看下vsftpd这个命令是如何响应的:

下面来实现PWD命令:

【说明】:

编译运行:

先看一下vsftpd中的"TYPE A"的响应:

如果是输入其它的TYPE命令呢?可以按如下步骤输出:

而如果输入"TYPE I"呢?

所以则照着这几种情况实现TYPE命令:

另外这个状态需要记录在session当中,之后会用到,所以需要增加一个变量至session结构体中:

修改它的初始化:

在do_type函数中来进行记录:

【说明】:Ascii和二进制协议进行传输的区别就在于是否要处理"\r\n",这个在之前FTP协议时有说明过。

编译运行:

接下来就要进行列表的传输了,在传输时需要创建数据连接通道,在创建通道之前是需要协商使用PORT模式还是PASV模式,所以这里先发送PASV模式出来了,这个下次来实现,先学到这~

Linux网络编程综合运用之MiniFtp实现(八)的更多相关文章

  1. Linux网络编程综合运用之MiniFtp实现(一)

    春节过后,万物复苏,在这元宵佳节的前一天,决定继续开启新年的学习计划,生命在于运动,提高源于学习,在经过漫长的Linux网络编程学习后,接下来会以一个综合的小项目来将所学的知识点综合运用,首先是对项目 ...

  2. Linux网络编程综合运用之MiniFtp实现(四)

    从今天开始,正式进入MiniFtp的代码编写阶段了,好兴奋,接下来很长一段时间会将整个实现过程从无到有一点点实现出来,达到综合应用的效果,话不多说正入正题: 这节主要是将基础代码框架搭建好,基于上节介 ...

  3. Linux网络编程综合运用之MiniFtp实现(九)

    上次中实现了FTP命令的映射来避免很多if....else的判断,这次主要是开始实现目录列表的传输,先看一下目前实现的: 数据连接创建好之后则开始进行目录列表的传输了,而要传输目录列表,首先要将目录列 ...

  4. Linux网络编程综合运用之MiniFtp实现(五)

    转眼兴奋的五一小长假就要到来了,在放假前夕还是需要保持一颗淡定的心,上次中已经对miniFTP有基础框架进行了搭建,这次继续进行往上加代码,这次主要还是将经历投射到handle_child()服务进程 ...

  5. Linux网络编程综合运用之MiniFtp实现(七)

    上节中实现了配置文件的解析,这节来实现用户登录的验证,首先用客户端来登录vsftpd来演示登录的过程: 接着再连接miniftpd,来看下目前的效果: 接下来实现它,与协议相关的模块都是在ftppro ...

  6. Linux网络编程综合运用之MiniFtp实现(六)

    间隔了一周时间没写了,由于今年的股势行情貌似不错的样子,对于对股市完全不懂的我也在蠢蠢欲动,所以最近一周业余时间在“不务正业”-----学习炒股.发现学习它其实挺费神的,满脑子都是走势图,而且是神经有 ...

  7. Linux网络编程综合运用之MiniFtp实现(三)

    前面已经对FTP相关的一些概念有了基本的认识,接下来就要进入代码编写阶段了,也是非常兴奋的阶段,在开启这个它之前先对项目需求进行一个梳理,对其我们要实现的FTP服务器是一个什么样子. ftp命令列表 ...

  8. 【深入浅出Linux网络编程】 "开篇 -- 知其然,知其所以然"

    [深入浅出Linux网络编程]是一个连载博客,内容源于本人的工作经验,旨在给读者提供靠谱高效的学习途径,不必在零散的互联网资源中浪费精力,快速的掌握Linux网络编程. 连载包含4篇,会陆续编写发出, ...

  9. 【linux草鞋应用编程系列】_5_ Linux网络编程

    一.网络通信简介   第一部分内容,暂时没法描述,内容实在太多,待后续专门的系列文章.   二.linux网络通信     在linux中继承了Unix下“一切皆文件”的思想, 在linux中要实现网 ...

随机推荐

  1. Navicat Premium教程

    介绍 Navicat premium是一款数据库管理工具,是一个可多重连线资料库的管理工具,它可以让你以单一程式同时连线到 MySQL.SQLite.Oracle 及 PostgreSQL 资料库,让 ...

  2. iOS 多线程的简单理解(1) 方式 :同步 异步

    最近遇到特别糟糕的面试,过程中提到多次对多线程的处理问题,并没有很好的给予答复和解决,所以在这里做个简单的备案: 期望能更加了解和熟练使用 多线程技术: 下面都是自己的总结,如果存在不对的,或者不足, ...

  3. bootstrap:时间日期日历控件(datetimepicker)

    https://blog.csdn.net/qq_33368846/article/details/82223676 Bootstrap datetimepicker控件的使用 1.支持日期选择,格式 ...

  4. Python文件的读取写入操作

    一.打开文件.关闭文件操作 想要读取文件或是写入文件,第一步便是打开文件,最后一步便是关闭文件.这里介绍两种打开(关闭)文件的方式: 1.open()方法 f=open(file_name[,acce ...

  5. Eclipse下Maven安装和配置

    1. 下载 Maven 在百度输入 Maven 搜索 ,找到它的官网(http://maven.apache.org/),点击进入下载页面. 下载页面地址: http://maven.apache.o ...

  6. 022 Android .9图片的含义及制作教程

    1.图片(.9.png格式)的概念 (1)9patch图片是andriod app开发里一种特殊的图片形式,文件的扩展名为:.9.png (2)9patch图片的作用就是在图片拉伸的时候保证其不会失真 ...

  7. Win10 将本地连接设置为按流量计费网络

    在Win10中默认是不允许用户将本地连接设置为按流量计费网络的,不过我们可以通过修改注册表的方式来实现. 将本地连接设置为按流量计费网络后,Windows更新将不会自动下载.同样,Windows应用商 ...

  8. 通俗理解TCP的三次握手

    三次握手流程的本质,可以这么理解:TCP的三次握手其实是双方各一次握手,各一次确认,只是其中一次握手和确认合并在一起. 当然也可以更通俗的去理解: "喂,你听得到吗?" " ...

  9. 了解CAdoSqlserver

    include <vector> 表示引用了vector类, vector是STL中的一种数据结构,或者叫容器,功能相当于数组,但是功能强大很多.vector在C++标准模板库中的部分内容 ...

  10. Jmeter-后置处理器--json提取器

    Token提取: 将token放入全局变量: 将token值设为全局变量,${__setProperty(newtoken,${token},)}  添加请求头部管理器作为全局使用,将变量token使 ...