002 远程控制网络编程设计

1 TCP网络通信流程

2 原始服务器代码

#include <iostream>
#include <WinSock2.h>              // 网络头文件,Windows socket第二版
#pragma comment(lib, "ws2_32.lib") // 加载网络库,Windows socket第二版,32位;
​
int main()
{
    // 1 打开网络库
    WSADATA data;
    // MAKEWORD:使用库版本号 data:系统获取网络配置信息,然后返回给此参数
    WSAStartup(MAKEWORD(2, 2), &data);
    // 2 创建服务端socket
    SOCKET socket_server = socket(AF_INET, SOCK_STREAM, 0);
    SOCKADDR_IN addr_server;
    addr_server.sin_family = AF_INET;
    addr_server.sin_addr.S_un.S_addr = INADDR_ANY;
    addr_server.sin_port = htons(9999);
    // 3 给服务端socket绑定地址,bind
    bind(socket_server, (sockaddr*)&addr_server, sizeof(SOCKADDR_IN));
    // 4 将套节字置入正在传入侦听连接的状态,listen
    listen(socket_server, 2);
    SOCKADDR_IN addr_client;
    int addr_client_len = sizeof(SOCKADDR_IN);
    while (true)
    {
        char buffer[4096] = "";
        // 5 等待客户端连接,accept
        SOCKET socket_clietn = accept(socket_server, (sockaddr*)&addr_client, &addr_client_len);
        // 6 接收数据
        int len = recv(socket_clietn, buffer, sizeof(buffer), 0);
        // 7 发送数据
        send(socket_clietn, buffer, sizeof(buffer), 0);
        // 关闭客户端套接字
        closesocket(socket_clietn);
    }
    // 关闭服务端套接字
    closesocket(socket_server);
    // 清除windows socket环境
    WSACleanup();
​
}   

从以上代码易看出,main函数里面代码结构混乱,无法一眼看出服务器执行逻辑 ==》可以使用类将接口封装

而且有些代码只会执行一次而又只会执行一次--》全局静态全局变量结合类

3 如何从原始服务器代码优化

3.0 其他代码优化

优化一:

代码只会执行一次但有只会执行一次,而且是在main函数之前执行,main函数之后执行,一些初始化环境的代码

例如以下代码

    // 打开网络库
    WSADATA data;
    // MAKEWORD:使用库版本号 data:系统获取网络配置信息,然后返回给此参数
    WSAStartup(MAKEWORD(2, 2), &data);
    
    // 关闭服务端套接字
    closesocket(socket_server);
    // 清除windows socket环境
    WSACleanup();

优化一方案:

使用全局静态变量,全局静态变量只会初始化一次,而且是在main函数之前初始化,main函数之后销毁

main函数之前不存在多线程,是个单线程环境,不存在上锁这些情况,而且mian函数结束后,可以放心的清理内存,不用担心

还有谁在使用某块内存

我们可以使用一个静态单例对象来解决,这个对象在main函数之前就会初始化,调用构造函数,在main函数之后才会析构,调用析构函数

3.1 抽象服务器通信过程

  • 套接字环境初始化

  • 服务器套接字初始化

  • 等待连接

  • 接收数据

  • 发送数据

3.2 设计单例

为什么使用单例:

从服务器类可知,该类只需要一个实例,不然会导致构造、析构函数被频繁调用,导致环境崩溃

怎么实现单例:

单例的本质是利用语法限制来使得一个类只允许有一个实例 -- 只能通过类里面的接口来创建对象

ServerSocket.h

class CServerSocket
{
    public:
    static CServerSocket* getInstance()
    {
        if (m_instance == NULL)
        {
            m_instance = new CServerSocket();
        }
        return m_instance;
    }
private:
    CServerSocket& operator=(const CServerSocket& s){}
    CServerSocket(const CServerSocket& s){}
    CServerSocket(){}
    ~CServerSocket(){}
    static CServerSocket* m_instance;
    
};

ServerSocket.cpp

