[MFC]MFC实现UDP客户端和服务端信息交互

目录

前言

一.预期实现效果

二.UDP服务器端

1.初始界面

2.ServerSocket.h

 3.ServerSocket.cpp

4.信息交互函数ProcessPendingRead()

三.UDP客户端 

 1.初始界面

2.OnInitDialog()

3.客户端信息交互函数ProcessPendingRead()

4.OnBnClickedButtonEnter()

5.OnBnClickedButtonExit() 

6.OnBnClickedButtonSend()

7.程序关闭DestroyWindow()

总结


前言

最近继续在学习MFC的相关操作,本次博客以实现一个UDP协议的服务端和客户端之间的信息交互,本博客将具体进行以下分析.

一.预期实现效果

 

二.UDP服务器端

UDP服务器端和UDP客户端一致,都是基于CSocket类进行实现.在首先创建MFC新项目时,需要在高级功能里选中Windows 套接字选项栏.

并在类视图中,添加一个CServerSocket类,继承CSocket类,并配置相应的头文件和源文件.

1.初始界面

2.ServerSocket.h

#pragma once
#include <afxsock.h>

class CUdpServerDlg;
class CServerSocket :
    public CSocket
{
public:
    CServerSocket(CUdpServerDlg* pdlg);
    virtual ~CServerSocket();
private:
    CUdpServerDlg* m_pMainDlg;
protected:
    virtual void OnReceive(int nErrorCode);//重写一个方法
};

 3.ServerSocket.cpp

#include"UdpServerDlg.h"
CServerSocket::CServerSocket(CUdpServerDlg* pdlg)
{
	this->m_pMainDlg = pdlg;
}

CServerSocket::~CServerSocket()
{

}

void CServerSocket::OnReceive(int nErrorCode)
{
	//接受函数信息
	CSocket::OnReceive(nErrorCode);
	m_pMainDlg->ProcessPendingRead();//主要的信息传输的功能函数
}

在源文件中,添加CServerSocket的构造函数和析构函数,并添加相应的虚函数OnReceive,用于数据的传输与接受

整个服务端要实现以下功能:显示客户端的进入和离开,显示当前客服端的数量以及客户端之间的信息交互.在头文件中声明相关变量:

public:
	CServerSocket* m_pServerSocket;
	CArray<ClientAddr,ClientAddr&>m_ClientAddrList;
	void ProcessPendingRead();

在源文件的初始化函数BOOL CUdpServerDlg::OnInitDialog()函数中,进行初始化操作:

    //初始化
	m_pServerSocket = new CServerSocket(this);//初始化,新创建一个对话框Socket
	m_pServerSocket->Create(8080, SOCK_DGRAM);//创建UDP连接

因为UDP和TCP协议都是继承的CSocket基类,这里主要的不同是主变量m_pServerSocket类中的创建类型,UDP协议就是SOCK_DGRAM. 

4.信息交互函数ProcessPendingRead()

