不知道这个Socket重叠IO这种模型是不是socket IO完成端口的基础,不过我感觉,学习一下这个再去学习socket IO完成端口是比较有好处的。
这个Scoket重叠IO我以前记得看过好几次,都没看懂。一部分原因是我没能静态心来写代码,还有更重要的原因就是,Socket重叠他们的结构体参数,还有传参数让人很难理解。下面我将对这些数据结构和参数进行一下讲解
int WSARecv( SOCKET s,//要接收消息的socket LPWSABUF lpBuffers, //一个结构体数组。当接收IO操作完毕后接收内容就在这个里面了 DWORD dwBufferCount, //要接多少个WSABUF LPDWORD lpNumberOfBytesRecvd,//接收了多少个字节 LPDWORD lpFlags, LPWSAOVERLAPPED lpOverlapped,//Overlapped结构体指针 LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine//在本节用不用 );
lpBuffers参数:这是一WSABUF数组,意思是这个函数可以接收不止一个字符缓冲,但是我们一般用一个就够了。 接收多个我还没能测试
dwBufferCount参数:是指上一个参数的数组个数
lpOverlapped参数:这个参数是Overlappad结构体指针,这个指针当IO操作完毕的时候,这里会被系统填充。当IO操作完成时这个结构也可以通过WSAGetOverlappedResult得到
返回值:
0:没有错误发生,IO操作当即完成
SOCKET_ERROR:发生错误
如果是SOCKET_ERROR并且WSAGetLastError() == WSA_IO_PENDING 这时表示操作已经提交。异步操作大部分都是这样的。
当异常操作完成时,Overlapped的hEvent这个事件会触发。这时Overlapped的InternalHigh表示接受的字节数。Internal表示错误代码。消息的内容即是你当初调用WSARecv时传入的lpBuffers参数。
以服务端为例
首先传入WSARecv的几个参数必定与一个socket关联。而且这些参数在异步调用完成之后,但是以后还要用(在WaitForMutiObjects时要用到),而且每一个socket得拥有一个不同的Event来标识是哪个客户端来消息了。所以为每一个客户端socket构造一个Overlapped结构。比如我测试的代码中每一个客户端都有这样一个结构体,而且当accept来的时候表示有新的socket连接,就得生成这样一个结构体,当客户端掉线的时候,就得删除这样一个结构体
下面就是这个结构体:
struct CClientInfo { public: CClientInfo() { ZeroMemory(&m_ol,sizeof(m_ol)); ZeroMemory(m_szBuf,256); m_ol.hEvent = WSACreateEvent(); } ~CClientInfo() { WSACloseEvent(m_ol.hEvent); } WSAOVERLAPPED m_ol; SOCKET sSocket; CString strIp; u_short nPort; CString GetShowText(); char m_szBuf[256]; };
下面是两个函数,一个是当客户端连接的时候,一个是当客户端断开的时候
CClientInfo * CServerDlg::OnSocketConnected(SOCKET sClientSocket,sockaddr_in * saClient) { u_short uPort = ntohs(((sockaddr_in *)saClient)->sin_port); CString strIp = CA2T(inet_ntoa(((sockaddr_in *)saClient)->sin_addr)); CClientInfo * pClientInfo = new CClientInfo; pClientInfo->nPort = uPort; pClientInfo->strIp = strIp; pClientInfo->sSocket = sClientSocket; LockClientArray(); m_ClientArray.Add(pClientInfo); int nIndexInserted = m_ClientListBox.AddString(pClientInfo->GetShowText()); m_ClientListBox.SetItemData(nIndexInserted,pClientInfo->sSocket); UnLockClientArray(); return pClientInfo; } void CServerDlg::OnSocketDisconnect(SOCKET aClientSocket) { LockClientArray(); for(int i = 0;i<m_ClientArray.GetCount();i++) { CClientInfo * pClientInfo = m_ClientArray.GetAt(i); if(pClientInfo->sSocket == aClientSocket) { m_ClientListBox.DeleteString(m_ClientListBox.FindString(0,pClientInfo->GetShowText())); delete pClientInfo; m_ClientArray.RemoveAt(i); break; } } UnLockClientArray(); }
如果WSARecv调用两次会是什么情况,有没有影响。
我没有用异步AcceptEx,感觉这个函数异步了,作用也不大,以后如果需要的话,再学习吧
发送的时候没有用WSASend,以后再学习
原文:http://www.cnblogs.com/zhangdongsheng/p/4850533.html