软件编程
位置:首页>> 软件编程>> C语言>> C++ socket实现miniFTP

C++ socket实现miniFTP

作者:ZJU_fish1996  发布时间:2022-05-01 11:45:16 

标签:C++,socket,miniFTP

本文实例为大家分享了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();
}
0
投稿

猜你喜欢

手机版 软件编程 asp之家 www.aspxhome.com