与上一个帖子对应,可以互相通讯。
头文件:
1 // TCPCustom_CE.h: interface for the CTCPCustom_CE class. 2 // 3 ////////////////////////////////////////////////////////////////////// 4 5 #if !defined(AFX_TCPCUSTOM_CE_H__0E8B4A18_8A99_438E_B5F6_B5985FFC117D__INCLUDED_) 6 #define AFX_TCPCUSTOM_CE_H__0E8B4A18_8A99_438E_B5F6_B5985FFC117D__INCLUDED_ 7 8 #if _MSC_VER > 1000 9 #pragma once 10 #endif // _MSC_VER > 1000 11 12 #include <winsock.h> 13 #include "TCPServer_CE.h" 14 15 class CTCPCustom_CE 16 { 17 public: 18 CTCPCustom_CE(); 19 virtual ~CTCPCustom_CE(); 20 public: 21 CTCPServer_CE * m_pTCPServer_CE; //引用TCP服务端监听Socket 22 23 CString m_RemoteHost; //远程主机IP地址 24 DWORD m_RemotePort; //远程主机端口号 25 SOCKET m_socket; //通讯Socket句柄 26 private: 27 HANDLE m_exitThreadEvent; //通讯线程退出事件句柄 28 HANDLE m_tcpThreadHandle; //通讯线程句柄 29 private: 30 //通讯线程函数 31 static DWORD SocketThreadFunc(PVOID lparam); 32 public: 33 //打开socket,创建通讯线程 34 bool Open(CTCPServer_CE *pTCPServer); 35 36 //关闭socket,关闭线程,释放Socket资源 37 bool Close(); 38 39 //向客户端发送数据 40 bool SendData(const char * buf , int len); 41 42 }; 43 44 #endif // !defined(AFX_TCPCUSTOM_CE_H__0E8B4A18_8A99_438E_B5F6_B5985FFC117D__INCLUDED_)
源文件:
1 // TCPCustom_CE.cpp: implementation of the CTCPCustom_CE class. 2 // 3 ////////////////////////////////////////////////////////////////////// 4 5 #include "stdafx.h" 6 #include "TCPServer.h" 7 #include "TCPCustom_CE.h" 8 9 #ifdef _DEBUG 10 #undef THIS_FILE 11 static char THIS_FILE[]=__FILE__; 12 #define new DEBUG_NEW 13 #endif 14 15 ////////////////////////////////////////////////////////////////////// 16 // Construction/Destruction 17 ////////////////////////////////////////////////////////////////////// 18 19 //构造函数 20 CTCPCustom_CE::CTCPCustom_CE() 21 { 22 //创建线程退出事件 23 m_exitThreadEvent = CreateEvent(NULL,FALSE,FALSE,NULL); 24 } 25 26 //析构函数 27 CTCPCustom_CE::~CTCPCustom_CE() 28 { 29 //关闭线程退出事件 30 CloseHandle(m_exitThreadEvent); 31 } 32 33 /*-------------------------------------------------------------------- 34 【函数介绍】: 此线程用于监听与客户端连接的socket通讯的事件,例如当接收到数据、 35 连接断开和通讯过程发生错误等事件 36 【入口参数】: lparam:无类型指针,可以通过此参数,向线程中传入需要用到的资源。 37 在这里我们将CTCPCustom_CE类实例指针传进来 38 【出口参数】: (无) 39 【返回 值】: 返回值没有特别的意义,在此我们将返回值设为0。 40 ---------------------------------------------------------------------*/ 41 DWORD CTCPCustom_CE::SocketThreadFunc(PVOID lparam) 42 { 43 CTCPCustom_CE *pSocket; 44 //得到CTCPCustom类实例指针 45 pSocket = (CTCPCustom_CE*)lparam; 46 //定义读事件集合 47 fd_set fdRead; 48 int ret; 49 TIMEVAL aTime; 50 aTime.tv_sec = 1; 51 aTime.tv_usec = 0; 52 while (TRUE) 53 { 54 //收到退出事件,结束线程 55 if (WaitForSingleObject(pSocket->m_exitThreadEvent,0) == WAIT_OBJECT_0) 56 { 57 break; 58 } 59 //置空读事件集合 60 FD_ZERO(&fdRead); 61 //给pSocket设置读事件 62 FD_SET(pSocket->m_socket,&fdRead); 63 //调用select函数,判断是否有读事件发生 64 ret = select(0,&fdRead,NULL,NULL,&aTime); 65 66 if (ret == SOCKET_ERROR) 67 { 68 //触发错误事件 69 pSocket->m_pTCPServer_CE->OnClientError(pSocket->m_pTCPServer_CE->m_pOwnerWnd,pSocket,1); 70 //关闭socket 71 closesocket(pSocket->m_socket); 72 break; 73 } 74 75 if (ret > 0) 76 { 77 //判断是否读事件 78 if (FD_ISSET(pSocket->m_socket,&fdRead)) 79 { 80 char recvBuf[1024]; 81 int recvLen; 82 ZeroMemory(recvBuf,1024); 83 recvLen = recv(pSocket->m_socket,recvBuf, 1024,0); 84 if (recvLen == SOCKET_ERROR) 85 { 86 int nErrorCode = WSAGetLastError(); 87 //触发与客户端端连接的Socket错误 88 pSocket->m_pTCPServer_CE->OnClientError(pSocket->m_pTCPServer_CE->m_pOwnerWnd,pSocket,nErrorCode); 89 //触发与客户端端连接的Socket关闭事件 90 pSocket->m_pTCPServer_CE->OnClientClose(pSocket->m_pTCPServer_CE->m_pOwnerWnd,pSocket); 91 //关闭socket 92 closesocket(pSocket->m_socket); 93 break; 94 95 } 96 //表示连接已经从容关闭 97 else if (recvLen == 0) 98 { 99 pSocket->m_pTCPServer_CE->OnClientClose(pSocket->m_pTCPServer_CE->m_pOwnerWnd,pSocket); 100 //关闭socket 101 closesocket(pSocket->m_socket); 102 break; 103 } 104 else 105 { 106 //触发与客户端端连接的Socket读事件 107 pSocket->m_pTCPServer_CE->OnClientRead(pSocket->m_pTCPServer_CE->m_pOwnerWnd,pSocket,recvBuf,recvLen); 108 } 109 } 110 } 111 } 112 return 0; 113 } 114 115 /*-------------------------------------------------------------------- 116 【函数介绍】: 打开socket,创建通讯线程 117 【入口参数】: pTCPServer指向服务器端监听socket 118 【出口参数】: (无) 119 【返回 值】: TRUE:打开成功;FALSE:打开失败 120 ---------------------------------------------------------------------*/ 121 bool CTCPCustom_CE::Open(CTCPServer_CE *pTCPServer) 122 { 123 //创建通讯线程 124 m_tcpThreadHandle = CreateThread(NULL,0,SocketThreadFunc,this,0,NULL); 125 if (m_tcpThreadHandle == NULL) 126 { 127 closesocket(m_socket); 128 return FALSE; 129 } 130 //设置通讯模式为异步模式 131 DWORD ul= 1; 132 ioctlsocket(m_socket,FIONBIO,&ul); 133 m_pTCPServer_CE = pTCPServer; 134 return TRUE; 135 } 136 137 /*-------------------------------------------------------------------- 138 【函数介绍】: 关闭socket,关闭线程,释放Socket资源 139 【入口参数】: (无) 140 【出口参数】: (无) 141 【返回 值】: TRUE:成功关闭;FALSE:关闭失败 142 ---------------------------------------------------------------------*/ 143 bool CTCPCustom_CE::Close() 144 { 145 //发送通讯线程结束事件 146 SetEvent(m_exitThreadEvent); 147 Sleep(1000); 148 //关闭Socket,释放资源 149 int err = closesocket(m_socket); 150 if (err == SOCKET_ERROR) 151 { 152 return FALSE; 153 } 154 return TRUE; 155 } 156 157 158 /*----------------------------------------------------------------- 159 【函数介绍】: 向客户端发送数据 160 【入口参数】: buf:待发送的数据 161 len:待发送的数据长度 162 【出口参数】: (无) 163 【返回 值】: TRUE:发送数据成功;FALSE:发送数据失败 164 ------------------------------------------------------------------*/ 165 bool CTCPCustom_CE::SendData(const char * buf , int len) 166 { 167 int nBytes = 0; 168 int nSendBytes=0; 169 170 while (nSendBytes < len) 171 { 172 nBytes = send(m_socket,buf+nSendBytes,len-nSendBytes,0); 173 if (nBytes==SOCKET_ERROR ) 174 { 175 int iErrorCode = WSAGetLastError(); 176 //触发socket的Error事件 177 m_pTCPServer_CE->OnClientError(m_pTCPServer_CE->m_pOwnerWnd,this,iErrorCode); 178 //触发与服务器端断开连接事件 179 m_pTCPServer_CE->OnClientClose(m_pTCPServer_CE->m_pOwnerWnd,this); 180 //关闭socket 181 Close(); 182 return FALSE; 183 } 184 185 nSendBytes = nSendBytes + nBytes; 186 187 if (nSendBytes < len) 188 { 189 Sleep(1000); 190 } 191 } 192 return TRUE; 193 }
调用示例:
1 BOOL CTCPServerDlg::OnInitDialog() 2 { 3 //m_bFullScreen = FALSE; 4 CDialog::OnInitDialog(); 5 6 // Set the icon for this dialog. The framework does this automatically 7 // when the application's main window is not a dialog 8 SetIcon(m_hIcon, TRUE); // Set big icon 9 SetIcon(m_hIcon, FALSE); // Set small icon 10 11 CenterWindow(GetDesktopWindow()); // center to the hpc screen 12 13 // TODO: Add extra initialization here 14 // 设置默认值 15 m_localPort = 5000; 16 UpdateData(FALSE); 17 return TRUE; // return TRUE unless you set the focus to a control 18 } 19 20 21 // 客户端连接建立事件处理函数 22 void CALLBACK CTCPServerDlg::OnClientConnect(CWnd* pWnd,CTCPCustom_CE* pTcpCustom) 23 { 24 CTCPServerDlg * pDlg = (CTCPServerDlg*)pWnd; 25 CListBox * pLstConn = (CListBox*)pDlg->GetDlgItem(IDC_LSTCONN); 26 ASSERT(pLstConn != NULL); 27 pLstConn->AddString(pTcpCustom->m_RemoteHost + _T("建立连接")); 28 29 RETAILMSG(1,(TEXT("==OnClientConnect=%s "),pTcpCustom->m_RemoteHost)); 30 31 gTcpSendObj = *pTcpCustom; 32 } 33 34 // 客户端SOCKET关闭事件处理函数 35 void CALLBACK CTCPServerDlg::OnClientClose(CWnd* pWnd,CTCPCustom_CE* pTcpCustom) 36 { 37 CTCPServerDlg * pDlg = (CTCPServerDlg*)pWnd; 38 int iIndex = 0; 39 40 CListBox * pLstConn = (CListBox*)pDlg->GetDlgItem(IDC_LSTCONN); 41 ASSERT(pLstConn != NULL); 42 iIndex = pLstConn->FindString(iIndex,pTcpCustom->m_RemoteHost + _T("建立连接")); 43 if (iIndex == LB_ERR) 44 { 45 return; 46 } 47 pLstConn->DeleteString(iIndex); 48 49 RETAILMSG(1,(TEXT("==OnClientClose=%s "),pTcpCustom->m_RemoteHost)); 50 } 51 52 // 服务器端收到来自客户端的数据 53 void CALLBACK CTCPServerDlg::OnClientRead(CWnd* pWnd,CTCPCustom_CE* pTcpCustom,const char *buf,int len) 54 { 55 CString strRecv; 56 CString strLen; 57 strLen.Format(L"%d",len); 58 strRecv = buf; 59 CTCPServerDlg * pDlg = (CTCPServerDlg*)pWnd; 60 CListBox * pLstRecv = (CListBox*)pDlg->GetDlgItem(IDC_LSTRECV); 61 ASSERT(pLstRecv != NULL); 62 63 pLstRecv->AddString(_T("************************************")); 64 pLstRecv->AddString(_T("来自: ") + pTcpCustom->m_RemoteHost); 65 pLstRecv->AddString(_T("数据长度:")+strLen); 66 pLstRecv->AddString(strRecv); 67 68 RETAILMSG(1,(TEXT("===%s "),pTcpCustom->m_RemoteHost)); 69 if (!pTcpCustom->SendData("OK",strlen("OK"))) 70 { 71 AfxMessageBox(_T("发送失败")); 72 } 73 } 74 75 //客户端Socket错误事件处理函数 76 void CALLBACK CTCPServerDlg::OnClientError(CWnd* pWnd,CTCPCustom_CE* pTcpCustom,int nErrorCode) 77 { 78 79 RETAILMSG(1,(TEXT("==OnClientError=%s "),pTcpCustom->m_RemoteHost)); 80 } 81 82 //服务器端Socket错误事件处理函数 83 void CALLBACK CTCPServerDlg::OnServerError(CWnd* pWnd,CTCPServer_CE* pTcpServer_CE,int nErrorCode) 84 { 85 } 86 87 // 监听按钮单击事件方法 88 void CTCPServerDlg::OnBtnlisten() 89 { 90 UpdateData(TRUE); 91 // 设置 m_tcpServer 属性 92 m_tcpServer.m_LocalPort = m_localPort; 93 m_tcpServer.m_pOwnerWnd = this; 94 m_tcpServer.OnClientConnect = OnClientConnect; 95 m_tcpServer.OnClientClose = OnClientClose; 96 m_tcpServer.OnClientRead = OnClientRead; 97 m_tcpServer.OnClientError = OnClientError; 98 m_tcpServer.OnServerError = OnServerError; 99 if (m_tcpServer.Open() <= 0) 100 { 101 AfxMessageBox(_T("监听失败")); 102 return; 103 } 104 CButton * pBtnListen = (CButton*)GetDlgItem(IDC_BTNLISTEN); 105 ASSERT(pBtnListen != NULL); 106 pBtnListen->EnableWindow(FALSE); 107 108 CButton * pBtnClose = (CButton*)GetDlgItem(IDC_BTNCLOSE); 109 ASSERT(pBtnClose != NULL); 110 pBtnClose->EnableWindow(TRUE); 111 112 CButton *pBtnSend = (CButton*)GetDlgItem(IDC_SendBtn); 113 ASSERT(pBtnSend != NULL); 114 pBtnSend->EnableWindow(TRUE); 115 } 116 117 //关闭按钮单击事件代码 118 void CTCPServerDlg::OnBtnclose() 119 { 120 if (m_tcpServer.Close() <=0) 121 { 122 AfxMessageBox(_T("关闭TCP服务器失败")); 123 return; 124 } 125 CButton * pBtnListen = (CButton*)GetDlgItem(IDC_BTNLISTEN); 126 ASSERT(pBtnListen != NULL); 127 pBtnListen->EnableWindow(TRUE); 128 129 CButton * pBtnClose = (CButton*)GetDlgItem(IDC_BTNCLOSE); 130 ASSERT(pBtnClose != NULL); 131 pBtnClose->EnableWindow(FALSE); 132 133 CButton *pBtnSend = (CButton*)GetDlgItem(IDC_SendBtn); 134 ASSERT(pBtnSend != NULL); 135 pBtnSend->EnableWindow(FALSE); 136 137 CListBox * pLstConn = (CListBox*)GetDlgItem(IDC_LSTCONN); 138 ASSERT(pLstConn != NULL); 139 140 CListBox * pLstRecv = (CListBox*)GetDlgItem(IDC_LSTRECV); 141 ASSERT(pLstRecv != NULL); 142 143 pLstConn->ResetContent(); 144 pLstRecv->ResetContent(); 145 146 } 147 148 void CTCPServerDlg::OnSendBtn() 149 { 150 // TODO: Add your control notification handler code here 151 152 if (!gTcpSendObj.SendData("Send data ok(Server)",strlen("Send data ok(Server)"))) 153 { 154 AfxMessageBox(_T("发送失败")); 155 } 156 }