为什么说CAsyncSocket是多线程的?

zrdongjiao 2013-11-27 06:23:44
这个类本身是封装wsaasyncselect,为什么会是多线程呢?
...全文
202 13 打赏 收藏 转发到动态 举报
写回复
用AI写文章
13 条回复
切换为时间正序
请发表友善的回复…
发表回复
bsnry 2013-12-03
  • 打赏
  • 举报
回复
引用 10 楼 combobox2013 的回复:
[quote=引用 6 楼 healer_kx 的回复:] http://www.cnblogs.com/healerkx/archive/2011/09/24/2189526.html 代码早就找不到了,但是后来我弄WTL的工程,借助于WTL的消息循环又封装了一次,有blog在此,区区代码根本不在了。
为什么事件选择模型可以 用多线程实现 ? 原因是因为 楼上所说的, 因为不存在窗口,所以 可以把 代码片段: // 处理网络事件 while(TRUE) { // 在所有事件对象上等待 int nIndex = ::WSAWaitForMultipleEvents(nEventTotal, eventArray, FALSE, WSA_INFINITE, FALSE); // 对每个事件调用WSAWaitForMultipleEvents函数,以便确定它的状态 nIndex = nIndex - WSA_WAIT_EVENT_0; for(int i=nIndex; i<nEventTotal; i++) { nIndex = ::WSAWaitForMultipleEvents(1, &eventArray[i], TRUE, 1000, FALSE); if(nIndex == WSA_WAIT_FAILED || nIndex == WSA_WAIT_TIMEOUT) { continue; } else 放到 子线程中吗? [/quote] 参考王艳萍的代码, 它的是一个线程负责64个(即代码里的那个宏) 她写的比较复杂, 用线程池来维护多个套接字(>64)
bsnry 2013-11-30
  • 打赏
  • 举报
回复
没用任何证据,证明,这个类本身就是有线程。
combobox2013 2013-11-30
  • 打赏
  • 举报
回复
引用 10 楼 combobox2013 的回复:
[quote=引用 6 楼 healer_kx 的回复:] http://www.cnblogs.com/healerkx/archive/2011/09/24/2189526.html 代码早就找不到了,但是后来我弄WTL的工程,借助于WTL的消息循环又封装了一次,有blog在此,区区代码根本不在了。
为什么事件选择模型可以 用多线程实现 ? 原因是因为 楼上所说的, 因为不存在窗口,所以 可以把 代码片段: // 处理网络事件 while(TRUE) { // 在所有事件对象上等待 int nIndex = ::WSAWaitForMultipleEvents(nEventTotal, eventArray, FALSE, WSA_INFINITE, FALSE); // 对每个事件调用WSAWaitForMultipleEvents函数,以便确定它的状态 nIndex = nIndex - WSA_WAIT_EVENT_0; for(int i=nIndex; i<nEventTotal; i++) { nIndex = ::WSAWaitForMultipleEvents(1, &eventArray[i], TRUE, 1000, FALSE); if(nIndex == WSA_WAIT_FAILED || nIndex == WSA_WAIT_TIMEOUT) { continue; } else 放到 子线程中吗? [/quote] 顶一下哎,
combobox2013 2013-11-29
  • 打赏
  • 举报
回复
引用 6 楼 healer_kx 的回复:
http://www.cnblogs.com/healerkx/archive/2011/09/24/2189526.html 代码早就找不到了,但是后来我弄WTL的工程,借助于WTL的消息循环又封装了一次,有blog在此,区区代码根本不在了。
为什么事件选择模型可以 用多线程实现 ? 原因是因为 楼上所说的, 因为不存在窗口,所以 可以把 代码片段: // 处理网络事件 while(TRUE) { // 在所有事件对象上等待 int nIndex = ::WSAWaitForMultipleEvents(nEventTotal, eventArray, FALSE, WSA_INFINITE, FALSE); // 对每个事件调用WSAWaitForMultipleEvents函数,以便确定它的状态 nIndex = nIndex - WSA_WAIT_EVENT_0; for(int i=nIndex; i<nEventTotal; i++) { nIndex = ::WSAWaitForMultipleEvents(1, &eventArray[i], TRUE, 1000, FALSE); if(nIndex == WSA_WAIT_FAILED || nIndex == WSA_WAIT_TIMEOUT) { continue; } else 放到 子线程中吗?
bsnry 2013-11-28
  • 打赏
  • 举报
