现在的位置: 首页 > 综合 > 正文

Windows CE 下的 TCP 客户端类

2013年10月13日 ⁄ 综合 ⁄ 共 8230字 ⁄ 字号 评论关闭

以前一直在使用 UDP 与服务器进行通讯,这次一个新的项目需要采用 TCP 来实现与服务器的通讯。

先写了一个 TCP 客户端的类,同时也做了一个服务器用于测试。先把客户端的 TCP 类代码分分享出来吧。

头文件:

// CeTcpClient.h: interface for the CCeTcpClient class.
//
//////////////////////////////////////////////////////////////////////

#if !defined(AFX_CETCPCLIENT_H__B7856B99_69E7_4868_9BA3_96152245C65E__INCLUDED_)
#define AFX_CETCPCLIENT_H__B7856B99_69E7_4868_9BA3_96152245C65E__INCLUDED_

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000

#include <winsock.h>

// 连接断开
typedef void (CALLBACK *ONDISCONNECT)(CWnd *);
// 数据接收
typedef void (CALLBACK *ONREAD)(CWnd *,const char *,int);
// Socket 错误
typedef void (CALLBACK *ONERROR)(CWnd *,int);

class CCeTcpClient
{
public:
	CCeTcpClient();
	virtual ~CCeTcpClient();

public:
	// 服务器 IP 地址
	CString	 m_csRemoteHost;
	// 服务器端口
    int m_iPort;
    
	// 连接断开
	ONDISCONNECT OnDisConnect;
	// 接收数据
	ONREAD OnRead;
	// 发生错误
	ONERROR OnError;
	
public:
	// 打开 Socket
	bool Open(CWnd *pWnd);
	// 关闭 Socket
	bool Close();
	// 与服务器端建立连接
	bool Connect();
	// 向服务器端发送数据
	bool SendData(const char *pcBuffer,int iLen);

private:
	// Socket 句柄
	SOCKET m_socket;
	// 通讯线程退出事件
	HANDLE m_hExitThreadEvent;
	// 通讯线程
	HANDLE m_hTcpThreadHandle;
	// 父窗口句柄
	CWnd *m_pOwnerWnd;

private:
    // 通讯线程
	static DWORD SocketControlThread(LPVOID lparam);
};

#endif // !defined(AFX_TCPCLIENT_CE_H__B7856B99_69E7_4868_9BA3_96152245C65E__INCLUDED_)

源文件:

// CeTcpClient.cpp: implementation of the CCeTcpClient class.
// Leo.Zheng 2012.04.09
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "TCPClient.h"
#include "CeTcpClient.h"

#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
CCeTcpClient::CCeTcpClient()
{
	int iInitWSA = 0;
	// 初始化 Socket, 版本: 1.1
	WSADATA wsd;

	iInitWSA = WSAStartup(MAKEWORD(1,1),&wsd);
	if(0 != iInitWSA)
	{
		RETAILMSG(1,(L"[CCeTcpClient::CCeTcpClient]WSA start up failed: %d\r\n",iInitWSA));
	}
	// 创建线程退出事件
	m_hExitThreadEvent = CreateEvent(NULL,FALSE,FALSE,NULL);
}

CCeTcpClient::~CCeTcpClient()
{
	// 释放 Socket
	WSACleanup();
	// 关闭线程退出事件
	CloseHandle(m_hExitThreadEvent);
}

/*
 * 功能: 打开 Socket
 * 参数: pWnd 用于指定父窗口句柄
 * 返回值: TRUE 成功; FALSE 失败
*/
bool CCeTcpClient::Open(CWnd *pWnd)
{
	ResetEvent(m_hExitThreadEvent);
	m_pOwnerWnd = pWnd;
	
	// 创建 TCP 套接字 
	m_socket = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
	if(SOCKET_ERROR == m_socket)
	{
		return FALSE;
	}

	// 创建通讯线程
	m_hTcpThreadHandle = CreateThread(NULL,0,SocketControlThread,this,0,NULL);
	if(NULL == m_hTcpThreadHandle)
	{
		closesocket(m_socket);
		return FALSE;
	}
	
	return TRUE;
}