void CUdpServerDlg::ProcessPendingRead()
{
	TCHAR buff[4096];
	ClientAddr clientAddr;
	int nRead = m_pServerSocket->ReceiveFrom(buff, 4096, clientAddr.strIP, clientAddr.uiPort);

	if (nRead == SOCKET_ERROR)
	{
		return;
	}
	//很明显,这里的含义是C语言和C++中的字符串以'\0'结尾

	buff[nRead] = _T('\0');
	CString strTemp(buff);
	int i;

	//1.进入房间enter 2.离开房间 leave 3.聊天信息
	if (strTemp.CompareNoCase(_T("enter")) == 0)
	{
		//新的客户端
		//1.添加新的客户端IP地址
		m_ClientAddrList.Add(clientAddr);

		//2.通知其他客户端,有新的用户加入房间
		CString strEnterMsg;

		//格式化相关的消息
		strEnterMsg.Format(_T("系统消息:%s(%d) 进入了房间"), clientAddr.strIP, clientAddr.uiPort);
		for (i = 0; i < m_ClientAddrList.GetSize(); i++)
		{
			ClientAddr& tempClient = m_ClientAddrList.ElementAt(i);
			m_pServerSocket->SendTo(strEnterMsg, strEnterMsg.GetLength() + 1000, tempClient.uiPort, tempClient.strIP);

		}

		//3.更新界面
		SetDlgItemInt(IDC_EDIT_CLIENT_NUMBER, m_ClientAddrList.GetSize());
		CString allMsg;
		GetDlgItemText(IDC_EDIT_CHAT_MESSAGE, allMsg);//提取原来控件上的信息
		SetDlgItemText(IDC_EDIT_CHAT_MESSAGE, allMsg + _T("\r\n") + strEnterMsg);//界面更新

	}
	//离开房间
	if(strTemp.CompareNoCase(_T("leave")) == 0)
	{
		//1.移除用户
		//遍历列表
		for (i = 0; i < m_ClientAddrList.GetSize(); i++)
		{
			//遍历每一个Addr的参数类型
			ClientAddr& tempCLient = m_ClientAddrList.ElementAt(i);

			if (tempCLient.uiPort == clientAddr.uiPort && tempCLient.strIP.Compare(clientAddr.strIP) == 0)
			{
				break;
			}
		}
		if (i < m_ClientAddrList.GetSize())
		{
			m_ClientAddrList.RemoveAt(i);
		}
		//2.通知其他客户端,有人离开了

		CString strLeaveMsg;
		strLeaveMsg.Format(_T("系统消息:%s(%d)离开了房间!"), clientAddr.strIP, clientAddr.uiPort);
		//通知大家有人离开了
		for (i = 0; i < m_ClientAddrList.GetSize(); i++)
		{
			ClientAddr& tempClient = m_ClientAddrList.ElementAt(i);
			m_pServerSocket->SendTo(strLeaveMsg, strLeaveMsg.GetLength() + 1000, tempClient.uiPort, tempClient.strIP);

		}

		//3.更新界面

		SetDlgItemInt(IDC_EDIT_CLIENT_NUMBER, m_ClientAddrList.GetSize());
		CString allMsg;
		GetDlgItemText(IDC_EDIT_CHAT_MESSAGE, allMsg);
		SetDlgItemText(IDC_EDIT_CHAT_MESSAGE, allMsg + _T("\r\n") + strLeaveMsg);
	}
	else
	{
	    //转发客户端消息
		CString strMsg;
		//转发消息
		strMsg.Format(_T("%s(%d):%s"), clientAddr.strIP, clientAddr.uiPort, strTemp);
		for (i = 0; i < m_ClientAddrList.GetSize(); i++)
		{
			ClientAddr& tempClient = m_ClientAddrList.ElementAt(i);
			m_pServerSocket->SendTo(strMsg, strMsg.GetLength() + 1000, tempClient.uiPort, tempClient.strIP);
		}
		//2.更新界面
		CString allMsg;
		GetDlgItemText(IDC_EDIT_CHAT_MESSAGE, allMsg);
		SetDlgItemText(IDC_EDIT_CHAT_MESSAGE, allMsg + _T("\r\n") + strMsg);
	}

}

三.UDP客户端 

客户端的基本配置和服务器端基本一致,也是继承于CSocket基类,在头文件中声明以下变量,对应客户端之间要实现进入、离开、发送信息、以及最后的程序关闭操作.

public:
	CClientSocket* m_pClientSocket;
	BOOL m_bEnterRoom;//用来表示客户端是否进入了房间
	void ProcessPendingRead();

	afx_msg void OnBnClickedButtonEnter();
	afx_msg void OnBnClickedButtonExit();
	afx_msg void OnBnClickedButtonSend();
	virtual BOOL DestroyWindow();

 1.初始界面

