MFC网络通信-Udp服务端

目录

1、UI的布局

2、代码的实现:

(1)、自定义的子类CServerSocket

(2)、重写OnReceive事件

(3)、在CUdpServerDlg类中处理

(4)、在OnInitDialog函数中

(5)、实现自定义函数ProcessPendingRead()处理接收到的数据

(6)、加入新客户的消息先进性判断是否位enter

(7)、加入的新消息如果是leave

(8)、普通信息


1、UI的布局

添加一个CServerSocket类继承于CSocket

2、代码的实现:

(1)、自定义的子类CServerSocket

所有的显示应该显示在框架中,在构造函数传入一个框架的指针然后进行初始化

class CUdpServerDlg;//声明一下dlg类

public:

CServerSocket(CUdpServerDlg* pdlg);//所有的操作显示在dlg上面

~CServerSocket();

private:

CUdpServerDlg *m_pMainDlg;//指针来接收
CServerSocket::CServerSocket(CUdpServerDlg* pdlg)//传递的消息统一放在对话框中处理,所以初始化传递一个对话框指针

{

this->m_pMainDlg = pdlg;

}

(2)、重写OnReceive事件

(父类的OnReceive是纯虚函数,如果有数据可读就会调用该方法)

protected:

virtual void OnReceive(int nErrorCode);//重写onReceive()方法,如果有数据可读就会调用该方法

//在WINSOCKET父类中有这个纯虚函数来接收数据

//有数据可读就会调用这个纯虚函数

void CServerSocket::OnReceive(int nErrorCode)

{

CSocket::OnReceive(nErrorCode);

/*该行代码的作用是确保底层的数据接收和处理机制正常运行,并在此基础上执行自定义的数据处理逻辑。*/

m_pMainDlg->ProcessPendingRead();//消息处理统一放在对话框

}

(3)、在CUdpServerDlg类中处理

struct ClientAddr//自定义结构体存放IP和端口号

{

CString strIP;

UINT inPort;

};
CServerSocket *m_pServerSocket;

CArray<ClientAddr,ClientAddr&>m_ClientAddList;//客户端发送的所有消息

/*,m_ClientAddList是一个包含ClientAddr类型对象的数组,可以使用Add()方法向其中添加元素,使用GetAt()方法访问已添加的元素。这个数组被用来存储客户端连接的地址信息等数据。*/

void ProcessPendingRead();

(4)、在OnInitDialog函数中

m_pServerSocket = new CServerSocket(this);

在当前对话框中需要使用到通信(SOCKET),所以在堆区创建一个CServerSocket的对象,并且还要这个对象和当前窗口相关联,然后用一个m_pServerSocket指针指向新建的对象。

m_pServerSocket->Create(8080, SOCK_DGRAM);//用于创建一个 UDP 套接字,并将其绑定到本地 IP 地址和指定的端口号(这里是 8080)上。

(5)、实现自定义函数ProcessPendingRead()处理接收到的数据

初始化接收数据数组和客户端结构体对象

TCHAR buffer[4096];//接收数据的数组

ClientAddr clietAddr;//客户端结构体对象

判断读取的内容的是否有效

int nRead = m_pServerSocket->ReceiveFrom(buffer, 4096, clietAddr.strIP, clietAddr.inPort);//接收数据的字节数nRead

//缓冲区地址,接收数据大小,客户端的IP,客户端的端口

if (nRead == SOCKET_ERROR)//如果读出错

{

return;

}

如果内容有效字符串结尾加上\0,并且转换类型位CString类型

buffer[nRead] = L'\0';

//在接收到的数据后面加上结束符(索引0开始,所以nRead代表最后一位加1)

CString strTemp(buffer);

//char *类型的buffer转成CString类型的strTemp,代表接收到的内容。

strTemp是接收到的消息,消息又分为三种,加入新客户,删除客户,还有就是普通消息

(6)、加入新客户的消息先进性判断是否位enter

if (strTemp.CompareNoCase(_T("enter"))==0)

/*比较 strTemp 和 "enter" 是否相等,不区分大小写。如果相等,则返回 0,否则返回一个非零值*/

在条件下我们将客户加入到列表中

/把新的客户加入到列表中

m_ClientAddList.Add(clietAddr);

通知其它客户端有用户加入

首先加入的信息需要规范一下

CString strEnterMsg;

strEnterMsg.Format(_T("系统消息:%s(%d)进入了房间"), clietAddr.strIP, clietAddr.inPort);

通知其他客户端

for (i = 0;i<m_ClientAddList.GetSize();i++)//通知所有的客户端

{

ClientAddr& tempClient = m_ClientAddList.ElementAt(i);//获取所有的客户端

m_pServerSocket->SendTo(strEnterMsg, strEnterMsg.GetLength() + 1000, tempClient.inPort, tempClient.strIP);//发送消息

}

控件上的显示更新(人数的更新还有内容的更新)

SetDlgItemInt(IDC_EDIT_NUMBER, m_ClientAddList.GetSize());//当前人数在文本上设置

//之前的消息可能存在,需要拿出来放在alMsg的后面

CString alMsg;

GetDlgItemText(IDC_EDIT_CHAT_MESSAGE, alMsg);//取消消息到alMsg