回复
引用 3 楼 healer_kx 的回复:
我的理解不可能错, 一个窗口可以支持N个异步选择Socket,窗口的根本是UI线程循环。 但是一个进程,可能有多个UI线程,虽然99%的程序不是这样的,但是只要是UI线程,那么这个线程,就可以再支持N个异步Socket。 我10年前就封装过这个Socket。
又翻了一遍源码看, 我们这里的用法是这样的: 创建线程池, 每一个线程 由于都调用了peekmesage ,成为ui线程, ui工作线程里 调用局部对象 CAsycnSocket的派生类 干活。 而派生类的create函数加锁,防止 有的函数因为多线程会导致 异常。 这样让 我们的派生类成为多线程安全。 呵呵, 又加深了一遍印象了
healer_kx 2013-11-28
  • 打赏
  • 举报
回复
这个模型比较简单,适合Client开发,本身WTL也是适合Client的开发的 WTL比MFC有些地方是优越的~是360,腾讯很多公司的实际上UI的基础库,而不是MFC。
bsnry 2013-11-28
  • 打赏
  • 举报
回复
引用 6 楼 healer_kx 的回复:
http://www.cnblogs.com/healerkx/archive/2011/09/24/2189526.html 代码早就找不到了,但是后来我弄WTL的工程,借助于WTL的消息循环又封装了一次,有blog在此,区区代码根本不在了。
看不懂啊,不会 wtl, 太难了, 不过这模型被广泛使用的原因,可能是mfc封装过了,所以很多人在用。 事件选择模型都比它强一些
healer_kx 2013-11-28
  • 打赏
  • 举报
回复
http://www.cnblogs.com/healerkx/archive/2011/09/24/2189526.html 代码早就找不到了,但是后来我弄WTL的工程,借助于WTL的消息循环又封装了一次,有blog在此,区区代码根本不在了。
bsnry 2013-11-28
  • 打赏
  • 举报
回复
引用 3 楼 healer_kx 的回复:
我的理解不可能错, 一个窗口可以支持N个异步选择Socket,窗口的根本是UI线程循环。 但是一个进程,可能有多个UI线程,虽然99%的程序不是这样的,但是只要是UI线程,那么这个线程,就可以再支持N个异步Socket。 我10年前就封装过这个Socket。
求源码 ,看看你如何封装的 多个的? 我贴个例子:

LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
int main()
{
	char szClassName[] = "MainWClass";	
	WNDCLASSEX wndclass;
	// 用描述主窗口的参数填充WNDCLASSEX结构
	wndclass.cbSize = sizeof(wndclass);	
	wndclass.style = CS_HREDRAW|CS_VREDRAW;	
	wndclass.lpfnWndProc = WindowProc;	
	wndclass.cbClsExtra = 0;		
	wndclass.cbWndExtra = 0;		
	wndclass.hInstance = NULL;		
	wndclass.hIcon = ::LoadIcon(NULL, IDI_APPLICATION);	
	wndclass.hCursor = ::LoadCursor(NULL, IDC_ARROW);		
	wndclass.hbrBackground = (HBRUSH)::GetStockObject(WHITE_BRUSH);	
	wndclass.lpszMenuName = NULL;		
	wndclass.lpszClassName = szClassName ;
	wndclass.hIconSm = NULL;	
	::RegisterClassEx(&wndclass); 
	// 创建主窗口
	HWND hWnd = ::CreateWindowEx( 
		0,						
		szClassName,			
		"",	
		WS_OVERLAPPEDWINDOW,	
		CW_USEDEFAULT,	
		CW_USEDEFAULT,		
		CW_USEDEFAULT,	
		CW_USEDEFAULT,			
		NULL,				
		NULL,		
		NULL,	
		NULL);		
	if(hWnd == NULL)
	{
		::MessageBox(NULL, "创建窗口出错!", "error", MB_OK);
		return -1;
	}

	USHORT nPort = 4567;	// 此服务器监听的端口号

	// 创建监听套节字
	SOCKET sListen = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);	
	sockaddr_in sin;
	sin.sin_family = AF_INET;
	sin.sin_port = htons(nPort);
	sin.sin_addr.S_un.S_addr = INADDR_ANY;
	// 绑定套节字到本地机器
	if(::bind(sListen, (sockaddr*)&sin, sizeof(sin)) == SOCKET_ERROR)
	{
		printf(" Failed bind() \n");
		return -1;
	}

	// 将套接字设为窗口通知消息类型。
	::WSAAsyncSelect(sListen, hWnd, WM_SOCKET, FD_ACCEPT|FD_CLOSE);

	// 进入监听模式
	::listen(sListen, 5);

	// 从消息队列中取出消息
	MSG msg;	
	while(::GetMessage(&msg, NULL, 0, 0))
	{
		// 转化键盘消息
		::TranslateMessage(&msg);
		// 将消息发送到相应的窗口函数
		::DispatchMessage(&msg);
	}
	// 当GetMessage返回0时程序结束
	return msg.wParam;
}


LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	switch (uMsg)
	{	
	case WM_SOCKET:
		{
			// 取得有事件发生的套节字句柄
			SOCKET s = wParam;
			// 查看是否出错
			if(WSAGETSELECTERROR(lParam))
			{
				::closesocket(s);
				return 0;
			}
			// 处理发生的事件
			switch(WSAGETSELECTEVENT(lParam))
			{
			case FD_ACCEPT:		// 监听中的套接字检测到有连接进入
				{
					SOCKET client = ::accept(s, NULL, NULL);
					::WSAAsyncSelect(client, hWnd, WM_SOCKET, FD_READ|FD_WRITE|FD_CLOSE);
				}
				break;
			case FD_WRITE:
				{
				}
				break;
			case FD_READ:
				{
					char szText[1024] = { 0 };
					if(::recv(s, szText, 1024, 0) == -1)
						::closesocket(s);
					else
						printf("接收数据:%s", szText);
				}
				break;
			case FD_CLOSE:
				{ 
					::closesocket(s);
				}
				break;
			}
		}
		return 0;
	case WM_DESTROY:
		::PostQuitMessage(0) ;
		return 0 ;
	}

	// 将我们不处理的消息交给系统做默认处理
	return ::DefWindowProc(hWnd, uMsg, wParam, lParam);
}
这个例子不完整,但是很经典的讲解了 异步选择的用法, 一个窗口可以绑定到 多个套接字, 比如监听套接字, 客户端套接字等多个上。 楼主说的是: 为什么异步选择不能是 多线程 ? 我曾经思考过这方面的问题,我还曾干过,在多线程中使用 CAsyncSocket的傻事。 个人总结认为: 消息循环和 处理网络消息的代码必须是同一个ui线程。 从这点上,已经根本决定了 没法子 让 异步选择变成多线程。 我们公司源码是在CAsyncSocket的写的, 也是一个线程干活。 再和一个比较类似的模型进行做比较 事件选择模型: 以下是经典例子:

