FTP客户端代码实现demo_v1.0
写的不是特别好,算是初次尝试,还是懂的了不少。
写一个Ftp客户端第一件事情应该是初始化winsock。
////////////////////////////////////////////////////////////////// //author:YSI //date:2012-04-21 //version:1.0 #include "main.h" #include "initsock.h" using namespace std; CInitSock initSock;//初始化Winsock库
CInitSock类主要是初始化Winsock类的,完成加载Winsock(WSAStartup),确定使用的sock版本等内容。
////////////////////////////////////////////////////////// // initsock.h文件 #include <winsock2.h> #pragma comment(lib, "WS2_32") // 链接到WS2_32.lib class CInitSock { public: CInitSock(BYTE minorVer = 2, BYTE majorVer = 2) { // 初始化WS2_32.dll WSADATA wsaData; //WSA结构体里有DLL库的详细信息 WORD sockVersion = MAKEWORD(minorVer, majorVer);//将BYTE型的min和maj合并成一个WORD型 if(::WSAStartup(sockVersion, &wsaData) != 0)//加载Winsock库 { exit(0); } } ~CInitSock() { ::WSACleanup(); } };
Socket套接字就像是一个窗口一样,而输送的信息就好似传信的鸽子一般,只会飞回到他自己的那个窗口中的巢穴里。
窗口(Socket套接字)在刚开始的使用时需要初始化的。
SOCKET control_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_HOPOPTS); //初始化一个套接字,套接字就好像一个窗口一样 if (control_sock == INVALID_SOCKET) { cout << "Failed Socket"<<endl; return 0; } /* int socket(int domain, int type, int protocol); 应用程序调用socket函数来创建一个能够进行网络通信的套接字。 第一个参数指定应用程序使用的通信协议的协议族,对于TCP/IP协议族,该参数置AF_INET; 第二个参数指定要创建的套接字类型,流套接字类型为SOCK_STREAM、数据报套接字类型为SOCK_DGRAM、原始套接字SOCK_RAW (WinSock接口并不适用某种特定的协议去封装它,而是由程序自行处理数据包以及协议首部); 第三个参数指定应用程序所使用的通信协议。这里IPPROTO_HOPOPTS为宏,其实就是0. */
在初始化好了套接字之后,就可以开始设置你的鸟儿要飞到什么地方的哪个窗口去了----使用sockaddr_in 结构体来初始化服务器的地址和端口号等。
sockaddr_in serverAddr; //SOCKADDR_IN结构体来指定IP地址和端口号。 serverAddr.sin_family = AF_INET; serverAddr.sin_port = htons(21);//设定要连接的服务器端口号 serverAddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");//指定服务器地址
之后,使用套接字连接,这样,你的鸽子就知道你家在哪里,应该从哪里飞进来,也知道要去哪里,应该从哪里进去。连接成功后会从服务器端发回欢迎信息。
if (connect(control_sock, (sockaddr*)&serverAddr, sizeof(serverAddr)) == -1)//用指定的套接字对指定的地址(服务器)进行连接 { cout << "Failed connect"<<endl;//建立失败就返回 return 0; }
recv接受欢迎信息。
char buff[256]; int nRecv = recv(control_sock, buff, 256, 0);//建立好后开始接受数据,接受欢迎数据,等等。nRecv得到的是他发送过来的字节数 if (nRecv > 0) { buff[nRecv] = '\0'; cout << buff << endl; }
接受到欢迎信息之后,就可以进行登录,认证后可以进行下载上传。
char send_buff[256], read_buff[256]; memset(send_buff, 0, 256); memset(read_buff, 0, 256); sprintf(send_buff, "USER %s\r\n", "yc"); send(control_sock, send_buff, strlen(send_buff), 0); recv(control_sock, read_buff, 256, 0); cout << read_buff << endl; memset(send_buff, 0, 256); memset(read_buff, 0, 256); sprintf(send_buff, "PASS %s\r\n", "123");
这里使用ftp被动模式(passive)。在被动模式中需要注意的是,他发送过来的端口号是<h1,h2,h3,h4,p1,p2>的模式。前四个目前不会用到,而他传数据的端口
p=p1*256+p2;其他都还好。
sprintf(send_buff,"PASV\r\n");//进入被动模式 send(control_sock, send_buff, strlen(send_buff), 0); recv(control_sock, read_buff, 256, 0); //得到的最终端口号为p = p1×256+p2,这里受到<h1,h2,h3,h4,p1,p2>
于是,我们可以得到端口号,可以建立一个新的套接字,进行数据的传送了。
SOCKET data_sock = socket(AF_INET, SOCK_STREAM, 0); serverAddr.sin_port = htons(Portnum);//设定端口号p if (connect(data_sock, (sockaddr*)&serverAddr, sizeof(serverAddr)) == -1)//用指定的套接字对指定的地址(服务器)进行连接 { cout << "Failed data_connect"<<endl;//建立失败就返回 return 0; }
然后经过命令CWD,SIZE,得到了我们所需下载的东西的地址和大小。现在可以开始下载了。下载是RETR命令。
sprintf(send_buff,"RETR \\boltsdk\\tools\\CurveTool.exe\r\n"); send(control_sock, send_buff, strlen(send_buff), 0);//准备好收东西啦 recv(control_sock,read_buff, 256, 0); cout << read_buff << endl;
之后就是下载啦。这里用的是FILE类,很久没有用过文件操作了,一开始用还不好使,汗呀。。
FILE *f1; f1 = fopen("H:\\CurveTool.exe","a+b"); if (f1 == NULL) { cout << "File ERROR"<<endl; return 0; } while (srcsize > 0) { memset(read_buff, 0 , 256); int revsize = recv(data_sock, read_buff, 256, 0); srcsize = srcsize - revsize; fwrite(read_buff, sizeof(char), revsize, f1); } fclose(f1);
完成了操作之后就关闭套接字,就OK了=。=~
closesocket(control_sock);
closesocket(data_sock);
很久没有写网络编程了,东西都被我忘记的差不多了,由于之前那个博客因为很多原因被自己删掉了,所以很多做过的东西忘记了也很难在从曾经的项目上找到痕迹,这让我领悟到留下曾经痕迹的重要性。这是我决定重新开一个放代码博客的原因。
TX空间是很好,但是代码这样的东西,还是分享出来的好。
空间就拿来写日记好啦。这里放点自己写的心得体会的好。