SetDlgItemText(IDC_EDIT_CHAT_MESSAGE, alMsg + _T("\r\n") + strEnterMsg);//将新的消息叠加到alMsg后面

(7)、加入的新消息如果是leave

if (strTemp.CompareNoCase(_T("leave")) == 0)

//离开房间

从列表中遍历寻找要删除的用户

//列表中移除用户

for (i = 0;i<m_ClientAddList.GetSize();i++)

{

ClientAddr& tempClient = m_ClientAddList.ElementAt(i);//遍历每一个

if (tempClient.inPort == clietAddr.inPort&&tempClient.strIP.Compare(clietAddr.strIP) == 0)

{

break;

//遍历找到了要移除的端口号和IP都相等的tempClient,break

}

}

一旦找到用户break,然后删除用户

if (i<m_ClientAddList.GetSize())//如果遍历没完就说明找到了。

{

m_ClientAddList.RemoveAt(i);//移除

}

格式化发送的消息

CString strLeaveMsg;

strLeaveMsg.Format(_T("通知消息:%s(%d)离开了房间"), clietAddr.strIP, clietAddr.inPort);//通知的内容

遍历发送所有的客户端

for (i = 0; i < m_ClientAddList.GetSize(); i++)//通知每一个客户端有用户离开了

{

ClientAddr& tempClient = m_ClientAddList.ElementAt(i);

m_pServerSocket->SendTo(strLeaveMsg, strLeaveMsg.GetLength() + 1000, tempClient.inPort, tempClient.strIP);

}

更新控件上的内容

SetDlgItemInt(IDC_EDIT_NUMBER, m_ClientAddList.GetSize());//更新当前人数

CString alMsg;

GetDlgItemText(IDC_EDIT_CHAT_MESSAGE, alMsg);

SetDlgItemText(IDC_EDIT_CHAT_MESSAGE, alMsg + _T("\r\n") + strLeaveMsg);//更新消息

(8)、普通信息

else//普通的聊天信息

{

CString strMsg;

strMsg.Format(_T("%s(%d):%s"), clietAddr.strIP, clietAddr.inPort,strTemp);//格式化普通聊天信息的内容

for (i = 0; i < m_ClientAddList.GetSize(); i++)

{ //转发所有的人消息

ClientAddr& tempClient = m_ClientAddList.ElementAt(i);

m_pServerSocket->SendTo(strMsg, strMsg.GetLength() + 1000, tempClient.inPort, tempClient.strIP);

}

CString alMsg;

GetDlgItemText(IDC_EDIT_CHAT_MESSAGE, alMsg);

SetDlgItemText(IDC_EDIT_CHAT_MESSAGE, alMsg + _T("\r\n") + strMsg);//更新聊天框的内容

}

(3)、优化处理

初始化默认服务端的端口和IP在框架的入口函数

//设置服务端默认的端口和IP

SetDlgItemText(IDC_EDIT_SERVER_IP, L"127.0.0.1");

SetDlgItemText(IDC_EDIT_SERVER_PORT, L"8080");

初始化按钮的一些状态

没有加入房间,发送按钮和退出按钮都不能点击

//设置按钮初始化状态

GetDlgItem(IDC_BUTTON_OUT)->EnableWindow(FALSE);

GetDlgItem(IDC_BUTTON_SEND)->EnableWindow(FALSE);

初始化发送消息的EDIT只读,服务端IP和PORT可以修改

GetDlgItem(IDC_EDIT_SEND_MESSAGE)->EnableWindow(FALSE);

GetDlgItem(IDC_EDIT_SERVER_IP)->EnableWindow(TRUE);

GetDlgItem(IDC_EDIT_SERVER_PORT)->EnableWindow(TRUE);

加入房间之后重新设置按钮的状态

//设置按钮文本框的状态

//设置按钮初始化状态

GetDlgItem(IDC_BUTTON_OUT)->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);

离开房间重新设置按钮的状态

GetDlgItem(IDC_BUTTON_OUT)->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);

重写框架类的关闭窗口函数

实现关闭窗口之后调用离开房间按钮事件。

BOOL CUdpClientDlg::DestroyWindow()

{

// TODO: 在此添加专用代码和/或调用基类

if (m_bEnterRoom)

{

OnBnClickedButtonOut();

}

return CDialogEx::DestroyWindow();

}

相关推荐
UestcXiye1 小时前
《TCP/IP网络编程》学习笔记 | Chapter 3:地址族与数据序列
c++·计算机网络·ip·tcp
霁月风2 小时前
设计模式——适配器模式
c++·适配器模式
jrrz08283 小时前
LeetCode 热题100(七)【链表】(1)
数据结构·c++·算法·leetcode·链表
咖啡里的茶i3 小时前
Vehicle友元Date多态Sedan和Truck
c++
海绵波波1073 小时前
Webserver(4.9)本地套接字的通信
c++
@小博的博客3 小时前
C++初阶学习第十弹——深入讲解vector的迭代器失效
数据结构·c++·学习
爱吃喵的鲤鱼4 小时前
linux进程的状态之环境变量
linux·运维·服务器·开发语言·c++
7年老菜鸡5 小时前
策略模式(C++)三分钟读懂
c++·qt·策略模式
Ni-Guvara5 小时前
函数对象笔记
c++·算法
似霰5 小时前
安卓智能指针sp、wp、RefBase浅析
android·c++·binder