目录
源码地址:【免费】MFC:TCP单向文件传输资源-优快云文库
一、先导
来看效果:

服务端选择对应文件发送,客户端接收后直接按对应路径保存。
要实现广域网下的通信,则需要公网IP,其一的办法是使用内网穿透工具。
这里推荐一款免费的内网穿透工具:
https://www.cpolar.com/?channel=0&invite=4Mz5
只需登录邮箱就可长期免费使用1M宽带(128KB/S),但要注意:
每24小时该工具提供的公网IP与端口会发生变化。
协议选择TCP,创建好的隧道将有一个网址,使用cmd的Ping命令即可获取公网IP。
将本地服务器的端口映射至该工具提供的公网IP,客户端直接连接该工具提供的公网IP与端口。
二、实现
服务端

新建项目,选择Window套接字功能
添加两个MFC类:CListenSocket用于监听,CClientSocket用于传输
基类选择CSocket

定义数据包结构体
//StdPack.h
struct NETPACKAGE
{
int len;//4字节
int type;//4字节
char buf[50000];//50000字节
};
//数据包长度50008字节
CListenSocket与CClientSocket均封装一个SetDlg
//CListenSocket.h
public:
CFileServerDlg *dlg;
void SetDlg(CFileServerDlg *pDlg);
//CListenSocket.cpp
void CListenSocket::SetDlg(CFileServerDlg * pDlg)
{
dlg = pDlg;
}
//CClientSocket.cpp
CClientSocket::CClientSocket()
{
pkbuf = NULL;
bufset = 0;
pklen = 50008;//数据包长度
}
void CClientSocket::SetDlg(CFileServerDlg * pDlg)
{
dlg = pDlg;
pkbuf = new char[50000];
}
重写OnAccept及OnReceive,当有消息转至主对话框
//CListenSocket.cpp
void CListenSocket::OnAccept(int nErrorCode)
{
dlg->mAccept(this);
CSocket::OnAccept(nErrorCode);
}
主对话框
//FileServerDlg.h
public:
void mAccept(CListenSocket*pL);
void mRecvData(CClientSocket*pC);
CListenSocket*pListen;
CClientSocket*pClient;
//FileServerDlg.cpp
void CFileServerDlg::mAccept(CListenSocket * pL)
{
pClient = new CClientSocket;
pClient->SetDlg(this);
pL->Accept(*pClient);
}
void CFileServerDlg::mRecvData(CClientSocket * pC)
{
NETPACKAGE pack;
pC->GetPackage((char*)&pack);
switch (pack.type)
{
case 1:
break;
case 2:
break;
case 3:
break;
}
}
服务端开启与关闭
//FileServerDlg.cpp
void CFileServerDlg::OnBnClickedStart()
{
pListen = new CListenSocket;
pListen->Create(m_port);
pListen->SetDlg(this);
pListen->Listen(10);
GetDlgItem(IDC_START)->EnableWindow(FALSE);
GetDlgItem(IDC_CLOSE)->EnableWindow(TRUE);
}
void CFileServerDlg::OnBnClickedClose()
{
if (pListen != NULL)
{
pListen->Close();
delete pListen;
}
if (pClient != NULL)
{
pClient->Close();
delete pClient;
}
GetDlgItem(IDC_START)->EnableWindow(TRUE);
GetDlgItem(IDC_CLOSE)->EnableWindow(FALSE);
}
在CClientSocket.cpp中封装SendData与GetPackage用于发送与接收
//CClientSocket.cpp
void CClientSocket::SendData(int len, int type, char * buf)
{
NETPACKAGE pack;
pack.len = len;
pack.type = type;
memcpy(pack.buf, buf, len);
Send((char*)&pack, pklen, 0);
}
void CClientSocket::GetPackage(char * buf)
{
do
{
recvlen = Receive(pkbuf + bufset, pklen, 0);//Receive返回接收的字节数
if (recvlen > 0)
{
bufset += recvlen;
}
} while (bufset < pklen);
memcpy(buf, pkbuf, pklen);
bufset -= pklen;
if (bufset > 0)
memcpy(pkbuf, pkbuf + pklen, bufset);
}
GetPackage要保证接收一个完整的数据包
其中Receive的void*可理解为从第bufset字节继续向pkbuf接收
原型virtual int Receive(void* lpBuf, int nBufLen, int nFlags = 0);
浏览文件
//FileServerDlg.cpp
void CFileServerDlg::OnBnClickedBrowse()
{
CFileDialog dlg(TRUE);
if (dlg.DoModal() == IDOK)
{
m_fpath = dlg.GetPathName();
UpdateData(FALSE);
}
}
发送文件
//FileServerDlg.cpp
void CFileServerDlg::OnBnClickedSend()
{
if (pClient == NULL)
{
MessageBox(_T("Sock null!"));
return;
}
UpdateData(TRUE);
int len = 0;
char *buf = new char[50000];
file = new CFile;
file->Open(m_fpath, CFile::modeCreate
| CFile::modeNoTruncate
| CFile::modeRead);
pClient->SendData(10000, 1, "");
do
{
len = file->Read(buf, 50000);//Read返回读取的字节数
pClient->SendData(len, 2, buf);
} while (len > 0);
pClient->SendData(10000, 3, "");
file->Close();
delete file;
delete buf;
}
客户端
客户端与服务端大同小异,主要有以下区别:
1、客户端只需要CClientSocket,并调用Connect与服务端主动连接。
2、客户端的接收类型包括写入文件。
//CFileClientDlg.cpp
void CFileClientDlg::OnBnClickedConnect()
{
UpdateData(TRUE);
pClient = new CClientSocket;
pClient->Create();
pClient->SetDlg(this);
int res = pClient->Connect(m_ipaddr, m_port);
if (res == 1)
{
CString str;
str.Format(_T("Connect-%s:%d"), m_ipaddr, m_port);
SetWindowText(str);
}
else
{
CString str;
str.Format(_T("连接服务器失败-%d"), GetLastError());
MessageBox(str);
}
UpdateData(FALSE);
}
//CClientSocket.cpp
void CClientSocket::SetDlg(CFileClientDlg * pDlg)
{
dlg = pDlg;
pkbuf = new char[100000];//大于数据包长度
}
//CFileClientDlg.cpp
void CFileClientDlg::mRecvData(CClientSocket * pC)
{
NETPACKAGE pack;
pC->GetPackage((char*)&pack);
switch (pack.type)
{
case 1://开始接收
UpdateData(TRUE);
file = new CFile;
file->Open(m_fpath, CFile::modeCreate
| CFile::modeNoTruncate
| CFile::modeWrite);
break;
case 2:
file->Write(pack.buf, pack.len);
break;
case 3://结束接收
file->Close();
delete file;
break;
}
}
文章介绍了如何使用MFC通过TCP协议实现单向文件传输,包括服务端和客户端的实现细节,如CListenSocket和CClientSocket类的使用,数据包结构体NETPACKAGE的设计,以及内网穿透工具的使用来实现在广域网下的通信。
666

被折叠的 条评论
为什么被折叠?



