c/c++开发分享C++实现FTP综合应用详解为大家分享了ftp综合应用编程(c++),供大家参考,具体内容如下
1.学校实验
借鉴了网上一位大佬的作品,然后自己改改拿来完成了算是还行的作品。
代码一共大概是900多行,可以直接粘贴下来看看,其实还是很容易理解的。
运行的截图附上:
客户端:
服务器端:
2.服务器端代码
头文件:(sizes.h)
#pragma once //服务器侦听控制连接请求的端口 #define cmd_port 5858 //客户机侦听数据连接请求的端口 #define data_port 5850 //命令报文参数缓存的大小 #define cmd_param_size 256 //回复报文消息缓存的大小 #define rspns_text_size 256 #define backlog 10 #define data_bufsize 4096 //命令类型 typedef enum { ls, pwd, cd, down, up, quit } cmdid; //命令报文,从客户端发往服务器 typedef struct _cmdpacket { cmdid cmdid; char param[cmd_param_size]; } cmdpacket; //回复报文的类型 typedef enum { ok, err } rspnsid; //回复报文,从服务器发往客户端 typedef struct _rspnspacket { rspnsid rspnsid; char text[rspns_text_size]; } rspnspacket;
源文件:(服务器端.cpp)
#include <winsock2.h> #include "sizes.h" #include <iostream> #pragma comment(lib, "ws2_32.lib") //创建线程时传递的数据结构,内含控制连接套接字和客户端地址信息: struct threaddata { socket tcps; sockaddr_in clientaddr; }; //全局函数声明: //ftp初始化,创建一个侦听套接字: int initftp(socket *plistensock); int initdatasocket(socket *pdatatcps, sockaddr_in *pclientaddr); int processcmd(socket tcps, cmdpacket* pcmd, sockaddr_in *pclientaddr); int sendrspns(socket tcps, rspnspacket* prspns); int recvcmd(socket tcps, char* pcmd); int sendfilelist(socket datatcps); int sendfilerecord(socket datatcps, win32_find_data* pfd); int sendfile(socket datatcps, file* file); int recvfile(socket datatcps, char* filename); int fileexists(const char *filename); //线程函数,参数包括相应控制连接的套接字: dword winapi threadfunc(lpvoid lpparam) { socket tcps; sockaddr_in clientaddr; tcps = ((struct threaddata *)lpparam)->tcps; clientaddr = ((struct threaddata *)lpparam)->clientaddr; printf("socket的编号是:%u.n", tcps); //发送回复报文给客户端,内含命令使用说明: printf("serve client %s:%dn", inet_ntoa(clientaddr.sin_addr), ntohs(clientaddr.sin_port)); rspnspacket rspns = { ok, "欢迎进入ftp综合应用系统!n" "你可以使用的命令:n" "lst<展示当前目录下的文件(夹),无需参数>n" "pwdt<展示当前目录的绝对路径,无需参数>n" "cdt<切换到指定目录,参数为路径>n" "downt<下载文件,参数为文件名>n" "upt<上传文件,参数为文件名>n" "quitt<退出系统,无需参数>n" }; sendrspns(tcps, &rspns); //循环获取客户端命令报文并进行处理 for (;;) { cmdpacket cmd; if (!recvcmd(tcps, (char *)&cmd)) break; if (!processcmd(tcps, &cmd, &clientaddr)) break; } //线程结束前关闭控制连接套接字: closesocket(tcps); delete lpparam; return 0; } int main(int argc, char* argv[]) { socket tcps_listen; //ftp服务器控制连接侦听套接字 struct threaddata *pthinfo; if (!initftp(&tcps_listen)) //ftp初始化 return 0; printf("ftp服务器开始监听,端口号为:%d。。。。。。n", cmd_port); //循环接受客户端连接请求,并生成线程去处理: for (;;) { pthinfo = null; pthinfo = new threaddata; if (pthinfo == null) { printf("为新线程申请空间失败。n"); continue; } int len = sizeof(struct threaddata); //等待接受客户端控制连接请求 pthinfo->tcps = accept(tcps_listen, (sockaddr*)&pthinfo->clientaddr, &len); //创建一个线程来处理相应客户端的请求: dword dwthreadid, dwthrdparam = 1; handle hthread; hthread = createthread( null, //无需安全性的继承 0, //默认线程栈大小 threadfunc, //线程入口函数 pthinfo, //线程入口函数的参数 0, //立即启动线程 &dwthreadid); //返回线程的id值 //检查返回值是否创建线程成功 if (hthread == null) { printf("创建线程失败。n"); closesocket(pthinfo->tcps); delete pthinfo; } } return 0; } //ftp初始化,创建一个侦听套接字: int initftp(socket *plistensock) { //按照此步骤创建新的服务器端套接字,嗯,没错,前三个都是这个步骤 //startup->socket->bind->listen word wversionrequested; wsadata wsadata; int err; socket tcps_listen; wversionrequested = makeword(2, 2); err = wsastartup(wversionrequested, &wsadata); if (err != 0) { printf("winsock初始化时发生错误!n"); return 0; } if (lobyte(wsadata.wversion) != 2 || hibyte(wsadata.wversion) != 2) { wsacleanup(); printf("无效winsock版本!n"); return 0; } tcps_listen = socket(af_inet, sock_stream, ipproto_tcp); if (tcps_listen == invalid_socket) { wsacleanup(); printf("创建socket失败!n"); return 0; } sockaddr_in tcpaddr; tcpaddr.sin_family = af_inet; tcpaddr.sin_port = htons(cmd_port); tcpaddr.sin_addr.s_un.s_addr = htonl(inaddr_any); err = bind(tcps_listen, (sockaddr*)&tcpaddr, sizeof(tcpaddr)); if (err != 0) { err = wsagetlasterror(); wsacleanup(); printf("scoket绑定时发生错误!n"); return 0; } err = listen(tcps_listen, 3); if (err != 0) { wsacleanup(); printf("scoket监听时发生错误!n"); return 0; } *plistensock = tcps_listen; return 1; } //建立数据连接 //pdatatcps:用于存储数据连接套接字 //pclientaddr:指向客户端的控制连接套接字地址,需要使用其中的ip地址 //返回值:0表示失败,1正常 int initdatasocket(socket *pdatatcps, sockaddr_in *pclientaddr) { socket datatcps; //创建socket datatcps = socket(af_inet, sock_stream, ipproto_tcp); if (datatcps == invalid_socket) { printf("creating data socket failed!n"); return 0; } sockaddr_in tcpaddr; memcpy(&tcpaddr, pclientaddr, sizeof(sockaddr_in)); tcpaddr.sin_port = htons(data_port); //如若有什么意外只需要在头文件修改端口值 //请求连接客户端 if (connect(datatcps, (sockaddr*)&tcpaddr, sizeof(tcpaddr)) == socket_error) { printf("connecting to client failed!n"); closesocket(datatcps); return 0; } *pdatatcps = datatcps; return 1; } //处理命令报文 //tcps:控制连接套接字 //pcmd:指向待处理的命令报文 //pclientaddr:指向客户端控制连接套接字地址 //返回值:0表示有错或者需要结束连接,1正常 int processcmd(socket tcps, cmdpacket* pcmd, sockaddr_in *pclientaddr) { socket datatcps; //数据连接套接字 rspnspacket rspns; //回复报文 file* file; //根据命令类型分派执行: switch (pcmd->cmdid) { case ls://展示当前目录下的文件列表 //首先建立数据连接: if (!initdatasocket(&datatcps, pclientaddr)) return 0; //发送文件列表信息: if (!sendfilelist(datatcps)) return 0; break; case pwd://展示当前目录的绝对路径 rspns.rspnsid = ok; //获取当前目录,并放至回复报文中 if (!getcurrentdirectory(rspns_text_size, rspns.text)) strcpy(rspns.text, "can't get current dir!n"); if (!sendrspns(tcps, &rspns)) return 0; break; case cd://设置当前目录,使用win32 api 接口函数 if (setcurrentdirectory(pcmd->param)) { rspns.rspnsid = ok; if (!getcurrentdirectory(rspns_text_size, rspns.text)) strcpy(rspns.text, "切换当前目录成功!但是不能获取到当前的文件列表!n"); } else { strcpy(rspns.text, "不能更换到所选目录!n"); } if (!sendrspns(tcps, &rspns)) //发送回复报文 return 0; break; case down://处理下载文件请求: file = fopen(pcmd->param, "rb"); //打开要下载的文件 if (file) { rspns.rspnsid = ok; sprintf(rspns.text, "下载文件%sn", pcmd->param); if (!sendrspns(tcps, &rspns)) { fclose(file); return 0; } else { //创建额外的数据连接来传送数据: if (!initdatasocket(&datatcps, pclientaddr)) { fclose(file); return 0; } if (!sendfile(datatcps, file)) return 0; fclose(file); } } else //打开文件失败 { rspns.rspnsid = err; strcpy(rspns.text, "不能打开文件!n"); if (!sendrspns(tcps, &rspns)) return 0; } break; case up://处理上传文件请求 //首先发送回复报文 char filename[64]; strcpy(filename, pcmd->param); //首先看一下服务器上是否已经有这个文件里,如果有就告诉客户端不用传输了 if (fileexists(filename)) { rspns.rspnsid = err; sprintf(rspns.text, "服务器已经存在免费精选名字大全为%s的文件!n", filename); if (!sendrspns(tcps, &rspns)) return 0; } else { rspns.rspnsid = ok; if (!sendrspns(tcps, &rspns)) return 0; //另建立一个数据连接来接受数据: if (!initdatasocket(&datatcps, pclientaddr)) return 0; if (!recvfile(datatcps, filename)) return 0; } break; case quit: printf("客户端断开连接。n"); rspns.rspnsid = ok; strcpy(rspns.text, "常来啊!n"); sendrspns(tcps, &rspns); return 0; } return 1; } //发送回复报文 int sendrspns(socket tcps, rspnspacket* prspns) { if (send(tcps, (char *)prspns, sizeof(rspnspacket), 0) == socket_error) { printf("与客户端失去连接。n"); return 0; } return 1; } //接收命令报文 //tcps:控制连接套接字 //pcmd:用于存储返回的命令报文 //返回值:0表示有错或者连接已经断开,1表示正常 int recvcmd(socket tcps, char* pcmd) { //used to receive command from client int nret; int left = sizeof(cmdpacket); //从控制连接中读取数据,大小为 sizeof(cmdpacket): while (left) { nret = recv(tcps, pcmd, left, 0); if (nret == socket_error) { printf("从客户端接受命令时发生未知错误!n"); return 0; } if (!nret) { printf("客户端关闭了连接!n"); return 0; } left -= nret; pcmd += nret; } return 1; //成功获取命令报文 } //发送一项文件信息: int sendfilerecord(socket datatcps, win32_find_data* pfd) { //used to send response to client char filerecord[max_path + 32]; filetime ft; filetimetolocalfiletime(&pfd->ftlastwritetime, &ft); systemtime lastwtime; filetimetosystemtime(&ft, &lastwtime); char* dir = (char*)(pfd->dwfileattributes&file_attribute_directory ? "<dir>" : ""); sprintf(filerecord, "%04d-%02d-%02d%02d:%02d %5s %10d %-20sn", lastwtime.wyear, lastwtime.wmonth, lastwtime.wday, lastwtime.whour, lastwtime.wminute, dir, pfd->nfilesizelow, pfd->cfilename); if (send(datatcps, filerecord, strlen(filerecord), 0) == socket_error) { printf("发送文件列表时发生未知错误!n"); return 0; } return 1; } //发送文件列表信息 //datatcps:数据连接套接字 //返回值:0表示出错,1表示正常 int sendfilelist(socket datatcps) { handle hff; win32_find_data fd; //搜索文件 hff = findfirstfile("*", &fd); if (hff == invalid_handle_value) //发生错误 { const char* errstr = "不能列出文件!n"; printf("文件列表输出失败!n"); if (send(datatcps, errstr, strlen(errstr), 0) == socket_error) { printf("发送给文件列表时发生未知错误!n"); } closesocket(datatcps); return 0; } bool fmorefiles = true; while (fmorefiles) { //发送此项文件信息: if (!sendfilerecord(datatcps, &fd)) { closesocket(datatcps); return 0; } //搜索下一个文件 fmorefiles = findnextfile(hff, &fd); } closesocket(datatcps); return 1; } //通过数据连接发送文件 int sendfile(socket datatcps, file* file) { char buf[1024]; printf("发送文件数据中。。。。。。"); for (;;) { //从文件中循环读取数据并发送客户端 int r = fread(buf, 1, 1024, file); if (send(datatcps, buf, r, 0) == socket_error) { printf("与客户端失去连接!n"); closesocket(datatcps); return 0; } if (r < 1024) //文件传输结束 { break; } } closesocket(datatcps); printf("完成传输!n"); return 1; } //接收文件 //datatcps:数据连接套接字,通过它来接收数据 //filename:用于存放数据的文件名 int recvfile(socket datatcps, char* filename) { char buf[1024]; file* file = fopen(filename, "wb"); if (!file) { printf("写入文件时发生未知错误!n"); fclose(file); closesocket(datatcps); return 0; } printf("接受文件数据中。。。。。。"); while (1) { int r = recv(datatcps, buf, 1024, 0); if (r == socket_error) { printf("从客户端接受文件时发生未知错误!n"); fclose(file); closesocket(datatcps); return 0; } if (!r) { break; } fwrite(buf, 1, r, file); } fclose(file); closesocket(datatcps); printf("完成传输!n"); return 1; } //检测文件是否存在: int fileexists(const char *filename) { win32_find_data fd; if (findfirstfile(filename, &fd) == invalid_handle_value) return 0; return 1; }
3.客户端
头文件:(sizes.h)
#pragma once //服务器侦听控制连接请求的端口 #define cmd_port 5858 //客户机侦听数据连接请求的端口 #define data_port 5850 //命令报文参数缓存的大小 #define cmd_param_size 256 //回复报文消息缓存的大小 #define rspns_text_size 256 #define backlog 10 #define data_bufsize 4096 //命令类型 typedef enum { ls, pwd, cd, down, up, quit } cmdid; //命令报文,从客户端发往服务器 typedef struct _cmdpacket { cmdid cmdid; char param[cmd_param_size]; } cmdpacket; //回复报文的类型 typedef enum { ok, err } rspnsid; //回复报文,从服务器发往客户端 typedef struct _rspnspacket { rspnsid rspnsid; char text[rspns_text_size]; } rspnspacket;
源文件:(客户端.cpp)
#include <winsock2.h> #include <windows.h> #include "sizes.h" #include <tchar.h> #include <iostream> #pragma comment(lib, "ws2_32.lib") //读取回复报文 void do_read_rspns(socket fd, rspnspacket *ptr) { int count = 0; int size = sizeof(rspnspacket); while (count < size) { int nread = recv(fd, (char *)ptr + count, size - count, 0); if (nread <= 0) { printf("读取服务器的回复失败!n"); closesocket(fd); exit(1); } count += nread; } } //发送命令报文 void do_write_cmd(socket fd, cmdpacket *ptr) { int size = sizeof(cmdpacket); int flag = send(fd, (char *)ptr, size, 0); if (flag == socket_error) { printf("给服务器发送命令失败!n"); closesocket(fd); wsacleanup(); exit(1); } } //创建数据连接套接字并进入侦听状态 socket create_data_socket() { socket sockfd; struct sockaddr_in my_addr; //创建用于数据连接的套接字 if ((sockfd = socket(af_inet, sock_stream, ipproto_tcp)) == invalid_socket) { printf("创建用于数据连接的套接字失败!n"); wsacleanup(); exit(1); } my_addr.sin_family = af_inet; my_addr.sin_port = htons(data_port); my_addr.sin_addr.s_addr = htonl(inaddr_any); memset(&(my_addr.sin_zero), 0, sizeof(my_addr.sin_zero)); //绑定 if (bind(sockfd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr)) == socket_error) { int err = wsagetlasterror(); printf("绑定地址失败,错误代码:%dn", err); closesocket(sockfd); wsacleanup(); exit(1); } //侦听数据连接请求 if (listen(sockfd, 1) == socket_error) { printf("监听数据连接失败!n"); closesocket(sockfd); wsacleanup(); exit(1); } return sockfd; } //处理list命令 void list(socket sockfd) { int sin_size; int nread; cmdpacket cmd_packet; socket newsockfd, data_sockfd; struct sockaddr_in their_add; char data_buf[data_bufsize]; //创建数据连接 newsockfd = create_data_socket(); //构建命令报文并发送至服务器 cmd_packet.cmdid = ls;//没有参数 do_write_cmd(sockfd, &cmd_packet); sin_size = sizeof(struct sockaddr_in); //接受服务器的数据连接请求 if ((data_sockfd = accept(newsockfd, (struct sockaddr*)&their_add, &sin_size)) == invalid_socket) { printf("获取文件列表失败!n"); closesocket(newsockfd); closesocket(sockfd); wsacleanup(); exit(1); } //每次读到多少数据就显示多少,直到数据连接断开 while (true) { nread = recv(data_sockfd, data_buf, data_bufsize - 1, 0); if (nread == socket_error) { printf("读取服务器回复失败!n"); closesocket(data_sockfd); closesocket(newsockfd); closesocket(sockfd); wsacleanup(); exit(1); } if (nread == 0)//数据读取结束 break; //显示数据 data_buf[nread] = ' '; printf("%s", data_buf); } closesocket(data_sockfd); closesocket(newsockfd); } //处理pwd命令: void pwd(int sockfd) { cmdpacket cmd_packet; rspnspacket rspns_packet; cmd_packet.cmdid = pwd; //发送命令报文并读取回复: do_write_cmd(sockfd, &cmd_packet); do_read_rspns(sockfd, &rspns_packet); printf("%sn", rspns_packet.text); } //处理cd命令: void cd(int sockfd) { cmdpacket cmd_packet; rspnspacket rspns_packet; cmd_packet.cmdid = cd; scanf("%s", cmd_packet.param); //发送命令报文并读取回复: do_write_cmd(sockfd, &cmd_packet); do_read_rspns(sockfd, &rspns_packet); if (rspns_packet.rspnsid == err) printf("%s", rspns_packet.text); } //处理down命令,即下载文件: void get_file(socket sockfd) { file *fd; char data_buf[data_bufsize]; cmdpacket cmd_packet; rspnspacket rspns_packet; socket newsockfd, data_sockfd; struct sockaddr_in their_addr; int sin_size; int count; //设置命令报文: cmd_packet.cmdid = down; scanf("%s", cmd_packet.param); //打开或者创建本地文件以供写数据: fd = fopen(cmd_packet.param, "wb");//使用二进制方程 if (fd == null) { printf("打开文件%s来写入失败!n", cmd_packet.param); return; } //创建数据连接并侦听服务器的连接请求: newsockfd = create_data_socket(); //发送报文请求: do_write_cmd(sockfd, &cmd_packet); //读取回复报文: do_read_rspns(sockfd, &rspns_packet); if (rspns_packet.rspnsid == err) { printf("%s", rspns_packet.text); closesocket(newsockfd); fclose(fd); //删除文件: deletefile(cmd_packet.param); return; } sin_size = sizeof(struct sockaddr_in); //等待接受服务器的连接请求 if ((data_sockfd = accept(newsockfd, (struct sockaddr *)&their_addr, &sin_size)) == invalid_socket) { printf("获取文件失败!n"); closesocket(newsockfd); fclose(fd); //删除文件: deletefile(cmd_packet.param); return; } //循环读取网络数据并写入文件: while ((count = recv(data_sockfd, data_buf, data_bufsize, 0)) > 0) fwrite(data_buf, sizeof(char), count, fd); closesocket(data_sockfd); closesocket(newsockfd); fclose(fd); } //处理put命令,即上传文件 void put_file(socket sockfd) { file *fd; cmdpacket cmd_packet; rspnspacket rspns_packet; char data_buf[data_bufsize]; socket newsockfd, data_sockfd; struct sockaddr_in their_addr; int sin_size; int count; cmd_packet.cmdid = up; scanf("%s", cmd_packet.param); //打开本地文件用于读取数据 fd = fopen(cmd_packet.param, "rb"); if (fd == null) { printf("打开文件%s来读取数据失败!n", cmd_packet.param); return; } //创建数据连接套接字并进入侦听状态; newsockfd = create_data_socket(); //发送命令报文 do_write_cmd(sockfd, &cmd_packet); //读取回复报文 do_read_rspns(sockfd, &rspns_packet); if (rspns_packet.rspnsid == err) { printf("%s", rspns_packet.text); closesocket(newsockfd); fclose(fd); return; } sin_size = sizeof(struct sockaddr_in); //准备接受数据连接 if ((data_sockfd = accept(newsockfd, (struct sockaddr *)&their_addr, &sin_size)) == invalid_socket) { printf("上传文件传输错误!n"); closesocket(newsockfd); fclose(fd); return; } //循环从文件中读取数据并发给服务器 while (true) { count = fread(data_buf, sizeof(char), data_bufsize, fd); send(data_sockfd, data_buf, count, 0); if (count < data_bufsize)//数据已经读完或者发生cuowu break; } closesocket(data_sockfd); closesocket(newsockfd); fclose(fd); } //处理退出命令 void quit(int sockfd) { cmdpacket cmd_packet; rspnspacket rspns_packet; cmd_packet.cmdid = quit; do_write_cmd(sockfd, &cmd_packet); do_read_rspns(sockfd, &rspns_packet); printf("%s", rspns_packet.text); getchar(); } void main() { socket sockfd; struct sockaddr_in their_addr; char cmd[10]; rspnspacket rspns_packet; word wversionrequested; wsadata wsadata; int err; wversionrequested = makeword(2, 2); //winsock初始化 err = wsastartup(wversionrequested, &wsadata); if (err != 0) { printf("winsock初始化失败!n"); return; } //确认windsock dll的版本是2.2 if (lobyte(wsadata.wversion) != 2 || hibyte(wsadata.wversion) != 2) { printf("windsock版本不是2.2!n"); wsacleanup(); return; } //创建用于控制谅解的socket sockfd = socket(af_inet, sock_stream, ipproto_tcp); if (sockfd == invalid_socket) { printf("创建套接字失败!n"); wsacleanup(); exit(1); } their_addr.sin_family = af_inet; their_addr.sin_port = htons(cmd_port); their_addr.sin_addr.s_un.s_addr = inet_addr("127.0.0.1"); memset(&(their_addr.sin_zero), 0, sizeof(their_addr.sin_zero)); //连接服务器 if (connect(sockfd, (struct sockaddr*)&their_addr, sizeof(struct sockaddr)) == socket_error) { printf("连接服务器失败!n"); closesocket(sockfd); wsacleanup(); exit(1); } //连接成功后,首先接受服务器发回的消息 do_read_rspns(sockfd, &rspns_packet); printf("%s", rspns_packet.text); //主循环:读取用户输入并分配执行 while (true) { scanf("%s", cmd); switch (cmd[0]) { case 'l'://处理list命令 list(sockfd); break; case 'p'://处理pwd命令 pwd(sockfd); break; case 'c'://处理cd命令 cd(sockfd); break; case 'd'://处理down命令 get_file(sockfd); break; case 'u'://处理up命令 put_file(sockfd); break; case 'q'://处理quit命令 quit(sockfd); break; default: printf("不存在的命令!n"); break; } if (cmd[0] == 'q') break; } wsacleanup(); }
以上就是c/c++开发分享C++实现FTP综合应用详解的全部内容,希望对大家的学习有所帮助,也希望大家多多支持<计算机技术网(www.ctvol.com)!!>。
需要了解更多c/c++开发分享C++实现FTP综合应用详解,都可以关注C/C++技术分享栏目—计算机技术网(www.ctvol.com)!
本文来自网络收集,不代表计算机技术网立场,如涉及侵权请联系管理员删除。
ctvol管理联系方式QQ:251552304
本文章地址:https://www.ctvol.com/c-cdevelopment/1186052.html