int main()
{
	// 事件句柄和套节字句柄表
	WSAEVENT	eventArray[WSA_MAXIMUM_WAIT_EVENTS];
	SOCKET		sockArray[WSA_MAXIMUM_WAIT_EVENTS];
	int nEventTotal = 0;

	USHORT nPort = 4567;	// 此服务器监听的端口号

	// 创建监听套节字
	SOCKET sListen = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);	
	sockaddr_in sin;
	sin.sin_family = AF_INET;
	sin.sin_port = htons(nPort);
	sin.sin_addr.S_un.S_addr = INADDR_ANY;
	if(::bind(sListen, (sockaddr*)&sin, sizeof(sin)) == SOCKET_ERROR)
	{
		printf(" Failed bind() \n");
		return -1;
	}
	::listen(sListen, 5);

	// 创建事件对象,并关联到新的套节字
	WSAEVENT event = ::WSACreateEvent();
	::WSAEventSelect(sListen, event, FD_ACCEPT|FD_CLOSE);
	// 添加到表中
	eventArray[nEventTotal] = event;
	sockArray[nEventTotal] = sListen;	
	nEventTotal++;

	// 处理网络事件
	while(TRUE)
	{
		// 在所有事件对象上等待
		int nIndex = ::WSAWaitForMultipleEvents(nEventTotal, eventArray, FALSE, WSA_INFINITE, FALSE);
		// 对每个事件调用WSAWaitForMultipleEvents函数,以便确定它的状态
		nIndex = nIndex - WSA_WAIT_EVENT_0;
		for(int i=nIndex; i<nEventTotal; i++)
		{
			nIndex = ::WSAWaitForMultipleEvents(1, &eventArray[i], TRUE, 1000, FALSE);
			if(nIndex == WSA_WAIT_FAILED || nIndex == WSA_WAIT_TIMEOUT)
			{
				continue;
			}
			else
			{
				// 获取到来的通知消息,WSAEnumNetworkEvents函数会自动重置受信事件
				WSANETWORKEVENTS event;
				::WSAEnumNetworkEvents(sockArray[i], eventArray[i], &event);
				if(event.lNetworkEvents & FD_ACCEPT)				// 处理FD_ACCEPT通知消息
				{
					if(event.iErrorCode[FD_ACCEPT_BIT] == 0)
					{
						if(nEventTotal > WSA_MAXIMUM_WAIT_EVENTS)
						{
							printf(" Too many connections! \n");
							continue;
						}
						SOCKET sNew = ::accept(sockArray[i], NULL, NULL);
						WSAEVENT event = ::WSACreateEvent();
						::WSAEventSelect(sNew, event, FD_READ|FD_CLOSE|FD_WRITE);
						// 添加到表中
						eventArray[nEventTotal] = event;
						sockArray[nEventTotal] = sNew;	
						nEventTotal++;
					}
				}
				else if(event.lNetworkEvents & FD_READ)			// 处理FD_READ通知消息
				{
					if(event.iErrorCode[FD_READ_BIT] == 0)
					{
						char szText[256];
						int nRecv = ::recv(sockArray[i], szText, strlen(szText), 0);
						if(nRecv > 0)				
						{
							szText[nRecv] = '\0';
							printf("接收到数据:%s \n", szText);
						}
					}
				}
				else if(event.lNetworkEvents & FD_CLOSE)		// 处理FD_CLOSE通知消息
				{
					if(event.iErrorCode[FD_CLOSE_BIT] == 0)
					{
						::closesocket(sockArray[i]);
						for(int j=i; j<nEventTotal-1; j++)
						{
							sockArray[j] = sockArray[j+1];
							sockArray[j] = sockArray[j+1];	
						}
						nEventTotal--;
					}
				}
				else if(event.lNetworkEvents & FD_WRITE)		// 处理FD_WRITE通知消息
				{
				}
			}
		}
	}
	return 0;
}
这个模型就完全可以用多线程处理,比如处理网络消息这快代码,可以用 个工作线程负责。 主线程只负责创建监听套接字、事件对象等工作。 这个模型没有所谓的消息循环, 很容易拆开。
allenltiverson 2013-11-27
  • 打赏
  • 举报
回复
额。。。围观
healer_kx 2013-11-27
  • 打赏
  • 举报
回复
我的理解不可能错, 一个窗口可以支持N个异步选择Socket,窗口的根本是UI线程循环。 但是一个进程,可能有多个UI线程,虽然99%的程序不是这样的,但是只要是UI线程,那么这个线程,就可以再支持N个异步Socket。 我10年前就封装过这个Socket。
zrdongjiao 2013-11-27
  • 打赏
  • 举报
回复
引用 1 楼 healer_kx 的回复:
当然不是多线程的了。如果要多线程,就要先创建多个UI Thread了。通常不这么做。
个人觉得你的理由似乎不对,尽管结果对了。 我是这么理解的: 用win32 api的话,一个窗口用一个异步选择,只能一个线程。 如果是多线程,根本用不了, 技法再高超的c++, 任你封装,也不可能改成多线程吧。
healer_kx 2013-11-27
  • 打赏
  • 举报
回复
当然不是多线程的了。如果要多线程,就要先创建多个UI Thread了。通常不这么做。

16,472

社区成员

发帖
与我相关
我的任务
社区描述
VC/MFC相关问题讨论
社区管理员
  • 基础类社区
  • Web++
  • encoderlee
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

        VC/MFC社区版块或许是CSDN最“古老”的版块了,记忆之中,与CSDN的年龄几乎差不多。随着时间的推移,MFC技术渐渐的偏离了开发主流,若干年之后的今天,当我们面对着微软的这个经典之笔,内心充满着敬意,那些曾经的记忆,可以说代表着二十年前曾经的辉煌……
        向经典致敬,或许是老一代程序员内心里面难以释怀的感受。互联网大行其道的今天,我们期待着MFC技术能够恢复其曾经的辉煌,或许这个期待会永远成为一种“梦想”,或许一切皆有可能……
        我们希望这个版块可以很好的适配Web时代,期待更好的互联网技术能够使得MFC技术框架得以重现活力,……

试试用AI创作助手写篇文章吧