C++ socket實(shí)現(xiàn)miniFTP
本文實(shí)例為大家分享了C++ socket實(shí)現(xiàn)miniFTP的方法,供大家參考,具體內(nèi)容如下
客戶(hù)端:
服務(wù)端:
建立連接
連接使用 TCP 連接,服務(wù)器和客戶(hù)端分別創(chuàng)建自己的套接字一端,服務(wù)器等待連接,客戶(hù)端發(fā)起連接(并指定服務(wù)器 ip)。在兩者端口號(hào)一致且不被占用的情況下,連接建立。
在整個(gè)過(guò)程中,服務(wù)器對(duì)每一個(gè)來(lái)訪(fǎng)的客戶(hù)端建立一個(gè)連接,在客戶(hù)未請(qǐng)求與服務(wù)器斷開(kāi)時(shí),該連接一直存在,用戶(hù)可以不斷向服務(wù)器發(fā)出請(qǐng)求。(持久性、流水線(xiàn)型連接 )
客戶(hù)端斷開(kāi)后,關(guān)閉客戶(hù)端的套接字部分,服務(wù)器繼續(xù)等待新的連接。服務(wù)器一次只能處理一個(gè)客戶(hù)端的連接,不支持并發(fā)訪(fǎng)問(wèn)。
PDU 格式
由于 ftp 應(yīng)當(dāng)支持幾乎任意類(lèi)型文件,而幾乎所有類(lèi)型文件都能用二進(jìn)制來(lái)解析,所以我們采用了二進(jìn)制的格式來(lái)讀取以及寫(xiě)入文件。在整個(gè)過(guò)程中,我們并不關(guān)心文件的具體內(nèi)容,也無(wú)需在程序中解析文件,而是將其當(dāng)作數(shù)據(jù)流看待。
受到緩存區(qū)大小的限制,我們無(wú)法一次性傳輸整個(gè)文件,所以我們將文件按緩存區(qū)大小拆分成數(shù)據(jù)包分批發(fā)送,我們可以將數(shù)據(jù)及時(shí)從緩存區(qū)寫(xiě)入文件,這樣就讓出了緩存區(qū)空間。數(shù)據(jù)包僅僅包含數(shù)據(jù),不包含頭部或尾部信息。
此外,接收文件時(shí),recv()函數(shù)將會(huì)循環(huán)調(diào)用,因此,我們需要一個(gè)信號(hào)來(lái)通知什么時(shí)候發(fā)送完畢。
一個(gè)想法是發(fā)送終止信號(hào),這是可行的,但更好的方法是在一開(kāi)始發(fā)送文件總字節(jié)數(shù),讓接收方根據(jù)剩余字節(jié)大小判斷什么時(shí)候接收完畢。因?yàn)樵趯?xiě)入文件時(shí),我們需要指定寫(xiě)入的字節(jié)數(shù),尤其是在發(fā)來(lái)的數(shù)據(jù)流字節(jié)數(shù)不等于緩沖區(qū)大小時(shí)。寫(xiě)入字節(jié)數(shù)的錯(cuò)誤會(huì)導(dǎo)致文件受損。
接收確認(rèn)
我們知道 TCP 是可靠傳輸協(xié)議,它采取了一系列措施來(lái)保證傳輸不會(huì)出錯(cuò)。所以在使用 TCP 連接時(shí),我們相信數(shù)據(jù)在鏈路層上沒(méi)有出差錯(cuò),它一定會(huì)成功發(fā)送到對(duì)方手上。但是在客戶(hù)端接收服務(wù)器發(fā)來(lái)的文件的時(shí)候,我們?nèi)匀恍枰?wù)器發(fā)來(lái)確認(rèn)信息。原因在于,雖然我們可以保證鏈路層不出錯(cuò),但是我們無(wú)法保證應(yīng)用層不出錯(cuò)。例如,客戶(hù)端可能會(huì)給出錯(cuò)誤的文件名,因?yàn)榻邮詹坏椒?wù)器發(fā)來(lái)的信息,所以會(huì)陷入空等狀態(tài)。
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"); } //確認(rè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客戶(hù)端,您可以輸入help查看操作方法,輸入quit退出客戶(hù)端\n"); while (1) { scanf("%s", c); if (strcmp(c, "help") == 0) { printf("get [fileName] -- 下載文件\n" "put [fileName] -- 上傳文件\n" "ftp [ip] -- 登錄FTP\n" "pwd -- 顯示服務(wù)器當(dāng)前工作文件夾\n" "cd [dirName] -- 更改當(dāng)前文件夾\n" "close -- 關(guān)閉與當(dāng)前ftp的連接\n" "quit -- 退出客戶(hù)端\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) { //創(chuàng)建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協(xié)議家族AF_INET serverChannel.sin_addr.S_un.S_addr = inet_addr(hostName);//地址 serverChannel.sin_port = htons(SERVER_PORT);//服務(wù)器端口 //建立連接 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; //打開(kāi)文件 in.open(name, std::ios::binary); if (!in) { printf("cannot open the file\n"); return false; } memset(buffer, 0, sizeof(buffer)); //得到文件的字節(jié)數(shù) in.seekg(0, std::ios_base::end); int sp = in.tellg(); int total_size = 0; int r; char length[20]; sprintf(length, "%d", sp); //發(fā)送字節(jié) 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; //發(fā)送文件 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:字符串末沒(méi)有斜線(xiàn)?。? int serverSocket; //socket int clientSocket; bool sendFile(); bool receiveFile(); bool doPwd(); bool doCd(); bool isValidPath(char* path); public: ftpServer(); bool start();//開(kāi)啟服務(wù)器 }; 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"); } //確認(rèn)WinSock DLL支持版本2.2: if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2) { WSACleanup(); printf("Invalid Winsock version!\n"); } //workDir初始化為當(dāng)前路徑 system("cd > tempFile"); std::ifstream in("tempFile", std::ifstream::in); in >> workDir; in.close(); } bool ftpServer::start() { int on = 1; //初始化服務(wù)器 memset(&serverChannel, 0, sizeof(serverChannel)); serverChannel.sin_family = AF_INET; serverChannel.sin_addr.s_addr = htonl(INADDR_ANY); serverChannel.sin_port = htons(SERVER_PORT); //創(chuàng)建套接字 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"); //監(jiān)聽(tīng) int l = listen(serverSocket, QUEUE_SIZE); if (l < 0) { printf("listen failed\n"); return false; } else printf("successfully listen\n"); int len = sizeof(serverChannel); //服務(wù)器等待連接 while (1) { printf("waiting for connection...\n"); //接受一個(gè)連接 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(); }
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
windows下在vim中搭建c語(yǔ)言開(kāi)發(fā)環(huán)境的詳細(xì)過(guò)程
這篇文章主要介紹了windows下在vim中搭建c語(yǔ)言開(kāi)發(fā)環(huán)境,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-05-05C語(yǔ)言數(shù)據(jù)結(jié)構(gòu)實(shí)現(xiàn)鏈表去重的實(shí)例
這篇文章主要介紹了C語(yǔ)言數(shù)據(jù)結(jié)構(gòu)實(shí)現(xiàn)鏈表去重的實(shí)例的相關(guān)資料,這里提供了題目及實(shí)例代碼,需要的朋友可以參考下2017-07-07C語(yǔ)言實(shí)現(xiàn)數(shù)組元素排序方法詳解
這篇文章主要為大家介紹了C語(yǔ)言算法練習(xí)中數(shù)組元素排序的實(shí)現(xiàn)方法,文中的示例代碼講解詳細(xì),對(duì)我們學(xué)習(xí)C語(yǔ)言有一定幫助,需要的可以參考一下2023-02-02c與c++之間的相互調(diào)用及函數(shù)區(qū)別示例詳解
這篇文章主要為大家介紹了c與c++相互調(diào)用的使用示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-06-06