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

服务端:

建立连接
连接使用 TCP 连接,服务器和客户端分别创建自己的套接字一端,服务器等待连接,客户端发起连接(并指定服务器 ip)。在两者端口号一致且不被占用的情况下,连接建立。
在整个过程中,服务器对每一个来访的客户端建立一个连接,在客户未请求与服务器断开时,该连接一直存在,用户可以不断向服务器发出请求。(持久性、流水线型连接 )
客户端断开后,关闭客户端的套接字部分,服务器继续等待新的连接。服务器一次只能处理一个客户端的连接,不支持并发访问。
PDU 格式
由于 ftp 应当支持几乎任意类型文件,而几乎所有类型文件都能用二进制来解析,所以我们采用了二进制的格式来读取以及写入文件。在整个过程中,我们并不关心文件的具体内容,也无需在程序中解析文件,而是将其当作数据流看待。
受到缓存区大小的限制,我们无法一次性传输整个文件,所以我们将文件按缓存区大小拆分成数据包分批发送,我们可以将数据及时从缓存区写入文件,这样就让出了缓存区空间。数据包仅仅包含数据,不包含头部或尾部信息。
此外,接收文件时,recv()函数将会循环调用,因此,我们需要一个信号来通知什么时候发送完毕。
一个想法是发送终止信号,这是可行的,但更好的方法是在一开始发送文件总字节数,让接收方根据剩余字节大小判断什么时候接收完毕。因为在写入文件时,我们需要指定写入的字节数,尤其是在发来的数据流字节数不等于缓冲区大小时。写入字节数的错误会导致文件受损。
接收确认
我们知道 TCP 是可靠传输协议,它采取了一系列措施来保证传输不会出错。所以在使用 TCP 连接时,我们相信数据在链路层上没有出差错,它一定会成功发送到对方手上。但是在客户端接收服务器发来的文件的时候,我们仍然需要服务器发来确认信息。原因在于,虽然我们可以保证链路层不出错,但是我们无法保证应用层不出错。例如,客户端可能会给出错误的文件名,因为接收不到服务器发来的信息,所以会陷入空等状态。
ftpClient.h
?
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
#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
?| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 |
#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
?| 1 2 3 4 5 6 7 8 9 10 11 12 |
#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
?| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
#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
|
?
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 |
#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
?| 1 2 3 4 5 6 7 |
#include"ftpServer.h"
#pragma(lib,"ws2_32.lib")
int main()
{
ftpServer f;
f.start();
}
|
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持服务器之家。








发表评论
◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。