在网上找了一大圈没有一个满意的,我主要是想把客户端连接保存起来这样可以向所有客户端发消息或者管理客户端进入退出事件,主要卡在我自己定一个socket服务器类,但是这个自定义类自定义接收客户端消息不触发,看到了这篇博客http://www.cnblogs.com/rainbow70626/p/8034895.html,知道了原因,但是还是不行,提示错误,希望有大神可以解决一下接收数据自定义消息问题。总之,目前卡在接收这个地方。好了废话不多说,下面其实改一改是一个非常好的类。经过测试,如果直接在对话框里面实现,基本无什么大问题。封装一个类当然是最好。
先定义一个保存客户端对象,ip地址和端口的类,头文件代码
#pragma once class CClientSet { public: CClientSet(); ~CClientSet(); SOCKET ClientSocket;//客户端对象 int nPort;//客户端端口 char *strIp;//客户端IP }; 这里cpp就不需要写代码了。
然后就是自己封装一个类ServerSocket.h代码
#pragma once #include<vector> #include "ClientSet.h" using namespace std; #define WM_SERVER_ACCEPT WM_USER + 101 typedef void(*ReciveData)(char[]);//回调函数,接受客户端发来数据 class CServerSocket :public CWnd { public: CServerSocket(); //CServerSocket(CWnd *pParent=NULL); ~CServerSocket(); SOCKADDR_IN ServerAddr; SOCKET ListeningSocket;//监听的socket对象 int nPort;//端口号 int nClientCount = 0; char * strIp;//IP地址 void Start();//开启服务器 void Stop();//关闭服务器 void SendData(char buffer[]);//向所有客户端发消息 void SendData(char buffer[],char* strIp);//向指定的IP发消息 // LRESULT OnSocket(WPARAM wParam, LPARAM lParam); vector<CClientSet> vecClientSocket;//保存所有客户端
protected: // Generated message map functions //{{AFX_MSG(CServerDlg) afx_msg LRESULT OnServerAccept(WPARAM wParam, LPARAM lParam); //}}AFX_MSG DECLARE_MESSAGE_MAP() };
实现:
#include "stdafx.h" #include "ServerSocket.h" #include "SocketHelperDlg.h" #include "ClientSet.h" #include <vector>
using namespace std; CServerSocket::CServerSocket() { //CWnd::Create(NULL, "ServerSocket", WS_CHILD, CRect(0, 0, 0, 0), ::AfxGetMainWnd(), 1234); } //CServerSocket::CServerSocket(CWnd *pParent) //{ // CWnd::Create(NULL, "ServerSocket", WS_CHILD, CRect(0, 0, 0, 0), pParent, 1234); //}
CServerSocket::~CServerSocket() { //CWnd::DestroyWindow(); }
BEGIN_MESSAGE_MAP(CServerSocket, CWnd) //{{AFX_MSG_MAP(CServerSocket) ON_MESSAGE(WM_SERVER_ACCEPT, OnServerAccept) //}}AFX_MSG_MAP END_MESSAGE_MAP() void CServerSocket::Start() { WSADATA wsaData; WSAStartup(MAKEWORD(2, 2), &wsaData); // WSAStartup(0x0202, &wsaData); ListeningSocket = socket(AF_INET, SOCK_STREAM, 0); if (ListeningSocket == INVALID_SOCKET)//创建失败 { WSACleanup(); closesocket(ListeningSocket); return; }
ServerAddr.sin_family = AF_INET; ServerAddr.sin_addr.s_addr = inet_addr(strIp);//INADDR_ANY ServerAddr.sin_port = htons(nPort); WSAAsyncSelect(ListeningSocket,m_hWnd, WM_SERVER_ACCEPT, FD_ACCEPT | FD_READ | FD_CLOSE); /*设置为异步通讯模式,并为它注册各种网络异步事件, 其中m_hWnd 为应用程序的主对话框或主窗口的句柄 当检测到相应时间后才为窗口句柄发送消息*/
//关键问题在m_hWnd句柄问题,到底是主窗口句柄还是这个类句柄,目前没解决 if (bind(ListeningSocket, (sockaddr *)&ServerAddr, sizeof(ServerAddr)) == SOCKET_ERROR) { WSACleanup(); closesocket(ListeningSocket); return; } if (listen(ListeningSocket, 20) == SOCKET_ERROR) { WSACleanup(); closesocket(ListeningSocket); return; }
} void CServerSocket::Stop() { //清除所有客户端 for (int i = 0; i<vecClientSocket.size(); i++) { vecClientSocket[i].ClientSocket = INVALID_SOCKET; vecClientSocket.erase(vecClientSocket.begin() + i); } nClientCount = 0;//客户端归0,其实用不上,一般关闭,软件也关闭 WSACleanup(); shutdown(ListeningSocket, 2); closesocket(ListeningSocket); } void CServerSocket::SendData(char buffer[]) { //向所有客户端发消息 for (int i = 0; i<vecClientSocket.size(); i++) { if (vecClientSocket[i].ClientSocket != INVALID_SOCKET) send(vecClientSocket[i].ClientSocket, buffer, sizeof(buffer),NULL); } } void CServerSocket::SendData(char buffer[],char* strIp) { //向指定IP端发消息 for (int i = 0; i<vecClientSocket.size(); i++) { if (vecClientSocket[i].ClientSocket != INVALID_SOCKET && vecClientSocket[i].strIp==strIp) send(vecClientSocket[i].ClientSocket, buffer, sizeof(buffer), NULL); } }
//添加异步处理消息响应函数 ,消息没有响应,目前网上资料就一篇讲述自定义类自定义消息触发问题,资料试过,不知道为什么不行 LRESULT CServerSocket::OnServerAccept(WPARAM wParam, LPARAM lParam){ int iEvent = WSAGETSELECTEVENT(lParam); CString str; str.Format("%d", iEvent); AfxMessageBox(str); switch (iEvent) { case FD_ACCEPT://客户端连接事件 { SOCKADDR_IN addr; int nLen = sizeof(SOCKADDR_IN); SOCKET ClientSocket = accept(ListeningSocket, (sockaddr*)&addr, &nLen); if (ClientSocket != INVALID_SOCKET)
{ CClientSet ccs;//声明一个客户端对象集 ccs.ClientSocket = ClientSocket;//存储客户端对象 ccs.nPort = addr.sin_port;//存储客户端端口 ccs.strIp = inet_ntoa(addr.sin_addr);//存储客户端的ip地址 vecClientSocket.push_back(ccs);//存储的客户端对象 nClientCount++;//客户端来了就加一个 } } break; case FD_READ://客户端发送数据过来 { int nMsgLen; char buffer[1024]; for (int i = 0; i < vecClientSocket.size(); i++) { if (vecClientSocket[i].ClientSocket == wParam); { nMsgLen = recv(vecClientSocket[i].ClientSocket, buffer, 1024, NULL); //查找是哪个客户端发数据过来,找到就接收它发来的数据 buffer[nMsgLen] = '\0'; ReciveData(buffer);//回调函数,注意回调函数必须是静态或者全局函数 break; } } } break; case FD_CLOSE://客户端断开事件 { for (int i = 0; i<vecClientSocket.size(); i++) { if (vecClientSocket[i].ClientSocket == wParam); { //查找是谁断开客户端,找到就将客户端对象清除 vecClientSocket[i].ClientSocket = INVALID_SOCKET; vecClientSocket.erase(vecClientSocket.begin() + i); nClientCount--;//客户端减一 break; }
} }
break;
} return NULL; }
如果这个类消息可以触发,则调用非常简单,新建一个对话框,初始化加入代码:
css.strIp = "127.0.0.1"; css.nPort = 5200; css.Start();
注意头文件需要什么CServerSocket css;
窗口destroy则只需要调用一下代码
css.Stop();
处理客户端消息可以用回调函数:
void CSocketHelperDlg::OnReciveData(char buffer[]) { CString str(buffer); AfxMessageBox(str); }
这个构架很好,只是卡在不能接受消息,如果大神看到这篇文章,请指点一下。