MFC(基于VS2013)连接下位机简单实例(下位机连接以及Socket数据发送接收)

很多人需要编写基于MFC的上位机程序,而这类程序首先要解决的就是网络通信的问题,这里也抽出一点时间写了一个简单的额范例,如果你的需求是如下几幅图片所示(这里用TCP调试助手模拟下位机,以便更清楚的看到我们发的信息),那么不妨可以下载下来看一看,程序的代码已经给了非常详细的注释,这里不再赘述,只放一些核心代码:



这里附上程序的下载地址,自己手工 码的程序,收一点点豆子,核心代码我也直接附出来:程序下载地址

TCP调试助手程序下载地址(必须要设置下载豆,这个软件大家也可以网上下载,豆多的掠过):TCP/UDP调试助手下载地址

先说一下程序的运行平台,以免各位下载程序跑的时候出现莫名的报错,本程序已经经过VS2013编译通过及运行:

同时要设置本机的IP地址,比如我的这里设置的是:192.168.0.105,具体设置方法请百度:


TCP模拟调试助手时,我们选择创建服务器,具体创建过程见图:


创建成功后启动:


//设备连接按钮的代码
void CconnectDlg::OnBnClickedConnect()
{
	// TODO:  在此添加控件通知处理程序代码
	//测试能不能ping通
	int ping_ok = 1;
	CString serv_addr, serv_port;

	SECURITY_ATTRIBUTES sa;
	HANDLE hRead, hWrite;
	sa.nLength = sizeof(SECURITY_ATTRIBUTES);
	sa.lpSecurityDescriptor = NULL;
	sa.bInheritHandle = TRUE;


	CString filename = "ipconfig.ini";
	CStdioFile mFileIP(filename, CFile::modeCreate | CFile::modeWrite);

	//CString str_ip;
	GetDlgItem(IDC_IP)->GetWindowText(serv_addr);
	mFileIP.WriteString(serv_addr);
	mFileIP.Close();
	filename = "portconfig.ini";
	CStdioFile mFilePort(filename, CFile::modeCreate | CFile::modeWrite);
	GetDlgItem(IDC_Port)->GetWindowText(serv_port);
	mFilePort.WriteString(serv_port);
	mFilePort.Close();

	CString strCmd;
	strCmd = "cmd /k ping ";//加上"cmd /k"是为了能执行类似dir的命令
	strCmd += serv_addr;

	//创建命名管道
	if (!CreatePipe(&hRead, &hWrite, &sa, 0)) {
		return;
	}
	STARTUPINFO si;
	PROCESS_INFORMATION pi;
	si.cb = sizeof(STARTUPINFO);
	GetStartupInfo(&si);
	si.hStdError = hWrite;//数据输出用的文件句柄
	si.hStdOutput = hWrite;//数据输出用的文件句柄
	si.wShowWindow = SW_HIDE;
	si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;


	OutputDebugString("1");

	if (!CreateProcess(NULL, strCmd.GetBuffer(strCmd.GetLength())//执行cmd命令,并在命名中管道中写入cmd命令返回的串
		, NULL, NULL, TRUE, NULL, NULL, NULL, &si, &pi)) {
		return;
	}

	CloseHandle(hWrite);
	OutputDebugString("2");

	char buffer[4096] = { 0 };
	DWORD bytesRead;
	CString strResult = "";


	while (1)
	{
		memset(buffer, 0, 4096);
		if (ReadFile(hRead, buffer, 4096, &bytesRead, NULL) != NULL)//从命名管道中读取数据
		{
			if (buffer[18] == 'T')
			{

				ping_ok = 0;
				int port;
				SOCKADDR_IN addr;

				WORD   wVersionRequested;//定义socket1.1或者socket2.0     
				WSADATA   wsaData;   //定义装载socket版本的变量  
				int   err;   //错误变量  

				wVersionRequested = MAKEWORD(2, 2);   //定义连接为socket2.0  

				err = WSAStartup(wVersionRequested, &wsaData);   //装载socket2.0支持  
				if (0 != err)//判断是否装载成功  
				{
					return;
				}

				if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2)//判断版本号,是否和定义的一样  
				{
					WSACleanup();   //若出问题,卸载支持,并结束程序返回-1  
					return;
				}
				m_socket = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, 0);

				if (INVALID_SOCKET == m_socket)
				{
					MessageBox("创建套接字失败!");
					return;
				}

				if (SOCKET_ERROR == WSAAsyncSelect(m_socket, m_hWnd, WM_SOCK, FD_READ))
				{
					MessageBox("注册网络读取事件失败!");
					return;
				}

				if (serv_port == "" || serv_addr == "")
				{
					MessageBox("服务器地址或端口不能为空!!!");//该函数功能是弹出一个显示s你写的这些内容的框

				}
				else
				{
					port = atoi(serv_port.GetBuffer(1));//将端口字符串转换为数字
					addr.sin_family = AF_INET;
					addr.sin_addr.S_un.S_addr = inet_addr(serv_addr.GetBuffer(1));//转换服务器ip地址
					addr.sin_port = ntohs(port);

					//设置非阻塞模式
					unsigned long ul = 1;
					int ret = ioctlsocket(m_socket, FIONBIO, (unsigned long*)&ul);
					if (ret == SOCKET_ERROR)
						exit(0);

					if (::connect(m_socket, (SOCKADDR*)&addr, sizeof(SOCKADDR)))
					{
						CString Str;
					}
					else
					{
					}

				}
				break;
			}
			OutputDebugString(buffer);

		}
		else
		{
			break;
		}
	}
	CloseHandle(hRead);

	if (ping_ok)
	{
		MessageBox("设备连接出错,请检查连接!");
	}
	else
	{
		m_connect.EnableWindow(0);
		m_cut.EnableWindow(1);
		GetDlgItem(IDC_STATIC_SHOW)->SetWindowText(_T("设备已连接"));
		AddToInfRec("设备已连接", IDC_Zhuangtai, TRUE, TRUE);
		CString connected;
		connected = "设备已经连接";
		send(m_socket, connected, 50, 0);
		MyDevFounded = TRUE;
	}
}
//发送命令按钮代码
void CconnectDlg::OnBnClickedButtonSend()
{
	// TODO:  在此添加控件通知处理程序代码
	CString message, showtext, datatest;
	GetDlgItem(IDC_EDIT_Send)->GetWindowText(message); //从发送数据的edit框中获取信息,存储到message变量中

	if (MyDevFounded == FALSE)   //进行设备连接状态的判断,通过设置MyDevFounded,初始值为false,设备连接成功后置true
	{
		AddToInfRec("设备未找到", IDC_Zhuangtai, TRUE, TRUE);//设备未成功连接,提示设备未找到
		return;
	}
	if (message == "")
	{
		MessageBox("消息不能为空!");  //对发送数据框进行判断 
	}
	else
	{
		AddToInfRec("发送数据:", IDC_Zhuangtai,0,0);  
		AddToInfRec(message, IDC_Zhuangtai,1,1);   //显示发送的数据到状态框
		::send(m_socket, message.GetBuffer(1), message.GetLength(), 0);  //通过sock发送数据
	}
}
//onsock函数代码
afx_msg LRESULT CconnectDlg::OnSock(WPARAM wParam, LPARAM lParam)
{
	char cs[512] = "";  //定义一个用来存放接收数据的字符串cs
	if (lParam == FD_READ) //sock收到消息,触发FD_READ
	{
		if (SOCKET_ERROR == recv(m_socket, cs, 512, NULL))//使用recv函数来进行判别收到的内容
		{
			MessageBox("接收数据失败!"); //如果触发连接错误,则弹出接收数据失败提示框
			return FALSE;
		}
		else{
			AddToInfRec("接收数据:", IDC_EDIT_REC, 0, 0);
			AddToInfOut(cs, TRUE, TRUE);//如果没有出现错误,将cs中的数据传递给AddToInfOut来进行处理
		}
	}
	return 0;
}
//数据处理函数代码
void CconnectDlg::AddToInfOut(CString InStr, BOOL AddTime, BOOL NewLine)
{
	CString str, str1;
	UINT i;
	CHAR SysTime[10];
	GetDlgItemText(IDC_EDIT_REC, str); //从数据接收的Edit框中获取已经接收到的信息
	str += InStr; //新收到的信息通过InStr传递到本函数中,声明一个字符串str来存放,此条命令是将新收到的信息叠加到以前的信息中

	//需要添加时间信息
	if (AddTime == TRUE)
	{
		_strtime(SysTime);
		str1 = SysTime;
		str1 = " (" + str1 + ")";
		str += str1;
	}

	//需要换行
	if (NewLine == TRUE)
	{
		str += "\r\n";
	}

	//设置新的文本
	SetDlgItemText(IDC_EDIT_REC, str); //将处理后的信息显示到接收的编辑框中
	//滚动条自动滚动到最后一行
	i = ((CEdit*)GetDlgItem(IDC_EDIT_REC))->GetLineCount();
	((CEdit*)GetDlgItem(IDC_EDIT_REC))->LineScroll(i, 0);
}


