}
else
{
ar>>m_nType;
ar>>m_strFileName;
ar>>m_dwFileSize;
}
}
在写这个类的时候,我们可以使用任何一个文本编(如记事本或是UltraEdit等)辑器进行编写,把类的定义保存在Message.h中,把类的实现保存在Message.cpp文件中。
要注意的是我们还需要在Message.cpp文件的首部加上#include"stdafx.h"和#include"Message.h"两行代码,将stdafx.h文件和Message.h文件包含进来。
最后我们还需要把这个类加到工程中,先把Message.h和Message.cpp文件复制到工程目录下,然后在VC中通过Project菜单->AddToProject->Files把这两个文件添加到工程中。
2.CListenSocket类
负责监听管理的套接字类CListenSocket。
使用ClassView或ClassWizard进行创建,如图7所示。
图7
使用ClassView或手工加入如下的函数及成员变量的定义:
public:
CListenSocket(CFileTransfersDlg*pdlgMain);
protected:
CFileTransfersDlg*m_pdlgMain;
CListenSocket(CFileTransfersDlg*pdlgMain)为重载的构造函数;m_pdlgMain为指向主对话框类CFileTransfersDlg的指针。
添加完后我们还需要对类中的两个构造函数的内容进行修改以实现对类的初始化,其源代码如下:
CListenSocket:
:
CListenSocket(CFileTransfersDlg*pdlgMain)
{
m_pdlgMain=pdlgMain;
}
CListenSocket:
:
CListenSocket()
{
m_pdlgMain=NULL;
}
重载基类的OnAccept函数以使对来自客户的连接请求作出响应,OnAccept函数的源代码如下:
voidCListenSocket:
:
OnAccept(intnErrorCode)
{
m_pdlgMain->ProcessAccept();
CSocket:
:
OnAccept(nErrorCode);
}
当该套接字接收到客户的连接请求时,就调用CFileTransfersDlg对象的ProcessAccept()函数进行处理。
由于该类中使用到了CFileTransfersDlg类,因此在文件ListenSocket.h的首部还需加入如下头文件的包含语句:
#include"FileTransfersDlg.h"
3.CClientSocket类
该类用于连接的管理,其创建的方法与CListenSocket相似,其定义如下:
classCClientSocket:
publicCSocket
{
//Attributes
public:
//Operations
public:
CClientSocket();
virtual~CClientSocket();
//Overrides
public:
CSocketFile*m_pFile;
CArchive*m_pArchiveIn;
CArchive*m_pArchiveOut;
voidInit();
voidAbort();
BOOLSendMsg(CMessage*pMsg);
voidReceiveMsg(CMessage*pMsg);
CClientSocket(CFileTransfersDlg*pdlgMain);
//ClassWizardgeneratedvirtualfunctionoverrides
//{{AFX_VIRTUAL(CClientSocket)
public:
virtualvoidOnReceive(intnErrorCode);
//}}AFX_VIRTUAL
//Generatedmessagemapfunctions
//{{AFX_MSG(CClientSocket)
//NOTE-theClassWizardwilladdandremovememberfunctionshere.
//}}AFX_MSG
//Implementation
protected:
CFileTransfersDlg*m_pdlgMain;
};
在CClientSocket套接字类封装了串行化功能,这也就是说,该类封装了客户应用程序的大部分功能,因此可以认为该套接字类所管理的就是一个客户应用程序。
其中,m_pFile为一个CSocketFile类型的指针用于连接到一个CSocket对象;m_pArchiveIn和m_pArchiveOut均为CArchive类型的指针,分别用于接受和发送。
两个构造函数的源代码如下:
CClientSocket:
:
CClientSocket(CFileTransfersDlg*pdlgMain)
{
m_pdlgMain=pdlgMain;
m_pFile=NULL;
m_pArchiveIn=NULL;
m_pArchiveOut=NULL;
}
CClientSocket:
:
CClientSocket()
{
m_pdlgMain=NULL;
m_pFile=NULL;
m_pArchiveIn=NULL;
m_pArchiveOut=NULL;
}
Init成员函数用于串行化的初始化,其源代码如下:
voidCClientSocket:
:
Init()
{
m_pFile=newCSocketFile(this);
m_pArchiveIn=newCArchive(m_pFile,CArchive:
:
load);
m_pArchiveOut=newCArchive(m_pFile,CArchive:
:
store);
}
Abort成员函数用于对m_pArchiveOut指针进行释放,其源代码如下:
voidCClientSocket:
:
Abort()
{
if(m_pArchiveOut!
=NULL)
{
m_pArchiveOut->Abort();
deletem_pArchiveOut;
m_pArchiveOut=NULL;
}
}
SendMsg成员函数用于发送信息,其源代码如下:
BOOLCClientSocket:
:
SendMsg(CMessage*pMsg)
{
if(m_pArchiveOut!
=NULL)
{
TRY
{
//采用串行化技术进行信息的发送
pMsg->Serialize(*m_pArchiveOut);
m_pArchiveOut->Flush();
returnTRUE;
}
CATCH(CFileException,e)
{
m_pArchiveOut->Abort();
deletem_pArchiveOut;
m_pArchiveOut=NULL;
}
END_CATCH
}
returnFALSE;
}
ReceiveMsg成员函数用于接受信息,其源代码如下:
voidCClientSocket:
:
ReceiveMsg(CMessage*pMsg)
{
//采用串行化技术进行信息的接收
pMsg->Serialize(*m_pArchiveIn);
}
重载基类函数OnReceive,使用此函数接受Socket连接另一端发送的信息,其源代码如下:
voidCClientSocket:
:
OnReceive(intnErrorCode)
{
m_pdlgMain->ProcessReceive(this);
CSocket:
:
OnReceive(nErrorCode);
}
代码的作用是当有信息发送到时,调用主对话框类的ProcessReceive函数进行信息的接收。
由于用到了CMessage和CFileTransfersDlg类,所以在ClientSocket.h文件首部还需加入如下的头文件包含:
#include"Message.h"
#include"FileTransfersDlg.h"
四.两个线程
上面所定义的类只实现了控制信息的传送,我们还需要写两个线程_SendThread和_ListenThread,它们分别用于发送和接收文件。
1.发送文件线程
UINT_SendThread(LPVOIDlparam)
{
CFileTransfersDlg*pDlg=(CFileTransfersDlg*)lparam;
//创建套接字
CSocketsockClient;
if(!
sockClient.Create())
{
pDlg->TransfersFailed();
:
:
MessageBox((HWND)lparam,pDlg->GetError(GetLastError()),_T("错误"),MB_ICONHAND|MB_OK);
return-1;
}
CStringstrIPAddress;
UINTnPort;
pDlg->m_psockClient->GetPeerName(strIPAddress,nPort);
//连接到服务器端
if(!
sockClient.Connect(strIPAddress,pDlg->m_wPort+PORT))
{
pDlg->TransfersFailed();
:
:
MessageBox((HWND)lparam,pDlg->GetError(GetLastError()),_T("错误"),MB_ICONHAND|MB_OK);
return-1;
}
//调用主对话框类中的SendFile成员函数进行文件的发送
pDlg->SendFile(sockClient);
return0;
}
2.接收文件线程
UINT_ListenThread(LPVOIDlparam)
{
CFileTransfersDlg*pDlg=(CFileTransfersDlg*)lparam;
//创建套接字
CSocketsockSrvr;
if(!
sockSrvr.Create(pDlg->m_wPort+PORT))
{
pDlg->TransfersFailed();
:
:
MessageBox((HWND)lparam,pDlg->GetError(GetLastError()),_T("错误"),MB_ICONHAND|MB_OK);
return-1;
}
//开始监听
if(!
sockSrvr.Listen())
{
pDlg->TransfersFailed();
:
:
MessageBox((HWND)lparam,pDlg->GetError(GetLastError()),_T("错误"),MB_ICONHAND|MB_OK);
return-1;
}
//向主对话框发送一个自定义消息WM_ACCEPT_TRANSFERS
//发送一个信息告诉发送方可以开始发送文件
pDlg->SendMessage(WM_ACCEPT_TRANSFERS);
//接受连接
CSocketrecSo;
if(!
sockSrvr.Accept(recSo))
{
:
:
MessageBox((HWND)lparam,pDlg->GetError(GetLastError()),_T("错误"),MB_ICONHAND|MB_OK);
return-1;
}
sockSrvr.Close();
//调用主对话框类中的ReceiveFile成员函数进行文件的接受
pDlg->ReceiveFile(recSo);
return0;
}
这两个函数只需要放在FileTransfersDlg.cpp文件中,在该文件中还需要加入两个CWinThread*类型的全局变量pThreadListen和pThreadSend用于对线程进行管理。
五.在主对话框类内组织程序
1.添加成员变量
使用ClassWizard为控件添加成员变量,其对应关系如下所示。
控件ID变量类型变量名
IDC_FILE_NAMECStringm_strFileName
IDC_FILE_SIZECStringm_strFileSize
IDC_PORTUINTm_wPort
IDC_IPADDRESSCProgressCtrlm_ctrlProgress
IDC_RADIO_SERVERintm_nServerType
2.添加按钮消息的处理函数
添加按钮消息的处理函数,如下所示。
资源ID消息类型函数名称
IDC_RADIO_SERVERBN_CLICKEDOnRadioServer
IDC_RADIO_CLIENTBN_CLICKEDOnRadioClient
IDC_BEGINBN_CLICKEDOnBegin
IDC_DISCONNECTBN_CLICKEDOnDisconnect
IDC_SELECT_FILEBN_CLICKEDOnSelectFile
IDC_STOP_TRANSFERSBN_CLICKEDOnStopTransfers
IDCANCELBN_CLICKEDOnCancel
3.添加WM_TIMER消息控制函数
使用ClassWizard为CFileTransfersDlg类中加一个WM_TIMER消息控制函数。
4.加入必要的宏
在定义文件FileTransfersDlg.h的首部加入如下的宏,以增强程序的可读性。
#definePORT1024//文件传输套接字的端口号
#defineBLOCKSIZE1024//每次要发送或是接受的文家大小
#defineSERVER0//表示当前为服务器端
#defineCLIENT1//表示当前为客户端
#defineCONNECT_BE_ACCEPT0x00//客户端的连接申请被接受
#defineCONNECT_BE_REFUSE0x01//客户端的连接申请被拒绝
#defineDISCONNECT0x02//连接被断开
#defineREQUEST0x03//请求发送文件
#defineACCEPT0x04//同意发送文件
#defineREFUSE0x05//拒绝发送文件
#defineCANCEL0x06//取消文件的发送
5.自定义消息
自定义一个消息WM_ACCEPT_TRANSFERS,用于当文件接收方同意接收文件且文件接收线程_ListenThread已经准备好接收文件是,发送一个信息给文件发送方,说可以开始发送文件。
第一步,我需要在主窗口类CFileTransfersDlg的实现文件FileTransfersDlg.cpp的首部加入一句:
#defineWM_ACCEPT_TRANSFERSWM_USER+1