当前位置: 首页 > news >正文

MFC-TCP网络编程服务端-Socket

目录

1、通过Socket建立服务端:

2、UI设计:

3、代码的实现:

(1)、CListenSocket类

(2)、CConnectSocket类

(3)、CTcpServerDlg类


1、通过Socket建立服务端:

(1)、创建一个Socket对象并绑定一个端口:CListenSocket 监听Socket

(2)、通过Listen()函数,监听来自客户端的连接请求

(3)、连接请求到来之后,建立一个通信用的Socket,CConnectSocket,并且加入数组中

(4)、使用Send()、Receive()函数,接收客户端发送的数据,并遍历数组,一次转发消息

2、UI设计:

3、代码的实现:

(1)、CListenSocket类

CListenSocket的目的是等待客户端的连接请求,继承Socket类,重写Socket的OnAccept函数,创建Socket对象,连接的初始化和等待客户端连接。

CListenSocket.h中

CListenSocket(CTcpServerDlg * pdlg);//传递对话框的指针
    ~CListenSocket();
    virtual void OnAccept(int nErrorCode);//重写父类
    CTcpServerDlg *m_pMainDlg;//接收传递的对话框指针

CListenSocket.cpp中

CListenSocket::CListenSocket(CTcpServerDlg * pdlg)
{
    m_pMainDlg = pdlg; //初始化接收的对话框指针
}


void CListenSocket::OnAccept(int nErrorCode)
{
    // TODO: 在此添加专用代码和/或调用基类

    CSocket::OnAccept(nErrorCode);

    m_pMainDlg->ProcessPendingAccept();//对于连接函数的处理放在对话框函数中处理
}

(2)、CConnectSocket类

CConnectSocket的目的是通讯用的Socket,继承Socket类,重写Socket的OnReceive函数,数据的接收和处理以及数据的解析。

CConnectSocket.h中

CConnectSocket(CTcpServerDlg *pdlg);
    ~CConnectSocket();
    virtual void OnReceive(int nErrorCode);
    CTcpServerDlg *m_pMainDlg;

CConnectSocket.cpp中

CConnectSocket::CConnectSocket(CTcpServerDlg *pdlg)
{
    m_pMainDlg = pdlg;
}
void CConnectSocket::OnReceive(int nErrorCode)
{
    // TODO: 在此添加专用代码和/或调用基类

    CSocket::OnReceive(nErrorCode);
    m_pMainDlg->ProcessPendingRead(this);//接收消息必须要传递指针进去,调用接收函数
}

(3)、CTcpServerDlg类

1)、声明服务端用于监听的socket

//服务端声明的用于监听的socket
    CListenSocket *m_pListenSocket;

2)、声明一个CArray的对象,里面存放的CConectSocket指针

CArray<CConnectSocket*, CConnectSocket*&>m_ConnectSocketList;
    //m_ConnectSocketList是CArray的一个对象,里面放着CConnectSocket的指针,并且可以对里面的数据进行访问和修改。
    // CConnectSocket*&当数组元素被传递给函数时,使用引用方式传递元素,可以有效避免不必要的拷贝和内存开销。

3)、定义一个结构体,保存客户端的IP和Port

struct ClientAddr
{
    CString strIp;
    UINT uiPort;
};

4)、框架类的入口函数初始化监听Socket和端口号

    m_pListenSocket = new CListenSocket(this);
    //m_pListenSocket用来监听服务器的连接所以要分配内存
    m_pListenSocket->Create(8080);
    //初始化端口号
    if (!m_pListenSocket->Listen())
    {
        MessageBox(L"监听失败!");
        return FALSE;
    }

5)、实现ProcessPendingAccept()函数

主要实现监听服务器的连接,监听到了就加到列表中

CConnectSocket *pConnectSocket = new CConnectSocket(this);
    if (m_pListenSocket->Accept(*pConnectSocket))
    {
        m_ConnectSocketList.Add(pConnectSocket);
    }
    else {
        delete pConnectSocket;
    }

6)、实现ProcessPendingRead()函数

数据的接收,并且存放在buff中

TCHAR buff[4096];//存放接收到的数据

    //接收数据
    int nRead = connectSocket->Receive(buff, 4096);
    if (nRead == SOCKET_ERROR)
    {
        return;
    }

    buff[nRead] = L'\0';//接收到的数据末尾加上结束符

CString strTemp(buff);//格式化buff

把连接到的客户端IP和Port存放在结构体中

 ClientAddr clientAddr;//用来存放客户端的IP和Port

//获取连接上来的客户端的IP和端口
    connectSocket->GetPeerName(clientAddr.strIp, clientAddr.uiPort);

解析数据:加入房间enter

