《使用TCP協(xié)議實(shí)現(xiàn)文件傳輸.doc》由會(huì)員分享,可在線閱讀,更多相關(guān)《使用TCP協(xié)議實(shí)現(xiàn)文件傳輸.doc(17頁珍藏版)》請?jiān)谘b配圖網(wǎng)上搜索。
使用TCP協(xié)議實(shí)現(xiàn)文件傳輸
2013-01-18 10:35:43 我來說兩句 作者:hanchaoqi
收藏 我要投稿
使用TCP協(xié)議實(shí)現(xiàn)文件傳輸。程序會(huì)分為服務(wù)器端和客戶端,首先運(yùn)行服務(wù)器端,監(jiān)聽來自客戶端的連接,客戶端運(yùn)行后會(huì)通過程序內(nèi)的服務(wù)器端IP地址,向服 務(wù)器發(fā)送連接請求。雙方建立請求之后,客戶端將所需文件的文件名和絕對(duì)路徑傳輸給服務(wù)器,如果服務(wù)器找到此文件,則將此文件傳輸給客戶端,然后斷開連接。
具體算法描述如下:
【1】服務(wù)器端:
1、初始化socket服務(wù)
2、監(jiān)聽連接請求并做相應(yīng)的處理
2.1創(chuàng)建監(jiān)聽套接字
2.2監(jiān)聽套接口
2.3接受套接字的連接
2.4接收客戶端傳來的數(shù)據(jù)
case 文件絕對(duì)路徑:
按照路徑找到文件,并打開。提取本地文件名,發(fā)回給客戶端
發(fā)送文件總長度給客戶端
case 已準(zhǔn)備接收文件完畢
if 發(fā)送緩沖區(qū)為空
讀取文件,寫入緩沖區(qū)
將文件流分成大小相同的組(最后一組可能會(huì)小一點(diǎn)),順次發(fā)送給客戶端
將緩沖區(qū)清空
case 文件成功傳送
打印消息,退出
case 文件已存在
打印消息,退出
2.5關(guān)閉同客戶端的連接
3、釋放socket服務(wù)
【2】客戶端:
1、初始化socket,winsock服務(wù)
2、連接服務(wù)器,進(jìn)行數(shù)據(jù)的傳輸
2.1初始化,創(chuàng)建套接字
2.2通過IP地址,向服務(wù)器發(fā)送連接請求,建立連接
2.3主動(dòng)發(fā)送所求文件絕對(duì)路徑
2.4接受服務(wù)器端數(shù)據(jù)并做相應(yīng)處理
case 打開文件錯(cuò)誤:
重新發(fā)送文件絕對(duì)路徑至服務(wù)器,請求重發(fā)
case 文件長度:
打印消息
case 文件名:
if 文件已經(jīng)存在
發(fā)送“文件已經(jīng)存在”
else
分配緩沖區(qū),并向服務(wù)器發(fā)送“Ready”消息
case 文件流:
為已接收文件名創(chuàng)建文件
打開文件,將文件流數(shù)據(jù)寫入文件,直至接收所有分組數(shù)據(jù)
發(fā)送“成功接收“消息
3、關(guān)閉套接字
釋放服務(wù)
源程序:
【1】服務(wù)器端:
頭文件:
[cpp]
/*server.h*/
#pragma comment(lib, "WS2_32")
#include
#include
#include
#include
#ifndef COMMONDEF_H
#define COMMONDEF_H
#define MAX_PACKET_SIZE 10240 // 數(shù)據(jù)包的最大長度,單位是sizeof(char)
#define MAXFILEDIRLENGTH 256 // 存放文件路徑的最大長度
#define PORT 4096 // 端口號(hào)
//#define SERVER_IP "127.0.0.1" // server端的IP地址
// 各種消息的宏定義
#define INVALID_MSG -1 // 無效的消息標(biāo)識(shí)
#define MSG_FILENAME 1 // 文件的名稱
#define MSG_FILELENGTH 2 // 傳送文件的長度
#define MSG_CLIENT_READY 3 // 客戶端準(zhǔn)備接收文件
#define MSG_FILE 4 // 傳送文件
#define MSG_SENDFILESUCCESS 5 // 傳送文件成功
#define MSG_OPENFILE_ERROR 10 // 打開文件失敗,可能是文件路徑錯(cuò)誤找不到文件等原因
#define MSG_FILEALREADYEXIT_ERROR 11 // 要保存的文件已經(jīng)存在了
class CCSDef
{
public:
#pragma pack(1) // 使結(jié)構(gòu)體的數(shù)據(jù)按照1字節(jié)來對(duì)齊,省空間
// 消息頭
struct TMSG_HEADER
{
char cMsgID; // 消息標(biāo)識(shí)
TMSG_HEADER(char MsgID = INVALID_MSG)
: cMsgID(MsgID)
{
}
};
// 請求傳送的文件名
// 客戶端傳給服務(wù)器端的是全路徑名稱
// 服務(wù)器傳回給客戶端的是文件名
struct TMSG_FILENAME : public TMSG_HEADER
{
char szFileName[256]; // 保存文件名的字符數(shù)組
TMSG_FILENAME()
: TMSG_HEADER(MSG_FILENAME)
{
}
};
// 傳送文件長度
struct TMSG_FILELENGTH : public TMSG_HEADER
{
long lLength;
TMSG_FILELENGTH(long length)
: TMSG_HEADER(MSG_FILELENGTH), lLength(length)
{
}
};
// Client端已經(jīng)準(zhǔn)備好了,要求Server端開始傳送文件
struct TMSG_CLIENT_READY : public TMSG_HEADER
{
TMSG_CLIENT_READY()
: TMSG_HEADER(MSG_CLIENT_READY)
{
}
};
// 傳送文件
struct TMSG_FILE : public TMSG_HEADER
{
union // 采用union保證了數(shù)據(jù)包的大小不大于MAX_PACKET_SIZE * sizeof(char)
{
char szBuff[MAX_PACKET_SIZE];
struct
{
int nStart;
int nSize;
char szBuff[MAX_PACKET_SIZE - 2 * sizeof(int)];
}tFile;
};
TMSG_FILE()
: TMSG_HEADER(MSG_FILE)
{
}
};
// 傳送文件成功
struct TMSG_SENDFILESUCCESS : public TMSG_HEADER
{
TMSG_SENDFILESUCCESS()
: TMSG_HEADER(MSG_SENDFILESUCCESS)
{
}
};
// 傳送出錯(cuò)信息,包括:
// MSG_OPENFILE_ERROR:打開文件失敗
// MSG_FILEALREADYEXIT_ERROR:要保存的文件已經(jīng)存在了
struct TMSG_ERROR_MSG : public TMSG_HEADER
{
TMSG_ERROR_MSG(char cErrorMsg)
: TMSG_HEADER(cErrorMsg)
{
}
};
#pragma pack()
};
#endif
cpp文件:
[cpp]
/*Server.cpp*/
#include"Server.h"
char g_szNewFileName[MAXFILEDIRLENGTH];
char g_szBuff[MAX_PACKET_SIZE + 1];
long g_lLength;
char* g_pBuff = NULL;
//初始化socket庫
bool InitSocket();
//關(guān)閉socket庫
bool CloseSocket();
//解析消息并進(jìn)行相應(yīng)的處理
bool ProcessMsg(SOCKET sClient);
//監(jiān)聽Client消息
void ListenToClient();
//打開文件
bool OpenFile(CCSDef::TMSG_HEADER* pMagHeader,SOCKET sClient);
//傳送文件
bool SendFile(SOCKET sClient);
//讀取文件進(jìn)緩沖區(qū)
bool ReadFile(SOCKET sClient);
int main()
{
while(1)
{
InitSocket();
ListenToClient();
CloseSocket();
system("del E:\\test1.A_exp");
}
//system("pause");
return 0;
}
//初始化socket庫
bool InitSocket()
{
WSADATA wsaData;
WORD socketVersion=MAKEWORD(2,2);
if(::WSAStartup(socketVersion,&wsaData)!=0)
{//初始化WinSock服務(wù)
printf("Init socket dll error\n");
return false;
}
return true;
}
//關(guān)閉socket庫
bool CloseSocket()
{//釋放winsock庫
::WSACleanup();
if(g_pBuff != NULL)
{
delete [] g_pBuff;
g_pBuff = NULL;
}
return true;
}
//解析消息并進(jìn)行相應(yīng)的處理
bool ProcessMsg(SOCKET sClient)
{
//從套接口中接收數(shù)據(jù),返回copy的字節(jié)數(shù)
int nRecv = ::recv(sClient,g_szBuff,MAX_PACKET_SIZE+1,0);
if(nRecv>0)
{
g_szBuff[nRecv]=\0;
}
//解析命令
CCSDef::TMSG_HEADER* pMsgHeader=(CCSDef::TMSG_HEADER*)g_szBuff;
switch(pMsgHeader->cMsgID)
{
case MSG_FILENAME://文件名
{
OpenFile(pMsgHeader,sClient);
}
break;
case MSG_CLIENT_READY://客戶端已準(zhǔn)備完畢,開始傳送文件
{
SendFile(sClient);
}
break;
case MSG_SENDFILESUCCESS://傳送文件成功
{
printf("Send File Success!\n");
return false;
}
break;
case MSG_FILEALREADYEXIT_ERROR://要保存的文件已經(jīng)存在
{
printf("The file ready to send already exit!\n");
return false;
}
break;
}
return true;
}
//監(jiān)聽Client消息
void ListenToClient()
{
//創(chuàng)建套接字
SOCKET sListen = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if(sListen == SOCKET_ERROR)
{
printf("Init Socket Error!\n");
return;
}
//綁定socket
sockaddr_in sin;
sin.sin_family=AF_INET;
sin.sin_port=htons(PORT);
sin.sin_addr.S_un.S_addr=INADDR_ANY;
if (::bind(sListen, (LPSOCKADDR)&sin, sizeof(sockaddr_in)) == SOCKET_ERROR)
{
printf("Bind Error!\n");
return;
}
// 設(shè)置socket進(jìn)入監(jiān)聽狀態(tài)
if(::listen(sListen,10)==SOCKET_ERROR)
{
printf("Listen Error!\n");
return;
}
printf("Listening To Client...\n");
//循環(huán)接收client端的連接請求
sockaddr_in ClientAddr;
int nAddrLen = sizeof(sockaddr_in);
SOCKET sClient;
//取隊(duì)列最前端客戶連接請求,創(chuàng)建套接字連接通道
while((sClient=::accept(sListen,(sockaddr*)&ClientAddr,&nAddrLen))==INVALID_SOCKET)
{}
//解析消息并進(jìn)行相應(yīng)的處理
//int count=10;//作為定時(shí)當(dāng)程序執(zhí)行10s未完成時(shí)直接退出
//while(ProcessMsg(sClient)==true&&count>0)
//{
// Sleep(1000);
// count--;
//}
while(ProcessMsg(sClient)==true)
{
Sleep(1000);
}
//關(guān)閉同客戶端的連接
::closesocket(sClient);
::closesocket(sListen);
}
//打開文件
bool OpenFile(CCSDef::TMSG_HEADER* pMsgHeader,SOCKET sClient)
{
CCSDef::TMSG_FILENAME* pRequstFileNameMsg=(CCSDef::TMSG_FILENAME*)pMsgHeader;
//對(duì)文件名進(jìn)行處理
char *p1,*p2;
for(p1=pRequstFileNameMsg->szFileName,p2=g_szNewFileName;*p1!=\0;p1++,p2++)
{
if(*p1!=\n)
{
*p2=*p1;
}
if(*p2==\\)//將‘\’轉(zhuǎn)換為‘\\’
{
*(++p2)=\\;
}
}
*p2=\0;
ReadFile(sClient);
return true;
}
//傳送文件
bool SendFile(SOCKET sClient)
{
if (NULL == g_pBuff)
{//如果緩沖區(qū)為空
ReadFile(sClient);
}
int nPacketBufferSize = MAX_PACKET_SIZE - 2 * sizeof(int); // 每個(gè)數(shù)據(jù)包存放文件的buffer大小
// 如果文件的長度大于每個(gè)數(shù)據(jù)包所能傳送的buffer長度那么就分塊傳送
for (int i = 0; i < g_lLength; i += nPacketBufferSize)
{
CCSDef::TMSG_FILE tMsgFile;
tMsgFile.tFile.nStart = i;
if (i + nPacketBufferSize + 1> g_lLength)
{//文件塊已經(jīng)是最后一塊
tMsgFile.tFile.nSize = g_lLength - i;
}
else
{
tMsgFile.tFile.nSize = nPacketBufferSize;
}
memcpy(tMsgFile.tFile.szBuff, g_pBuff + tMsgFile.tFile.nStart, tMsgFile.tFile.nSize);//copy到緩沖區(qū)
::send(sClient, (char*)(&tMsgFile), sizeof(CCSDef::TMSG_FILE), 0);
Sleep(0.5);
}
delete [] g_pBuff;
g_pBuff = NULL;
return true;
}
//讀取文件進(jìn)緩沖區(qū)
bool ReadFile(SOCKET sClient)
{
if(g_pBuff!=NULL)
{//如果緩沖區(qū)不為空
return true;
}
//打開文件
FILE *pFile;
if((pFile = fopen(g_szNewFileName, "rb"))==NULL)
{//文件打開失敗,發(fā)送錯(cuò)誤報(bào)告
printf("Cannot find the file, request the client input file name again\n");
CCSDef::TMSG_ERROR_MSG tMsgErrorMsg(MSG_OPENFILE_ERROR);
::send(sClient, (char*)(&tMsgErrorMsg), sizeof(CCSDef::TMSG_ERROR_MSG), 0);
return false;
}
//傳送文件長度到Client
fseek(pFile,0,SEEK_END);//重定位指針到文件末尾
g_lLength=ftell(pFile);//返回文件指針相對(duì)于文件頭的偏移量
printf("File Length = %d\n", g_lLength);
CCSDef::TMSG_FILELENGTH tMsgFileLength(g_lLength);
::send(sClient,(char*)(&tMsgFileLength), sizeof(CCSDef::TMSG_FILELENGTH), 0);
// 處理文件全路徑名,把文件名分解出來
//磁盤號(hào),目錄,文件名,后綴名
char szDrive[_MAX_DRIVE], szDir[_MAX_DIR], szFname[_MAX_FNAME], szExt[_MAX_EXT];
_splitpath(g_szNewFileName, szDrive, szDir, szFname, szExt);
strcat(szFname,szExt);
CCSDef::TMSG_FILENAME tMsgFileName;
strcpy(tMsgFileName.szFileName, szFname);
printf("Send File Name: %s\n", tMsgFileName.szFileName);
::send(sClient, (char*)(&tMsgFileName), sizeof(CCSDef::TMSG_FILENAME), 0);
//分配緩沖區(qū),讀取文件內(nèi)容
g_pBuff = new char[g_lLength + 1];
if (g_pBuff == NULL)
{
return false;
}
fseek(pFile, 0, SEEK_SET);
fread(g_pBuff, sizeof(char), g_lLength, pFile);
g_pBuff[g_lLength] = \0;
fclose(pFile);
return true;
}
【2】客戶端:
頭文件同服務(wù)器端頭文件
源程序文件:
[cpp]
/*Client.cpp*/
#include"Client.h"
long g_lLength = 0;
char* g_pBuff = NULL;
char g_szFileName[MAXFILEDIRLENGTH];
char g_szBuff[MAX_PACKET_SIZE+1];
SOCKET g_sClient;
// 初始化socket庫
bool InitSocket();
// 關(guān)閉socket庫
bool CloseSocket();
// 把用戶輸入的文件路徑傳送到server端
bool SendFileNameToServer();
// 與server端連接
bool ConectToServer();
// 打開文件失敗
bool OpenFileError(CCSDef::TMSG_HEADER *pMsgHeader);
// 分配空間以便寫入文件
bool AllocateMemoryForFile(CCSDef::TMSG_HEADER *pMsgHeader);
// 寫入文件
bool WriteToFile(CCSDef::TMSG_HEADER *pMsgHeader);
// 處理server端傳送過來的消息
bool ProcessMsg();
int main()
{
while(1)
{
InitSocket();
ConectToServer();
CloseSocket();
}
//system("pause");
return 0;
}
// 初始化socket庫
bool InitSocket()
{
//初始化SOCKET
WSADATA wsaData;
WORD socketVersion=MAKEWORD(2,2);
if(::WSAStartup(socketVersion,&wsaData)!=0)
{
printf("Init socket dll error\n");
exit(-1);
}
return true;
}
// 關(guān)閉socket庫
bool CloseSocket()
{
// 關(guān)閉套接字
::closesocket(g_sClient);
// 釋放winsock庫
::WSACleanup();
return true;
}
// 把用戶輸入的文件路徑傳送到server端
bool SendFileNameToServer()
{
char szFileName[MAXFILEDIRLENGTH];
printf("Input the File Directory: ");
//fgets(szFileName, MAXFILEDIRLENGTH, stdin);
strcpy(szFileName,"E:\\test1.A_exp");
// 把文件路徑發(fā)到server端
CCSDef::TMSG_FILENAME tMsgRequestFileName;
strcpy(tMsgRequestFileName.szFileName, szFileName);
if (::send(g_sClient, (char*)(&tMsgRequestFileName), sizeof(CCSDef::TMSG_FILENAME), 0) == SOCKET_ERROR)
{
printf("Send File Name Error!\n");
exit(-1);
}
return true;
}
// 與server端連接
bool ConectToServer()
{
// 初始化socket套接字
if ((g_sClient = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == SOCKET_ERROR)
{
printf("Init Socket Error!\n");
exit(-1);
}
sockaddr_in servAddr;
servAddr.sin_family = AF_INET;
servAddr.sin_port = htons(PORT);
servAddr.sin_addr.S_un.S_addr = ::inet_addr(SERVER_IP);
if ((::connect(g_sClient, (sockaddr*)&servAddr, sizeof(sockaddr_in))) == INVALID_SOCKET)
{
printf("Connect to Server Error!\n");
exit(-1);
}
// 輸入文件路徑傳輸?shù)絪erver端
SendFileNameToServer();
// 接收server端傳過來的信息,直到保存文件成功為止
while (ProcessMsg() == true)
{
Sleep(1000);
}
return true;
}
// 打開文件失敗
bool OpenFileError(CCSDef::TMSG_HEADER *pMsgHeader)
{
if (g_pBuff != NULL)//如果緩沖區(qū)內(nèi)有數(shù)據(jù)
return true;
assert(pMsgHeader != NULL);
printf("Cannot find file!\n");
// 重新輸入文件名稱
SendFileNameToServer();
return true;
}
// 分配空間以便寫入文件
bool AllocateMemoryForFile(CCSDef::TMSG_HEADER *pMsgHeader)
{
assert(pMsgHeader != NULL);
if (g_pBuff != NULL)
{
return true;
}
CCSDef::TMSG_FILENAME* pRequestFilenameMsg = (CCSDef::TMSG_FILENAME*)pMsgHeader;
printf("File Name: %s\n", pRequestFilenameMsg->szFileName);
// 把文件的路徑設(shè)置為D盤根目錄下
strcpy(g_szFileName, "D:\\");
strcat(g_szFileName, "test2.B_imp");
//strcat(g_szFileName, pRequestFilenameMsg->szFileName);
// 查找相同文件名的文件是否已經(jīng)存在,如果存在報(bào)錯(cuò)退出
FILE* pFile;
if ((pFile = fopen(g_szFileName, "r")) != NULL)
{
// 文件已經(jīng)存在,要求重新輸入一個(gè)文件
printf("The file already exist!\n");
CCSDef::TMSG_ERROR_MSG tMsgErrorMsg(MSG_FILEALREADYEXIT_ERROR);
::send(g_sClient, (char*)(&tMsgErrorMsg), sizeof(CCSDef::TMSG_ERROR_MSG), 0);
fclose(pFile);
return false;
}
// 分配緩沖區(qū)開始接收文件,如果分配成功就給server端發(fā)送開始傳送文件的要求
g_pBuff = new char[g_lLength + 1];
if (g_pBuff != NULL)
{
memset(g_pBuff, \0, g_lLength + 1);
printf("Now ready to get the file %s!\n", pRequestFilenameMsg->szFileName);
CCSDef::TMSG_CLIENT_READY tMsgClientReady;
if (::send(g_sClient, (char*)(&tMsgClientReady), sizeof(CCSDef::TMSG_CLIENT_READY), 0) == SOCKET_ERROR)
{
printf("Send Error!\n");
exit(-1);
}
}
else
{
printf("Alloc memory for file error!\n");
exit(-1);
}
return true;
}
// 寫入文件
bool WriteToFile(CCSDef::TMSG_HEADER *pMsgHeader)
{
assert(pMsgHeader != NULL);
CCSDef::TMSG_FILE* pMsgFile = (CCSDef::TMSG_FILE*)pMsgHeader;
int nStart = pMsgFile->tFile.nStart;
int nSize = pMsgFile->tFile.nSize;
memcpy(g_pBuff + nStart, pMsgFile->tFile.szBuff, nSize);
if (nStart == 0)
{
printf("Saving file into buffer...\n");
}
memcpy(g_pBuff + nStart, pMsgFile->tFile.szBuff, nSize);
// 如果已經(jīng)保存到緩沖區(qū)完畢就寫入文件
if (nStart + nSize >= g_lLength)
{
printf("Writing to disk....\n");
// 寫入文件
FILE* pFile;
pFile = fopen(g_szFileName, "w+b");
fwrite(g_pBuff, sizeof(char), g_lLength, pFile);
delete [] g_pBuff;
g_pBuff = NULL;
fclose(pFile);
// 保存文件成功傳送消息給server退出server
CCSDef::TMSG_SENDFILESUCCESS tMsgSendFileSuccess;
while (::send(g_sClient, (char*)(&tMsgSendFileSuccess), sizeof(CCSDef::TMSG_SENDFILESUCCESS), 0) == SOCKET_ERROR)
{
}
printf("Save the file %s success!\n", g_szFileName);
return true;
}
else
{
return false;
}
}
// 處理server端傳送過來的消息
bool ProcessMsg()
{
CCSDef::TMSG_HEADER *pMsgHeader;
int nRecv = ::recv(g_sClient, g_szBuff, MAX_PACKET_SIZE + 1, 0);
pMsgHeader = (CCSDef::TMSG_HEADER*)g_szBuff;
switch (pMsgHeader->cMsgID)
{
case MSG_OPENFILE_ERROR: // 打開文件錯(cuò)誤
{
OpenFileError(pMsgHeader);
}
break;
case MSG_FILELENGTH: // 文件的長度
{
if (g_lLength == 0)
{
g_lLength = ((CCSDef::TMSG_FILELENGTH*)pMsgHeader)->lLength;
printf("File Length: %d\n", g_lLength);
}
}
break;
case MSG_FILENAME: // 文件名
{
return AllocateMemoryForFile(pMsgHeader);
}
break;
case MSG_FILE: // 傳送文件,寫入文件成功之后退出這個(gè)函數(shù)
{ www.2cto.com
if (WriteToFile(pMsgHeader))
{
/*Sleep(1000);*/
return false;
}
}
break;
}
return true;
}
鏈接地址:http://www.820124.com/p-8871247.html