⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 server.c

📁 ftp客户端
💻 C
字号:
//SFTPWinSever.cpp
#include"define.h"
#include<stdio.h>
#include<winsock2.h>
#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=((threadData *)lpParam)->tcps;
 clientaddr=((threadData *)lpParam)->clientaddr;
 printf("socket id is %u.\n",tcps);

 //发送回复报文给客户端,内含命令使用说明
 printf("Serve client %s:%d\n",inet_ntoa(clientaddr.sin_addr),ntohs(clientaddr.sin_port));
 RspnsPacket rspns={OK,
  "connection ready!\n"
  "type 'help' for assitant.\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("Mini FTP Sever listening on %d port...\n",CMD_PORT);

 //循环接受客户端连接请求,并声成现程去处理
 for(;;)
 {
  pThInfo=NULL;
  pThInfo=new threadData;
  if(pThInfo==NULL)
  {
   printf("malloc space failed!\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);
  //check the return value for success
  if(hThread==NULL)
  {
   printf("CreateThread failed\n");
   closesocket(pThInfo->tcps);
   delete pThInfo;
  }
 }
 return 0;
}
//ftp初始化,创建一个侦听套接字
int InitFTP(SOCKET *pListenSock)
{
 //these steps are necessary to use winsock
 //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("socket绑定时发生错误!\n");
  return 0;
 }
 err=listen(tcps_listen,3);
 if(err!=0)
 {
  WSACleanup();
  printf("socket监听时错误!\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,"CD succeed!But can't get current dir!\n");
  }
  else
  {
   strcpy(rspns.text,"can't change to that dir!");
  }
  if(!SendRspns(tcps,&rspns))//发送回复报文
   return 0;
  break;
 case GET:
  //处理下载文件请求
  file=fopen(pCmd->param,"rb");//打开下载的文件
  if(file)
  {
   rspns.rspnsid=OK;
   sprintf(rspns.text,"get file %s\n", 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,"can't open file!\n");
   if(!SendRspns(tcps,&rspns))
    return 0;
  }
  break;
 case PUT://处理上传文件请求
   //首先发送回复报文
   char filename[64];
   strcpy(filename,pCmd->param);
   //verify no file with same exits to make sure that nho file will bi overwritten
   if(FileExists(filename))
   {
    rspns.rspnsid=ERR;
    sprintf(rspns.text,"remote file named %s exits!\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("Thanks for using! \nconnection turned off!\n");
  rspns.rspnsid=OK;
  strcpy(rspns.text,"Thanks for using!\n xiaOe6521@yahoo.com.cn\n hainanwei@stu.xjtu.edu.cn\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("lost the connection to client!\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("error occurs when receiving command from client!\n");
   return 0;
  }
  if(!nRet)
  {
   printf("connection is closed by client!\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=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)
 {
  printf("Error occurs when sending file list!\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="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);
  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("sending file data..");
 for(;;)
 //从文件中循环读取数据并发送客户端
 {
  int r=fread(buf,1,1024,file);
  if(send(datatcps,buf,r,0)==SOCKET_ERROR)
  {
   printf("lost thr connection to client!\n");
   closesocket(datatcps);
   return 0;
  }
  if(r<1024)//文件传送结束
   break;
 }
 closesocket(datatcps);
 printf("done\n");
 return 1;
}
//接受文件
//datatcps 数据连接套接字,通过它来接收数据
//filename 用于存放数据的文件名
int RecvFile(SOCKET datatcps,char* filename)
{
 char buf[1024];
 FILE* file=fopen(filename,"wb");
 if(!file)
 {
  printf("error occurs when open file to write!\n");
  fclose(file);
  closesocket(datatcps);
  return 0;
 }
 //循环接受所有数据,并写往文件
 printf("receiving file data...");
 while(1)
 {
  int r=recv(datatcps,buf,1024,0);
  if(r==SOCKET_ERROR)
  {
   printf("error occurs when receiving file from client!\n");
   fclose(file);
   closesocket(datatcps);
   return 0;
  }
  if(!r)//数据传送结束
   break;
  fwrite(buf,1,r,file);
 }
 fclose(file);
 closesocket(datatcps);
 printf("done\n");
 return 1;
}
//监测文件是否存在
int FileExists(const char* filename)
{
 WIN32_FIND_DATA fd;
 if(FindFirstFile(filename,&fd)==INVALID_HANDLE_VALUE)
  return 0;
 return 1;
}
 

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -