基于TCP的文件传输程序Word文件下载.docx

上传人:b****5 文档编号:21101694 上传时间:2023-01-27 格式:DOCX 页数:37 大小:4.74MB
下载 相关 举报
基于TCP的文件传输程序Word文件下载.docx_第1页
第1页 / 共37页
基于TCP的文件传输程序Word文件下载.docx_第2页
第2页 / 共37页
基于TCP的文件传输程序Word文件下载.docx_第3页
第3页 / 共37页
基于TCP的文件传输程序Word文件下载.docx_第4页
第4页 / 共37页
基于TCP的文件传输程序Word文件下载.docx_第5页
第5页 / 共37页
点击查看更多>>
下载资源
资源描述

基于TCP的文件传输程序Word文件下载.docx

《基于TCP的文件传输程序Word文件下载.docx》由会员分享,可在线阅读,更多相关《基于TCP的文件传输程序Word文件下载.docx(37页珍藏版)》请在冰豆网上搜索。

基于TCP的文件传输程序Word文件下载.docx

传输前的接收提醒以及传输过程中的控制。

2、软件模块划分

本程序可以划分成以上三个模块:

传输控制模块,文件传输模块和服务连接模块。

其中:

服务连接模块用来建立客户端到服务器的连接

文件传输模块主要用两个线程:

_SendThread和_ListenThread来完成,实现发送和接收文件的功能。

传输控制模块主要通过封装好的可串行化信息类CMessage互相传递两端的文件操作消息,响应“暂停传输”,“关闭连接”等功能

五、设计步骤

1、服务连接模块

先要建立起客户端与服务器之间的连接,大致过程如下:

1服务器启动:

if(m_nServerType==SERVER)

{

//创建服务器套接字

m_psockServer=newCListenSocket(this);

if(!

m_psockServer->

Create(m_wPort))//绑定端口

{

deletem_psockServer;

m_psockServer=NULL;

MessageBox(GetError(GetLastError()),_T("

错误"

),MB_ICONHAND);

return;

}

//开启线程,监听客户端连接

Listen())

}

2客户端填入相应的IP地址,连接服务器

if(((CIPAddressCtrl*)GetDlgItem(IDC_IPADDRESS))->

IsBlank())

MessageBox("

IP地址不能为空"

"

MB_ICONHAND);

//创建客户端套接字

m_psockClient=newCClientSocket(this);

m_psockClient->

Create())

deletem_psockClient;

m_psockClient=NULL;

//与服务器建立连接

CStringstrIPAddress;

GetDlgItem(IDC_IPADDRESS)->

GetWindowText(strIPAddress);

Connect(strIPAddress,m_wPort))//Connect连接服务器,服务器用OnAccept响应

//初始化客户端套接字

m_psockClient->

Init();

3服务器响应

CClientSocket*pSocket=newCClientSocket(this);

//接收请求后获得新的套接字,交由新的套接字处理请求

if(m_psockServer->

Accept(*pSocket))//非零则接收连接请求成功

//初始化Accpet返回的套接字

pSocket->

CMessage*pMsg;

//检测套接字是否为空,空则还没有连接任何客户端

if(m_psockClient==NULL)

pMsg=newCMessage(CONNECT_BE_ACCEPT);

pSocket->

SendMsg(pMsg);

//发回“这边可以连接”消息回去,客户端的Receive接收

m_psockClient=pSocket;

//若为服务器,则之前未初始化,可保存这个套接字,用来跟客户端交互

GetDlgItem(IDC_SELECT_FILE)->

EnableWindow(TRUE);

else

pMsg=newCMessage(CONNECT_BE_REFUSE);

4客户端收到连接成功消息

if(pMsg->

m_nType==CONNECT_BE_ACCEPT)

GetDlgItem(IDC_SELECT_FILE)->

return;

2、传输控制模块

该工程用了两对套接字,一对用来接收传输控制消息,一对用来收发文件。

我们这个模块用的是第一对

在MFC中定义了一个在套接字编程中使用的CSocketFile类,可以充分发挥CSocket类的特性。

CSocketFile类是CFile的派生类,主要用来在WindowsSockets编程中发送和接收序列化数据(如结构体数据)。

通过把CSocketFile类对象、CArchive对象和CSocket创建的套接字对象联系起来,可以实现数据的加载(接收)和存储(发送)。