2.OnInitDialog()

    // TODO: 在此添加额外的初始化代码

	//1.创建UDPSocket
	m_pClientSocket = new CClientSocket(this);
	m_pClientSocket->Create(0, SOCK_DGRAM);//使用数据包Socket类型,也就是UDP类型

	//2.获取Socket绑定的ip和端口
	//获取本机的IP和端口号
	CString strIp;
	UINT uiPort;
	//获取本地的服务号和端口号
	m_pClientSocket->GetSockName(strIp, uiPort);
	//显示本地的端口号和IP号
	SetDlgItemText(IDC_EDIT_LOCAL_IP, strIp);
	SetDlgItemInt(IDC_EDIT_LOCAL_PORT, uiPort);

	//3.连接到服务器端的端口号
	SetDlgItemText(IDC_EDIT_SERVER_IP, _T("127.0.0.1"));
	SetDlgItemInt(IDC_EDIT_SERVER_PORT, 8080);//连接到服务器到的PORT

	//4.设置按钮的初始化状态
	GetDlgItem(IDC_BUTTON_EXIT)->EnableWindow(FALSE);
	GetDlgItem(IDC_BUTTON_SEND)->EnableWindow(FALSE);
     

    //设置只读控件
	((CEdit*)GetDlgItem(IDC_EDIT_SEND_MESSAGE))->SetReadOnly(TRUE);
	((CEdit*)GetDlgItem(IDC_EDIT_SERVER_IP))->SetReadOnly(FALSE);
	((CEdit*)GetDlgItem(IDC_EDIT_SERVER_PORT))->SetReadOnly(FALSE);

	return TRUE;  // 除非将焦点设置到控件,否则返回 TRUE

在初始化函数中,包括对客户端程序界面的一些细节设计,例如在刚进入到程序界面时,本地IP和本地端口设置为只读,不可修改,客户端IP和服务端端口可以进行修改,发送信息控制和发送按钮也设置成只读,不可进行修改.

 客户端和服务端之间的连接,我们在设置客户端的参数时,创建的端口号为8080,在进行连接客户端端口号时,也是直接连接8080端口,连接客户端IP时,这里直接连接127.0.0.1.

3.客户端信息交互函数ProcessPendingRead()

//客户端在此处理接受到的数据
void CUdpClientDlg::ProcessPendingRead()
{
	//处理接受到的数据
	TCHAR buff[4096];

	//读取数据
	int nRead = m_pClientSocket->ReceiveFrom(buff, 4096, NULL, NULL);
	//判断一下
	if (nRead == SOCKET_ERROR)
	{
		return;
	}
	//字符串结束的末尾
	buff[nRead] = _T('\0');
	CString strMsg(buff);
	CString allMsg;

	//获取现有数据
	GetDlgItemText(IDC_EDIT_CHAT_MESSAGE, allMsg);
	//添加数据
	SetDlgItemText(IDC_EDIT_CHAT_MESSAGE, allMsg + _T("\r\n") + strMsg);
}

4.OnBnClickedButtonEnter()

void CUdpClientDlg::OnBnClickedButtonEnter()
{
	// TODO: 在此添加控件通知处理程序代码
	//1.获取服务端IP和端口
	CString strIp;
	UINT uiPort;
	
	GetDlgItemText(IDC_EDIT_SERVER_IP, strIp);
	uiPort = GetDlgItemInt(IDC_EDIT_SERVER_PORT);

	//2.发送enter消息
	CString strEnterMsg("enter");
	int nSend = m_pClientSocket->SendTo(strEnterMsg, strEnterMsg.GetLength() + 100, uiPort, strIp);
	if (nSend == SOCKET_ERROR)
	{
		MessageBox(_T("进入聊天室失败,可能是服务端尚未运行"));
		return;
	}

	//3.设置按钮、文本框的状态
	//4.设置按钮的初始化状态
	GetDlgItem(IDC_BUTTON_EXIT)->EnableWindow(TRUE);
	GetDlgItem(IDC_BUTTON_SEND)->EnableWindow(TRUE);

	((CEdit*)GetDlgItem(IDC_EDIT_SEND_MESSAGE))->SetReadOnly(FALSE);
	((CEdit*)GetDlgItem(IDC_EDIT_SERVER_IP))->SetReadOnly(TRUE);
	((CEdit*)GetDlgItem(IDC_EDIT_SERVER_PORT))->SetReadOnly(TRUE);

	//设置标志位
	m_bEnterRoom = true;

}

