CSocket

2015-1-26 flyfish

继承关系

class CSocket : public CAsyncSocket

class CAsyncSocket : public CObject

class CSocketWnd : public CWnd

TCP服务器流程

socket()

bind()

listen()

accept()

receive() / send()

close()

CSocket::Create

调用的是父类CAsyncSocket::Create,Create函数中调用了bind

CAsyncSocket::Create(nSocketPort, nSocketType, FD_READ | FD_WRITE | FD_OOB | FD_ACCEPT | FD_CONNECT | 

FD_CLOSE, lpszSocketAddress);

BOOL CAsyncSocket::Create(UINT nSocketPort, int nSocketType,
	long lEvent, LPCTSTR lpszSocketAddress)
{
	if (<strong>Socket</strong>(nSocketType, lEvent))
	{
		if (Bind(nSocketPort,lpszSocketAddress))
			return TRUE;
		int nResult = GetLastError();
		Close();
		WSASetLastError(nResult);
	}
	return FALSE;
}

BOOL CAsyncSocket::Socket(int nSocketType, long lEvent,
	int nProtocolType, int nAddressFormat)
{
	ASSERT(m_hSocket == INVALID_SOCKET);

	m_hSocket = socket(nAddressFormat,nSocketType,nProtocolType);
	if (m_hSocket != INVALID_SOCKET)
	{
		<strong>CAsyncSocket::AttachHandle</strong>(m_hSocket, this, FALSE);
		return <strong>AsyncSelect</strong>(lEvent);
	}
	return FALSE;
}

创建一个不可见的窗口CSocketWnd

第一步new 一个C++对象

第二步调用CWnd的成员函数Create创建真正的Windows对象

管理windows窗口对象是通过句柄完成的

Attach是将C++对象与WINDOWS对象关联

detach是分离关联

所以多线程使用CAsyncSocket 要么attach和detach操作,要么利用窗口句柄向窗口发送消息

windows程序的运行的本质就是 以消息为基础(Message Based),事件驱动(Event Driven)。

把socket的消息映射到windows窗口的消息循环,很符合windows自身的运作模式

void PASCAL CAsyncSocket::AttachHandle(
	SOCKET hSocket, CAsyncSocket* pSocket, BOOL bDead)
{
	_AFX_SOCK_THREAD_STATE* pState = _afxSockThreadState;

	BOOL bEnable = AfxEnableMemoryTracking(FALSE);

	TRY
	{
		if (!bDead)
		{
			ASSERT(CAsyncSocket::LookupHandle(hSocket, bDead) == NULL);
			if (pState->m_pmapSocketHandle->IsEmpty())
			{
				ASSERT(pState->m_pmapDeadSockets->IsEmpty());
				ASSERT(pState->m_hSocketWindow == NULL);

				<strong>CSocketWnd* pWnd = new CSocketWnd;
				pWnd->m_hWnd = NULL;

				if (!pWnd->CreateEx(0, AfxRegisterWndClass(0),
					_T("Socket Notification Sink"),
					WS_OVERLAPPED, 0, 0, 0, 0, NULL, NULL))
				{
					TRACE(traceSocket, 0, "Warning: unable to create socket notify window!\n");
					delete pWnd;
					AfxThrowResourceException();
				}

				ASSERT(pWnd->m_hWnd != NULL);
				ASSERT(CWnd::FromHandlePermanent(pWnd->m_hWnd) == pWnd);
				pState->m_hSocketWindow = pWnd->m_hWnd;</strong>
			}
			pState->m_pmapSocketHandle->SetAt((void*)hSocket, pSocket);
		}
		else
		{
			void* pvCount;
			INT_PTR nCount;
			if (pState->m_pmapDeadSockets->Lookup((void*)hSocket, pvCount))
			{
				nCount = (INT_PTR)pvCount;
				nCount++;
			}
			else
				nCount = 1;
			pState->m_pmapDeadSockets->SetAt((void*)hSocket, (void*)nCount);
		}
	}
	CATCH_ALL (e)
	{
		AfxEnableMemoryTracking(bEnable);
		THROW_LAST();
	}
	END_CATCH_ALL

	AfxEnableMemoryTracking(bEnable);
}
BOOL CAsyncSocket::AsyncSelect(long lEvent)
{
	ASSERT(m_hSocket != INVALID_SOCKET);

	_AFX_SOCK_THREAD_STATE* pState = _afxSockThreadState;
	ASSERT(pState->m_hSocketWindow != NULL);

	return <strong>WSAAsyncSelect</strong>(m_hSocket, pState->m_hSocketWindow,
		WM_SOCKET_NOTIFY, lEvent) != SOCKET_ERROR;
}