MFC集成CSocketFile、CSocket、CArchive可以简化网络传输。

下图可以直观地反应这三者是如何结合来在客户端和服务器之间传输消息的

先来看看服务器和客户端的套接字初始化工作:

m_pFile=newCSocketFile(this);

m_pArchiveIn=newCArchive(m_pFile,CArchive:

:

load);

m_pArchiveOut=newCArchive(m_pFile,CArchive:

store);

这样一来,当使用m_pArchiveIn对象来调用可串行化消息类CMessage的Serialize时,接收方就能响应OnReceive消息,来完成控制信息的传递。

本工程在主类CsendFileDlg中定义了传输的三种状态:

m_bIsWait:

已连接,正在等待传输文件

m_bIsTransmitting:

正在传输文件

m_bIsStop:

传输已终止

本工程定义的可串行化消息类CMessage,用来传递文件名m_strFileName、文件长度m_dwFileSize以及当前的传输状态m_nType,大致流程如下图:

其中,我在建工程的时候,把客户端和服务器集成在了一个界面OnReceive就用来接收两方的消息,关键代码如下:

CMessage*pMsg=newCMessage();

pSocket->

ReceiveMsg(pMsg);

m_nType==CONNECT_BE_REFUSE)

MessageBox(_T("

服务器正在和另外的客户端连接,稍等"

),_T("

deletem_psockClient;

m_psockClient=NULL;

return;

if(pMsg->

m_nType==DISCONNECT)

对方已经关闭"

),_T("

警告"

if(m_psockClient!

=NULL)

//接收请求

m_nType==REQUEST)

m_bIsWait=TRUE;

//忙状态置1

m_strFileName=pMsg->

m_strFileName;

m_dwFileSize=pMsg->

m_dwFileSize;

CStringstrName,strSize;

strName.Format("

文件名:

%s"

m_strFileName);

strSize.Format("

文件大小:

%ld字节"

strSize);

//这里完成了发送文件前给对方提示的功能

if(MessageBox(strName+"

\r\n"

+strSize,"

对方请求向你发送文件"

MB_OKCANCEL)==IDCANCEL)

CMessage*pMsg=newCMessage(REFUSE);

m_psockClient->

return;

CFileDialogdlg(FALSE,NULL,NULL,OFN_HIDEREADONLY|OFN_OVERWRITEPROMPT,"

所有文件(*.*)|*.*||"

this);

dlg.m_ofn.lpstrTitle=_T("

另存为"

);

strcpy(dlg.m_ofn.lpstrFile,m_strFileName.GetBuffer(m_strFileName.GetLength()));

if(dlg.DoModal()==IDOK){

if(m_bIsWait==FALSE){

MessageBox(_T("

对方已经取消文件发送"

),MB_ICONEXCLAMATION);

return;

}

m_bIsClient=FALSE;

m_strPath=dlg.GetPathName();

//启动接收文件的线程

GetDlgItem(IDC_SR)->

SetWindowText("

已接收:

"

pThreadListen=:

AfxBeginThread(_ListenThread,this);

//服务器接收到RUEST,开启接收文件线程

if(m_bIsWait==TRUE)

{

//告诉对方文件发送请求被拒绝

CMessage*pMsg=newCMessage(REFUSE);

m_bIsWait=FALSE;

//当对方同意,并且已经准备好接收文件的时候

m_nType==ACCEPT)

KillTimer

(1);

m_bIsWait=FALSE;

//等待状态清空

//启动发送文件线程

GetDlgItem(IDC_SR)->

已发送:

pThreadSend=:

AfxBeginThread(_SendThread,this);

//当发送文件请求被拒绝

m_nType==REFUSE){

请求被拒绝"

//当对方取消文件传输时执行该if语句里面的内容

m_nType==CANCEL)

3、文件传输模块

两个线程:

_ListenThread():

建立新的监听套接字,准备接收文件

_SendThread():

建立客户端套接字,连接监听套接字,发送文件

当两方建立了可靠的连接,一方选择文件后,就会使用传输控制模块给对方发送文件发送请求,对方的ProcessReceive接收到后查看当前的消息状态,(上个模块的OnReceive已经写得很清楚),同意接受后,开启文件接收线程,同时给发送方返回一个准备完成状态,于是,发送方开启文件发送线程,最后完成文件的传输工作,同时置状态m_bIsStop为TRUE,便于双方传输控制

1发送线程关键函数SendFile

voidCSendFileDlg:

SendFile(CSocket&

senSo)

{

m_bIsTransmitting=TRUE;

CFilefile;

if(!

file.Open(m_strPath,CFile:

modeRead|CFile:

typeBinary))

AfxMessageBox(_T("

文件打开失败"

));

senSo.Close();

m_ctrlProgress.SetRange32(0,m_dwFileSize);

intnSize=0,nLen=0;

DWORDdwCount=0;

charbuf[BLOCKSIZE]={0};

file.Seek(0,CFile:

begin);

//开始传送

while

(1)

//每次读取BLOCKSIZE大小

nLen=file.Read(buf,BLOCKSIZE);

if(nLen==0)

break;

//发送文件内容

nSize=senSo.Send(buf,nLen);

dwCount+=nSize;

m_ctrlProgress.SetPos(dwCount);

CStringstrTransfersSize;

strTransfersSize.Format("

%ld字节"

dwCount);

//设置进度条属性

GetDlgItem(IDC_RECEIVE_SIZE)->

SetWindowText(strTransfersSize);

intp=((double)dwCount)/((int)m_dwFileSize+1)*100+1;

%d"

p);

strTransfersSize+="

%"

;

//判断用户是否停止发送

if(m_bIsStop)

m_bIsStop=FALSE;

if(nSize==SOCKET_ERROR)

file.Close();

senSo.Close();

if(m_dwFileSize==dwCount)

文件发送成功"

else

文件发送失败"

m_ctrlProgress.SetPos(0);

m_bIsTransmitting=FALSE;

}

2接收线程关键函数ReceiveFile

ReceiveFile(CSocket&

recSo)

//停止等待超时计时器

KillTimer

(2);

m_bIsWait=FALSE;

//正在传输置1

intnSize=0;

DWORDdwCount=0;

CFilefile(m_strPath,CFile:

modeCreate|CFile:

modeWrite);

//开始接收文件

while

(1)

//每次接收BLOCKSIZE大小的内容

nSize=recSo.Receive(buf,BLOCKSIZE);

if(nSize==0)

//写入文件

file.Write(buf,nSize);

GetDlgItem(IDC_RECEIVE_SIZE)->

GetDlgItem(IDC_PECENT)->

SetWindowText(strTransfersSize)

//用户是否停止了接收

recSo.Close();

文件接收成功"

文件接收失败"

六、关键问题及其解决方法

1、在传输文件前、中、后能让程序响应各类控制消息

集成后的互通消息代码很简单:

voidSerialize(CArchive&

ar){

if(ar.IsStoring()){

ar<

<

m_nType;

m_strFileName;

m_dwFileSize;

ar>

>

CSocket、CSocketFile、CArchive三个类的联动,完美地解决了这个问题,在传输文件的过程中,若一方有事件发生(暂停传输、关闭连接等等),则改变预先设定的状态值,SendMsg给CSocketFile,触发对方的虚函数OnReceive,从而解决通信问题。

2、解决单线程传输阻塞的非“非阻塞”式方法

在使用AfxBeginThread实现多线程的同时,也增加了诸如发送超时、接收超时等判断(使用Windows定时器SetTimer响应OnTime来实现),这一定程度上增强了程序的鲁棒性

文件的传输时阻塞式的,所以在主线程中直接传输文件必然导致界面卡死,所以这里我用到了多线程技术,为传输文件和接受文件分别开设线程。

1_ListenThread

UINT_ListenThread(LPVOIDlparam)

CSendFileDlg*pDlg=(CSendFileDlg*)lparam;

//创建服务器端套接字

CSocketsockSrvr;

if(!

sockSrvr.Create(pDlg->

m_wPort+PORT))

pDlg->

TransfersFailed();

:

MessageBox((HWND)lparam,pDlg->

GetError(GetLastError()),_T("

),MB_ICONHAND|MB_OK);

return-1;

sockSrvr.Listen())//监听

//接收套接字已经创建向主对话框发送自定义消息,该消息发送一个消息给发送方,可以开始传数据

pDlg->

SendMessage(WM_ACCEPT_TRANSFERS);

CSocketrecSo;

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 农林牧渔 > 林学

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1