




已阅读5页,还剩26页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
0、可运行实例及基本知识 1、如何设置 socket 函数的非阻塞调用? 2、深入 CSocket 编程之阻塞和非阻塞模 式 3、SOCKET 类的设计和实现 服务器 #include “stdafx.h“ #include #include #include #include #include #include #pragma comment(lib, “ws2_32.lib“) #define SERVPORT 7861 /*服务器监听端口号*/ #define MAXDATASIZE 100 #define BACKLOG 10 using namespace std; std:vector client_fd; char bufMAXDATASIZE; DWORD WINAPI qtPingServerThreadFunc(LPVOID lpThreadParameter); int _tmain(int argc, _TCHAR* argv) SOCKET sockfd; /*sock_fd:监听socket;client_fd:数据传输socket */ struct sockaddr_in my_addr; /* 本机地址信息*/ struct sockaddr_in remote_addr; /* 客户端地址信息*/ char szMsg=“hello“; WORD wVersionRequested; WSADATA wsaData; int err; wVersionRequested = MAKEWORD(2,0); err = WSAStartup(wVersionRequested, if (0 != err) cout:iterator itr_end = client_fd.end(); std:vector:iterator itr = std:find(client_fd.begin(),itr_end,nSocket); if (itr = itr_end) client_fd.push_back(nSocket); printf( “received a connection from %sn“, inet_ntoa(remote_addr.sin_addr); DWORD dwPingThreadID; HANDLE hPingHandle = CreateThread(0, 0, qtPingServerThreadFunc, 0, 0, for (int n = 0;n #pragma comment(lib, “ws2_32.lib“) using namespace std; #define SERVPORT 7861 /*服务器监听端口号*/ #define DEST_IP “192.168.1.35“ #define MAXDATASIZE 100; int _tmain(int argc, _TCHAR* argv) int sockfd, recvbytes; char bufMAXDATASIZE; struct hostent *host; struct sockaddr_in serv_addr; struct sockaddr_in dest_addr; /* 目的地址*/ WORD wVersionRequested; WSADATA wsaData; int err; wVersionRequested = MAKEWORD(2,0); err = WSAStartup(wVersionRequested, if (0 != err) couth_addr);*/ serv_addr.sin_addr.s_addr = inet_addr(DEST_IP); memset( if (connect(sockfd, (struct sockaddr *) /exit(1); char szMsg = “hao“; int nlen = sizeof(serv_addr); int uIndex = 0; while (1) Sleep(1000); if (send(sockfd, “Hello, are connected!n“, 23, 0) = -1) coutm_hSocketWindow != NULL); return WSAAsyncSelect(m_hSocket, pState-m_hSocketWindow, WM_SOCKET_NOTIFY, lEvent) != SOCKET_ERROR; 函数参数 lEvent 表示希望监视的网络事件。 _ afxSockThreadState 得到的是当前的模块线程状态,m_ hSocketWindow 是本 模块在当前线 程的“socket 窗口” ,指定监视 m_hSocket 的网络事件,如指定 事件发生,给窗口 m_hSocketWindow 发送 WM_SOCKET_NOTIFY 消息。 被指定的网络事件对应的网络 I/O 将是异步操作,是非阻塞操作。例如:指 定 FR_READ 导致 Receive 是一个异步操作,如果不能立即读到数据,则返回 一个错误 WSAEWOULDBLOCK。在数 据到达之后,WinSock 通知窗口 m_hSocketWindow,导致 OnReceive 被调用。 指定 FR_WRITE 导致 Send 是一个异步操作,即使数据没有送出也返回一个 错误 WSAEWOULDBLOCK 。在数据可以发送之后,WinSock 通知窗口 m_hSocketWindow,导致 OnSend 被调用。 指定 FR_CONNECT 导致 Connect 是一个异步操作,还没有连接上就返回错 误信息 WSAEWOULDBLOCK,在连接完成之后,WinSock 通知窗口 m_hSocketWindow,导致 OnConnect 被 调用。 对于其他网络事件,就不一一解释了。 所以,使用 CAsyncSocket 时,如果使用 Create 缺省创建 socket,则所有网络 I/O 都是异步操 作,进行有关网络 I/O 时则必须覆盖以下的相关函数: OnAccept、OnClose、OnConnect、OnOutOfBandData、OnReceive、OnSend。 (5)Bind 函数 经过上述过程,socket 创建完毕,下面,调用 Bind 函数给 m_hSocket 指定本 地端口和 IP 地址 。Bind 的实现如下: BOOL CAsyncSocket:Bind(UINT nSocketPort, LPCTSTR lpszSocketAddress) USES_CONVERSION; /使用 WinSock 的地址结构构造地址信息 SOCKADDR_IN sockAddr; memset( /得到地址参数的值 LPSTR lpszAscii = T2A(LPTSTR)lpszSocketAddress); /指定是 Internet 地址类型 sockAddr.sin_family = AF_INET; if (lpszAscii = NULL) /没有指定地址,则自动得到一个本地 IP 地址 /把 32 比特的数据从主机字节序转换成网络字节序 sockAddr.sin_addr.s_addr = htonl(INADDR_ANY); else /得到地址 DWORD lResult = inet_addr(lpszAscii); if (lResult = INADDR_NONE) WSASetLastError(WSAEINVAL); return FALSE; sockAddr.sin_addr.s_addr = lResult; /如果端口为 0,则 WinSock 分配一个端口(10245000) /把 16 比特的数据从主机字节序转换成网络字节序 sockAddr.sin_port = htons(u_short)nSocketPort); /Bind 调用 WinSock API 函数 bind return Bind(SOCKADDR*) 其中:函数参数 1 指定了端口;参数 2 指定了一个包含本地地址的字符串, 缺省是 NULL。 函数 Bind 首先使用结构 SOCKADDR_IN 构造地址信息。该结构的域 sin_family 表示地址格式 (TCP/IP 同协议族) ,赋值为 AF_INET(Internet 地址 格式) ;域 sin_port 表示端口,如果参 数 1 为 0,则 WinSock 分配一个端口给 它,范围在 1024 和 5000 之间;域 sin_addr 是表示地址信 息,它是一个联合 体,其中 s_addr 表示如下形式的字符串, “28.56.22.8”。如果参数没有 指定地 址,则 WinSock 自动地得到本地 IP 地址(如果有几个网卡,则使用其中一个的地址) 。 (6)总结 Create 的过程 首先,调用 socket 函数创建一个 socket;然后把创建的 socket 对象映射到 CAsyncSocket 对象 (捆绑在一起) ,指定本 socket 要通知的网络事件,并创建 一个“socket 窗口 ”来接收网络 事件消息,最后,指定 socket 的本地信息。 下一步,是使用成员函数 Connect 连接远地主机,配置 socket 的远地信息。 函数 Connect 类似 于 Bind,把指定的远地地址转换成 SOCKADDR_IN 对象 表示的地址信息(包括网络字节序的转 换) ,然后调用 WinSock 函数 Connect 连接远地主机,配置 socket 的远地端口和远地 IP 地址。 3.异步网络事件的处理 当网络事件发生时, “socket 窗口”接收 WM_SOCKET_NOTIFY 消息,消息 处理函数 OnSocketNotify 被调用。 “socket 窗口”的定义和消息处理是 MFC 实现的,这里不作详细的 讨论。 OnSocketNotify 回调 CAsyncSocket 的成员函数 DoCallBack,DoCallBack 调 用事件处理函数, 如 OnRead、OnWrite 等。摘录 DoCallBack 的一段代码如 下: switch (WSAGETSELECTEVENT(lParam) case FD_READ: DWORD nBytes; /得到可以一次读取的字节数 pSocket-IOCtl(FIONREAD, if (nBytes != 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; lParam 是 WM_SOCKET_NOFITY 的消息参数, OnSocketNotify 传递给函数 DoCallBack,表示通知 事件。 函数 IOCtl 是 CAsyncSocket 的成员函数,用来对 socket 的 I/O 进行控制。这 里的使用表示本次 调用 Receive 函数至多可以读 nBytes 个字节。 从上面的讨论可以看出,从创建 socket 到网络 I/O,CAsyncSocket 直接封装 了低层的 WinSock API,简化了 WinSock 编程,实现了一个异步操作的界面。 如果希望某个操作是阻塞操作,则 在调用 Create 时不要指定该操作对应的网 络事件。例如,希望 Connect 和 Send 是阻塞操作, 在任务完成之后才返回, 则可以使用如下的语句: pSocket-Create(0, SOCK_STREAM, FR_WRITE|FR_OOB|FR_ACCEPT|FR_CLOSE); 这样,在 Connect 和 Send 时,如果是用户界面线程的话,可能阻塞线程消息 循环。所以,最 好在工作者线程中使用阻塞操作。 2.CSocket 如果希望在用户界面线程中使用阻塞 socket,则可以使用 CSocket。它在非阻塞 socket 基础之上实 现了阻塞操作,在阻塞期间实现了消息循环。 对于 CSocket,处理网络事件通知的函数 OnAccept、OnClose、OnReceive 仍然可 以使用, OnConnect、OnSend 在 CSocket 中永远不会被调用,另外 OnOutOfBandData 在 CSocket 中不鼓励使用 。 CSocket 对象在调用 Connect、Send、Accept 、Close、Receive 等成员函数后,这些 函数在完成任 务之后(连接被建立、数据被发送、连接请求被接收、socket 被关闭、 数据被读取)之后才会返 回。因此,Connect 和 Send 不会导致 OnConnect 和 OnSend 被调用。如果覆盖虚拟函数 OnReceive、 OnAccept、OnClose,不主动调用 Receive、Accept、Close,则在网络事件到达之后导致对应的虚 拟函数被调用,虚 拟函数的实现应该调用 Receive、Accept、Close 来完成操作。下面,就一个函 数 Receive 来考察 CSocket 如何实现阻塞操作和消息循环的。 int CSocket:Receive(void* lpBuf, int nBufLen, int nFlags) /m_pbBlocking 是 CSocket 的成员变量,用来标识当前是否正在进行 /阻塞操作。但不能同时进行两个阻塞操作。 if (m_pbBlocking != NULL) WSASetLastError(WSAEINPROGRESS); return FALSE; /完成数据读取 int nResult; while (nResult = CAsyncSocket:Receive(lpBuf, nBufLen, nFlags) = SOCKET_ERROR) if (GetLastError() = WSAEWOULDBLOCK) /进入消息循环,等待网络事件 FD_READ if (!PumpMessages(FD_READ) return SOCKET_ERROR; else return SOCKET_ERROR; return nResult; 其中: 参数 1 指定一个缓冲区保存读取的数据;参数 2 指定缓冲区的大小;参数 3 取值 MSG_PEEK(数据拷贝 到缓冲区,但不从输入队列移走) ,或者 MSG_OOB(处理 带外数据) ,或者 MSG_PEEK|MSG_OOB。 Receive 函数首先判断当前 CSocket 对象是否正在处理一个阻塞操作,如果是,则 返回错误 WSAEINPROGRESS;否则,开始数据读取的处理。 读取数据时,如果基类 CAsyncSocket 的 Receive 读取到了数据,则返回;否则, 如果返回一个错误 ,而且错误号是 WSAEWOULDBLOCK,则表示操作阻塞,于 是调用 PumpMessage 进入消息循环等待数据 到达(网络事件 FD_READ 发生) 。数 据到达之后退出消息循环,再次调用 CAsyncSocket 的 Receive 读取数据,直到没有 数据可读为止。 PumpMessages 是 CSocket 的成员函数,它完成以下工作: (1)设置 m_pbBlocking,表示进入阻塞操作。 (2)进行消息循环,如果有以下事件发生则退出消息循环:收到指定定时器的定时 事件消息 WM_TIMER,退出循环,返回 TRUE;收到发送给本 socket 的消息 WM_SOCKET_NOTIFY,网络事件 FD_CLOSE 或者等待的网络事件发生,退出循 环,返回 TRUE;发送错误或者收到 WM_QUIT 消息,退出 循环,返回 FALSE; (3)在消息循环中,把 WM_SOCKET_DEAD 消息和发送给其他 socket 的通知消 息 WM_SOCKET_NOFITY 放 进模块线程状态的通知消息列表 m_listSocketNotifications,在阻塞操作完成之后处理;对其他 消息,则把它们送给 目的窗口的窗口过程处理。 3.CSocketFile MFC 还提供了一个网络编程模式,可以充分利用 CSocket 的特性。该模式的基础 是 CSocketFile 类。 使用方法如下: 首先,构造一个 CSocket 对象;调用 Create 函数创建一个 socket 对象 (SOCK_STREAM 类型) 。 接着,如果是客户程序,调用 Connect 连接到远地主机;如果是服务器程序,先调 用 Listen 监听 socket 端口,收到连接请求后调用 Accept 接收请求。 然后,创建一个和 CSocket 对象关联的 CSocketFile 对象,创建一个和 CSocketFile 对象关联的 CArchive 对象,指定 CArchive 对象是用于读或者写。如果既要读 又要写,则创建两个 CArchive 对 象。 创建工作完成之后,使用 CArchive 对象在客户和服务器之间传送数据 使用完毕,销毁 CArchive 对象、CSocketFile 对象、CSocket 对象。 从前面的章节可以知道,CArchive 可以以一个 CFile 对象为基础,通过操 作符完成对文件的 二进制 流的操作。所以可以从 CFile 派生一个类,实现 CFile 的操作界面(Read 和 Write) 。由于 CSocket 提供了阻 塞操作,所以完 全可以像读写文件一样读写 socket 数据。 下面,分析 CSocketFile 的设计和实现。 1.CSocketFile 的构造函数和析构函数的实现 构造函数的实现: CSocketFile:CSocketFile(CSocket* pSocket, BOOL bArchiveCompatible) m_pSocket = pSocket; m_bArchiveCompatible = bArchiveCompatible; #ifdef _DEBUG ASSERT(m_pSocket != NULL); ASSERT(m_pSocket-m_hSocket != INVALID_SOCKET); int nType = 0; int nTypeLen = sizeof(int); ASSERT(m_pSocket-GetSockOpt(SO_TYPE, ASSERT(nType = SOCK_STREAM); #endif / _DEBUG 其中: 构造函数的参数 1 指向关联的 CSocket 对象,被保存在成员变量 m_pSocket 中; 参数 2 指定该对象是否和一个 CArchive 对象关联(不关联则独立使用) ,被保存在 成员变量 bArchiveCompatible 中。 Degug 部分用于检测 m_pSocket 是否是 SOCK_STREAM 类型。 析构函数的实现 CSocketFile:CSocketFile() (2)CSocketFile 的读写的实现 分析 CSocketFile 如何用文件的读写实现网络 I/O。 文件读的实现 UINT CSocketFile:Read(void* lpBuf, UINT nCount) ASSERT(m_pSocket != NULL); int nRead; /CSocketFile 对象独立使用 if (!m_bArchiveCompatible) int nLeft = nCount; PBYTE pBuf = (PBYTE)lpBuf; /读完 nCount 个字节的数据 while(nLeft 0) /CSocket 的 Receive,阻塞操作,读取到数据才继续 nRead = m_pSocket-Receive(pBuf, nLeft); if (nRead = SOCKET_ERROR) int nError = m_pSocket-GetLastError(); AfxThrowFileException(CFileException:generic, nError); ASSERT(FALSE); else if (nRead = 0) return nCount - nLeft; nLeft -= nRead; pBuf += nRead; return nCount - nLeft; /和一个 CArchive 对象关联使用 /读取数据,能读多少是多少 nRead = m_pSocket-Receive(lpBuf, nCount, 0); if (nRead = SOCKET_ERROR) int nError = m_pSocket-GetLastError(); AfxThrowFileException(CFileException:generic, nError); ASSERT(FALSE); return nRead; 文件写的实现 void CSocketFile:Write(const void* lpBuf, UINT nCount) ASSERT (m_pSocket!=NULL); /CSocket 的函数 Send,阻塞操作,发送完毕才继续 int nWritten = m_pSocket-Send(lpBuf, nCount); if (nWritten = SOCKET_ERROR) int nError = m_pSocket-GetLastError(); AfxThrowFileException(CFileException:generic, nError); 从 CSockefFile 的读写实现可以看出,CSocketFile 如果独立使用,在 Read 操作时可能出现 无限等待,因为数据是分多个消息多次送达的,没有读取到指定长度的数据并不表示数据 读取完毕。但是和 CArchive 配合使用,则仅仅读取到数据就返回。至于数据是否读取完毕, 可以使用 CArchive 的 IsBufferEmpty 函数来判断。 其他 CFile 界面,CSocketFile 没有实现。 从 CScocketFile 的设计和实现来看, CSocketFile 是使用 CSocket 的一个很好的例子,也是 使用 CFile 的一个例子。 2 深入 CSocket 编程之阻塞和非阻塞模式 2007 年 09 月 30 日 星期日 19:25 有时,花上几个小时阅读、调试、跟踪优秀的源码程序,能够更快地掌握某些 技术关键点和精髓。当然,前提是对这些技术大致上有一个了解。 我通过几个采用 CSocket 类编写并基于 Client/Server (客户端 / 服务 端)的网络聊天和传输文件的程序 ( 详见: 源代码参考 ) ,在调试这些程序 的过程中,追踪深入至 CSocket 类核心源码 Sockcore.cpp , 对于 CSocket 类的运行机制可谓是一览无遗,并且对于阻塞和非阻塞方式下的 socket 程序 的编写也是稍有体会。 阅读本文请先注意: 这里的阻塞和非阻塞的概念仅适用于 Server 端 socket 程序。socket 意 为套接字,它与 Socket 不同,请注意首字母的大小写。 客户端与服务端的通信简单来讲:服务端 socket 负责监听,应答,接收 和发送消息,而客户端 socket 只是连接,应答,接收,发送消息。此外,如 果你对于采用 CSocket 类编写 Client/Server 网络程序的原理不是很了解, 请先查询一下( 详见:参考书籍和在线帮助 )。 在此之前,有必要先讲述一下: 网络传输服务提供者, ws2_32.dll , socket 事件 和 socket window 。 1、网络传输服务提供者(网络传输服务进程), Socket 事件, Socket Window 网络传输服务提供者 ( transport service provider )是以 DLL 的形 式存在的,在 windows 操作系统启动时由服务进程 svchost.exe 加载。当 socket 被创建时,调用 API 函数 Socket (在 ws2_32.dll 中), Socket 函数会传递三个参数 : 地址族,套接字类型 ( 注 2 ) 和协议,这三个参数决 定了是由哪一个类型的 网络传输服务提供者 来启动网络传输服务功能。所有 的网络通信正是由网络传输服务提供者完成 , 这里将 网络传输服务提供者 称 为 网络传输服务进程 更有助于理解,因为前文已提到 网络传输服务提供者 是由 svchost.exe 服务进程所加载的。 下图描述了网络应用程序、 CSocket ( WSock32.dll )、 Socket API(ws2_32.dll) 和 网络传输服务进程 之间的接口层次关系: 当 Client 端 socket 与 Server 端 socket 相互通信时,两端均会触发 socket 事件。这里仅简要说明两个 socket 事件: FD_CONNECT: 连接事件 , 通常 Client 端 socket 调用 socket API 函 数 Connect 时所触发,这个事件发生在 Client 端。 FD_ACCEPT :正在引入的连接事件,通常 Server 端 socket 正在接收 来自 Client 端 socket 连接时触发,这个事件发生在 Server 端。 网络传输服务进程 将 socket 事件 保存至 socket 的事件队列中。此外, 网络传输服务进程 还会向 socket window 发送消息 WM_SOCKET_NOTIFY , 通 知有 socket 事件 产生,见下文对 socket window 的详细说明。 调用 CSocket:Create 函数后,socket 被创建。 socket 创建过程中调 用 CAsyncSocket:AttachHandle(SOCKET hSocket, CAsyncSocket* pSocket, BOOL bDead) 。该函数的作用是: 将 socket 实例句柄和 socket 指针添加至 当前模块状态 ( 注 1 ) 的一个映射表变量 m_pmapSocketHandle 中。 在 AttachHandle 过程中,会 new 一个 CSocketWnd 实例 ( 基于 CWnd 派生 ) ,这里将这个实例称之为 socket window ,进一步理解为它是 存放所有 sockets 的消息池 ( window 消息),请仔细查看,这里 socket 后多加了一个 s ,表示创建的多个 socket 将共享一个 消息池 。 当 Client 端 socket 与 Server 端相互通信时 , 此时 网络传输服务 进程 向 socket window 发送消息 WM_SOCKET_NOTIFY ,需要说明的是 CSocketWnd 窗口句柄保存在 当前模块状态 的 m_hSocketWindow 变量 中。 2、阻塞模式 阻塞模式下 Server 端与 Client 端之间的通信处于同步状态下。在 Server 端直接实例化 CSocket 类,调用 Create 方法创建 socket ,然后调 用方法 Listen 开始侦听,最后用一个 while 循环阻塞调用 Accept 函数用于 等待来自 Client 端的连接,如果这个 socket 在主线程(主程序)中运行, 这将导致主线程的阻塞。因此,需要创建一个新的线程以运行 socket 服务。 调试跟踪至 CSocket:Accept 函数源码: while(!Accept(.) / The socket is marked as nonblocking and no connections are present to be accepted. if (GetLastError() = WSAEWOULDBLOCK) PumpMessage(FD_ACCEPT); else return FALSE; 它不断调用 CAsyncSocket:Accept ( CSocket 派生自 CAsyncSocket 类) 判断 Server 端 socket 的事件队列中是否存在正在引入的连接事件 - FD_ACCEPT (见 1 ),换句话说,就是判断是否有来自 Client 端 socket 的 连接请求。 如果当前 Server 端 socket 的事件队列中存在正在引入的连接事件, Accept 返回一个非 0 值。否则, Accept 返回 0,此时调用 GetLastError 将返回错误代码 WSAEWOULDBLOCK ,表示队列中无任何连接请求。注意到在循 环体内有一句代码: PumpMessage(FD_ACCEPT); PumpMessage 作为一个消息泵使得 socket window 中的消息能够维持在活 动状态。实际跟踪进入 PumpMessage 中,发现这个消息泵与 Accept 函数的调 用并不相关,它只是使很少的 socket window 消息(典型的是 WM_PAINT 窗口 重绘消息)处于活动状态,而绝大部分的 socket window 消息被阻塞,被阻塞 的消息中含有 WM_SOCKET_NOTIFY。 很显然,如果没有来自 Client 端 socket 的连接请求, CSocket 就会不 断调用 Accept 产生循环阻塞,直到有来自 Client 端 socket 的连接请求而 解除阻塞。 阻塞解除后,表示 Server 端 socket 和 Client 端 socket 已成功连接, Server 端与 Client 端彼此相互调用 Send 和 Receive 方法开始通信。 3、非阻塞模式 在非阻塞模式下 利用 socket 事件 的消息机制, Server 端与 Client 端之间的通信处于异步状态下。 通常需要从 CSocket 类派生一个新类,派生新类的目的是重载 socket 事 件 的消息函数,然后在 socket 事件 的消息函数中添入合适的代码以完成 Client 端与 Server 端之间的通信,与阻塞模式相比,非阻塞模式无需创建一 个新线程。 这里将讨论当 Server 端 socket 事件 FD_ACCEPT 被触发后,该事件 的处理函数 OnAccept 是如何进一步被触发的。其它事件的处理函数如 OnConnect, OnReceive 等的触发方式与此类似。 在 1 中已提到 Client/Server 端通信时, Server 端 socket 正在接收来自 Client 端 socket 连接请求,这将会触发 FD_ACCEPT 事件,同时 Server 端 的 网络传输服务进程 向 Server 端的 socket window (CSocketWnd )发送事 件通知消息 WM_SOCKET_NOTIFY , 通知有 FD_ACCEPT 事件产生 , CsocketWnd 在收到事件通知消息后,调用消息处理函数 OnSocketNotify: LRESULT CSocketWnd:OnSocketNotify(WPARAM wParam, LPARAM lParam) CSocket:AuxQueueAdd(WM_SOCKET_NOTIFY, wParam, lParam); CSocket:ProcessAuxQueue(); return 0L ; 消息参数 wParam 是 socket 的句柄, lParam 是 socket 事件 。这里稍 作解释一下,CSocketWnd 类是作为 CSocket 类的 友元类 ,这意味着它可以 访问 CSocket 类中的保护和私有成员函数和变量, AuxQueueAdd 和 ProcessAuxQueue 是 CSocket 类的静态成员函数,如果你对友元不熟悉,请迅 速找本有关 C+ 书看一下友元的使用方法吧! ProcessAuxQueue 是实质处理 socket 事件的函数,在该函数中有这样一句代 码: CAsyncSocket* pSocket = CAsyncSocket:LookupHandle(SOCKET)wParam, TRUE); 其实也就是由 socket 句柄得到发送事件通知消息的 socket 指针 pSocket:从 m_pmapSocketHandle 中查找(见 1 )! 最后, WSAGETSELECTEVENT(lParam) 会取出事件类型,在一个简单的 switch 语句中判断事件类型并调用事件处理函数。在这里,事件类型是 FD_ACCEPT ,当然就调用 pSocket-OnAccept ! 结束语 Server 端 socket 处于阻塞调用模式下,它必须在一个新创建的线程中工 作,防止主线程被阻塞。 当有多个 Client 端 socket 与 Server 端 socket 连接及通信时, Server 端采用阻塞模式就显得不适合了,应该采用非阻塞模式 , 利用 socket 事件 的消息机制来接受多个 Client 端 socket 的连接请求并进行通信。 在非阻塞模式下,利用 CSocketWnd 作为所有 sockets 的消息池,是实现 socket 事件 的消息机制的关键技术。文中存在用词不妥和可能存在的技术问 题,请大家原谅,也请批评指正,谢谢! 注: 1. 当前模块状态用于保存当前线程和模块状态的一个结构,可以通过 AfxGetThreadModule() 获得。AFX_MODULE_THREAD_STATE 在 CSocket 重新定义为 _AFX_SOCK_THREAD_STATE 。 2. socket 类型在 TCP/IP 协议中, Client/Server 网络程序采用 TCP 协议:即 socket 类型为 SOCK_STREAM ,它是可靠的连接方式
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 班会春节文化课件
- 广东省建筑施工企业安全生产管理人员安全生产(安全管理)测试题及答案(附解析)
- 2025年2月新能源汽车习题库及参考答案解析
- 玩具行业人才激励计划考核试卷
- 初中语文教学设计与指导
- 稀土金属矿选矿厂生产自动化与信息集成考核试卷
- 眼科疾病诊疗与视力保护考核试卷
- 曼汉教育费用分析
- 发动机结构与维修技术考核试卷
- 肉类产品陈列与货架管理技巧考核试卷
- 第4章我们生活的大地知识点清单-2024-2025学年浙教版七年级下册科学
- 军事通信基础知识
- 建筑工地挖掘机吊装施工方案
- 8.2 法治政府 课件-高中政治统编版必修三政治与法治
- 糖尿病合并痛风
- 中西文化鉴赏知到智慧树章节测试课后答案2024年秋郑州大学
- 《天津市新型职业农民培育问题研究》
- 车险理赔重大案管理办法
- 牙科市场细分领域分析-洞察分析
- 第16课《经济危机与资本主义国家的应对》中职高一下学期高教版(2023)世界历史全一册
- 货运车队的管理制度模版(2篇)
评论
0/150
提交评论