CAsyncSocket封装了socket api 并且使用WSAAsyncSelect实现了异步选择I/O模型

该窗口对象处理Socket的消息,CSocketWnd收到Socket消息之后,

通过CAsyncSocket::DoCallBack(pMsg->wParam, pMsg->lParam);

回调CAsyncSocket类的OnReceive(),OnSend(),OnOutOfBandData(),OnAccept(),OnConnect()

#define WSAGETSELECTEVENT(lParam)           LOWORD(lParam)

#define WSAGETSELECTERROR(lParam)          HIWORD(lParam)

lParam参数的高字位 包含出错码,

lParam参数的低字位 标识网络事件代码(FD_XXX)

void PASCAL CAsyncSocket::DoCallBack(WPARAM wParam, LPARAM lParam)
{
	if (wParam == 0 && lParam == 0)
		return;

	// Has the socket be closed - lookup in dead handle list
	CAsyncSocket* pSocket = CAsyncSocket::LookupHandle((SOCKET)wParam, TRUE);

	// If yes ignore message
	if (pSocket != NULL)
		return;

	pSocket = CAsyncSocket::LookupHandle((SOCKET)wParam, FALSE);
	if (pSocket == NULL)
	{
		// Must be in the middle of an Accept call
		pSocket = CAsyncSocket::LookupHandle(INVALID_SOCKET, FALSE);
		ASSERT(pSocket != NULL);

		if(pSocket == NULL)
			return;

		pSocket->m_hSocket = (SOCKET)wParam;
		CAsyncSocket::DetachHandle(INVALID_SOCKET, FALSE);
		CAsyncSocket::AttachHandle(pSocket->m_hSocket, pSocket, FALSE);
	}

	int nErrorCode = WSAGETSELECTERROR(lParam);
	switch (WSAGETSELECTEVENT(lParam))
	{
	case FD_READ:
		{
			fd_set fds;
			int nReady;
			timeval timeout;

			timeout.tv_sec = 0;
			timeout.tv_usec = 0;

			FD_ZERO(&fds);
			FD_SET(pSocket->m_hSocket, &fds);
			nReady = select(0, &fds, NULL, NULL, &timeout);
			if (nReady == SOCKET_ERROR)
				nErrorCode = WSAGetLastError();
			if ((nReady == 1) || (nErrorCode != 0))
				pSocket->OnReceive(nErrorCode);
		}
		break;
	case FD_WRITE:
		pSocket->OnSend(nErrorCode);
		break;
	case FD_OOB:
		pSocket->OnOutOfBandData(nErrorCode);
		break;
	case FD_ACCEPT:
		pSocket->OnAccept(nErrorCode);
		break;
	case FD_CONNECT:
		pSocket->OnConnect(nErrorCode);
		break;
	case FD_CLOSE:
		pSocket->OnClose(nErrorCode);
		break;
	}
}

CSocket的Accept

BOOL CSocket::Accept(CAsyncSocket& rConnectedSocket, SOCKADDR* lpSockAddr, int* lpSockAddrLen)
{
	if (m_pbBlocking != NULL)
	{
		WSASetLastError(WSAEINPROGRESS);
		return FALSE;
	}
	while (!CAsyncSocket::Accept(rConnectedSocket, lpSockAddr, lpSockAddrLen))
	{
		if (GetLastError() == WSAEWOULDBLOCK)
		{
			if (!<strong>PumpMessages</strong>(FD_ACCEPT))
				return FALSE;
		}
		else
			return FALSE;
	}
	return TRUE;
}

AfxGetThread获取当前执行的线程的对象的指针

PumpMessages函数不断调用PeekMessage函数,直到获取到期望的消息时返回

