011 客户端网络模块开发

0 备注

从客户端网络模块开发可以看出面向对象代码复用性还是很高的,只要设计的好

1 添加了一个将错误码格式化的函数

// 格式化错误码信息
std::string GetErrorInfo(int errCode)
{
    std::string ret;
    LPVOID lpMsgBuf = NULL;
    FormatMessage(
        FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_ALLOCATE_BUFFER,
        NULL,
        errCode,
        MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
        (LPTSTR)lpMsgBuf,0 ,NULL
    );
    if (lpMsgBuf != NULL)
    {
        ret = (char*)lpMsgBuf;
    }
    LocalFree(lpMsgBuf);
    return ret;
}
// 使用例子
TRACE("连接失败,:%d %s", GetLastError(), GetErrorInfo(GetLastError()).c_str());

2 客户端网络模块

逻辑步骤

  • 新建一个CClientSocket 类

  • 把服务器的CServerScoekt类直接拷贝过来

  • 删除AcceptClient 函数,客户端不需要accept,等待连接

  • 在InitSocket接口 删除bind、listen 相关代码,客户端也不需要bind和listen

  • 在InitSocket接口 添加connect函数,发起连接(向服务器发起三次握手)

在这里我要说一下tcp网络通信流程,客户端调用connect函数是发起三次握手连接、服务端是堵塞在accept函数的(等待listen开启的监听队列里面完成三次握手的socket),如果有就会进入堵塞在recv,等待客户端发送数据,客户端这边connect三次握手后,就可以直接向服务器发送数据了,然后等待服务器响应数据

参考代码:

class CClientSocket
{
public:
    static CClientSocket* getInstance()
    {
        if (m_instance == NULL)
        {
            m_instance = new CClientSocket();
        }
        return m_instance;
    }
    // 初始化套接字,true成功,false失败
    bool InitSocket(std::string& ipAddress)
    {
        if (m_socket_server == INVALID_SOCKET)
        {
            return false;
        }
        SOCKADDR_IN addr_server;
        addr_server.sin_family = AF_INET;
        // htons 主机字节序转为网络字节序
        addr_server.sin_port = htons(60000);
        // inet_addr 函数将包含 IPv4 点十进制地址的字符串转换为IN_ADDR结构的正确地址。
        addr_server.sin_addr.S_un.S_addr = inet_addr(ipAddress.c_str());
        // 服务器地址是否成功设置
        if (addr_server.sin_addr.S_un.S_addr == NULL)
        {
            AfxMessageBox("指定ip地址无效\r\n");
            TRACE("连接失败,:%d %s", GetLastError(), GetErrorInfo(GetLastError()).c_str());
            return false;
        }
        // 发起三次握手,连接成功后服务器就阻塞在recv等待对方发送数据
        int ret = connect(m_socket_server, (sockaddr*)&addr_server, sizeof(SOCKADDR_IN));
        if (ret == -1)
        {
            AfxMessageBox("连接失败\r\n");
            TRACE("连接失败,:%d %s", GetLastError(), GetErrorInfo(GetLastError()).c_str());
            return false;
        }
​
        return true;
    }
​
#define BUFFER_SIZE 4096
    // 循环接收处理数据,存放在m_packet里面
    int DealCommand()
    {
        if (m_socket_server == INVALID_SOCKET)
        {
            return false;
        }
        char* buffer = new char[BUFFER_SIZE];
        memset(buffer, 0, BUFFER_SIZE);
​
        // 缓冲区当前所有数据长度
        static int index = 0;
        while (true)
        {
            size_t len = recv(m_socket_server, buffer + index, BUFFER_SIZE - index, 0);
            if (len <= 0)
            {
                return -1;
            }
            // 每次接收数据后,index加上这个长度
            index += len;
            // 让每次解析都是全部的数据长度,如果直接传index进去解析,会导致index值被修改
            len = index;
            // len是个引用 本次解析的数据长度
            m_packet = CPacket((BYTE*)buffer, len);
            if (len > 0)
            {
                // 解析完了后,把buffer往前移
                memmove(buffer, buffer + len, BUFFER_SIZE - len);
                // index 减去用掉的长度
                index -= len;
                return m_packet.sCmd;
            }
        }
    }
    // send 原始数据
    bool Send(const char* pData, size_t nSize)
    {
        if (m_socket_server == INVALID_SOCKET)
        {
            return false;
        }
        return send(m_socket_server, pData, sizeof(nSize), 0) > 0;
    }
    // send一个packet
    bool Send(CPacket& packet)
    {
        if (m_socket_server == INVALID_SOCKET)
        {
            return false;
        }
        // 把packet转换为一个缓冲区
        return send(m_socket_server, packet.Data(), packet.Size(), 0);
    }
    // 从包里面取出路径信息
    bool GetFilePath(std::string& strPath)
    {
        if ((m_packet.sCmd >= 2) && (m_packet.sCmd <= 4))
        {
            strPath = m_packet.strData;
            return true;
        }
        return false;
    }
    // 获取鼠标结构信息
    bool GetMoueEvent(MOUSEEVENT& mouse)
    {
        if (m_packet.sCmd == 11)
        {
            memcpy(&mouse, m_packet.strData.c_str(), sizeof(MOUSEEVENT));
            return true;
        }
        return false;
    }
private:
    SOCKET m_socket_server;
    CPacket m_packet;
    CClientSocket& operator=(const CClientSocket& s)
    {
        m_socket_server = s.m_socket_server;
    }
    CClientSocket(const CClientSocket& s)
    {
        m_socket_server = s.m_socket_server;
    }
    CClientSocket()
    {
        if (InitSocketEnv() == FALSE)
        {
            MessageBox(NULL, _T("无法初始化套接字环境,请检查网络环境"), _T("初始化错误"), MB_OK | MB_ICONERROR);
            exit(0);
        }
        m_socket_server = socket(AF_INET, SOCK_STREAM, 0);
    }
​
    ~CClientSocket()
    {
        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 CClientSocket* m_instance;
    static void ReleaseInstance()
    {
        if (m_instance != NULL)
        {
            CClientSocket* tmp = m_instance;
            m_instance = NULL;
            delete tmp;
        }
    }
    class CHelper
    {
    public:
        CHelper()
        {
            CClientSocket::getInstance();
        }
        ~CHelper()
        {
            CClientSocket::ReleaseInstance();
        }
    };
    static CHelper m_helper;
};
  • 24
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值