MFC:广域网下TCP通信,文件传输

文章介绍了如何使用MFC通过TCP协议实现单向文件传输,包括服务端和客户端的实现细节,如CListenSocket和CClientSocket类的使用,数据包结构体NETPACKAGE的设计,以及内网穿透工具的使用来实现在广域网下的通信。

目录

一、先导

二、实现

服务端

客户端


源码地址:【免费】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;
	}

}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值