SOCKET编程之FTP SERVER

//  Server.cpp : 定义控制台应用程序的入口点。
// 需在项目设置 链接中添加ws2_32.lib

#include 
" stdafx.h "
#include 
< winsock2.h >
#include 
< iostream >
#include 
< fstream >
#include 
< io.h >      
#include 
< string >    
#include 
< vector >    
#include 
< iomanip >    
#include 
< ctime >  

using   namespace  std;

#define  DEFAULT_PORT 2100  // 默认服务器端口
#define  DEFAULT_BUFFER 4096   // 默认缓冲区大小
char  path[ 1024 ];  // FTP服务器的服务目录
# define DEFAULT_PATH  " C: "   // 默认目录

bool  sendList(LPVOID ,vector < _finddata_t >   & ,vector < _finddata_t >   & );    // 发送文件列表
bool  down(LPVOID,vector < _finddata_t >   & );   // 发送文件
DWORD WINAPI ClientThread(LPVOID );   // 服务器线程
void  setDirectory();   // 设置FTP服务器的服务目录
void  exit();   // 退出动作

int  main()
{
 setDirectory();
//设置FTP服务器目录
 
 WSADATA wsd; 
//The WSADATA structure contains information about the Windows Sockets implementation
 SOCKET sListen,sClient;//服务器和客户端的套接
 int iAddrSize;
 HANDLE hThread;
 DWORD dwThreadId;
 
struct sockaddr_in local,client; // address information

 
if(WSAStartup(MAKEWORD(2,2),&wsd)!=0)
 
{
  cout
<<"Faile to load Winsock!"<<' ';
  exit(); 
//退出动作
  return 1;
 }


 sListen
=socket(AF_INET,SOCK_STREAM,IPPROTO_IP);//创建监听套接字

 
if(sListen==SOCKET_ERROR)
 
{
  cout
<<"socket() failed: "<<WSAGetLastError()<<' ';
  exit(); 
//退出动作
  return 1;
 }



 local.sin_addr.s_addr
=htonl(INADDR_ANY);
 local.sin_family
=AF_INET;
 local.sin_port
=htons(DEFAULT_PORT);

 
if(bind(sListen,(struct sockaddr *)&local,sizeof(local))==SOCKET_ERROR) //监听套接字与本地IP绑定
 {
  cout
<<"bind() failed :"<<WSAGetLastError()<<' ';
  exit(); 
//退出动作
  return 1;
 }


 listen(sListen,
32);//服务器监听,32为连接队列的长度

 cout
<<"服务器已启动"<<' ';

 
while(1//循环等待进入的客户端连接,一旦检查到连接,创建线程并且传送句柄
 {
  iAddrSize
=sizeof(client);
  sClient
=accept(sListen,(struct sockaddr*)&client,&iAddrSize);
  
if(sClient==INVALID_SOCKET)
  
{
   cout
<<"accept() faild: "<<WSAGetLastError()<<' ';
   
break;
  }

  cout
<<"Accepted client: "<<inet_ntoa(client.sin_addr)<<":"<<ntohs(client.sin_port)<<' ';


  hThread
=CreateThread(NULL,0,ClientThread,(LPVOID)sClient,0,&dwThreadId); //启动线程
  if(hThread==NULL)
  
{
   cout
<<"CreateThread() failed: "<<GetLastError()<<' ';
   
break;
  }

  CloseHandle(hThread);
 }

 closesocket(sListen);
//关闭监听套接
 WSACleanup();//WSACleanup terminates Windows Sockets operations for all threads
 exit(); //退出动作
 return 0;
}



void  exit()   // 退出动作
{
 cout
<<"按任意键退出"<<" ";  
 
if(getchar())  //防止cmd立即退出
  return;
}

bool  sendList(LPVOID lpParam,vector < _finddata_t >   & fileDec,vector < _finddata_t >   & dirDec)   // 发送文件列表
{

 SOCKET sock
=(SOCKET)lpParam;  //客户端套接

 
char Buffer[DEFAULT_BUFFER]; //缓冲区
 struct _finddata_t fileinfo;    //文件信息 
 int ret;
 
long   hFile   =0;    //文件句柄  


 
int j=0;
 
char tpath[1024];
 strcpy(tpath,path);
 strcat(tpath,
"/*.*");
 
if((hFile=_findfirst(tpath,&fileinfo))   !=   -1)     //检查是不是目录   
 {
  
do   
  
{   
   
if   ((fileinfo.attrib   &   _A_SUBDIR))   
   

    
if   (strcmp(fileinfo.name,".")   !=   0   &&   strcmp(fileinfo.name,"..")   !=   0)   //如果是,再检查是不是   .   或   ..
    dirDec.push_back(   fileinfo   );  //把文件夹信息放入目录容器
   }
   
   
else    //如果不是,进行迭代  
   {   
    fileDec.push_back(   fileinfo   );  
//把文件信息放入文件容器 
   }
   
  }
   while   (_findnext(   hFile,   &fileinfo   )   ==   0);   
  _findclose(hFile);
 }

 
else
 
{
  cout
<<path<<" 不是一个目录"<<' ';
  
return false;
 }


 fd_set sf;  
//The fd_set structure is used by various Windows Sockets functions and service providers, such as the select function, 
     
//to place sockets into a "set" for various purposes, such as testing a given socket for readability using the readfds parameter of 
     
//the select function
 sf.fd_count=0;
 sf.fd_array[sf.fd_count
++]=sock;

 timeval to;  
//The timeval structure is used to specify time values
 to.tv_sec=60//60s超时
 to.tv_usec=0//毫秒

 
for (j=0;j<dirDec.size();++j) //首先发送文件夹信息
 {
  
for(int i=0;i<2;i++)
  
{
   
if(i==0)
   
{
    
if(select(0,NULL,&sf,NULL,&to))  //checked for writability
    {
     ret
=send(sock,dirDec[j].name,DEFAULT_BUFFER,0); //发送文件夹名
     if(ret==0)
      
return false;
     
else if(ret==SOCKET_ERROR)
     
{
      cout
<<"send() faild: "<<WSAGetLastError()<<' ';
      
return false;
     }
 
    }

    
else 
    
{
     cout
<<"time out"<<' ';
     
return false;
    }


    
   }


   
else
   
{    
    strcpy(Buffer,asctime(   gmtime(   
&dirDec[j].time_create   )   )); //把timeval转换为string

    
if(select(0,NULL,&sf,NULL,&to))  //checked for writability
    {
     ret
=send(sock, Buffer,DEFAULT_BUFFER,0);  //发送文件夹最后修改时间
     if(ret==0)
      
return false;
     
else if(ret==SOCKET_ERROR)
     
{
      cout
<<"send() faild: "<<WSAGetLastError()<<' ';
      
return false;
     }

    }

    
else 
    
{
     cout
<<"time out"<<' ';
     
return false;
    }

    
   }

  }

 }

 
if(select(0,NULL,&sf,NULL,&to))  //checked for writability
 
  ret
=send(sock," ",DEFAULT_BUFFER,0);//发送结束标志
  if(ret==0)
   
return false;
  
else if(ret==SOCKET_ERROR)
  
{
   cout
<<"send() faild: "<<WSAGetLastError()<<' ';
   
return false;
  
  }

 }

 