/*
 * 功能: 关闭 Socket
 * 参数: 无
 * 返回值: TRUE 成功; FALSE 失败
*/
bool CCeTcpClient::Close()
{
	// 设置通讯线程结束事件
	SetEvent(m_hExitThreadEvent);
	Sleep(1000);

	// 关闭 Socket
	int iError = closesocket(m_socket);
	if(SOCKET_ERROR == iError)
	{
		return FALSE;
	}

	return TRUE;
}

/*
 * 功能: 与 TCP 服务器建立连接
 * 参数: 无
 * 返回值: TRUE 成功; FALSE 失败
*/
bool CCeTcpClient::Connect()
{
	struct sockaddr_in addr;
	int iError = 0;
	char cAnsiRemoteHost[255 + 1];

	addr.sin_family = AF_INET;
	addr.sin_port = htons(m_iPort);
	ZeroMemory(cAnsiRemoteHost,sizeof(char) * (255 + 1));
	WideCharToMultiByte(CP_ACP,WC_COMPOSITECHECK,m_csRemoteHost,wcslen(m_csRemoteHost),cAnsiRemoteHost,wcslen(m_csRemoteHost),NULL,NULL);

   addr.sin_addr.s_addr = inet_addr(cAnsiRemoteHost);
   // 采用同步连接方式, connect 直接返回成功或是失败
   iError = connect(m_socket,(struct sockaddr *)&addr,sizeof(addr));
   if(SOCKET_ERROR == iError) 
   {
	   return FALSE;
   }
   // 设置通讯模式为异步模式
   DWORD dwMode = 1;
   ioctlsocket(m_socket,FIONBIO,&dwMode);

   return TRUE;
}

/*
 * 功能: 向服务器端发送数据
 * 参数: buf 待发送的数据
	len 待发送的数据长度
 * 返回值: TRUE 成功; FALSE 失败
*/
bool CCeTcpClient::SendData(const char *pcBuffer,int iLen)
{
	int iBytes = 0;
	int iSendBytes = 0;
			
	while(iSendBytes < iLen)
	{
	    iBytes = send(m_socket,pcBuffer + iSendBytes,iLen - iSendBytes,0);
		if(SOCKET_ERROR == iBytes)
		{
			int iErrorCode = WSAGetLastError();
			OnError(m_pOwnerWnd,iErrorCode);
			OnDisConnect(m_pOwnerWnd);
			//关闭 Socket
			Close();
			return FALSE;
		}

		iSendBytes = iSendBytes + iBytes;
		
		if(iSendBytes < iLen)
		{
		    Sleep(1000);
		}
	} 
	return TRUE; 
}

