大二期间学习计算机网络时,做的实验:
本课程设计主要说明了在windows下如何用c语言编写设计ftp服务器和客户端的基本方法及其实现。包含了TCP/IP协议基本通信原理,Winsock编程基本原理以及ftp工作原理。
关键词:TCP/IP Winsock ftp 文件传输
文件传输源程序代码
(1)客户端client程序代码 ---- client.cpp
(1)客户端client程序代码 ---- client.cpp
#include "Winsock.h"
#include "windows.h"
#include "stdio.h"
#include "time.h"
#include <iostream.h>
#define RECV_PORT 3312 //定义接收端口
#define SEND_PORT 4302 //定义发送端口
#pragma comment(lib, "wsock32.lib") //加载 ws2_32.dll
SOCKET sockclient; //建立客户端对象
char filename[20]; //文件名
sockaddr_in ServerAddr; //服务器地址
char rbuff[1024]; //接收缓冲区
char sbuff[1024]; //发送缓冲区
char InputIP[20]; //存储输入的服务器IP
void help() { //处理help命令
cout<<" 欢迎进入迷你FTP帮助菜单 "<<endl
<<" * * * * * * * * * * * * * * * * * * * * * "<<endl
<<" 1.get....................下载(接受)文件 "<<endl
<<" get的用法: get 文件名 "<<endl<<endl
<<" 2.put....................上传(发送)文件 "<<endl
<<" put的用法:put 文件名 "<<endl
<<" 3.pwd..........显示当前文件夹的绝对路径 "<<endl
<<" 4.dir............显示远方当前目录的文件 "<<endl<<endl
<<" 5.cd.............改变远方当前目录和路径 "<<endl
<<" cd的用法(进入下级目录): cd 路径名 "<<endl
<<" cd的用法(进入上级目录): cd .. "<<endl<<endl
<<" 6.?或者help................进入帮助菜单 "<<endl
<<" 7.quit..........................退出FTP "<<endl
<<" * * * * * * * * * * * * * * * * * * * * * "<<endl;
}
/************************dir:列出远方当前目录**********************/
void list(SOCKET sockfd)
{
int nRead;
while(true)
{
nRead=recv(sockclient,rbuff,1024,0);
//recv函数通过sockclient套接口接受数据存入rbuff缓冲区,返回接受到的字节数
if(nRead==SOCKET_ERROR)
{ printf("read response error!\n");
exit(1);
}
if(nRead==0)//数据读取结束
break;
//显示数据
rbuff[nRead]='\0';
printf("%s",rbuff);
}
}
/*********************** put:传给远方一个文件***************************/
int SendFile(SOCKET datatcps,FILE* file)
{
printf(" sending file data..");
for(;;) //从文件中循环读取数据并发送客户端
{
int r=fread(sbuff,1,1024,file);//fread函数从file文件读取1个1024长度的数据到sbuff,返回成功读取的元素个数
if(send(datatcps,sbuff,r,0)==SOCKET_ERROR)
{
printf("lost the connection to client!\n");
closesocket(datatcps);
return 0;
}
if(r<1024) //文件传送结束
break;
}
closesocket(datatcps);
printf("done\n");
return 1;
}
DWORD StartSock() //启动winsock,初始化winsock
{
WSADATA WSAData;
char a[20];
memset(a,0,20);
if(WSAStartup(MAKEWORD(2,2),&WSAData)!=0)//加载winsock版本
{
printf("sock init fail!\n");
return (-1); }
if(strncmp(InputIP,a,20)==0)
{
printf("请输入连接的主机IP:");
scanf("%s",&InputIP);
}
//设置地址结构
ServerAddr.sin_family=AF_INET;//AF_INET表示使用IP地址族
ServerAddr.sin_addr.s_addr=inet_addr(InputIP);//指定服务器IP
ServerAddr.sin_port=htons(RECV_PORT);//设置端口号
return(1);
}
建立连接,并进行相应的命令操作,get/put/pwd/cd/..
DWORD CreateSocket()
{
sockclient=socket(AF_INET,SOCK_STREAM,0);//当socket函数成功调用时返回一个新的SOCKET(Socket Descriptor)
if(sockclient==SOCKET_ERROR)
{
printf("sockclient create fail! \n");
WSACleanup();
return(-1);
}
return(1);
}
DWORD CallServer() //发送连接请求
{
CreateSocket();
if(connect(sockclient,(struct sockaddr*)&ServerAddr,sizeof(ServerAddr)) ==SOCKET_ERROR) //connect函数创建与指定外部端口的连接
{
printf("Connect fail \n");
memset(InputIP,0,20);
`return(-1);
}
return(1);
}
DWORD TCPSend(char data[]) //发送命令
{
int length;
length=send(sockclient,data,strlen(data),0); //send函数通过sockclient接口发送data里面的数据,发送成功返回发送的字节数
if(length<=0)
{
printf("send data error ! \n");
closesocket(sockclient);
WSACleanup(); //清除记录
return(-1);
}
return(1);
}
int main()
{
char messge1[10]; //定义输入要处理的文件名
char messge2[20]; //定义输入要处理的文件名
char order[30]; //输入的命令
order[0]='\0';
char buff[80]; //用以存储经过字串格式化的order
FILE *fd; //File协议主要用于访问本地计算机中的文件,fd指针指向要访问的目标文件
FILE *fd2;
int count;
int sin_size=sizeof(ServerAddr);
StartSock();
if(CallServer()==-1)
return main(); //发送连接请求失败,返回主函数
printf("\n请输入命令(输入?或help进入帮助菜单):\n");
memset(buff,0,80); //清空数组
memset(messge2,0,20); //清空数组
memset(order,0,30); //清空数组
memset(messge1,0,10);
memset(rbuff,0,1024);
memset(sbuff,0,1024);
scanf("%s",&messge1);
//原型:extern int strcmp(char *s1,char * s2,int n);
// strncmp函数功能:比较字符串s1和s2的前n个字符
if(strncmp(messge1,"get",3)==0)
scanf("%s",&messge2);
if(strncmp(messge1,"put",3)==0)
scanf("%s",&messge2);
if(strncmp(messge1,"cd",2)==0)
scanf("%s",&messge2);
strcat(order,messge1); //把messge1加在order的末尾
strcat(order," "); //命令中间的空格
strcat(order,messge2); //把messge2加在order的末尾
sprintf(buff,order); //把调整格式的order存入buff
//help和?
if(strncmp(messge1,"help",4)==0) {
help();
}
if(strncmp(messge1,"?",1)==0){
help();
}
if(strncmp(messge1,"quit",4)==0)
{
printf(" 欢迎再次进入迷你FTP,谢谢使用!\n");
closesocket(sockclient);
WSACleanup();
return 0;
}
TCPSend(buff); //发送buff里面的数据
recv(sockclient,rbuff,1024,0);
printf(rbuff);
if(strncmp(rbuff,"get",3)==0) //get
{
fd=fopen(messge2,"wb"); //使用二进制方式,打开文件,wb只写打开或新 建一个二进制文件;只允许写数据。
if(fd==NULL)
{
printf("open file %s for weite failed!\n",messge2);
return 0;
}
while((count=recv(sockclient,rbuff,1024,0))>0)
{
fwrite(rbuff,sizeof(rbuff),count,fd);
}
//把count个数据长度为size0f()的数据从 rbuff输入到fd指向的目标文件
fclose(fd); //关闭文件
}
if(strncmp(rbuff,"put",3)==0) //put
{
strcpy(filename,rbuff+9);
fd2=fopen(filename,"rb");//rb读写打开一个二进制文件,只允许读写数据。
if(fd2)
{
if(!SendFile(sockclient,fd2)){
printf("send failed!");
return 0;
}
fclose(fd2);}//关闭文件
else//打开文件失败
{
strcpy(sbuff,"can't open file!\n");
if(send(sockclient,sbuff,1024,0))
return 0;
}
}
if(strncmp(rbuff,"dir",3)==0) //dir
{
printf("\n");
list(sockclient); //列出接受到的列表内容
}
if(strncmp(rbuff,"pwd",3)==0)
{
list(sockclient); //列出接受到的内容--绝对路径
}
if(strncmp(rbuff,"cd",2)==0){ } //cd
closesocket(sockclient); //关闭连接
WSACleanup(); //释放Winsock
return main();
}
#include "Winsock.h"
#include "windows.h"
#include "stdio.h"
#define RECV_PORT 3312
#define SEND_PORT 4302
#pragma comment(lib, "wsock32.lib") //windows下socket依赖 ws2_32.dll,必须提前加载
//加载 ws2_32.dll,支持所有的规范
//最新的DLL是 ws2_32.dll,大小为 69KB,对应的头文件为 winsock2.h。
//Winsock网络应用程序利用API 函数(如accept、send、recv等函数)进行I/O操作时有阻塞和非阻塞两种模式。
SOCKET sockclient,sockserver; //定义socket对象
sockaddr_in ServerAddr; //服务器地址
sockaddr_in ClientAddr; //客户端地址
/*******************************全局变量***********************************/
int Addrlen; //地址长度
char filename[20]; //文件名
char order[10]; //命令
char rbuff[1024]; //接收缓冲区
char sbuff[1024]; //发送缓冲区
DWORD StartSock() //初始化winsock,Winsock是Windows下的网络编程接口
{
//每个Winsock程序必须使用WSAStartup载入合适的Winsock动态链接库,如果载入失败,WSAStartup将返回SOCKET_ERROR,
//WSAStartup(MAKEWORD(2, 2), &wsaData); //主版本号为2,副版本号为2,返回 0x0202
//WSAStartup() 函数执行成功后,会将与 ws2_32.dll 有关的信息写入 WSAData 结构体变量
//WinSock 编程的第一步就是加载 ws2_32.dll,然后调用 WSAStartup() 函数进行初始化,并指明要使用的版本号。
WSADATA WSAData;
if(WSAStartup(MAKEWORD(2,2),&WSAData)!=0)
{
printf("socket init fail!\n");
return (-1);
}
return(1);
}
DWORD CreateSocket()
{
//1.创建套接字
sockclient=socket(AF_INET,SOCK_STREAM,0);
//每个 socket 被创建后,都会分配两个缓冲区,输入缓冲区和输出缓冲区。
// IP 地址类型,常用的有 AF_INET 和 AF_INET6。AF_INET 表示 IPv4 地址,例如 127.0.0.1;AF_INET6 表示 IPv6 地址,例如 1030::C9B4:FF12:48AA:1A2B。
if(sockclient==SOCKET_ERROR)
{
printf("sockclient create fail ! \n");
WSACleanup(); //清除记录
return(-1);
}
ServerAddr.sin_family=AF_INET; // IPv4 地址
ServerAddr.sin_addr.s_addr=htonl(INADDR_ANY); //ServerAddr.sin_addr.s_addr是服务器ip地址。
//你的机器上可能有多块网卡,也就有多个IP地址,指定为INADDR_ANY,那么系统将绑定默认的网卡【即IP地址】。
ServerAddr.sin_port=htons(RECV_PORT); //转码端口,sin_prot 为端口号
//2.绑定套接字
if(bind(sockclient,(struct sockaddr FAR *)&ServerAddr,sizeof(ServerAddr))==SOCKET_ERROR)
{ //bind函数将套接字与特定的IP地址和端口绑定起来
printf("bind is the error");
return(-1);
}
return (1);
}
int SendFileRecord(SOCKET datatcps,WIN32_FIND_DATA *pfd) //用来发送当前文件记录
{
char filerecord[MAX_PATH+32];
FILETIME ft; //文件建立时间
FileTimeToLocalFileTime(&pfd->ftLastWriteTime,&ft);
SYSTEMTIME lastwtime; //systemtime系统时间数据结构
FileTimeToSystemTime(&ft,&lastwtime);
char *dir=pfd->dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY?"<DIR>":" ";
sprintf(filerecord,"%04d-%02d-%02d %02d:%02d %5s %10d %-20s\n",
lastwtime.wYear,
lastwtime.wMonth,
lastwtime.wDay,
lastwtime.wHour,
lastwtime.wMinute,
dir,
pfd->nFileSizeLow, //文件大小
pfd->cFileName); //文件名
if(send(datatcps,filerecord,strlen(filerecord),0)==SOCKET_ERROR)
{
//通过datatcps接口发送filerecord数据,成功返回发送的字节数
//send向客户端发送数据
//原形: send(Sock, buf, Len, 0); //将数据原样返回
//sock 为要发送数据的套接字,buf 为要发送的数据的缓冲区地址,len 为要发送的数据的字节数,flags 为发送数据时的选项。
//最后的 flags 参数一般设置为 0 或 NULL,初学者不必深究。
printf("Error occurs when sending file list!\n");
return 0;
}
return 1;
}
int SendFileList(SOCKET datatcps)
{
HANDLE hff; //建立一个线程
WIN32_FIND_DATA fd; //搜索文件
hff=FindFirstFile("*",&fd); //可以通过FindFirstFile()函数根据当前的文件存放路径查找该文件来把待操作文件的相关属性读取到WIN32_FIND_DATA结构中去:
if(hff==INVALID_HANDLE_VALUE) //发生错误
{
const char *errstr="can't list files!\n";
printf("list file error!\n");
if(send(datatcps,errstr,strlen(errstr),0)==SOCKET_ERROR)
{
printf("error occurs when senging file list!\n");
}
closesocket(datatcps); // 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)
{
printf(" sending file data..");
for(;;) //从文件中循环读取数据并发送客户端
{
int r=fread(sbuff,1,1024,file);//把file里面的内容读到sbuff缓冲区
if(send(datatcps,sbuff,r,0)==SOCKET_ERROR)
{
printf("lost the connection to client!\n");
closesocket(datatcps); // closesocket意思是封闭插座。
return 0;
}
if(r<1024)//文件传送结束
break;
}
closesocket(datatcps);
printf("done\n");
return 1;
}
//建立连接
DWORD ConnectProcess()
{
Addrlen=sizeof(sockaddr_in);
if(listen(sockclient,5)<0)
{
printf("Listen error");
return(-1);
}
printf("服务器监听中...\n");
for(;;)
{
//接收客户端请求
sockserver=accept(sockclient,(struct sockaddr FAR *)&ClientAddr,&Addrlen);
//accept函数取出连接队列的第一个连接请求,sockclient是处于监听的套接字,ClientAddr 是监听的对象地址,
//Addrlen是对象地址的长度
for(;;)
{
memset(rbuff,0,1024); //说明:memset(ch,0,sizeof(ch));表示将ch的内容置为0,也就是清空ch
memset(sbuff,0,1024);
if(recv(sockserver,rbuff,1024,0)<=0)
{ //recv() 函数从输入缓冲区中读取数据,而不是直接从网络中读取。
break;
}
printf("\n");
printf("获取并执行的命令为:");
printf(rbuff);
//原型:extern int strcmp(char *s1,char * s2,int n);
// strncmp函数功能:比较字符串s1和s2的前n个字符
if (strncmp(rbuff,"get",3)==0)
{
strcpy(filename,rbuff+4);
printf(filename);
FILE *file; //定义一个文件访问指针
//处理下载文件请求
file=fopen(filename,"rb"); //打开下载的文件,只允许读写
if(file)
{
sprintf(sbuff,"get file %s\n",filename);
if(!send(sockserver,sbuff,1024,0))
{
fclose(file); //关闭文件流
return 0;
}
else
{ //创建额外数据连接传送数据
if(!SendFile(sockserver,file))
return 0;
fclose(file);
}
}//file
else//打开文件失败
{
strcpy(sbuff,"can't open file!\n");
if(send(sockserver,sbuff,1024,0))
return 0;
} //lost
}//get
if(strncmp(rbuff,"put",3)==0) //put上传文件
{
FILE *fd;
int count;
strcpy(filename,rbuff+4);
fd=fopen(filename,"wb");
if(fd==NULL)
{
printf("open file %s for weite failed!\n",filename);
return 0;
}
sprintf(sbuff,"put file %s",filename);
if(!send(sockserver,sbuff,1024,0)) //发送数据1024字节
{
fclose(fd);
return 0;
}
while((count=recv(sockserver,rbuff,1024,0))>0)
//recv函数返回接受的字节数赋给count
fwrite(rbuff,sizeof(char),count,fd);
//把count个数据长度为size0f()的数据从rbuff输入到fd指向的目标文件
printf(" get %s succed!\n",filename);
fclose(fd);
}//put
if(strncmp(rbuff,"pwd",3)==0){ //pwd..........显示当前文件夹的绝对路径
char path[1000];
GetCurrentDirectory(1000,path);//找到当前进程的当前目录
strcpy(sbuff,path);
send(sockserver,sbuff,1024,0);
}//pwd
if(strncmp(rbuff,"dir",3)==0){
strcpy(sbuff,rbuff);
send(sockserver,sbuff,1024,0);
SendFileList(sockserver);//发送当前列表
}//dir
if(strncmp(rbuff,"cd",2)==0)
{
strcpy(filename,rbuff+3);
strcpy(sbuff,rbuff);
send(sockserver,sbuff,1024,0);
SetCurrentDirectory(filename); //设置当前目录,更换路径
}//cd
closesocket(sockserver);
}//for 2
}//for 1
}
int main()
{
if(StartSock()==-1) //初始化winsock
return(-1);
if(CreateSocket()==-1) //创建套接字,绑定等一系列操作
return(-1);
if(ConnectProcess()==-1) //建立连接,并进行相应的命令操作
return(-1);
return(1);
}
完毕。