本文实例为大家分享了C++ socket实现miniFTP的方法,供大家参考,具体内容如下
客户端:
服务端:
建立连接
连接使用 TCP 连接,服务器和客户端分别创建自己的套接字一端,服务器等待连接,客户端发起连接(并指定服务器 ip)。在两者端口号一致且不被占用的情况下,连接建立。
在整个过程中,服务器对每一个来访的客户端建立一个连接,在客户未请求与服务器断开时,该连接一直存在,用户可以不断向服务器发出请求。(持久性、流水线型连接 )
客户端断开后,关闭客户端的套接字部分,服务器继续等待新的连接。服务器一次只能处理一个客户端的连接,不支持并发访问。
PDU 格式
由于 ftp 应当支持几乎任意类型文件,而几乎所有类型文件都能用二进制来解析,所以我们采用了二进制的格式来读取以及写入文件。在整个过程中,我们并不关心文件的具体内容,也无需在程序中解析文件,而是将其当作数据流看待。
受到缓存区大小的限制,我们无法一次性传输整个文件,所以我们将文件按缓存区大小拆分成数据包分批发送,我们可以将数据及时从缓存区写入文件,这样就让出了缓存区空间。数据包仅仅包含数据,不包含头部或尾部信息。
此外,接收文件时,recv()函数将会循环调用,因此,我们需要一个信号来通知什么时候发送完毕。
一个想法是发送终止信号,这是可行的,但更好的方法是在一开始发送文件总字节数,让接收方根据剩余字节大小判断什么时候接收完毕。因为在写入文件时,我们需要指定写入的字节数,尤其是在发来的数据流字节数不等于缓冲区大小时。写入字节数的错误会导致文件受损。
接收确认
我们知道 TCP 是可靠传输协议,它采取了一系列措施来保证传输不会出错。所以在使用 TCP 连接时,我们相信数据在链路层上没有出差错,它一定会成功发送到对方手上。但是在客户端接收服务器发来的文件的时候,我们仍然需要服务器发来确认信息。原因在于,虽然我们可以保证链路层不出错,但是我们无法保证应用层不出错。例如,客户端可能会给出错误的文件名,因为接收不到服务器发来的信息,所以会陷入空等状态。
ftpClient.h
#pragma #include<winsock.h> class ftpClient { private: enum { SERVER_PORT = 9999, BUFFER_SIZE = 4096 }; sockaddr_in serverChannel; char buffer[BUFFER_SIZE]; int serverSocket; int clientSocket; bool isConnect; char name[50]; bool getFile(); bool putFile(); bool acknowledge(); bool sendRequest(char* instruction); bool connect2Host(const char* hostName); bool getWorkDir(); public: ftpClient(); ~ftpClient(); void start(); };</div>
ftpClient.cpp
#define _CRT_SECURE_NO_WARNINGS #include"ftpClient.h" #include<cstdio> #include<io.h> #include<cstring> #include<fstream> ftpClient::ftpClient() { WORD wVersionRequested; WSADATA wsaData; int ret; //WinSock初始化: wVersionRequested = MAKEWORD(2, 2);//希望使用的WinSock DLL的版本 ret = WSAStartup(wVersionRequested, &wsaData); if (ret != 0) { printf("WSAStartup() failed!\n"); } //确认WinSock DLL支持版本2.2: if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2) { WSACleanup(); printf("Invalid Winsock version!\n"); } isConnect = false; } void ftpClient::start() { char c[100]; char d[100]; printf("这里是FTP客户端,您可以输入help查看操作方法,输入quit退出客户端\n"); while (1) { scanf("%s", c); if (strcmp(c, "help") == 0) { printf("get [fileName] -- 下载文件\n" "put [fileName] -- 上传文件\n" "ftp [ip] -- 登录FTP\n" "pwd -- 显示服务器当前工作文件夹\n" "cd [dirName] -- 更改当前文件夹\n" "close -- 关闭与当前ftp的连接\n" "quit -- 退出客户端\n" ); } else if (strcmp(c, "get") == 0) { scanf("%s", d); strcat(c, " "); strcat(c, d); if (!isConnect) { printf("you haven't connected to any server!\n"); } else sendRequest(c); } else if (strcmp(c, "put") == 0) { scanf("%s", d); strcat(c, " "); strcat(c, d); if (!isConnect) { printf("you haven't connected to any server!\n"); } else sendRequest(c); } else if (strcmp(c, "ftp") == 0) { scanf("%s", d); if (!isConnect&&connect2Host(d)) { isConnect = true; } else if(isConnect){ printf("you have already connected to server\n" "please close the connection before connect to a new server\n"); } } else if (strcmp(c, "pwd") == 0) { if (!isConnect) { printf("you haven't connected to any server!\n"); } else sendRequest(c); } else if (strcmp(c, "cd") == 0) { scanf("%s", d); strcat(c, " "); strcat(c, d); if (!isConnect) { printf("you haven't connected to any server!\n"); } else sendRequest(c); } else if (strcmp(c, "quit") == 0) { if (isConnect) { strcpy(c, "close"); isConnect = false; send(clientSocket, c, strlen(c) + 1, 0); closesocket(clientSocket); } break; } else if (strcmp(c, "close") == 0) { if (isConnect) { isConnect = false; send(clientSocket, c, strlen(c) + 1, 0); closesocket(clientSocket); } } else { printf("syntex error\n"); } } } bool ftpClient::connect2Host(const char* hostName) { //创建socket clientSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (clientSocket < 0) { printf("cannot create socket\n"); return false; } else printf("successfully create socket\n"); memset(&serverChannel, 0, sizeof(serverChannel));//初始化为0 serverChannel.sin_family = AF_INET;//channel协议家族AF_INET serverChannel.sin_addr.S_un.S_addr = inet_addr(hostName);//地址 serverChannel.sin_port = htons(SERVER_PORT);//服务器端口 //建立连接 serverSocket = connect(clientSocket, (sockaddr*)&serverChannel, sizeof(serverChannel)); if (serverSocket < 0) { printf("cannot connect to the host\n"); return false; } else { printf("successfully connect to the host\n"); return true; } } bool ftpClient::sendRequest(char* instruction) { int r = send(clientSocket, instruction, strlen(instruction) + 1, 0); if (r == SOCKET_ERROR) { printf("request failed\n"); return false; } else { printf("request success\n"); char opt[5]; int i = 0, j = 0; while (instruction[i] != ' '&&instruction[i] != '\0') { opt[i] = instruction[i]; i++; } opt[i] = '\0'; i++; while (instruction[i] != '\0') { name[j] = instruction[i]; i++, j++; } name[j] = '\0'; if (strcmp(opt, "get") == 0) { if (getFile()) { printf("successfully download\n"); } else printf("download failed\n"); } else if (strcmp(opt, "put") == 0) { if (putFile()) { printf("successfull