else 
 
{
  cout
<<"time out"<<' ';
  
return false;
 }

 
for   (   j=0;   j<fileDec.size();   ++j)  //然后发送文件信息
 {
  
for(int i=0;i<3;i++)
  
{
   
if(i==0)
   
{
    
if(select(0,NULL,&sf,NULL,&to))  //checked for writability
    {
     ret
=send(sock,fileDec[j].name,DEFAULT_BUFFER,0); //发送文件名
     if(ret==0)
      
return false;
     
else if(ret==SOCKET_ERROR)
     
{
      cout
<<"send() faild: "<<WSAGetLastError()<<' ';
      
return false;
     }
 
    }

    
else 
    
{
     cout
<<"time out"<<' ';
     
return false;
    }


    
   }


   
else if(i==1)
   
{
    itoa(fileDec[j].size, Buffer,
10);  //把long转为string

    
if(select(0,NULL,&sf,NULL,&to))  //checked for writability
    {
     ret
=send(sock, Buffer,DEFAULT_BUFFER,0); //发送文件大小
     if(ret==0)
      
return false;
     
else if(ret==SOCKET_ERROR)
     
{
      cout
<<"send() faild: "<<WSAGetLastError()<<' ';
      
return false;
     }
 
    }

    
else 
    
{
     cout
<<"time out"<<' ';
     
return false;
    }

   }

    
   
else
   
{    
    strcpy( Buffer,asctime(   gmtime(   
&fileDec[j].time_create   )   ));  //把timeval转换为string
    if(select(0,NULL,&sf,NULL,&to))  //checked for writability
    {
     ret
=send(sock,Buffer,DEFAULT_BUFFER,0); //发送文件最后修改时间
     if(ret==0)
      
return false;
     
else if(ret==SOCKET_ERROR)
     
{
      cout
<<"send() faild: "<<WSAGetLastError()<<' ';
      
return false;
     }

    }

    
else 
    
{
     cout
<<"time out"<<' ';
     
return false;
    }

    
   }

  }

 }

 