int i = 0;
    //解析数据
    if (strTemp.CompareNoCase(L"enter") == 0)
    {
        //通知其他客户端有人进入房间
        CString strEnterMsg;
        strEnterMsg.Format(L"系统消息:%s(%d)进入了房间", clientAddr.strIp, clientAddr.uiPort);
        for ( i;i<m_ConnectSocketList.GetSize();i++)
        {//遍历每一个客户端,并且发送消息给每个客户端。
            CConnectSocket *& tempClient = m_ConnectSocketList.ElementAt(i);
            tempClient->Send(strEnterMsg, strEnterMsg.GetLength() + 100);
        }

显示到界面

//更新界面
        SetDlgItemInt(IDC_EDIT_NUMBER, m_ConnectSocketList.GetSize());//显示当前客户端人数

        CString ALmSG;
        GetDlgItemText(IDC_EDIT_MESSAGE, ALmSG);//EDIT上显示的之前的消息
        SetDlgItemText(IDC_EDIT_MESSAGE, ALmSG + L"\r\n" + strEnterMsg);
        //显示的现在的消息加上之前的消息

解析数据:离开房间leave

//从列表中移除
        for (i;i<m_ConnectSocketList.GetSize();i++)
        {
            CConnectSocket *&temClient = m_ConnectSocketList.ElementAt(i);
            ClientAddr temClientAddr;
            temClient->GetPeerName(temClientAddr.strIp, temClientAddr.uiPort);
            if (temClientAddr.uiPort == clientAddr.uiPort && temClientAddr.strIp.Compare(clientAddr.strIp) == 0)
            {//当前移除的IP和Port如果和加入的时候存放在结构体的一样直接跳出循环删除
                break;
            }
            if (i<m_ConnectSocketList.GetSize())
            {
                m_ConnectSocketList.RemoveAt(i);
            }

发送给每一个客户端消息

//通知所有人
            CString strleaveMsg;
            strleaveMsg.Format(L"系统消息:%s(%d)进入了房间", clientAddr.strIp, clientAddr.uiPort);
            for (i; i < m_ConnectSocketList.GetSize(); i++)
            {
                CConnectSocket *& tempClient = m_ConnectSocketList.ElementAt(i);
                tempClient->Send(strleaveMsg, strleaveMsg.GetLength() + 100);
            }

更新界面

//更新界面
            SetDlgItemInt(IDC_EDIT_NUMBER, m_ConnectSocketList.GetSize());

            CString ALmSG;
            GetDlgItemText(IDC_EDIT_MESSAGE, ALmSG);
            SetDlgItemText(IDC_EDIT_MESSAGE, ALmSG + L"\r\n" + strleaveMsg);
            

发送的是普通消息

//遍历每一个客户端发出,然后更新界面即可

CString strMsg;
        strMsg.Format(L"%s(%d):%s", clientAddr.strIp, clientAddr.uiPort,strTemp);
        for (i; i < m_ConnectSocketList.GetSize(); i++)
        {
            CConnectSocket *& tempClient = m_ConnectSocketList.ElementAt(i);
            tempClient->Send(strMsg, strMsg.GetLength() + 100);
        }
        //更新界面
        SetDlgItemInt(IDC_EDIT_NUMBER, m_ConnectSocketList.GetSize());

        CString ALmSG;
        GetDlgItemText(IDC_EDIT_MESSAGE, ALmSG);
        SetDlgItemText(IDC_EDIT_MESSAGE, ALmSG + L"\r\n" + strMsg);

相关文章:

  • 数据结构(C语言) 实验-栈与字符串
  • Win10共享打印机,别人连接不上出现无法连接到打印机错误码0x0000011b
  • CH11_重构API
  • java项目之戒烟网站(ssm+vue)
  • Zabbix深入解析与实战
  • 图形界面应用案例——关灯游戏(以及扩展)(python)
  • 用 winget 在 Windows 上安装 kubectl
  • centerOS下docker 搭建IotDB集群
  • python开发过程中注意编码规范~
  • python 字典Dict
  • 蓝桥杯每日一题203.11.7
  • 以 Kubernetes 原生方式实现多集群告警
  • ansible问题排查
  • 浙江大学漏洞报送证书
  • 代码提交记录时候,一般时候哪些单词作为前缀并代表什么含义
  • 2017届校招提前批面试回顾
  • canvas 五子棋游戏
  • Mocha测试初探
  • redis学习笔记(三):列表、集合、有序集合
  • Yeoman_Bower_Grunt
  • 观察者模式实现非直接耦合
  • 解析带emoji和链接的聊天系统消息
  • 紧急通知:《观止-微软》请在经管柜购买!
  • 马上搞懂 GeoJSON
  • 浅析微信支付:申请退款、退款回调接口、查询退款
  • 如何设计一个比特币钱包服务
  • 用 Swift 编写面向协议的视图
  • 在electron中实现跨域请求,无需更改服务器端设置
  • raise 与 raise ... from 的区别
  • # Panda3d 碰撞检测系统介绍
  • # 透过事物看本质的能力怎么培养?
  • ###51单片机学习(1)-----单片机烧录软件的使用,以及如何建立一个工程项目
  • #FPGA(基础知识)
  • #QT(串口助手-界面)
  • ${factoryList }后面有空格不影响
  • $NOIp2018$劝退记
  • (1)STL算法之遍历容器
  • (5)STL算法之复制
  • (二)丶RabbitMQ的六大核心
  • (分享)自己整理的一些简单awk实用语句
  • (六)vue-router+UI组件库
  • (论文阅读32/100)Flowing convnets for human pose estimation in videos
  • (一)Neo4j下载安装以及初次使用
  • (转)Spring4.2.5+Hibernate4.3.11+Struts1.3.8集成方案一
  • (转)清华学霸演讲稿:永远不要说你已经尽力了
  • .net 反编译_.net反编译的相关问题
  • .NET/C# 的字符串暂存池
  • .NET中GET与SET的用法
  • [C#] 我的log4net使用手册
  • [CC-FNCS]Chef and Churu
  • [CISCN2019 华北赛区 Day1 Web5]CyberPunk --不会编程的崽
  • [EFI]Dell Inspiron 15 5567 电脑 Hackintosh 黑苹果efi引导文件
  • [hdu 3065] 病毒侵袭持续中 [AC自动机] [病毒特征码匹配]
  • [Linux]——彻底学通权限
  • [MSSQL]GROUPING SETS,ROLLUP,CUBE初体验