客户端进入之后, 服务端IP和服务端端口更改为只读属性,离家房间按钮和发送信息按钮,以及发送信息Edit_Control控件可以进行修改.

5.OnBnClickedButtonExit() 

void CUdpClientDlg::OnBnClickedButtonExit()
{
	// TODO: 在此添加控件通知处理程序代码
	//1.获取服务端IP和端口
	CString strIp;
	UINT uiPort;
	GetDlgItemText(IDC_EDIT_SERVER_IP, strIp);
	uiPort = GetDlgItemInt(IDC_EDIT_SERVER_PORT);

	//2.发送leave消息
	CString strLeaveMsg("leave");
	int nSend = m_pClientSocket->SendTo(strLeaveMsg, strLeaveMsg.GetLength() + 100, uiPort, strIp);
	if (nSend == SOCKET_ERROR)
	{
		MessageBox(_T("退出聊天室失败"));
		return;
	}

	GetDlgItem(IDC_BUTTON_EXIT)->EnableWindow(FALSE);
	GetDlgItem(IDC_BUTTON_SEND)->EnableWindow(FALSE);

	((CEdit*)GetDlgItem(IDC_EDIT_SEND_MESSAGE))->SetReadOnly(TRUE);
	((CEdit*)GetDlgItem(IDC_EDIT_SERVER_IP))->SetReadOnly(FALSE);
	((CEdit*)GetDlgItem(IDC_EDIT_SERVER_PORT))->SetReadOnly(FALSE);

	//3.设置标志
	m_bEnterRoom = false;

}

6.OnBnClickedButtonSend()

void CUdpClientDlg::OnBnClickedButtonSend()
{
	// TODO: 在此添加控件通知处理程序代码
	//1.获取服务端的地址
	CString strIp;
	UINT uiPort;
	GetDlgItemText(IDC_EDIT_SERVER_IP, strIp);
	uiPort = GetDlgItemInt(IDC_EDIT_SERVER_PORT);

	//2.发送数据
	CString strMsg;
	GetDlgItemText(IDC_EDIT_SEND_MESSAGE, strMsg);
	m_pClientSocket->SendTo(strMsg, strMsg.GetLength() + 100, uiPort, strIp);

	//将原有的那个EDIT_control控件直接清空掉
	SetDlgItemText(IDC_EDIT_SEND_MESSAGE, _T(""));
}

7.程序关闭DestroyWindow()

BOOL CUdpClientDlg::DestroyWindow()
{
	// TODO: 在此添加专用代码或调用基类
	if (m_bEnterRoom)
	{
		OnBnClickedButtonExit();
	}
	//在销毁之前需要调用一下离开房间的操作
	return CDialogEx::DestroyWindow();
}

这步操作的主要目的,是当关闭客户端程序时,关联到服务端,如果客户端仍在房间内,则调用离开房间函数程序,还原程序操作.

总结

接下来可能会接着学习MFC的相关操作,并结合Modbus相关的串口连接助手进行调试,持续关注不迷路! 谢谢各位!

  • 4
    点赞
  • 42
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 9
    评论
