首页 > 编程 > C++ > 正文

C++ socket实现miniFTP

2020-05-23 13:59:37
字体:
来源:转载
供稿:网友

本文实例为大家分享了C++ socket实现miniFTP的方法,供大家参考,具体内容如下

客户端:

C++,socket,miniFTP

服务端:

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(); }; 

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("successfully upload/n");       }       else printf("upload failed/n");     }     else if (strcmp(opt, "pwd") == 0) {       if (!getWorkDir())         printf("get work directory failed/n");     }     else if (strcmp(opt, "cd") == 0) {       printf("operation finished/n");     }     else {       printf("syntex error/n");       return false;     }     return true;   } }  bool ftpClient::getFile() {   memset(buffer, 0, sizeof(buffer));   int ret;   char length[20];   ret = recv(clientSocket, length, sizeof(length), 0);   if (ret == SOCKET_ERROR) {     return false;   }   else if (strcmp(length, "NAK") == 0) {     return false;   }   int size = atoi(length);   std::ofstream out;    out.open(name, std::ios::binary);   if (!out) {     printf("cannot save the file/n");     return false;   }   while (size>0) {     ret = recv(clientSocket, buffer, BUFFER_SIZE, 0);     int s = size < BUFFER_SIZE ? size : BUFFER_SIZE;     if (ret == SOCKET_ERROR) {       out.close();       return false;     }     else if (strcmp(buffer, "NAK") == 0) {       out.close();       return false;     }     else {       out.write(buffer, s);     }     size -= BUFFER_SIZE;   }   out.close();   return acknowledge(); }  bool ftpClient::putFile() {   std::ifstream in;   //打开文件   in.open(name, std::ios::binary);   if (!in) {     printf("cannot open the file/n");     return false;   }   memset(buffer, 0, sizeof(buffer));   //得到文件的字节数   in.seekg(0, std::ios_base::end);   int sp = in.tellg();   int total_size = 0;   int r;   char length[20];   sprintf(length, "%d", sp);    //发送字节   r = send(clientSocket, length, sizeof(length), 0);   if (r == SOCKET_ERROR) {     return false;   }   while (sp > 0) {     in.clear();     in.seekg(total_size, std::ios_base::beg);     memset(buffer, 0, sizeof(buffer));     //读取文件     in.read(buffer, sizeof(buffer));     int size = sp < BUFFER_SIZE ? sp : BUFFER_SIZE;     total_size += size;     //发送文件     r = send(clientSocket, buffer, size, 0);      sp -= size;     if (r == SOCKET_ERROR) {       in.close();       return false;     }   }   in.close(); }  bool ftpClient::getWorkDir() {   printf("getWorkDir/n");   memset(buffer, 0, sizeof(buffer));   int ret;   char length[20];   ret = recv(clientSocket, length, sizeof(length), 0);   if (ret == SOCKET_ERROR) {     return false;   }   int size = atoi(length);   while (size>0) {     ret = recv(clientSocket, buffer, BUFFER_SIZE, 0);     if (ret == SOCKET_ERROR) {       return false;     }     else {       printf("%s", buffer);     }     size -= BUFFER_SIZE;   }   return true; }  bool ftpClient::acknowledge() {   int ret = recv(clientSocket, buffer, BUFFER_SIZE, 0);   if (ret > 0) {     if (strcmp(buffer, "NAK") == 0)return false;     else if (strcmp(buffer, "ACK") == 0)return true;   } }  ftpClient::~ftpClient() {   if (isConnect) {     isConnect = false;     char c[6];     strcpy(c, "close");     send(clientSocket, c, strlen(c) + 1, 0);     closesocket(clientSocket);   } } 

main.cpp

#define _CRT_SECURE_NO_WARNINGS #define _WINSOCK_DEPRECATED_NO_WARNINGS #pragma(lib,"ws2_32.lib") #include"ftpClient.h" #include<stdio.h>  int main() {   ftpClient a;   a.start();   return 0; } 

ftpServer.h

#pragma once #include<winsock.h>  class ftpServer { private:   enum {     SERVER_PORT = 9999,     BUFFER_SIZE = 4096,     QUEUE_SIZE = 10   };   char buffer[BUFFER_SIZE];   sockaddr_in serverChannel;   char name[50];   char workDir[100]; //store like C:/Users MARK:字符串末没有斜线!!   int serverSocket; //socket   int clientSocket;   bool sendFile();   bool receiveFile();   bool doPwd();   bool doCd();   bool isValidPath(char* path); public:   ftpServer();   bool start();//开启服务器 }; ftpServer.cpp

 

#define _CRT_SECURE_NO_WARNINGS  #include"ftpServer.h" #include<cstdio> #include<cstdlib> #include<fstream> #include<cstring>  ftpServer::ftpServer() {   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");   }   //workDir初始化为当前路径   system("cd > tempFile");   std::ifstream in("tempFile", std::ifstream::in);   in >> workDir;   in.close(); }  bool ftpServer::start() {   int on = 1;    //初始化服务器   memset(&serverChannel, 0, sizeof(serverChannel));   serverChannel.sin_family = AF_INET;   serverChannel.sin_addr.s_addr = htonl(INADDR_ANY);   serverChannel.sin_port = htons(SERVER_PORT);    //创建套接字   this->serverSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);   if (serverSocket < 0) {     printf("cannot create socket/n");     return false;   }   else printf("successfully create socket/n");   setsockopt(serverSocket, SOL_SOCKET, SO_REUSEADDR,     (char*)&on, sizeof(on));    //绑定   int b = bind(serverSocket, (sockaddr*)&serverChannel,     sizeof(serverChannel));   if (b < 0) {     printf("bind error/n");     return false;   }   else printf("successfully bind/n");   //监听   int l = listen(serverSocket, QUEUE_SIZE);   if (l < 0) {     printf("listen failed/n");     return false;   }   else printf("successfully listen/n");   int len = sizeof(serverChannel);   //服务器等待连接   while (1) {     printf("waiting for connection.../n");     //接受一个连接     clientSocket = accept(serverSocket, (sockaddr*)&serverChannel,       &len);     if (clientSocket < 0) {       printf("accept failed/n");     }     else {       printf("successfully connect/n");       while (1) {         memset(buffer, 0, sizeof(buffer));         int ret;          ret = recv(clientSocket, buffer, BUFFER_SIZE, 0);          if (ret == SOCKET_ERROR) {           printf("receive failed/n");         }         else {           char opt[50];           printf("successfully receive/n");           int i = 0, j = 0;           printf("buffer = %s/n", buffer);           while (buffer[i] != ' '&&buffer[i] != '/0') {             opt[i] = buffer[i];             i++;           }           opt[i] = '/0';           if (buffer[i] != '/0') {             i++;           }           while (buffer[i] != '/0') {             name[j] = buffer[i];             i++, j++;           }           name[j] = '/0';            if (strcmp(opt, "get") == 0) {             char ret[4];             if (!sendFile()) {               strcpy(ret, "NAK");               send(clientSocket, ret, sizeof(ret), 0);             }             else {               strcpy(ret, "ACK");               send(clientSocket, ret, sizeof(ret), 0);             }           }           else if (strcmp(opt, "put") == 0) {             receiveFile();           }           else if (strcmp(opt, "pwd") == 0) {             doPwd();           }           else if (strcmp(opt, "cd") == 0) {             doCd();           }           else if (strcmp(opt, "close") == 0) {             break;           }           else {             printf("syntex error/n");           }         }       }     }   }   return true; }  bool ftpServer::sendFile() {   std::ifstream in;   char path[100];   strcpy(path, workDir);   strcat(path, "//");   strcat(path, name);    in.open(path, std::ios::binary);   if (!in) {     printf("cannot open the file/n");     return false;   }   memset(buffer, 0, sizeof(buffer));   in.seekg(0, std::ios_base::end);   int sp = in.tellg();   int total_size = 0;   int r;   char length[20];   sprintf(length, "%d", sp);      r = send(clientSocket, length, sizeof(length), 0);    if (r == SOCKET_ERROR) {     printf("send failed/n");     return false;   }   else {     printf("send success/n");   }    while (sp > 0) {     in.clear();     in.seekg(total_size, std::ios_base::beg);     memset(buffer, 0, sizeof(buffer));     in.read(buffer, sizeof(buffer));     int size = sp < BUFFER_SIZE ? sp : BUFFER_SIZE;     total_size += size;     r = send(clientSocket, buffer, size, 0);      sp -= size;     if (r == SOCKET_ERROR) {       printf("send failed/n");       return false;     }     else {       printf("send success/n");     }   }   in.close();   return true; }  bool ftpServer::receiveFile() {   char path[100];   strcpy(path, workDir);   strcat(path, "//");   strcat(path, name);   memset(buffer, 0, sizeof(buffer));   int ret;   char length[20];   ret = recv(clientSocket, length, sizeof(length), 0);   if (ret == SOCKET_ERROR) {     printf("receive failed/n");     return false;   }   else {     printf("successfully receive/n");   }   int size = atoi(length);   std::ofstream out;    out.open(path, std::ios::binary);   if (!out) {     printf("cannot save the file/n");     return false;   }   while (size>0) {     int s = size < BUFFER_SIZE ? size : BUFFER_SIZE;     ret = recv(clientSocket, buffer, BUFFER_SIZE, 0);     if (ret == SOCKET_ERROR) {       printf("receive failed/n");       break;     }     else {       printf("successfully receive/n");       out.write(buffer, s);     }     size -= BUFFER_SIZE;   }   out.close();   return true; }  bool ftpServer::doPwd() {   char temCMD[150];   memset(temCMD, 0, sizeof(temCMD));   strcat(temCMD, "echo ");   strcat(temCMD, workDir);   strcat(temCMD, " > tempFile");   system(temCMD);   memset(temCMD, 0, sizeof(temCMD));   strcat(temCMD, "dir /b ");   strcat(temCMD, workDir);   strcat(temCMD, " >> tempFile");   system(temCMD);    std::ifstream in("tempFile", std::fstream::in);   if (!in) {     printf("cannot open the file/n");     return false;   }   memset(buffer, 0, sizeof(buffer));   in.seekg(0, std::ios_base::end);   int sp = in.tellg();   int total_size = 0;   int r;   char length[20];   sprintf(length, "%d", sp);   r = send(clientSocket, length, sizeof(length), 0);    if (r == SOCKET_ERROR) {     printf("send failed/n");     return false;   }   else {     printf("send success/n");   }   while (sp > 0) {     in.clear();     in.seekg(total_size, std::ios_base::beg);     memset(buffer, 0, sizeof(buffer));     in.read(buffer, sizeof(buffer));     int size = sp < BUFFER_SIZE ? sp : BUFFER_SIZE;     total_size += size;     printf("transfer size = %d/n", total_size);     r = send(clientSocket, buffer, size, 0);      sp -= size;     if (r == SOCKET_ERROR) {       printf("send failed/n");       return false;     }     else {       printf("send success/n");     }   }   in.close();   return true; }  bool ftpServer::isValidPath(char* path) {   char temCMD[100];   memset(temCMD, 0, sizeof(temCMD));   strcat(temCMD, "cd ");   strcat(temCMD, path);   int res = system(temCMD);   return res == 0; }  bool ftpServer::doCd() {   for (int i = 0; name[i] != '/0'; ++i) {     if (name[i] == '/')       name[i] = '//';   }   if (name[0] == '.'&&name[1] == '.') {     char temDir[100];     strcpy(temDir, workDir);     for (int i = sizeof(temDir); i >= 0; --i) {       if (temDir[i] == '//') {         temDir[i] = '/0';         break;       }     }     strcat(temDir, name + 2);     if (isValidPath(temDir)) {       strcpy(workDir, temDir);     }     else {       return false;     }   }   else if (name[0] == '.'&&name[1] != '.') {     char temDir[100];     strcpy(temDir, workDir);     strcat(temDir, name + 1);     if (isValidPath(temDir)) {       strcpy(workDir, temDir);     }     else {       return false;     }   }   else if (name[1] == ':') {     if (isValidPath(name)) {       strcpy(workDir, name);     }     else {       return false;     }   }   else {     char temDir[100];     strcpy(temDir, workDir);     strcat(temDir, "//");     strcat(temDir, name);     if (isValidPath(temDir)) {       strcpy(workDir, temDir);     }     else {       return false;     }   }   return true; } 

main.cpp

#include"ftpServer.h" #pragma(lib,"ws2_32.lib") int main() {  ftpServer f;  f.start(); } 

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持VEVB武林网。


发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表