CServerSocket* CServerSocket::m_instance = NULL
CServerSocket* pserver = CServerSocket::getInstance();

代码详解:实现一个单例类

  • 将所有的构造方法变成私有 -- 外部无法通过构造方法创建对象

  • 在类里面声明一个该类的静态指针成员

  • 在类里面定义一个getInstance() 接口,接口里面new 创建一个当前类的对象 -- 由于这个getInstance 方法是位于类里面的所有可以访问该类的构造方法

存在问题 -- 析构没有调用

如何在main函数结束后调用单例对象的析构?

在类里面再声明一个类CHelper,并且再声明一个该类的静态成员m_helper,在单例类里面,这个类的构造方法就调用单例类的getInstance 方法,析构就delete 单例类对象

因为这个m_helper是一个全局静态变量,所以程序初始化之前就会调用这个类的构造方法,main函数结束后就会调用这个对象的析构方法,这样在这个类的析构方法里面去delete 单例对象就没有问题了

代码实现

ServerSocket.h

class CHelper
    {
    public:
        CHelper()
        {
            CServerSocket::getInstance();
        }
        ~CHelper()
        {
            CServerSocket::ReleaseInstance();
        }
    };
    static CHelper m_helper;

ServerSocket.cpp

// 全局的静态变量
CServerSocket* CServerSocket::m_instance = NULL;
CServerSocket::CHelper CServerSocket::m_helper;
​
CServerSocket* pserver = CServerSocket::getInstance();

3.3 服务器功能封装

为什么要封装?

  • 代码复用 -- 其它地方想用这些功能可以直接拷贝走

  • 代码可读性

怎么去封装

!!!抽象化每个步骤, 先把上层框架搭好,再考虑具体功能代码实现!!!

从最原始的代码开始,抽象化每个具体步骤,根据每个步骤来实现接口简化主函数的逻辑

例如:服务器可分为以下几大步骤

  • 套接字环境初始化

  • 套接字初始化

  • accept

  • recv

  • send

服务器几大步骤:套接字环境初始化、创建服务器socket、为服务器初始化地址信息bind、将服务器套接字置于监听状态并开启一个完成三次握手的队列(传入连接队列)listen、从传入连接队列里面取出套接字上的连接返回一个新的连接套接字,如果侦听传入队列为空,该函数会阻塞accept、从连接套接字上接收数据recv、从连接套接字上发送数据

3.4 封装后的服务器逻辑

// 0 创建服务器对象
CServerSocket* pserver = CServerSocket::getInstance();
            int acceptCount = 0;
// 1 套接字环境初始化
            if (pserver->InitSocket() == false)
            {
                MessageBox(NULL, _T("网络初始化失败,请检查网络状态"), _T("网络初始化失败!!"), MB_OK | MB_ICONERROR);
                exit(0);
            }
            while (CServerSocket::getInstance() != NULL)
            {
                // 等待连接
                if (pserver->AcceptClient() == false)
                {
                    MessageBox(NULL, _T("接入用户失败,自动重试"), _T("接入用户失败!!"), MB_OK | MB_ICONERROR);
                    acceptCount += 1;
                    if (acceptCount >= 3)
                    {
                        MessageBox(NULL, _T("多次用户失败,退出程序"), _T("多次接入用户失败!!"), MB_OK | MB_ICONERROR);
                        exit(0);
                    }
                }
                // 处理命令
                int ret = pserver->DealCommand();
            }

5 完整代码

5.0 main.cpp

int main()
{
// 0 创建服务器对象
CServerSocket* pserver = CServerSocket::getInstance();
            int acceptCount = 0;
// 1 套接字环境初始化
            if (pserver->InitSocket() == false)
            {
                MessageBox(NULL, _T("网络初始化失败,请检查网络状态"), _T("网络初始化失败!!"), MB_OK | MB_ICONERROR);
                exit(0);
            }
            while (CServerSocket::getInstance() != NULL)
            {
                // 等待连接
                if (pserver->AcceptClient() == false)
                {
                    MessageBox(NULL, _T("接入用户失败,自动重试"), _T("接入用户失败!!"), MB_OK | MB_ICONERROR);
                    acceptCount += 1;
                    if (acceptCount >= 3)
                    {
                        MessageBox(NULL, _T("多次用户失败,退出程序"), _T("多次接入用户失败!!"), MB_OK | MB_ICONERROR);
                        exit(0);
                    }
                }
                // 处理命令
                int ret = pserver->DealCommand();
            }
}