/*
 * 功能: 此线程用于监听 TCP 客户端通讯的事件
	例如: 当接收到数据、连接断开或通讯过程发生错误等事件
 * 参数: lparam: 可以通过此参数,向线程中传入需要用到的资源
	在这里我们将 CCeTcpClient 类实例指针传进来
 * 返回值: 无意义,将返回值设为 0。
*/
DWORD CCeTcpClient::SocketControlThread(LPVOID lparam)
{
	CCeTcpClient *pSocket = NULL;
	fd_set fdRead;
	TIMEVAL	aTime;
	int iRet = 0;

	// 得到 CCeTcpClient 实例指针
	pSocket = (CCeTcpClient *)lparam;
	// 事件等待时间设置
	aTime.tv_sec = 1;
	aTime.tv_usec = 0;

	while(TRUE)
	{
        // 退出事件,结束线程
		if(WAIT_OBJECT_0 == WaitForSingleObject(pSocket->m_hExitThreadEvent,0))
		{
			break;
		}
		// 置 fdRead 为空
		FD_ZERO(&fdRead);
		// Socket 设置读事件
		FD_SET(pSocket->m_socket,&fdRead);

		// 判断是否有读事件
		iRet = select(0,&fdRead,NULL,NULL,&aTime);
		if(SOCKET_ERROR == iRet)
		{
			pSocket->OnError(pSocket->m_pOwnerWnd,1);
			pSocket->OnDisConnect(pSocket->m_pOwnerWnd);
			//关闭客户端 Socket
			closesocket(pSocket->m_socket);
			break;
		}
		
		if(iRet > 0)
		{
			if(FD_ISSET(pSocket->m_socket,&fdRead))
			{
				char cRecvBuffer[1024 + 1];
				int iRecvLength = 0;

				ZeroMemory(cRecvBuffer,sizeof(char) * (1024 + 1));
				// 接收数据
				iRecvLength = recv(pSocket->m_socket,cRecvBuffer,1024,0); 
				if(SOCKET_ERROR == iRecvLength)
				{
					int iError = WSAGetLastError();
					pSocket->OnError(pSocket->m_pOwnerWnd,iError);
					pSocket->OnDisConnect(pSocket->m_pOwnerWnd);
					// 关闭客户端 Socket
					closesocket(pSocket->m_socket);
					break;
				}
				else if(0 == iRecvLength)
				{
					pSocket->OnDisConnect(pSocket->m_pOwnerWnd);
					// 关闭客户端 Socket
					closesocket(pSocket->m_socket);
					break;
				}
				else
				{
				   // 触发数据接收事件
                   pSocket->OnRead(pSocket->m_pOwnerWnd,cRecvBuffer,iRecvLength);
				}
			}
		}
	}

	return 0;
}

测试代码如下:

BOOL CTCPClientDlg::OnInitDialog()
{
	//m_bFullScreen = FALSE;
	CDialog::OnInitDialog();
	
	// Set the icon for this dialog.  The framework does this automatically
	//  when the application's main window is not a dialog
	SetIcon(m_hIcon, TRUE);			// Set big icon
	SetIcon(m_hIcon, FALSE);		// Set small icon
	
	CenterWindow(GetDesktopWindow());	// center to the hpc screen
	
	// 初始化输入值
	m_csRemoteHost = GetLocalIP();		// 在模拟器上测试时用: 客户端与服务器都运行在模拟器上
	m_iRemotePort = 5000;
	UpdateData(FALSE);
	return TRUE;  // return TRUE  unless you set the focus to a control
}


// 连接断开
void CALLBACK CTCPClientDlg::OnDisConnect(CWnd *pWnd)
{
	CTCPClientDlg *pDlg = (CTCPClientDlg *)pWnd;
	
	CStatic *pStatus = (CStatic *)pDlg->GetDlgItem(IDC_LBLCONNSTATUS);
	ASSERT(NULL != pStatus);
	pStatus->SetWindowText(L"连接断开");
	
	CButton *pBtnConn = (CButton *)pDlg->GetDlgItem(IDC_BTNCONN);
	CButton *pBtnDisConn = (CButton *)pDlg->GetDlgItem(IDC_BTNDISCONN);
	CButton *pBtnSendData =  (CButton *)pDlg->GetDlgItem(IDC_BTNSENDDATA);
	ASSERT(NULL != pBtnConn);
	ASSERT(NULL != pBtnDisConn);
	ASSERT(NULL != pBtnSendData);
	pBtnConn->EnableWindow(TRUE);
	pBtnDisConn->EnableWindow(FALSE);
	pBtnSendData->EnableWindow(FALSE);
}

// 接收的数据显示
void CALLBACK CTCPClientDlg::OnRead(CWnd *pWnd,const char *pcBuffer,int iLen)
{
	CTCPClientDlg *pDlg = (CTCPClientDlg *)pWnd;
	CString csRecvStr = pcBuffer;
	CEdit *pEdtRecv = (CEdit *)pDlg->GetDlgItem(IDC_EDTRECV);

	ASSERT(NULL != pEdtRecv);
	//将接收的数据显示到接收文本框上
	pEdtRecv->SetWindowText(csRecvStr);
}

