• 基于MFC的socket编程


    网络编程

    1、windows 套接字编程(开放的网络编程接口)添加头文件#include<windows.h>

    2、套接字及其分类

    socket分为两种:(1)数据报socket:无连接套接字,可向指定的socket发送数据报消息,也可从指定的socket接收消息,提供双向的面向记录的数据流。但不能保证数据传输的顺序,也不能保证传输的可靠性。

    (2)数据流socket:(例如电话呼叫),基于显式连接的套接字,通过没有记录的双向字节数据流,具有可靠的发送顺序,接收也是可靠的,适合处理大量数据的传输。

    3、客户端/服务器模型(C/S模型)

    网络编程常用的一种架构模型,将应用程序分为:

    (1)前端客户端组件:运行在工作站上,负责从用户接收数据,为服务器处理数据,并形成到服务器的连接。

    (2)后台服务器组件:等待客户端的连接,当服务器收到客户端的连接请求后,服务器会处理并返回客户端响应信息,客户端收到响应信息后,通过用户接口,呈现给用户.

    目前很多项目都设计为分布式程序,以提高应用程序的性能。设计的关键是程序的性能和可扩展性。

    4、网络字节顺序

    标准的网络字节序是采用大端模式,而Intel采用小端模式。

    若使用MFC 的socket编程,即服务器和客户端都使用MFC,则无需关心字节顺序。

    5、使用WinSock API 进行编程

    6、MFC对WinSock API 进行了封装。

    提供了两个类:(1)类CAsynSocket一对一地封装了WinSock API。

    (2)类CSocket提供以CArchive对象中序列化数据的Socket功能。

    使用CSocket类,必须创建CSocket对象,使用对象底层的Socket句柄。

    对客户端使用默认参数调用create()方法就好,对服务器socket对象,必须在create()方法中指定IP地址和端口。

    下面是使用MFC socket编程的一个简单例子

    服务器端程序:

    (1)创建服务器

    (2)连接请求连接的客户端

    (3)与客户端进行数据传输(发送和接收)

    (4)客户端断开服务器

    界面如下图所示:

     部分代码:

    /*说明:本函数用于打开或关闭服务器,主要用到Create函数和Listen函数用于创建服务器和监听客户端。
    其中端口号从编辑框获取,应用程序的可用端口范围是1024-65535。*/
    void CPhoneServerDlg::OnBnClickedstartserver()
    {
        // TODO: 在此添加控件通知处理程序代码
        if (m_connect)
        {
            delete listenSocket;
            listenSocket = NULL;
            m_connect = false;
            SetDlgItemText(IDC_startserver, _T("打开服务器"));
            UpdateEvent(_T("系统关闭服务器"));
            return;
        }
        listenSocket = new CSerSocket();
        listenSocket->m_pDlg = this; // 指定对话框为主对话框,不能少了这句
    
        UpdateData(true);
        if (!listenSocket->Create(m_port))  // 创建服务器的套接字,IP地址默认本机IP
        {
            AfxMessageBox(_T("创建套接字错误!"));
            listenSocket->Close();
            return;
        }
        if (!listenSocket->Listen())
        {
            AfxMessageBox(_T("监听失败!"));
            listenSocket->Close();
            return;
        }
        m_connect = true;
        SetDlgItemText(IDC_startserver, _T("关闭服务器"));
        UpdateEvent(_T("系统打开服务器"));
    }
    
    /*说明:本函数在CServerSocket类中的OnAccept消息中调用,用于响应用户连接服务器的请求,主要函数为Accept,当连接成功后,
    通过链表m_clientList保存新用户,更新日志,向新用户发送“Hello”表示欢迎。*/
    void CPhoneServerDlg::AddClient()
    {
        CSerSocket *pSocket = new CSerSocket;
        pSocket->m_pDlg = this;
        listenSocket->Accept(*pSocket);
        pSocket->AsyncSelect(FD_READ | FD_WRITE | FD_CLOSE);
        m_clientList.AddTail(pSocket);
        m_usercount = m_clientList.GetCount();
        UpdateEvent(_T("用户连接服务器"));
        SendMSG(_T("hello"));
    }
    
    /*本函数在CServerSocket类中的OnClose消息中调用,用到POSITION结构,
    查找存储用户中哪位用户下线了,将下线用户释放,从链表中删除,并更新日志。*/
    void CPhoneServerDlg::RemoveClient(CSerSocket *pSocket)
    {
        POSITION npos = m_clientList.GetHeadPosition();
        POSITION nTmpPos = npos;
        while (npos)
        {
            CSerSocket *pSocketItem = (CSerSocket *)m_clientList.GetNext(npos);
            if (pSocketItem->m_hSocket == pSocket->m_hSocket)
            {
                pSocketItem->Close();
                delete pSocketItem;
                m_clientList.RemoveAt(nTmpPos);
                m_usercount = m_clientList.GetCount();
                UpdateData(false);
                UpdateEvent(_T("用户离开"));
                return;
            }
            nTmpPos = npos;
        }
    }
    
    /*说明:本函数在CServerSocket类中的OnReceive消息中调用,用于处理接收到的数据并控制电脑,
    并将数据转发给所有用户(类似于群消息),通过CSocket类的GetPeerName函数可以获取用户的IP和端口号。*/
    void CPhoneServerDlg::RecvData(CSerSocket *pSocket)
    {
        char *pData = NULL;
        pData = new char[1024];
        memset(pData, 0, sizeof(char) * 1024);
        UCHAR leng = 0;
        CString str;
        if (pSocket->Receive(pData, 1024, 0) != SOCKET_ERROR)
        {
            str = pData;
            ControlPC(str);
            SendMSG(str);
        }
        delete pData;
        pData = NULL;
    }
    
    /*说明:本函数在所有需要更新日志的地方都有调用,方便服务器记录用户的登录和退出事件。*/
    void CPhoneServerDlg::UpdateEvent(CString str)
    {
        CString string;
        CTime time = CTime::GetCurrentTime();
        // 获取系统当前时间
        str += _T("
    ");
        // 用于换行显示日志
        string = time.Format(_T("%Y/%m/%d %H:%M:%S  ")) + str;
        // 格式化当前时间
        int lastLine = m_event.LineIndex(m_event.GetLineCount() - 1);
        //获取编辑框最后一行索引
        m_event.SetSel(lastLine + 1, lastLine + 2, 0);
        //选择编辑框最后一行
        m_event.ReplaceSel(string);
    }
    
    /*说明:本函数在发送函数SendMSG中调用,用于字符集的转换,将宽字符转换为多字符集,不经转换的话,接收方只能接收一个字节。*/
    BOOL CPhoneServerDlg::WChar2MByte(LPCWSTR srcBuff, LPSTR destBuff, int nlen)
    {
        int n = 0;
        n = WideCharToMultiByte(CP_OEMCP, 0, srcBuff, -1, destBuff, 0, 0, FALSE);
        if (n<nlen)return FALSE;
        WideCharToMultiByte(CP_OEMCP, 0, srcBuff, -1, destBuff, nlen, 0, FALSE);
        return TRUE;
    }
    
    /*说明:发送函数,用于发送消息给所有用户,主要函数为Send,在AddClient和RecvData中都有调用,可以随时调用发消息给用户。*/
    void CPhoneServerDlg::SendMSG(CString str)
    {
        char *pSend = new char[str.GetLength()];
        memset(pSend, 0, str.GetLength()*sizeof(char));
        if (!WChar2MByte(str.GetBuffer(0), pSend, str.GetLength()))
        {
            AfxMessageBox(_T("字符转换失败"));
            delete pSend;
            return;
        }
        POSITION nPos = m_clientList.GetHeadPosition();
        while (nPos)
        {
            CSerSocket* pTemp = (CSerSocket*)m_clientList.GetNext(nPos);
            pTemp->Send(pSend, str.GetLength());
        }
        delete pSend;
    }

    客户端界面如下:

    部分代码:

    void CPhoneClinetDlg::OnBnClickedconnect()
    {
        // TODO: 在此添加控件通知处理程序代码
        if (m_connect)                             // 如果已经连接,则断开服务器
        {
            m_connect = false;
            pSock->Close();
            delete pSock;
            pSock = NULL;
            m_conpc.SetWindowTextW(_T("连接服务器"));
            UpdateData(false);
            return;
        }
        else                                                // 未连接,则连接服务器
        {
            pSock = new CClientSocket();
            if (!pSock->Create())         //创建套接字
            {
                AfxMessageBox(_T("创建套接字失败!"));
                return;
            }
        }
        if (!pSock->Connect(_T("127.0.0.1"), port))    //连接服务器
        {
            AfxMessageBox(_T("连接服务器失败!"));
            return;
        }
        else
        {
            m_connect = true;
            m_conpc.SetWindowTextW(_T("断开服务器"));
            UpdateData(false);
        }
    }

    void CPhoneClinetDlg::OnBnClickedsend()
    {
        // TODO: 在此添加控件通知处理程序代码
       if (!m_connect)return; //未连接服务器则不执行
       UpdateData(true); //获取界面数据
       if (m_datasend != "")
       {
          char* pBuff = new char[m_datasend.GetLength() * 2];
          memset(pBuff, 0, m_datasend.GetLength() * 2);
          WChar2MByte(m_datasend.GetBuffer(0), pBuff, m_datasend.GetLength() * 2);
          pSock->SendMSG(pBuff, m_datasend.GetLength() * 2);
       }
    }

     
  • 相关阅读:
    if控制器+循环控制器+计数器,控制接口分支
    前置处理器
    逻辑控制器
    配置元件
    基础元件
    docker etcdctl报错:etcdctl No help topic for 'put'
    celery定时执行ansible api返回为空的问题
    Ansible+Jenkins+Gitlab搭建及配置
    进击的Python【第十六章】:Web前端基础之jQuery
    进击的Python【第十五章】:Web前端基础之DOM
  • 原文地址:https://www.cnblogs.com/sunshine1218/p/6769353.html
Copyright © 2020-2023  润新知