### 回答1: MFC(Microsoft Foundation Classes)是微软公司开发的一套面向对象的C++类库,用于开发Windows应用程序。在MFC中,客户端服务端socket是用来进行网络通信的重要组件。 客户端socket指的是在网络中发起连接的一方。在MFC中,可以使用CSocket类来创建和管理客户端socket。通过CSocket类的成员函数,可以实现与服务器的连接、发送和接收数据等操作。客户端socket可以向服务器发送请求,获取相应的数据或服务。 服务端socket则是接受客户端连接的一方。也是通过CSocket类来创建和管理服务端socket。服务端socket可以监听端口,接受客户端的连接请求,并与客户端建立通信。服务端socket可以处理多个客户端的请求,提供相应的服务。 在使用MFC开发网络应用程序时,客户端服务端socket通常需要配合使用。客户端socket负责与服务端建立连接,并发送请求。服务端socket则负责接受客户端的连接请求,并处理客户端的请求。通过socket编程,可以实现客户端服务端的数据交互实现网络通信功能。 总结来说,MFC中的客户端服务端socket是实现网络通信的重要组件。通过客户端socket可以向服务器发送请求,获取相应的数据或服务。而服务端socket则接受客户端的连接请求,并提供相应的服务。通过MFC提供的CSocket类,我们可以方便地创建、管理和控制这些socket,实现客户端服务端之间的数据交互。 ### 回答2: MFC是Microsoft Foundation Classes的缩写,它是微软公司为了简化Windows应用程序的开发而开发的一个类库。客户端服务端socket则是在网络编程中使用的两种角色。 在MFC中,我们可以使用MFC类库提供的相关类来实现客户端服务端socket的通信。客户端socket通常用于向服务器发送请求并接收服务器的响应。我们可以使用MFC的CAsyncSocket类来创建一个客户端socket对象,然后通过对象的方法来连接服务器、发送数据和接收数据。 服务端socket则是用于接收客户端的请求并提供相应的服务。我们可以使用MFC的CSocket类来创建一个服务端socket对象,通过对象的方法来绑定本地地址和端口、监听客户端的连接请求、接收客户端发送的数据以及向客户端发送数据等操作。 在实际应用中,客户端服务端socket可以通过网络进行通信,比如TCP/IP协议。客户端通过连接服务端socket来发送请求和接收响应,而服务端socket则通过接收客户端连接来处理客户端的请求并提供相应的服务。 总之,MFC提供了方便易用的类库来实现客户端服务端socket的通信,开发人员可以使用这些类库来快速开发网络应用程序。 ### 回答3: MFC是微软基于Windows操作系统的应用程序开发框架,提供了许多方便的类和函数,用于开发Windows图形用户界面应用程序。MFC中提供了用于创建和管理客户端服务端socket的类。 客户端socket用于建立与服务器的连接,并向服务器发送请求。在MFC中,可以使用CSocket类创建和管理客户端socket。CSocket类提供了一组方法,如Create、Connect、Send等,可以方便地创建一个客户端socket,并与服务器建立连接,发送数据到服务器。 服务端socket用于接受客户端的连接请求,并处理客户端发送的数据。在MFC中,可以使用CSocket和CAsyncSocket类来创建和管理服务端socket。CSocket类提供了一个Accept方法,用于接受客户端的连接请求,当有新的客户端连接时,会触发一个Accept事件,开发者可以重写该事件处理函数来处理新的连接。CAsyncSocket类是一个异步的socket类,在服务端应用程序中常用于处理多个客户端的连接和数据交互。 在MFC中,客户端服务端socket之间的通信可以通过发送和接收数据来实现客户端可以使用Send方法发送数据到服务器,而服务端可以使用Send方法将数据发送给已连接的客户端客户端服务端之间的数据交换可以使用字节流,也可以使用特定的通信协议,如HTTP、TCP/IP等。 总之,使用MFC的CSocket类和CAsyncSocket类可以方便地创建和管理客户端服务端socket,实现客户端与服务器之间的通信。通过发送和接收数据,可以实现数据的传输和交换。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

胡须不排序H

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值