全部评论

相关推荐

2025-12-24 15:25
已编辑
门头沟学院 前端工程师
是腾讯的csig腾讯云,前天晚上九点突然打电话约面,激动的通宵学了一晚上,第二天状态很差改了今天(以后再也不通宵学习了)感觉自己浪费了面试官一个半小时单纯手写+场景,无八股无项目无算法,打击真的很大,全是在面试官提醒的情况下完成的,自己技术方面真的还是有待提高,实力匹配不上大厂和已经面试的两个公司完全不一样,很注重编码能力和解决问题的能力,然而我这两个方面都很薄弱,面试官人很好很耐心的等我写完题目,遇到瓶颈也会提醒我,写不出题也会很耐心的跟我讲解好感动,到最后面试结束还安慰我打算把下周最后一场面试面完之后就不面啦,如果能去实习还是很开心,但是最重要的还是好好努力提高技术以下是面经第一题// 实现一个解析 url 参数的函数function parseUrl(urlStr) {// TODO}parseUrl('*********************************************');// 返回 {a: 1, b: 2, c: 3}追问:在链接里见过什么部分?用 hash 路由的话放在哪第二题// 考虑有一个异步任务要执行,返回 Promise,这个任务可能会失败,请实现 retry 方法,返回新方法,可以在失败后自动重试指定的次数。/*** 异步任务重试* @param task 要执行的异步任务* @param times 需要重试的次数,默认为 3 次*/function retry(task, times = 3) {// TODO: 请实现}// ---------------测试示例 ----------------// 原方法const request = async (data) => {// 模拟失败if (Math.random() < 0.7) {throw new Error('request failed');}const res = await fetch('https://jsonplaceholder.typicode.com/posts', {method: 'POST',body: JSON.stringify(data),});return res.json();}// 新的方法const requestWithRetry = retry(request);// 使用async function run() {const res = await requestWithRetry({ body: 'content' });console.log(res);}run();第三题就是给 retry 函数添加类型注释,用到泛型第四题:在组件库中将 Alert 用 api 的形式实现(应该就是 message 这个组件)怎么渲染到一个浮层里而不是原地渲染出来
不知道怎么取名字_:技术这个东西,太杂了,而且要下功夫的
查看5道真题和解析
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务