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; };