if(select(0,NULL,&sf,NULL,&to))   //checked for writability
 
  ret
=send(sock," ",DEFAULT_BUFFER,0);//发送结束标志
  if(ret==0)
   
return false;
  
else if(ret==SOCKET_ERROR)
  
{
   cout
<<"send() faild: "<<WSAGetLastError()<<' ';
   
return false;
  
  }

 }

 
else 
 
{
  cout
<<"time out"<<' ';
  
return false;
 }

 
return true;

}


bool  down(LPVOID lpParam,vector < _finddata_t >   & fileDec)  // 发送文件
{
 SOCKET sock
=(SOCKET)lpParam; //客户端套接
 int ret;
 
char Buffer[DEFAULT_BUFFER]; //缓冲区
 fd_set sf,rf; //The fd_set structure is used by various Windows Sockets functions and service providers, such as the select function, 
     
//to place sockets into a "set" for various purposes, such as testing a given socket for readability using the readfds parameter of 
     
//the select function
     
//sf check for writability,rf check for readability

 

 rf.fd_count
=0;
 rf.fd_array[rf.fd_count
++]=sock;

 timeval to;  
//The timeval structure is used to specify time values
 to.tv_sec=60//60s超时
 to.tv_usec=0//毫秒

 
if(select(0,&rf,NULL,NULL,&to))  //checked for readability,接收要发送的文件目录信息
 {
  ret
=recv(sock,Buffer,DEFAULT_BUFFER,0);
  
if(ret==0)
   
return false;
  
else if(ret==SOCKET_ERROR)
  
{
   cout
<<"recv() faild: "<<WSAGetLastError()<<' ';
   
return false;
  }

 }

 
else 
 
{
  cout
<<"time out"<<' ';
  
return false;
 }

 
 
int   i   =   atoi(Buffer);  //把string转为int
 char tpath[1024];
 strcpy(tpath,path);
 strcat(tpath,
"/"); 
 strcat(tpath,fileDec[
--i].name);

 fstream instuf(tpath,ios::
in|ios::binary);  //以二字制只读方式打开要发送的文件
 if(!instuf)
 
{
  cerr
<<"File could not be open."<<' ';
  
return false;
 }


 instuf.seekp(
0,ios::end); //计算文件的大小
 unsigned long posEnd=instuf.tellp();
 instuf.seekp(
0,ios::beg);
 
 cout
<<"Sending file "<<tpath<<' ';

 sf.fd_count
=0;
 sf.fd_array[sf.fd_count
++]=sock;
 
 
 
while(posEnd>0)
 
{
  
if(posEnd>DEFAULT_BUFFER) //当文件大小大于缓冲区时,在文件中读取缓冲区大小的文件发送
  {
   instuf.read((
char*)Buffer,DEFAULT_BUFFER);

   
if(select(0,NULL,&sf,NULL,&to))  //checked for writability
   {
    ret
=send(sock,Buffer,DEFAULT_BUFFER,0);
    
if(ret==0)
     
return false;
    
else if(ret==SOCKET_ERROR)
    
{
     cout
<<"send() faild: "<<WSAGetLastError()<<' ';
     
return false;
    }

    posEnd
-=DEFAULT_BUFFER;
   }

   
else 
   
{
    cout
<<"time out"<<' ';
    
return false;
   }

  }

  
else
  
{
   instuf.read((
char*)Buffer,posEnd); //当文件大小小于缓冲区时,文件全部发送

   
if(select(0,NULL,&sf,NULL,&to))  ////checked for writability
   {  
    ret
=send(sock,Buffer,DEFAULT_BUFFER,0);
    
if(ret==0)
     
return false;
    
else if(ret==SOCKET_ERROR)
    
{
     cout
<<"send() faild: "<<WSAGetLastError()<<' ';
     
return false;
    }

    
    posEnd
=0;
   }

   
else 
   
{
    cout
<<"time out"<<' ';
    
return false;
   }

   
   
   

  }


 }

 
if(select(0,NULL,&sf,NULL,&to)) //checked for writability
 
  ret
=send(sock," ",DEFAULT_BUFFER,0); //发送结束标志
  if(ret==0)
   
return false;
  
else if(ret==SOCKET_ERROR)
  
{
   cout
<<"send() faild: "<<WSAGetLastError()<<' ';
    
return false;
  }

    
 }
 
 