5.1 ServerSocket.cpp

CServerSocket* CServerSocket::m_instance = NULL;
CServerSocket::CHelper CServerSocket::m_helper;
​
CServerSocket* pserver = CServerSocket::getInstance();

5.2 ServerSocket.h

class CServerSocket
{
public:
    static CServerSocket* getInstance()
    {
        if (m_instance == NULL)
        {
            m_instance = new CServerSocket();
        }
        return m_instance;
    }
    // 初始化套接字,true成功,false失败
    bool InitSocket()
    {
        if (m_socket_server == INVALID_SOCKET)
        {
            return false;
        }
        SOCKADDR_IN addr_server;
        addr_server.sin_family = AF_INET;
        addr_server.sin_port = htons(60000);
        addr_server.sin_addr.S_un.S_addr = INADDR_ANY;
        if (bind(m_socket_server, (sockaddr*)&addr_server, sizeof(addr_server)) == SOCKET_ERROR)
        {
            return false;
        }
        if (listen(m_socket_server, 1) == SOCKET_ERROR)
        {
            return false;
        }
        return true;
    }
    // accept 客户端,true accept成功,false失败
    bool AcceptClient()
    {
        SOCKADDR_IN addr_client;
        int naddr_client_len = sizeof(addr_client);
        m_socket_client = accept(m_socket_server, (sockaddr*)&addr_client, &naddr_client_len);
        if (m_socket_client == INVALID_SOCKET)
        {
            return false;
        }
        return true;
    }
#define BUFFER_SIZE 4096
    // 循环接收处理数据
    int DealCommand()
    {
        char buffer[BUFFER_SIZE] = "";
        recv(socket_clietn, recv, sizeof(buffer), 0);
        // TODO:处理数据
    }
    // send 原始数据
    bool Send(const char* pData, size_t nSize)
    {
        if (m_socket_client == INVALID_SOCKET)
        {
            return false;
        }
        return send(m_socket_client, pData, sizeof(nSize), 0) > 0;
    }
private:
    SOCKET m_socket_server, m_socket_client;
    CServerSocket& operator=(const CServerSocket& s)
    {
        m_socket_client = s.m_socket_client;
        m_socket_server = s.m_socket_server;
    }
    CServerSocket(const CServerSocket& s)
    {
        m_socket_client = s.m_socket_client;
        m_socket_server = s.m_socket_server;
    }
    CServerSocket()
    {
        if (InitSocketEnv() == FALSE)
        {
            MessageBox(NULL, _T("无法初始化套接字环境,请检查网络环境"), _T("初始化错误"), MB_OK|MB_ICONERROR);
            exit(0);
        }
        m_socket_server = socket(AF_INET, SOCK_STREAM, 0);
        m_socket_client = INVALID_SOCKET;
    }
​
    ~CServerSocket()
    {
        closesocket(m_socket_server);
        WSACleanup();
    }
    bool InitSocketEnv()
    {
        // 套接字环境初始化、socket、bind、listen、accept、recv、send
        WSAData data;
        if (WSAStartup(MAKEWORD(2, 2), &data) != 0)
        {
            return false;
        }
        return true;
    }
    static CServerSocket* m_instance;
    static void ReleaseInstance()
    {
        if (m_instance != NULL)
        {
            CServerSocket* tmp = m_instance;
            m_instance = NULL;
            delete tmp;
        }
    }
    class CHelper
    {
    public:
        CHelper()
        {
            CServerSocket::getInstance();
        }
        ~CHelper()
        {
            CServerSocket::ReleaseInstance();
        }
    };
    static CHelper m_helper;
};
  • 11
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值