C++ socket實(shí)現(xiàn)miniFTP
本文實(shí)例為大家分享了C++ socket實(shí)現(xiàn)miniFTP的方法,供大家參考,具體內(nèi)容如下
客戶端:

服務(wù)端:

建立連接
連接使用 TCP 連接,服務(wù)器和客戶端分別創(chuàng)建自己的套接字一端,服務(wù)器等待連接,客戶端發(fā)起連接(并指定服務(wù)器 ip)。在兩者端口號(hào)一致且不被占用的情況下,連接建立。
在整個(gè)過(guò)程中,服務(wù)器對(duì)每一個(gè)來(lái)訪的客戶端建立一個(gè)連接,在客戶未請(qǐng)求與服務(wù)器斷開時(shí),該連接一直存在,用戶可以不斷向服務(wù)器發(fā)出請(qǐng)求。(持久性、流水線型連接 )
客戶端斷開后,關(guān)閉客戶端的套接字部分,服務(wù)器繼續(xù)等待新的連接。服務(wù)器一次只能處理一個(gè)客戶端的連接,不支持并發(fā)訪問。
PDU 格式
由于 ftp 應(yīng)當(dāng)支持幾乎任意類型文件,而幾乎所有類型文件都能用二進(jìn)制來(lái)解析,所以我們采用了二進(jìn)制的格式來(lái)讀取以及寫入文件。在整個(gè)過(guò)程中,我們并不關(guān)心文件的具體內(nèi)容,也無(wú)需在程序中解析文件,而是將其當(dāng)作數(shù)據(jù)流看待。
受到緩存區(qū)大小的限制,我們無(wú)法一次性傳輸整個(gè)文件,所以我們將文件按緩存區(qū)大小拆分成數(shù)據(jù)包分批發(fā)送,我們可以將數(shù)據(jù)及時(shí)從緩存區(qū)寫入文件,這樣就讓出了緩存區(qū)空間。數(shù)據(jù)包僅僅包含數(shù)據(jù),不包含頭部或尾部信息。
此外,接收文件時(shí),recv()函數(shù)將會(huì)循環(huán)調(diào)用,因此,我們需要一個(gè)信號(hào)來(lái)通知什么時(shí)候發(fā)送完畢。
一個(gè)想法是發(fā)送終止信號(hào),這是可行的,但更好的方法是在一開始發(fā)送文件總字節(jié)數(shù),讓接收方根據(jù)剩余字節(jié)大小判斷什么時(shí)候接收完畢。因?yàn)樵趯懭胛募r(shí),我們需要指定寫入的字節(jié)數(shù),尤其是在發(fā)來(lái)的數(shù)據(jù)流字節(jié)數(shù)不等于緩沖區(qū)大小時(shí)。寫入字節(jié)數(shù)的錯(cuò)誤會(huì)導(dǎo)致文件受損。
接收確認(rèn)
我們知道 TCP 是可靠傳輸協(xié)議,它采取了一系列措施來(lái)保證傳輸不會(huì)出錯(cuò)。所以在使用 TCP 連接時(shí),我們相信數(shù)據(jù)在鏈路層上沒有出差錯(cuò),它一定會(huì)成功發(fā)送到對(duì)方手上。但是在客戶端接收服務(wù)器發(fā)來(lái)的文件的時(shí)候,我們?nèi)匀恍枰?wù)器發(fā)來(lái)確認(rèn)信息。原因在于,雖然我們可以保證鏈路層不出錯(cuò),但是我們無(wú)法保證應(yīng)用層不出錯(cuò)。例如,客戶端可能會(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客戶端,您可以輸入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 -- 顯示服務(wù)器當(dāng)前工作文件夾\n"
"cd [dirName] -- 更改當(dāng)前文件夾\n"
"close -- 關(guān)閉與當(dāng)前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)
{
//創(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;
//打開文件
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:字符串末沒有斜線??!
int serverSocket; //socket
int clientSocket;
bool sendFile();
bool receiveFile();
bool doPwd();
bool doCd();
bool isValidPath(char* path);
public:
ftpServer();
bool start();//開啟服務(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)聽
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ǔ)言開發(fā)環(huán)境的詳細(xì)過(guò)程
這篇文章主要介紹了windows下在vim中搭建c語(yǔ)言開發(fā)環(huán)境,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-05-05
C語(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-07
C語(yǔ)言實(shí)現(xiàn)數(shù)組元素排序方法詳解
這篇文章主要為大家介紹了C語(yǔ)言算法練習(xí)中數(shù)組元素排序的實(shí)現(xiàn)方法,文中的示例代碼講解詳細(xì),對(duì)我們學(xué)習(xí)C語(yǔ)言有一定幫助,需要的可以參考一下2023-02-02
c與c++之間的相互調(diào)用及函數(shù)區(qū)別示例詳解
這篇文章主要為大家介紹了c與c++相互調(diào)用的使用示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-06-06