else 
 
{
  cout
<<"time out"<<' ';
  
return false;
 }

 cout
<<"Send file "<<tpath<<" completely"<<' '
 instuf.close();
 
return true;
}

 

DWORD WINAPI ClientThread(LPVOID lpParam)  
// 线程方式调用,并且处理给定客户端连接,传递参数从accept函数调用中返回的套接
{
 SOCKET sock
=(SOCKET)lpParam; //客户端套接
 vector<_finddata_t>   fileDec;   //   文件容器
 vector<_finddata_t>   dirDec;    //目录容器
 char Buffer[DEFAULT_BUFFER]; //缓冲区
 int ret;
 

 
while(ret=recv(sock,Buffer,DEFAULT_BUFFER,0)) //接收客户端发送的命令
 {
  
if(ret==0)
   
return 1;
  
else if(ret==SOCKET_ERROR)
  
{
   
return 1;
  }

  
if(strcmp(Buffer,"LIST")==0)  //服务器接受"LIST"命令,则显示文件列表
  {
   fileDec.clear();
   dirDec.clear();
   
if(!sendList(lpParam,fileDec,dirDec))
    
return 1;
  }

 
  
else if(strcmp(Buffer,"DOWN")==0
  
{
   
if(!down(lpParam,fileDec))
    
return 1;
  }

 }


 
return 0;
}



void  setDirectory() // 设置FTP服务器目录

 
char c;
 _finddata_t fileinfo; 
//文件信息 
 long hFile;//文件句柄  
 bool set=false;
 cout
<<"输入FTP服务器目录,默认为C:,如果要保持默认目录,请直接输入ENTER:"<<' ';
 
while(!set)
 
{
  
int i=0;
  
while(c=getchar())
  
{

   
if(c==' ')
    
break;
   
else
    path[i]
=c;
   i
++;
   
if(strlen(path)>=1024)
    
break;
  }

  
  
if(i==0)
  
{
   strcpy(path,DEFAULT_PATH);
  }

 
  
else
  
{
   
--i;
   
while(path[i]=='/')
   
{
    path[i
--]='
 
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值