一个异步模型的聊天程序
服务器端:
自定义消息(放在StdAfx.h文件里面即可)
#define WM_SOCKET WM_USER + 1
然后注册消(在VS2012里面可以自动完成注册和映射 )
afx_msg LRESULT OnSocket(WPARAM wParam, LPARAM lParam);
然后写上 映射
ON_MESSAGE(WM_SOCKET, &CChatDlg::OnSocket)
对话框的头文件代码
// CChatDlg 对话框 class CChatDlg : public CDialogEx { // 构造 public: CChatDlg(CWnd* pParent = NULL); // 标准构造函数 // 对话框数据 enum { IDD = IDD_MY_DIALOG }; protected: virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支持 // 实现 protected: HICON m_hIcon; // 生成的消息映射函数 virtual BOOL OnInitDialog(); afx_msg void OnSysCommand(UINT nID, LPARAM lParam); afx_msg void OnPaint(); afx_msg HCURSOR OnQueryDragIcon(); DECLARE_MESSAGE_MAP() public: CString m_recv; CString m_send; public: SOCKET serverSocket; SOCKET sAccept; struct sockaddr_in serverAddress; char recvBuff[1024]; char sendBuff[1024]; sockaddr_in clientAddr; public: bool initSocket(); bool recvData(); protected: afx_msg LRESULT OnSocket(WPARAM wParam, LPARAM lParam); public: afx_msg void OnBnClickedButtonSend(); };
然后cpp文件
bool CChatDlg::initSocket() { WSADATA wsa; if(WSAStartup(MAKEWORD(2,2),&wsa)!=0){ MessageBox(_T("初始化套接字错误!")); return false; } if((serverSocket=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP))==INVALID_SOCKET){ MessageBox(_T("创建套接字失败!")); return false; } if (WSAAsyncSelect(serverSocket, this->m_hWnd, WM_SOCKET, FD_ACCEPT)==SOCKET_ERROR) { MessageBox(_T("WSAAyncSelect error!")); } memset(&serverAddress,0,sizeof(sockaddr_in)); serverAddress.sin_family=AF_INET; serverAddress.sin_addr.S_un.S_addr = INADDR_ANY; serverAddress.sin_port = htons(6666); if(bind(serverSocket,(sockaddr*)&serverAddress,sizeof(serverAddress))==SOCKET_ERROR){ MessageBox(_T("套接字绑定到端口失败!")); return false; } if (listen(serverSocket, 10)==SOCKET_ERROR) { MessageBox(_T("监听失败!")); return false; } int len = sizeof(serverAddress); //if((sAccept==accept(serverSocket, (sockaddr *)&serverAddress, &len)!=INVALID_SOCKET)) // MessageBox(_T("success!")); return true; } afx_msg LRESULT CChatDlg::OnSocket(WPARAM wParam, LPARAM lParam) { switch(WSAGETSELECTEVENT(lParam)) { case FD_ACCEPT://接受客户端连接请求。 { int len = sizeof(clientAddr); sAccept=accept(serverSocket, (sockaddr *)&clientAddr, &len); if(sAccept==INVALID_SOCKET) { MessageBox(_T("accept error!")); break; } if (WSAAsyncSelect(sAccept, m_hWnd, WM_SOCKET, FD_READ|FD_CLOSE)==SOCKET_ERROR) { CString temp; temp.Format(_T("%d"), WSAGetLastError()); MessageBox(temp); MessageBox(_T("WSAAyncSelect error!")); } MessageBox(_T("success!")); //在新接受的套接字发生FD_READ,FD_WRITE,FD_CLOSE网络事件发生,发送WM_SOCKET消息; } break; case FD_READ://可读,接收数据。 { MessageBox(_T("read!")); if (recvData()) UpdateData(false); } break; case FD_CLOSE://对方关闭套接字连接。 { closesocket(sAccept); WSACleanup(); } break; default: break; } return 0; } bool CChatDlg::recvData() { CString temp; int recvLen = 0; int len = 0; while (1) { len = recv(sAccept, recvBuff+recvLen, 100, 0); if (len < 100) break; recvLen += len; } WCHAR wch[1000]; int n = MultiByteToWideChar( //转换Unicode到Ansi 936, 0, recvBuff, -1, wch, //转换到缓冲区中 100000 //最多个字节 ); m_recv += _T("client: "); m_recv+=wch; return true; } void CChatDlg::OnBnClickedButtonSend() { UpdateData(true); USES_CONVERSION; //定义后才能使用T2A sprintf_s(sendBuff,1024,"%s ",T2A(m_send)); int err = send(sAccept, sendBuff, strlen(sendBuff), 0); if (SOCKET_ERROR == err) { MessageBox(_T("send error!")); } m_recv+=_T("server: "); m_recv+=m_send; m_recv+=_T(" "); m_send = _T(""); UpdateData(false); }
WSAAsyncSelect函数在服务器时,要在bind函数之前使用,在客户端在connect函数之前,客户端的不同之处就是就是没有fd_accept这个事件,直接 fd_read事件即可。