C++利用socket的客戶端和伺服器之間傳輸檔案
阿新 • • 發佈:2019-02-04
//伺服器的程式碼檔案有 /* message.h source.h source.cpp server.h server.cpp */ //客戶端的程式碼檔案有 /* message.h 和伺服器端一樣 client.h client.cpp */ //message.h #pragma once #include<iostream> using namespace std; #define MAX_PACK_SIZE 10240 //資料包的長度 #define MAX_FILE_NAME_LENGTH 256 //檔名的長度 #define INVALID_MSG -1 //無效的訊息 #define MSG_FILE_LENGTH 1 //檔案長度 #define MSG_FILE_NAME 2 //檔名 #define MSG_FILE 4 //檔案內容 #define MSG_READY 3 //準備好訊息 #define MSG_SEND_FILE 5 //傳送檔案 #define MSG_DOWNLOAD_FILE 6 //下載檔案 #define MSG_COMPLETE 7 //完成資訊 class Message { public: struct MsgHead //頭訊息 { int msgId; //訊息標識 MsgHead(int msg=INVALID_MSG):msgId(msg){}; }; struct MsgFileLength :public MsgHead { _int64 fileLength; //檔案長度 MsgFileLength():MsgHead(MSG_FILE_LENGTH){} }; struct MsgFileName:public MsgHead { char fileName[MAX_FILE_NAME_LENGTH]; MsgFileName():MsgHead(MSG_FILE_NAME){} }; struct MsgFile:public MsgHead { MsgFile():MsgHead(MSG_FILE){} }; struct MsgReady:public MsgHead //準備好訊息 { MsgReady():MsgHead(MSG_READY){} }; struct MsgSendFile:public MsgHead //傳送檔案訊息 { MsgSendFile():MsgHead(MSG_SEND_FILE){} }; struct MsgDownLoadFile:public MsgHead //下載檔案訊息 { MsgDownLoadFile():MsgHead(MSG_DOWNLOAD_FILE){} }; struct MsgComplete:public MsgHead { MsgComplete():MsgHead(MSG_COMPLETE){} }; }; //source.h 獲取指定檔案加下的符合要求的檔案 #pragma once #include<iostream> #include<fstream> #include<vector> #include<io.h> #include<string> using namespace std; class Source { public: vector<string> catalogInfo; void GetFiles(string path,string ext,vector<string> &files);//獲取檔案 }; //server.h #pragma once #include<iostream> #include<WinSock2.h> #include"message.h" #pragma comment(lib,"Ws2_32.lib") #define PORT 10000 using namespace std; class Server { public: SOCKET sd; _int64 fileLength; char fileName[MAX_FILE_NAME_LENGTH]; bool InitSock(); //初始winsocket SOCKET BindListen(); //繫結監聽套接字 SOCKET AcceptConnection(SOCKET sd); //接收客戶端 bool ProcessConnection(SOCKET sd); //傳送資料 bool ReceiveFile(SOCKET sd); //接收檔案內容 bool RecvFileName(SOCKET sd); //接收檔名 bool GetAndSendFileLength(SOCKET sd); //獲取檔案長度 bool SendFileName(SOCKET sd); //傳送檔名 bool SendFile(SOCKET sd); //傳送檔案 void CloseSocket(); //關閉套接字 }; //source.cpp #pragma once #include<iostream> #include<vector> #include<io.h> #include<string> #include"source.h" using namespace std; void Source::GetFiles(string path,string ext,vector<string> &files) { long hFile=0; //檔案控制代碼 _finddata_t fileInfo; //檔案資訊 string pathName; if((hFile=_findfirst(pathName.assign(path).append("\\*").c_str(),&fileInfo))!=-1) //判斷路徑是否有效並獲取第一個檔案 { do { if(fileInfo.attrib & _A_SUBDIR) //如果是子資料夾 { if(strcmp(fileInfo.name,".")!=0 && strcmp(fileInfo.name,"..")!=0) { GetFiles(pathName.assign(path).append("\\").append(fileInfo.name),ext,files); } } else { string filePath; filePath=pathName.assign(path).append("\\").append(fileInfo.name); char fileDrive[_MAX_DRIVE]; char fileDir[_MAX_DIR]; char fileName[_MAX_FNAME]; char fileExt[_MAX_EXT]; _splitpath(filePath.c_str(),fileDrive,fileDir,fileName,fileExt); //分解路徑獲取磁碟區路徑檔名字尾 if(strcmp(fileExt,ext.c_str())==0) { files.push_back(filePath); } } }while(_findnext(hFile,&fileInfo)==0); _findclose(hFile); } } //server.cpp #pragma once #include<iostream> #include<string> #include<fstream> #include<WinSock2.h> #include"message.h" #include"server.h" #include"source.h" using namespace std; int main() { Server server; if(!server.InitSock()) //初始化失敗 { cout<<"初始化失敗"<<endl; } server.sd=server.BindListen(); if(server.sd==INVALID_SOCKET) { return -1; } SOCKET sdListen=server.AcceptConnection(server.sd); if(sdListen==INVALID_SOCKET) { return -1; } while(server.ProcessConnection(sdListen)) { } server.CloseSocket(); return 0; } bool Server::InitSock() //初始化winsocket { WSADATA wsData; WORD wr=MAKEWORD(2,2); if(WSAStartup(wr,&wsData)==0) { return true; } return false; } SOCKET Server::BindListen() //繫結套接字 { SOCKET sd=socket(AF_INET,SOCK_STREAM,0); if(sd==INVALID_SOCKET) { cout<<"建立套接字失敗"<<WSAGetLastError()<<endl; return INVALID_SOCKET; } sockaddr_in sListen; sListen.sin_family=AF_INET; sListen.sin_addr.s_addr=htonl(INADDR_ANY); sListen.sin_port=htons(PORT); int nSize; nSize=sizeof(sockaddr_in); if(bind(sd,(sockaddr*)&sListen,nSize)==SOCKET_ERROR) { closesocket(sd); cout<<"繫結失敗"<<WSAGetLastError()<<endl; return INVALID_SOCKET; } if(listen(sd,10)==SOCKET_ERROR) { closesocket(sd); cout<<"監聽失敗"<<WSAGetLastError()<<endl; return INVALID_SOCKET; } return sd; } SOCKET Server::AcceptConnection(SOCKET sd) //接收客戶端 { sockaddr_in saRemote; int nSize=sizeof(sockaddr_in); SOCKET sdListen=accept(sd,(sockaddr*)&saRemote,&nSize); if(sdListen==INVALID_SOCKET) { cout<<"接收客戶端失敗"<<WSAGetLastError()<<endl; return INVALID_SOCKET; } return sdListen; } bool Server::ReceiveFile(SOCKET sd) { char buff[MAX_PACK_SIZE]; FILE *pFile; pFile=fopen(fileName,"a+b"); _int64 i=0; while(i+1<fileLength) { int nRecv=recv(sd,buff,MAX_PACK_SIZE,0); if(nRecv==SOCKET_ERROR) { return false; } fwrite(buff,sizeof(char),nRecv,pFile); i+=nRecv; memset(buff,0,sizeof(char)*MAX_PACK_SIZE); } fclose(pFile); return true; } void Server::CloseSocket() { closesocket(sd); WSACleanup(); } bool Server::ProcessConnection(SOCKET sd) { //---------------------------------------------- //可以將下面程式碼看做設定系統緩衝區 int nRecvBuf=1024000;//設定為1000K setsockopt(sd,SOL_SOCKET,SO_RCVBUF,(const char*)&nRecvBuf,sizeof(int)); //傳送緩衝區 int nSendBuf=1024000;//設定為1000K setsockopt(sd,SOL_SOCKET,SO_SNDBUF,(const char*)&nSendBuf,sizeof(int)); //------------------------------------------------------------- char buff[MAX_PACK_SIZE]; Message::MsgHead *msgHead; if(recv(sd,buff,MAX_PACK_SIZE,0)==SOCKET_ERROR) { cout<<"接收失敗"<<WSAGetLastError()<<endl; return false; } msgHead=(Message::MsgHead *)&buff; switch(msgHead->msgId) { case MSG_SEND_FILE: //客戶端向伺服器傳送檔案 cout<<"客戶端請求向伺服器傳送檔案"<<endl; break; case MSG_DOWNLOAD_FILE: //客戶端從伺服器下載檔案 { cout<<"客戶端請求從伺服器下載檔案"<<endl; Source source; //用來獲取指定檔案加下的字尾為jpg檔案 string sPath="E:\\圖片"; string sExt=".jpg"; source.GetFiles(sPath,sExt,source.catalogInfo); int nSize; nSize=source.catalogInfo.size(); cout<<"蒐集到"<<nSize<<"個檔案"<<endl; char buff[MAX_PACK_SIZE]; for(int i=0;i<nSize;i++) //將目錄資訊傳送到客戶端 { strcpy(buff,source.catalogInfo[i].c_str()); //cout<<source.catalogInfo[i]<<endl; if(send(sd,buff,MAX_PACK_SIZE,0)==SOCKET_ERROR) { cout<<"傳送目錄資訊失敗"<<WSAGetLastError()<<endl; return false; } Sleep(10); //睡眠10ms讓接收端將資訊取走 } Message::MsgComplete msgComplete; if(send(sd,(char *)&msgComplete,sizeof(Message::MsgComplete),0)==SOCKET_ERROR) { cout<<"傳送完成資訊失敗"<<WSAGetLastError()<<endl; return false; } Sleep(10); if(!RecvFileName(sd)) { return false; } Sleep(10); if(!GetAndSendFileLength(sd)) { return false; } Sleep(10); if(!SendFileName(sd)) { return false; } Sleep(10); if(!SendFile(sd)) { return false; } } break; case MSG_FILE_NAME: //傳送的檔名 { Message::MsgFileName *msgFileName; msgFileName=(Message::MsgFileName*)msgHead; strcpy(fileName,msgFileName->fileName); cout<<"收到傳送來的檔名"<<fileName<<endl; } break; case MSG_FILE_LENGTH: //傳送的檔案長度 { Message::MsgFileLength *msgFileLength; msgFileLength=(Message::MsgFileLength *)msgHead; fileLength=msgFileLength->fileLength; cout<<"接收到檔案的長度為"<<fileLength<<endl; } break; case MSG_FILE: //傳送的檔案內容 { cout<<"開始接收檔案"<<endl; if(!ReceiveFile(sd)) { cout<<"接收檔案失敗"<<endl; return false; } } break; default: cout<<"非標準訊息"<<endl; return false; } return true; } bool Server::RecvFileName(SOCKET sd) { //memset(fileName,0,sizeof(char)*MAX_FILE_NAME_LENGTH); //清空 char buff[MAX_PACK_SIZE]; Message::MsgFileName *msgFileName; if(recv(sd,buff,MAX_PACK_SIZE,0)==SOCKET_ERROR) { cout<<"接收檔名失敗"<<WSAGetLastError()<<endl; return false; } msgFileName=(Message::MsgFileName *)buff; strcpy(fileName,msgFileName->fileName); cout<<"接收的檔名為"<<fileName<<endl; return true; } bool Server::GetAndSendFileLength(SOCKET sd) //獲取客戶端要下載的檔案長度 { Message::MsgFileLength msgFileLength; FILE *pFile; pFile=fopen(fileName,"r+b"); if(pFile==NULL) { cout<<"開啟檔案失敗"<<endl; return false; } fseek(pFile,0,SEEK_END); fileLength=_ftelli64(pFile); fclose(pFile); msgFileLength.fileLength=fileLength; if(send(sd,(char*)&msgFileLength,sizeof(Message::MsgFileLength),0)==SOCKET_ERROR) { cout<<"傳送檔案長度失敗"<<WSAGetLastError()<<endl; return false; } return true; } bool Server::SendFileName(SOCKET sd) //向客戶端傳送檔名 { Message::MsgFileName msgFileName; char fileDrive[_MAX_DRIVE]; char fileDir[_MAX_DIR]; char Name[_MAX_FNAME]; char fileExt[_MAX_EXT]; _splitpath(fileName,fileDrive,fileDir,Name,fileExt); strcat(Name,fileExt); strcpy(msgFileName.fileName,Name); cout<<"要傳送的檔名為"<<Name<<endl; if(send(sd,(char *)&msgFileName,sizeof(Message::MsgFileName),0)==SOCKET_ERROR) { cout<<"傳送檔名出錯"<<WSAGetLastError()<<endl; return false; } return true; } bool Server::SendFile(SOCKET sd) //向客戶端傳送檔案 { cout<<"進入到傳送檔案內容"<<endl; cout<<"要傳送的檔案為"<<fileName<<endl; FILE *pFile; pFile=fopen(fileName,"r+b"); fseek(pFile,0,SEEK_SET); //定位到檔案首位置 _int64 i=0; char buff[MAX_PACK_SIZE]; cout<<"要傳送的檔案長度為"<<fileLength<<endl; while(i<fileLength) { int nSize; if(i+MAX_PACK_SIZE>fileLength) { nSize=(int)(fileLength-i); } else { nSize=MAX_PACK_SIZE-1; } fread(buff,sizeof(char),nSize,pFile); int nSend; nSend=send(sd,buff,nSize,0); if(nSend==SOCKET_ERROR) { cout<<"傳送失敗"<<WSAGetLastError()<<endl; return false; } i+=nSend; fseek(pFile,-(nSize-nSend),SEEK_CUR); //定位到實際已傳送到的位置 memset(buff,0,sizeof(char)*MAX_PACK_SIZE); //將buff清空 } fclose(pFile); return true; } //client.h #pragma once #include<iostream> #include<fstream> #include<vector> #include<WinSock2.h> #pragma comment(lib,"Ws2_32.lib") using namespace std; #define SERVER_IP "127.0.0.1" #define PORT 10000 class Client { public: _int64 nFileLength; char fileName[_MAX_FNAME+_MAX_EXT]; SOCKET sd; bool InitSock(); //初始化winsock u_long ResolveAdress(char *serverIp); //解析伺服器地址 SOCKET ConnectServer(u_long serverIp,int port);//連線伺服器 bool ProcessConnection(SOCKET sd); //客戶端伺服器互動 void CloseSocket(); //釋放套接字 bool SendFileLength(SOCKET sd,char *filePath); //傳送檔案長度 bool SendFile(SOCKET sd,char *filePath); //傳送檔案 bool RecvCatalogInfo(SOCKET sd); //接收目錄資訊 bool SendDownLoadFileName(SOCKET sd); //傳送要下載的檔名 bool ReceiveFileLength(SOCKET sd); //接收檔案長度 bool ReceiveFileName(SOCKET sd); //接收檔名 bool ReceiveFile(SOCKET sd); //接收檔案 //void DoWork(); //主體函式 }; //client.cpp #define _CRT_SECURE_NO_WARNINGS #pragma once #include<iostream> #include<vector> #include<WinSock2.h> #include"client.h" #include"message.h" using namespace std; int main() { Client client; if(!client.InitSock()) { cout<<"初始socket失敗"<<endl; return -1; } SOCKET saRemote=client.ConnectServer(client.ResolveAdress(SERVER_IP),PORT); if(saRemote==INVALID_SOCKET) { cout<<"連線伺服器失敗"<<endl; return -1; } if(!client.ProcessConnection(saRemote)) { return -1; } client.CloseSocket(); return 0; } bool Client::InitSock() //初始socket { WSADATA wsData; WORD wr=MAKEWORD(2,2); if(WSAStartup(wr,&wsData)==0) { return true; } return false; } u_long Client::ResolveAdress(char *serverIp) //解析IP地址 { u_long nAddr=inet_addr(serverIp); if(nAddr==INADDR_NONE) //表明serverIp使用的是主機名形式 { hostent *ent=gethostbyname(serverIp); if(ent==NULL) { cout<<"獲取主機名出錯"<<WSAGetLastError()<<endl; } else { nAddr=*((u_long *)ent->h_addr_list[0]); } } if(nAddr==INADDR_NONE) { cout<<"解析主機地址失敗"<<endl; } return nAddr; } SOCKET Client::ConnectServer(u_long serverIp,int port) //連線伺服器 { sd=socket(AF_INET,SOCK_STREAM,0); if(sd==INVALID_SOCKET) { cout<<"床架套接字失敗"<<endl; return INVALID_SOCKET; } sockaddr_in saServer; saServer.sin_family=AF_INET; saServer.sin_addr.S_un.S_addr=serverIp; saServer.sin_port=htons(port); if(connect(sd,(sockaddr*)&saServer,sizeof(sockaddr_in))==SOCKET_ERROR) { cout<<"連線伺服器失敗"<<WSAGetLastError()<<endl; closesocket(sd); return INVALID_SOCKET; } return sd; } bool Client::ProcessConnection(SOCKET sd) //進行通訊 { //------------------------------------------------- //可以將下面程式碼看做設定系統緩衝區 int nRecvBuf=1024000;//設定為1000K setsockopt(sd,SOL_SOCKET,SO_RCVBUF,(const char*)&nRecvBuf,sizeof(int)); //傳送緩衝區 int nSendBuf=1024000;//設定為1000K setsockopt(sd,SOL_SOCKET,SO_SNDBUF,(const char*)&nSendBuf,sizeof(int)); //--------------------------------------------------------- while(true) { cout<<"(1)向伺服器傳送檔案"<<endl; cout<<"(2)從伺服器下載檔案"<<endl; cout<<"(3)退出業務"<<endl; int n; loop:cin>>n; switch(n) { case 1: { //向伺服器傳送傳送檔案訊息 Message::MsgSendFile msgSendFile; if(send(sd,(char *)&msgSendFile,sizeof(Message::MsgSendFile),0)==SOCKET_ERROR) { cout<<"傳送訊息失敗"<<endl; return false; } Sleep(10); //睡眠10ms保證對方將傳送的訊息取走 char filePath[MAX_FILE_NAME_LENGTH]; cout<<"請輸入檔案路徑如:F:/a/b.jpg"<<endl; cin>>filePath; char fileDrive[_MAX_DRIVE]; char fileDir[_MAX_DIR]; char fileName[_MAX_FNAME]; char fileExt[_MAX_EXT]; _splitpath(filePath,fileDrive,fileDir,fileName,fileExt); //將檔案路徑解析 Message::MsgFileName msgFileName; strcat(fileName,fileExt); strcpy(msgFileName.fileName,fileName); if(send(sd,(char *)&msgFileName,sizeof(Message::MsgFileName),0)==SOCKET_ERROR) //傳送檔名 { cout<<"傳送檔名出錯"<<WSAGetLastError()<<endl; } Sleep(10); if(!SendFileLength(sd,filePath)) //傳送檔案長度 { cout<<"傳送檔案長度出錯"<<endl; return false; } Sleep(10); if(!SendFile(sd,filePath)) //傳送檔案 { cout<<"傳送檔案出錯"<<endl; return false; } } break; case 2: { Message::MsgDownLoadFile msgDownLoadFile; if(send(sd,(char *)&msgDownLoadFile,sizeof(Message::MsgDownLoadFile),0)==SOCKET_ERROR) { cout<<"傳送下載檔案訊息失敗"<<WSAGetLastError()<<endl; return false; } if(!RecvCatalogInfo(sd)) { return false; } if(!SendDownLoadFileName(sd)) { return false; } if(!ReceiveFileLength(sd)) { return false; } if(!ReceiveFileName(sd)) { return false; } if(!ReceiveFile(sd)) { return false; } } break; case 3: break; default: cout<<"你輸入的不符合要求,重新輸入"<<endl; goto loop; } } return true; } bool Client::SendFileLength(SOCKET sd,char *filePath) { FILE *pFile; pFile=fopen(filePath,"r+b"); fseek(pFile,0,SEEK_END); nFileLength=_ftelli64(pFile); Message::MsgFileLength msgFileLength; msgFileLength.fileLength=nFileLength; fclose(pFile); if(send(sd,(char *)&msgFileLength,sizeof(Message::MsgFileLength),0)==SOCKET_ERROR) { return false; } return true; } bool Client::SendFile(SOCKET sd,char *filePath) //傳送檔案 { cout<<"進入到傳送檔案內容"<<endl; Message::MsgFile msgFile; if(send(sd,(char *)&msgFile,sizeof(Message::MsgFile),0)==SOCKET_ERROR) { cout<<"傳送檔案訊息出錯"<<WSAGetLastError()<<endl; return false; } Sleep(10); FILE *pFile; pFile=fopen(filePath,"r+b"); fseek(pFile,0,SEEK_SET); //定位到檔案首位置 _int64 i=0; char buff[MAX_PACK_SIZE]; while(i<nFileLength) { int nSize; if(i+MAX_PACK_SIZE>nFileLength) { nSize=(int)(nFileLength-i); } else { nSize=MAX_PACK_SIZE-1; } fread(buff,sizeof(char),nSize,pFile); int nSend; nSend=send(sd,buff,nSize,0); if(nSend==SOCKET_ERROR) { cout<<"傳送失敗"<<endl; return false; } i+=nSend; fseek(pFile,-(nSize-nSend),SEEK_CUR); //定位到實際已傳送到的位置 memset(buff,0,sizeof(char)*MAX_PACK_SIZE); //將buff清空 } fclose(pFile); return true; } bool Client::RecvCatalogInfo(SOCKET sd) //接收目錄資訊 { int flag=1; //接收目錄資訊成功標誌 char buff[MAX_PACK_SIZE]; Message::MsgHead *msgHead; while(true) { if(recv(sd,buff,MAX_PACK_SIZE,0)==SOCKET_ERROR) { cout<<"接收目錄資訊失敗"<<WSAGetLastError()<<endl; flag=0; break; } msgHead=(Message::MsgHead *)buff; if(msgHead->msgId==MSG_COMPLETE) //判斷訊息是否是標準訊息 { cout<<"目錄資訊傳送完成"<<endl; break; } else { cout<<buff<<endl; //傳送來的是目錄資訊,即檔名 } } if(flag==0) { return false; } return true; } bool Client::SendDownLoadFileName(SOCKET sd) //傳送下載的檔名 { cout<<"請輸入你要下載的檔名"<<endl; char fileName[_MAX_FNAME+_MAX_EXT]; cin>>fileName; Message::MsgFileName msgFileName; strcpy(msgFileName.fileName,fileName); if(send(sd,(char *)&msgFileName,MAX_PACK_SIZE,0)==SOCKET_ERROR) { cout<<"傳送下載檔名出錯"<<WSAGetLastError()<<endl; return false; } return true; } bool Client::ReceiveFileLength(SOCKET sd) //接收下載的檔案長度 { char buff[MAX_PACK_SIZE]; Message::MsgFileLength *msgFileLength; if(recv(sd,buff,MAX_PACK_SIZE,0)==SOCKET_ERROR) { cout<<"接收檔案長度失敗"<<WSAGetLastError()<<endl; return false; } msgFileLength=(Message::MsgFileLength *)buff; nFileLength=msgFileLength->fileLength; cout<<"接收到檔案長度"<<nFileLength<<endl; return true; } bool Client::ReceiveFileName(SOCKET sd) //接收下載的檔名 { char buff[MAX_PACK_SIZE]; memset(fileName,0,sizeof(char)*(_MAX_FNAME+_MAX_EXT)); Message::MsgFileName *msgFileName; if(recv(sd,buff,MAX_PACK_SIZE,0)==SOCKET_ERROR) { cout<<"接收檔名出錯"<<endl; return false; } msgFileName=(Message::MsgFileName *)buff; strcpy(fileName,msgFileName->fileName); cout<<"接收到檔名"<<fileName<<endl; return true; } bool Client::ReceiveFile(SOCKET sd) //接收檔案內容 { char buff[MAX_PACK_SIZE]; FILE *pFile; pFile=fopen(fileName,"a+b"); _int64 i=0; while(i+1<nFileLength) { int nRecv=recv(sd,buff,MAX_PACK_SIZE,0); if(nRecv==SOCKET_ERROR) { return false; } fwrite(buff,sizeof(char),nRecv,pFile); i+=nRecv; memset(buff,0,sizeof(char)*MAX_PACK_SIZE); } fclose(pFile); return true; } void Client::CloseSocket() //關閉套接字 { closesocket(sd); WSACleanup(); }