PeekMessage和GetMessage都是从消息队列中获取消息,有消息时将消息分发出去。不同点是:当消息队列中没有消息时,GetMessage会一直等待,直到出现下一个消息时返回。而PeekMessage会在没有取得消息后,立即返回,这使得程序得以继续执行

BOOL CSocket::PumpMessages(UINT uStopFlag)
{
	// The same socket better not be blocking in more than one place.
	ASSERT(m_pbBlocking == NULL);

	_AFX_SOCK_THREAD_STATE* pState = _afxSockThreadState;

	ASSERT(pState->m_hSocketWindow != NULL);

	BOOL bBlocking = TRUE;
	m_pbBlocking = &bBlocking;
	CWinThread* pThread = AfxGetThread();

	// This is not a timeout in the WinSock sense, but more
	// like a WM_KICKIDLE to keep message pumping alive
	UINT_PTR nTimerID = ::SetTimer(pState->m_hSocketWindow, 1, m_nTimeOut, NULL);

	if (nTimerID == 0)
		AfxThrowResourceException();

	BOOL bPeek = TRUE;

	while (bBlocking)
	{
		TRY
		{
			MSG msg;
			if (::PeekMessage(&msg, pState->m_hSocketWindow,
				WM_SOCKET_NOTIFY, WM_SOCKET_DEAD, PM_REMOVE))
			{
				if (msg.message == WM_SOCKET_NOTIFY && (SOCKET)msg.wParam == m_hSocket)
				{
					if (WSAGETSELECTEVENT(msg.lParam) == FD_CLOSE)
					{
						break;
					}
					if (WSAGETSELECTEVENT(msg.lParam) == uStopFlag)
					{
						if (uStopFlag == FD_CONNECT)
							m_nConnectError = WSAGETSELECTERROR(msg.lParam);
						break;
					}
				}
				if (msg.wParam != 0 || msg.lParam != 0)
					CSocket::AuxQueueAdd(msg.message, msg.wParam, msg.lParam);

				bPeek = TRUE;
			}
			else if (::PeekMessage(&msg, pState->m_hSocketWindow,
						WM_TIMER, WM_TIMER, PM_REMOVE))
			{
			break;
			}

			if (bPeek && ::PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE))
			{
				if (OnMessagePending())
				{
					// allow user-interface updates
					ASSERT(pThread);
					pThread->OnIdle(-1);
				}
				else
				{
					bPeek = FALSE;
				}
			}
			else
			{
				// no work to do -- allow CPU to sleep
				WaitMessage();
				bPeek = TRUE;
			}
		}
		CATCH_ALL(e)
		{
			TRACE(traceSocket, 0, "Error: caught exception in PumpMessage - continuing.\n");
			DELETE_EXCEPTION(e);
			bPeek = TRUE;
		}
		END_CATCH_ALL
	}

	::KillTimer(pState->m_hSocketWindow, nTimerID);

	if (!bBlocking)
	{
		WSASetLastError(WSAEINTR);
		return FALSE;
	}
	m_pbBlocking = NULL;

	::PostMessage(pState->m_hSocketWindow, WM_SOCKET_NOTIFY, 0, 0);

	return TRUE;
}

::PostMessage(pState->m_hSocketWindow, WM_SOCKET_NOTIFY, 0, 0);

向先前创建的CSocketWnd窗口发送WM_SOCKET_NOTIFY消息

PeekMessage通常不从队列里清除WM_PAINT消息。该消息将保留在队列里直到处理完毕。

但如果WM_PAINT消息有一个  NULL update region,PeekMessage将从队列里清除WM_PAINT消息

时间: 2024-10-05 23:48:03

CSocket的相关文章

CSocket服务器

