一个简单的点对点文件传输程序
黄定伍 402431143
在网络飞速发展的今天,信息的共享给我们带来了莫大的益处,而文件传输又是网络数据交换的主要形式,谈到文件传输大家可能会想到FTP文件传输协议,在这篇文件章中,我们并不汲及FTP.而是用windows socket来实现一个点对点文件传输.你可以把它想像成跟QQ里面的文件传输功能类似,只是我也不知道QQ里面所使用的是FTP或者是其它什么技术,但我是通过模拟它而实现本程序的.阅读本文章你需要具备一些MFC网络编程,和基本文件操作知识. 现在,来看看我是如何实现点对点文件传输功能的: 程序分为两个模块,一模块实现文件发送功能(服务器端),它需要创建一个socket并指定端口,然后监听,并等待客户端的连接,客户端连接上后用fopen二进制只读模式打开本地自己选择的一个文件,然后读取文件数据存放到一个数据结构中,通过socket发送给客户端;当然另一个模块的功能就是实现文件接收并写入磁盘了。它也需要创建一个socket,然后填写IP地址对服务器发送连接请求,服务器接收连接后发送一个数据结构过来,客户端通过数据结构内容得知文件名,文件大小,然后用fopen函数的二进制只写模式创建一个指定文件,然后通过数据接收写入,就可以实现点对点的文件传输了。 我是在VC6.0+WinXp sp1个人版上调试通过的。步骤如下: (1)创建一个支持window socket MFC基于对话框程序 (2)加入两个按钮控件,与一个文本框控件,并设置一个CString 变量m_strIP与文本框关连。 (3)在单击监听按钮事件加入下面代码: int nSend=0,nFileSize=0;
int cbSocketSend=0,nRead=0,dataID=1,dataID2=0;
int nSumPack=0;
CString Info;
SetWindowText("服务器");
GetDlgItem(IDC_CONNECT)->EnableWindow(false);
GetDlgItem(IDC_listen)->EnableWindow(false); //使连接按钮与监听按 钮无效
m_Server.Create(5177);
m_Server.Listen();
if(m_Server.Accept(m_Client))
{
MessageBox("有客户端连接上!","服务器:",MB_OK);
CFileDialog openFile(true);
if(openFile.DoModal()==IDOK)
{
FILE *pFile;
_DATA data;
pFile=fopen(openFile.GetPathName(),"rb"); //用二进制只读模式打开所选文件
fseek(pFile,0,SEEK_END); //文件指针移到文件尾 data.nFileSize=ftell(pFile); //得到文件大小
data.cbBuf=520;
data.dataID=-1;
data.IsEnd=38; //上面三个数据无意义
strcpy(data.szFileName,openFile.GetFileName());
fseek(pFile,0,SEEK_SET); //文件指针到文件头
m_Client.Send(&data,sizeof(data));
m_strMsg="等待客户端回应....";
UpdateData(false);
m_Client.Receive(&dataID,sizeof(dataID)); //接收一个整型变量
if(dataID!=0)
MessageBox("对方未准备好!!");
nSumPack=data.nFileSize/4096+1;
m_strMsg.Format("开始传输数据...估计数据包为%d个",nSumPack);
UpdateData(false);
Sleep(2000);
dataID=8;
m_Client.Send(&dataID,sizeof(dataID)); //通知开始传数据
nFileSize=data.nFileSize;
nSumPack=0;
while(nSend<nFileSize)
{
nSumPack++;
data.cbBuf=4*1024;
nRead=fread(data.szBuf,1,data.cbBuf,pFile);//读取4096字节数据
nSend+=nRead;//记录总共从文件读取了多少数据
data.cbBuf=nRead;
dataID++;
data.dataID=dataID;
if(nRead<4*1024)
data.IsEnd=TRUE;
cbSocketSend+=m_Client.Send(&data,sizeof(d ata));//发送数据
m_Client.Receive(&dataID2,sizeof(dataID2));//发送数据后确认对方是否正确收到
if(dataID2==dataID) //对方接收正确
{
if(nSumPack%10==0)
{
m_strMsg.Format("发送数据包ID=%d %d字节 成功-->文件总共 %d 字节已传送%d字节",dataID,data.cbBuf,nFileSize,nSend);
UpdateData(false);
Sleep(5);
}
}
else{
m_strMsg.Format("发送数据包ID=%d 共%d字节 失败!!!",dataID,data.cbBuf);
UpdateData(false);
Sleep(2000);
}
}
fclose(pFile);
Info.Format("发送完毕,总共发送 %d 字节! 读取文件长度: %d 实际文件长度为 %d字节",cbSocketSend,nSend,nFileSize);
MessageBox(Info,"提示!",MB_OK); m_Client.Close();
m_Server.Close();
GetDlgItem(IDC_listen)->EnableWindow(true);
}
}
(4)发连接按钮添加事件代码: _DATA data;
int nReceive=0,nFileSize=0,nWrite=0,dataID=0;
int nSumPack;
CString Info;
data.cbBuf=4*1024;
UpdateData();
if(m_strIP.IsEmpty())
{
MessageBox("服务器IP不能为空!");
return ;
} SetWindowText("客户端");
GetDlgItem(IDC_listen)->EnableWindow(false);
GetDlgItem(IDC_CONNECT)->EnableWindow(false);
m_Client.Create();
if(m_Client.Connect(m_strIP,5177))
{
MessageBox("已与服务器连上!!","客户端!",MB_OK);
m_Client.Receive(&data,sizeof(data));
Info.Format(" nFileSize=%d/n cbBuf=%d/n dataID=%d/n szFileName=%s/n szBuf=%s/n IsEnd=%d",
data.nFileSize,
data.cbBuf,
data.dataID,
data.szFileName,
data.szBuf);
MessageBox(Info); // 显示文件信息,其实没意义
nReceive=0;
nFileSize=data.nFileSize; //得到将要接收文件的大小
FILE *pFile;
nSumPack=nFileSize/4096+1;
dataID=0;
m_Client.Send(&dataID,sizeof(dataID));//回应服务器
m_Client.Receive(&dataID,sizeof(dataID));
if(dataID==8)
{
m_strMsg.Format("开始接收数据...共 %d 包 %d字节",nSumPack,nFileSize);
UpdateData(false);
Sleep(2000);
}
else
{
m_strMsg="未接收到数据可能出错...";
UpdateData(false);
Sleep(2000);
}
pFile=fopen(data.szFileName,"wb");//在当前目录创建指定文件
nSumPack=0;
while(nWrite<nFileSize)
{
nSumPack++;
nReceive+=m_Client.Receive(&data,sizeof(data));
if(data.cbBuf<0) //对方文件已读完
break;
nWrite+=data.cbBuf; //记录总共写入文件字节数
fwrite(data.szBuf,1,data.cbBuf,pFile);
fflush(pFile); //写入文件
m_Client.Send(&data.dataID,sizeof(int)); //回应服务器已接收到
if(nSumPack%10==0)
{
m_strMsg.Format("文件共 %d字节 已接收%d字节,正接收第 %d 个数据包 %字节 成功! ",nFileSize,nWrite,data.dataID,data.cbBuf);
UpdateData(false);
Sleep(5);
}
if(data.cbBuf<4096)//也是文件接收完毕
break;
}
fclose(pFile);
Info.Format("接收完毕,总共接收 %d 字节! 实际文件长度为 %d字节/n写入 %d字节",nReceive,nFileSize,nWrite);
MessageBox(Info,"提示!",MB_OK);
m_Client.Close();
GetDlgItem(IDC_CONNECT)->EnableWindow(true); } 下面是_DATA 结构的定义: typedef struct _tagDATA
{
int nFileSize; //记录文件大小以字节为单位
int cbBuf;
int dataID;
char szFileName[MAX_PATH]; //记录文件名
char szBuf[4*1024]; //读取文件的数据缓冲区
BOOL IsEnd;
}_DATA; 本程序实际是将服务器端模块与客户端模块集成在一个程序里面,运行后单击监听,则这个程序实例就是服务器端,运行后如果单击的是连接那么就是客户端。程序只能单向传输,并且客户端是无条件接收并存放到客户端程序当前目录,有兴趣的可以将其改进并用CFile实现。 转载请联系我:402431143