要在linux下做一个模仿ftp的小型服务器,后来在百度文库中找到一份算是比较完整的实现,就在原代码一些重要部分上备注自己的理解(可能有误,千万不要轻易相信)。
客户端:
客户端要从服务器端中读取数据,然后将read到的数据存在rcv_buf数组中,再使用atoi函数将rcv_buf中的数字字符提取出来(atoi提取数字时有着自己的规则),这样客户端解可以根据reply_code这个返回值来判断服务器要告诉自己什么。
1 2 //从套接字描述sock_fd中获取服务器的回复// 3 int resp_from_server(int sock_fd) 4 { 5 static int reply_code=0,count=0; 6 char rcv_buf[512]; 7 count=read(sock_fd, rcv_buf, 510); 8 if(count>0) //如果从服务器端读取数据成功,那么就向服务器反馈reply_code=atoi(rcv_buf);然后服务器端用recv_client_info(client_sock);来接收反馈信息 9 { 10 reply_code=atoi(rcv_buf); //atoi函数把字符串转化成整型数,该函数返回转换后的长整数,如果没有执行有效的转换,则返回零。 11 //该函数要求被转换的字符串是按十进制数理解的。 12 } 13 else 14 return 0; 15 while(1) 16 { 17 if(count<=0) 18 break; 19 rcv_buf[count]='\0'; 20 printf("%s",rcv_buf); 21 count=read(sock_fd, rcv_buf, 510); 22 } 23 return reply_code; 24 } 25 /*详解一下这个resp_from_server()函数,在此之前先了解一下read函数的大致用法及其定义; 26 参考:https://blog.csdn.net/hhhlizhao/article/details/71552588 27 28 函数定义:ssize_t read(int fd, void * buf, size_t count); 29 30 函数说明:read()会把参数fd所指的文件传送count 个字节到buf 指针所指的内存中。 31 32 返回值:返回值为实际读取到的字节数, 如果返回0, 表示已到达文件尾或是无可读取的数据。若参数count 为0, 则read()不会有作用并返回0。 33 34 注意:read时fd中的数据如果小于要读取的数据,就会引起阻塞。 35 36 好的,read函数就点到为止;意犹未尽者可以自行请教搜索引擎; 37 38 简单总结一下,read函数就是把用第一个参数描述的文件的第三个参数的数量的字节写入到第二个参数中 39 那么,怎么会突然就read起来了呢?我怎么知道第一个参数第对哪个文件的描述? 40 请君息怒,您冷静分析,是不是 大部分函数在调用resp_from_server()函数之前都会调用send_to_server()或者其他的可以从服务器端得到数据的函数 41 或者你可以看一看服务器端的send_to_client()函数,是不是有很多个send_to_client()函数的第二个参数都是从以下的字符数组中传入的 42 char serverInfo220[]="220 登录服务器"; 43 char serverInfo230[]="230 root用户"; 44 char serverInfo331[]="331 请输入用户密码"; 45 char serverInfo221[]="221 "; 46 char serverInfo150[]="150 "; 47 char serverInfo226[]="226 关闭数据连接"; 48 char serverInfo200[]="200 "; 49 char serverInfo215[]="215 "; 50 char serverInfo213[]="213 "; 51 char serverInfo211[]="211 "; 52 char serverInfo350[]="350 "; 53 char serverInfo530[]="530 登录失败"; 54 char serverInfo531[]="531 匿名用户"; 55 char serverInfo123[]="123 wu"; 56 char serverInfo[]="202"; 57 58 这时我们要回到客户端了,看看这个函数count=read(sock_fd, rcv_buf, 510);如果read成功的话,就会返回第二个参数所读入的字节数 59 否则返回-1(至于返回0的情况就自行百度了) 60 61 这时我们再看int resp_from_server(int sock_fd)函数如果调用成功后的返回值是什么,没错就是reply_code; 62 那么reply_code是从何而来的? 63 请看 reply_code=atoi(rcv_buf); 64 这里有牵引出一个atoi函数了,至于它的作用就不详细介绍了 65 大意就是将一个字符串中的数字字符串转化成int型的,当然它并不是无条件转换的,atoi遇到空格会直接无视掉它; 66 “它们都从字符串开始寻找数字或者正负号或者小数点,然后遇到非法字符终止,不会报异常:”“” 67 看到了吧,所谓的非法字符就是从左到右遇到的第一个非数字或者正负号或者小数点的字符; 68 比如,atoi处理 "226 关闭数据连接";这个字符串,返回值是226; 69 再看,atoi处理 " 331 请输入用户密码"; 的返回值是331;【注意前面的空格】 70 这样atoi函数处理完后使得 resp_from_server(int sock_fd) 函数返回的reply_code是一个从字符串提取出来的int型; 71 好像可以理解为啥 char serverInfo[]="202";存储的字符串是几个数字了; 72 73 这就是为啥你会看到一堆的 如什么 226,227,331,220这样的数了; 74 75 当然,我的理解是,为啥服务器端要以这样的形式来告诉客户端呢? 76 你去看看服务器端的 do_client_work()函数中,人家服务器是不是每发送一次send_to_client(client_sock, serverInfo221, strlen(serverInfo221)); 77 这样的信息前面都会调用相关的函数来处理客户端发起的请求了,之所以要这样反馈我个人觉得有以下两个原因 78 一是客户端和服务器端进行的不是一次性处理事件,需要进行交互 79 二是服务器端为了提醒客户端,喂,我出来完你的请求了,你那边还要干什么,自己看着办吧 80 不然没有通知的话,我们人类就不能明显地发现二者之间什么时候完成处理,我们下一步要客户端做什么这样的事情了 81 以上是我理解 82 对于atoi函数的理解:https://www.cnblogs.com/wzxwhd/p/6030083.html 83 对于read函数的理解:http://c.biancheng.net/cpp/html/3037.html 84 */