我的理解:把服务器和客户端的交互工程比喻成外来人员访问公司,每来一个客户端访问,需要服务器的前台经理接待此客户,然后前台经理呼叫一个接待员来将客户带上楼.服务器的两个角色前台经理和接待员就是服务器的两个CSocket对象. 1.需要生成两个类对象,一个用来监听客户的访问,一个用来接待客户. 在类向导中新建类名:CSockL和CSockC,重写CSockL的OnAccept函数和CSockC的OnReceive函数: (1). void CSockL::OnAccept(int nErrorCod

[00017]-[2015-09-07]-[00]-[CSocket CArchive CSocketFile]

应用程序中使用CSocket, CArchive, CSocketFile 对象接收和发送数据[1]创建CSocketFile对象,使之与CSocket对象关联起来:如下 CSocketFile的构造函数:CSocketFile(CSoket* pSocket, BOOL bArchiveCompatible = TRUE); [2]创建CArchive对象,使之与CSocketFile对象关联起来.CArchive(CFile* pFile, UINT nMode, int bBufferSi

MFC CSocket AfxBeginThread PossMessage Menu 自定义消息

1 // StockServerDlg.h : 头文件 2 // 3 4 #pragma once 5 #include "afxwin.h" 6 7 UINT ThreadProc(LPVOID pParm); 8 9 struct threadInfo 10 { 11 char recvMsg[256]; 12 char sendMsg[256]; 13 HWND hWnd; //主窗口句柄,用于消息的发送 14 }; 15 16 // CStockServerDlg 对话框 17

CAsyncSocket,CSocket内幕及其用法

由于需要写个wince通信程序,首先想到的是c#,但觉得c#写那个太简单了点于是选择了稍微难点的mfc,但是没想到mfc中的csocket在wince中竟然会报异常,悲催,经查证,原来是indows CE Embeded不支持异步传输模式(例如WSAAsyncSelect函数),而mfc中的套接字都是基于异步实现的,因此,很无奈,只能使用原始的winsock了,具体如下 Socket API,CAsyncSocket,CSocket内幕及其用法jmcooler(原作)    关键字     So

深入 CSocket 编程之阻塞和非阻塞模式

有时,花上几个小时阅读.调试.跟踪优秀的源码程序,能够更快地掌握某些技术关键点和精髓.当然,前提是对这些技术大致上有一个了解. 我通过几个采用 CSocket 类编写并基于 Client/Server (客户端 / 服务端)的网络聊天和传输文件的程序 ( 详见: 源代码参考 ) ,在调试这些程序的过程中,追踪深入至 CSocket 类核心源码 Sockcore.cpp , 对于CSocket 类的运行机制可谓是一览无遗,并且对于阻塞和非阻塞方式下的 socket 程序的编写也是稍有体会. 阅读本

CSocket编程中字符乱码问题

*:first-child { margin-top: 0 !important; } body>*:last-child { margin-bottom: 0 !important; } /* BLOCKS =============================================================================*/ p, blockquote, ul, ol, dl, table, pre { margin: 15px 0; } /* HEAD

Csocket基本原理

我通过几个采用 CSocket 类编写并基于 Client/Server (客户端 / 服务端)的网络聊天和传输文件的程式  ,在调试这些程式的过程中,追踪深入至 CSocket 类核心源码 SockCore.cpp , 对于CSocket 类的运行机制可谓是一览无遗,并且对于阻塞和非阻塞方式下的 socket 程式的编写也是稍有体会. 阅读本文请先注意 : •  这里的阻塞和非阻塞的概念仅适用于 Server 端 socket 程式. •  socket 意为套接字,他和 Socket 不同,

[转载]CAsyncSocket及CSocket注解

MFC疑难注解:CAsyncSocket及CSocket MFC对SOCKET编程的支持其实是很充分的,然而其文档是语焉不详的.以至于大多数用VC编写的功能稍复杂的网络程序,还是使用API的.故CAsyncSocket及CSocket事实上成为疑难,群众多敬而远之.余好事者也,不忍资源浪费,特为之注解. 一.CAsyncSocket与CSocket的区别 前者是异步通信,后者是同步通信:前者是非阻塞模式,后者是阻塞模式.另外,异步非阻塞模式有时也被称为长连接,同步阻塞模式则被称为短连接.为了更明

MFC CSocket和CAsyncSocket的连接

MFC CSocket和CAsyncSocket的连接 flyfish 2015-1-31 CSocket的Connect是阻塞的. 所以代码类似 if(!Connect()) { } 判断成功还是失败 CAsyncSocket的Connect是非阻塞的 当调用CAsyncSocket::Connect连接一个服务器 虽然CAsyncSocket::Connect函数返回值是BOOL类型, 但代码不是 if(!Connect()) { } 当连接操作完成,完成(complete)!=成功(suc