我创建了一个socket,使用WSAEventSelect监视FD_READ | FD_WRITE | FD_CONNECT | FD_CLOSE等事件,同时我开了一个子线程,不停地用WSAWaitForMultipleEvents和WSAEnumNetworkEvents监视网络事件,我的问题是,现在已经收到有FD_READ事件,但是我怎么知道这个时刻,收到的数据大小有多少?
解决方案 »
- server.sin_addr.s_addr = inet_addr("127.0.0.1")的问题
- VS2008的不能正确设置环境变量
- 推荐一款 Office Access 2007版本的数据库管理工具!!
- ocx中初始化数码相机错误
- 高手们去哪了?
- 用VC的File::Open打开的文件怎么显示出来呀?
- 把doc等文件压缩成zip,和把zip文件解压的控件或dll(用VC或VB开发的)
- 我的组件为什么测试版本可以用而Release不能用呢?
- 如何让一个线程在结束以后,该线程的句柄还有效?
- 如何建立像Messagebox的对话框
- 如何计算两个日期之间相差的天数
- 急问,VC7的CHTMLDialog,滚动条无效,该如何解决?
来获取已经收到的字节.
前面提交WSARecv (Acceptedsocket,&DataBuf,1,&RecvBytes,&Flags,&AcceptOverlapped,NULL)
用WSARecv接收一段数据(比如1K大小字节),如果收到的数据未达到这个大小,则不管如何这次FD_READ结束,如果1K的I/O接收完成,则马上再一个while循环继续读下一个1K大小的数据,直到某次读取I/O未全部接收完,在这种情形下退出while,再继续判断下一个FD_READ事件。
但这样运算量稍大了些,如果可以有直接判断多少个数据在接收缓冲的SOCKET函数,那就在FD_READ的时候一个阻塞的recv函数把这些数据全部读完就可以了。
如果可以有直接判断多少个数据在接收缓冲的SOCKET函数,那就在FD_READ的时候一个阻塞的recv函数把这些数据全部读完就可以了。这句话说得不对,因为使用WSAEventSelec i/o模型,所以recv函数肯定是非阻塞的,也就是说WSAEventSelect通知你FD_READ的时候,你的socket缓冲区已经有数据了。你WSARecv一下就可以了,不要在FD_READ下边循环读,那样是busy loop;应该通知一下读一下。一般协议是这样定义结构体的 包头(数据包长度,命令字,状态字,序号等字段)+包体(数据)。
我想你做程序的时候肯定针对每个 socket句柄都定义了一个
struct my{
SOCKET s;
char sendbuf[2048];
char recvbuf[2048];
int send_len;
int recv_len;
}每次读写的时候同时更新 sendbuf和send_len,当send_len达到你协议规定的长度是,就可以解析数据报了
如果想知道
提交一次WSARecv之后,每次接收到FD_READ事件的时候,每次提交WSARecv的所接收到的字节数,如何处理?我的看法是,假设第一次提交WSARecv,请求接收2048个字节放入程序定义的2048字节buffer,因为是非堵塞模式,接收到FD_READ事件之后,有可能实际接收到了1024个字节,buffer并未填充完整,那么如何在处理这次FD_READ的时候,得到已经接收到的1024个字节。每次读写的时候同时更新 sendbuf和send_len,?接收第一次FD_READ的时候是否能确保接收到的字节数就是你所请求的字节数,此时更新这个长度似乎不大妥当?
The WSAEventSelect function automatically sets socket s to nonblocking mode, regardless of the value of lNetworkEvents. 1 既然是WSAEventSelect使用i/o模式,要明白由WSAEventSelect管理的socket对象中,如果其中有一个socket对象的缓冲区中已经收到数据,都会激活和socket关联的event object,并发生FD_READ事件;然后进程可以调用recv或者WSARecv函数收取数据,注意这个时候发生的是同步i/o,也就是说:recv或者WSARecv不把socket缓冲区的数据拷贝到进程自己的缓冲区,是不会返回的。收到的字节数是recv的返回值或者是WSARecv的第4个参数。
楼上模糊了一个概念,是收到fd_read再recv;而不是先recv,然后再收到fd_read;这个模型基本上和select模型一致
2 如果使用overlapped i/o模型,则创建socket的时候需要指定overlapped标志。这种模式下才是先WSARecv(提交i/o请求),WSARecv立即返回WSAEWOULDBLOCK;然后才在用户自己的线程中调用WSAGetOverlappedResult来获得已经提交i/o的结果。这是个 非阻塞异步i/o;包括iocp也是这样实现的。
我想你做程序的时候肯定针对每个 socket句柄都定义了一个
struct my{
SOCKET s;
char sendbuf[2048];
char recvbuf[2048];
int send_len;
int recv_len;
}每次读写的时候同时更新 sendbuf和send_len,当send_len达到你协议规定的长度是,就可以解析数据报了一般都需要这个结构,因为每次读写多少字节都不知道,重要保证一个当前读写指针吧
受教了,从你的回答里,顺便重温了一下EventSelect模型了解了FD_READ的究竟。还有一个问题需要确认。我先测试之后再来讨论
skywoodsky,这个方案恐怕不能接受。在TCP/UDP包中再次以自己的包头包尾格式封装,CPU计算
量及带宽上的消耗令人无法接受。
anjuta_c,我目前想获取的就是使用WSAEnumNetworkEvents判断网络事件,发生事件后再使用重叠的I/O操作读写数据,但我使用了重叠的I\O模拟阻塞的I\O,见我下面的函数。我测试了之后发现你的看法是正确的:“也就是说WSAEventSelect通知你FD_READ的时候,你的socket缓冲区已经有数据了。你WSARecv一下就可以了”
目前我的操作方式是:使用WSAEventSelect选择FD_READ事件,在该事件发生后使用ioctlsocket得到socket缓冲区中的数据数量 ,然后使用recv或者WSARecv(目前我只测试了WSARecv)读取数据,试验结果,使用WSARecv读取该长度的数据时,返回值显示立即成功,不再是WSA_IO_PENDING,这一点证明anjuta_c所说的,如果重叠I\O读取长度不超过缓冲区中数据的数量,那么该I\O是一次性的阻塞方式。
这是我的部分代码://新线程读取网络事件
DWORD WINAPI CTcpClient::ThreadProc(LPVOID lpParameter)
{
CTcpClient* pThis = (CTcpClient*)lpParameter; WSANETWORKEVENTS event;
while (WaitForSingleObject(pThis->m_hExit, 0) != WAIT_OBJECT_0)
{
if (WSAWaitForMultipleEvents(1, &pThis->m_hEvent, true, 200, false)
== WSA_WAIT_EVENT_0)
{
if (WSAEnumNetworkEvents(pThis->m_socClient, pThis->m_hEvent, &event) == 0)
{
if (pThis->m_pFuncMes != NULL)
{
unsigned long lRead = 0;
if ((event.lNetworkEvents & FD_READ) || (event.lNetworkEvents & FD_WRITE))
{
ioctlsocket(pThis->m_socClient, FIONREAD, &lRead);
pThis->m_pFuncMes(event.lNetworkEvents, pThis->m_pData, &lRead);
}
else
{
pThis->m_pFuncMes(event.lNetworkEvents, pThis->m_pData, NULL);
}
}
}
WSAResetEvent(pThis->m_hEvent);
}
}
return 0;
}
//在发生网络事件后,回调函数pThis->m_pFuncMes里面调用重叠I\O模拟的阻塞I\O方式读取数据,调试结果显示读取立即成功返回,没有WSA_IO_PENDING
int CTcpClient::ReadData(BYTE* pcBuf, int &nLen, WSAOVERLAPPED* pOverlapped)
{
if (pOverlapped == NULL)
{
int nRet = 0;
WSAEVENT Event = WSACreateEvent();
WSAOVERLAPPED Overlapped;
memset(&Overlapped, 0, sizeof(WSAOVERLAPPED));
Overlapped.hEvent = Event; DWORD dwFlag = 0; WSABUF buf;
buf.buf = (char*)pcBuf;
buf.len = nLen;
if (WSARecv(m_socClient, &buf, 1, (DWORD*)&nLen, &dwFlag, &Overlapped, NULL)
== SOCKET_ERROR)
{
TRACE("\n%d", WSAGetLastError());
if (WSAGetLastError() == WSA_IO_PENDING)
{
if (WSAWaitForMultipleEvents(1, &Event, true, WSA_INFINITE, false)
== WSA_WAIT_EVENT_0)
{
nRet = 0;
}
else
{
nRet = 1;
}
}
else
{
//CSngTpl<CErrorLog>::Instance()->SetSdkError(WSAGetLastError());
nRet = 1;
}
}
else
{
nRet = 0;
}
WSACloseEvent(Event);
return nRet;
}
else
{
WSABUF buf;
buf.buf = (char*)pcBuf;
buf.len = nLen;
DWORD dwFlag = 0; return WSARecv(m_socClient, &buf, 1, (DWORD*)&nLen, &dwFlag, pOverlapped, NULL);
}
}