// Socket 错误显示
void CALLBACK CTCPClientDlg::OnError(CWnd *pWnd,int iErrorCode)
{
	CString csErrorInfo;

	csErrorInfo.Format(L"%s:%d",L"客户端socket发生错误",iErrorCode);
	AfxMessageBox(csErrorInfo);
}

// 建立连接
void CTCPClientDlg::OnBtnconn() 
{
	CStatic *pStatus = (CStatic *)GetDlgItem(IDC_LBLCONNSTATUS);
	CButton *pBtnConn = (CButton *)GetDlgItem(IDC_BTNCONN);
	CButton *pBtnDisConn = (CButton *)GetDlgItem(IDC_BTNDISCONN);
	CButton *pBtnSendData =  (CButton *)GetDlgItem(IDC_BTNSENDDATA);
	
	ASSERT(NULL != pStatus);
	ASSERT(NULL != pBtnConn);
	ASSERT(NULL != pBtnDisConn);
	ASSERT(NULL != pBtnSendData);

	UpdateData(TRUE);
	// 设置 m_tcpClient 属性
	m_tcpClient.m_csRemoteHost = m_csRemoteHost;
	m_tcpClient.m_iPort = m_iRemotePort;
	m_tcpClient.OnDisConnect = OnDisConnect;
	m_tcpClient.OnRead = OnRead;
	m_tcpClient.OnError = OnError;
	// 打开客户端 socket
	m_tcpClient.Open(this);
	
	// 与服务器端连接
	if(m_tcpClient.Connect())
	{
		pStatus->SetWindowText(L"建立连接");
		UpdateData(FALSE);
	}
	else
	{
		AfxMessageBox(L"建立连接失败");
		pStatus->SetWindowText(L"连接断开");
		return;
	}

	pBtnConn->EnableWindow(FALSE);
	pBtnDisConn->EnableWindow(TRUE);
	pBtnSendData->EnableWindow(TRUE);
}

// 断开连接
void CTCPClientDlg::OnBtndisconn() 
{
	if(m_tcpClient.Close())
	{
		CStatic *pStatus = (CStatic *)GetDlgItem(IDC_LBLCONNSTATUS);
		CButton *pBtnConn =(CButton*)GetDlgItem(IDC_BTNCONN);
		CButton *pBtnDisConn = (CButton*)GetDlgItem(IDC_BTNDISCONN);
		CButton *pBtnSendData = (CButton*)GetDlgItem(IDC_BTNSENDDATA);

		ASSERT(NULL != pStatus);
		ASSERT(NULL != pBtnConn);
		ASSERT(NULL != pBtnDisConn);
		ASSERT(NULL != pBtnSendData);

		pStatus->SetWindowText(L"连接断开");
		pBtnConn->EnableWindow(TRUE);
		pBtnDisConn->EnableWindow(FALSE);
		pBtnSendData->EnableWindow(FALSE);
	}
	else
	{
		AfxMessageBox(L"连接断开失败");
	}	
}

// 发送数据
void CTCPClientDlg::OnBtnsenddata() 
{
	char *pcSendBuf;
	int iSendLength = 0;
	UpdateData(TRUE);
	
	iSendLength = m_csSendData.GetLength();
	pcSendBuf = new char[iSendLength * 2];
	ASSERT(NULL != pcSendBuf);
	wcstombs(pcSendBuf,m_csSendData,iSendLength);
	if(!m_tcpClient.SendData(pcSendBuf,iSendLength))
	{
		AfxMessageBox(L"发送失败");
	}
	delete[] pcSendBuf;
	pcSendBuf = NULL;		
}

抱歉!评论已关闭.