完成端口(I/O completion),哪里出错了?

xue23 2005-08-10 04:53:26
是不是不能用send()和完成端口工作?
大家看看我的代码
服务器端:
HANDLE hEvent = NULL;
HANDLE hIOCP = NULL;
SOCKET sockListen = INVALID_SOCKET;
UINT hListen;
UINT hRead;

UINT __stdcall ListenThread(void * p)
{
int nLen;
int nRet;
DWORD dwRet;
SOCKADDR_IN sa;
SOCKET csocket;
while(1)
{
Sleep(10);
dwRet = WSAWaitForMultipleEvents(1, &hEvent, FALSE, 100, FALSE);
if(dwRet == WSA_WAIT_TIMEOUT)
continue;
WSANETWORKEVENTS events;
nRet = WSAEnumNetworkEvents(sockListen, hEvent, &events);
if(nRet == SOCKET_ERROR)
{
break;
}
if(events.lNetworkEvents & FD_ACCEPT)
{
if(events.iErrorCode[FD_ACCEPT_BIT] == 0)
{
nLen = sizeof(sa);
csocket = WSAAccept(sockListen, (sockaddr*)&sa, &nLen, 0, 0);

Sleep(10);
if(csocket == SOCKET_ERROR)
continue;

//OVERLAPPED *pOverLapped = new OVERLAPPED;

HANDLE h = CreateIoCompletionPort((HANDLE) csocket, hIOCP, (DWORD)csocket, 0);
if(h != hIOCP)
continue;
// --- DavidTr: Slide 16 -------------------------------------------
//
// Disable send bufferring on the socket. Setting SO_SNDBUF
// to 0 causes winsock to stop bufferring sends and perform
// sends directly from our buffers, thereby reducing CPU
// usage.
//

int zero = 0;
nRet = setsockopt( csocket, SOL_SOCKET, SO_SNDBUF, (char *)&zero, sizeof(zero) );
if ( nRet == SOCKET_ERROR )
{
break;
}

}
else
{
break;
}
}
}
return 0;
}

UINT __stdcall ReadThread(void * p)
{

DWORD numberOfBytes;
DWORD completionKey;
OVERLAPPED ol;
LPOVERLAPPED lpOverlapped = &ol;
BOOL bIORet;

while(1)
{
Sleep(10);
bIORet = GetQueuedCompletionStatus(hIOCP, &numberOfBytes, &completionKey, &lpOverlapped, INFINITE);
DWORD dwIOError = GetLastError();
if(!bIORet && dwIOError != WAIT_TIMEOUT)
{
break;
}

}
return 0;
}
int CreateSocket()
{

sockListen = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_IP, NULL, NULL, WSA_FLAG_OVERLAPPED);
if(sockListen == INVALID_SOCKET)
{
return WSAGetLastError();
}
hEvent = WSACreateEvent();
if(hEvent == WSA_INVALID_EVENT)
{
return WSAGetLastError();
}
int nRet = WSAEventSelect(sockListen, hEvent, FD_ACCEPT);
if(nRet == SOCKET_ERROR)
{
return WSAGetLastError();
}
SOCKADDR_IN sa;
sa.sin_family = AF_INET;
sa.sin_addr.S_un.S_addr = INADDR_ANY;
sa.sin_port = htons(8886);

nRet = bind(sockListen, (sockaddr*)&sa, sizeof(sa));
if(nRet == SOCKET_ERROR)
{
return WSAGetLastError();
}

nRet =listen(sockListen, 5);
if(nRet == SOCKET_ERROR)
{
return WSAGetLastError();
}
return 0;
}

int CreateIOCP()
{
// SOCKET sock = socket(AF_INET, SOCK_STREAM, 0);
// if(sock == INVALID_SOCKET)
// return -1;
// SYSTEM_INFO si;
// ::GetSystemInfo(& si);
// DWORD dwNumberOfWorkers = 2 * si.dwNumberOfProcessors + 2;

hIOCP = CreateIoCompletionPort((HANDLE)INVALID_HANDLE_VALUE, NULL, 0, 0);
if(!hIOCP)
{
return -2;
}
// closesocket(sock);
return 0;
}

int main(int argc, char* argv[])
{
WORD rver = MAKEWORD(2, 2);
WSADATA wsaData;
if(0 != WSAStartup(rver, &wsaData))
return -1;
if(HIBYTE(wsaData.wVersion) !=2 || HIBYTE(wsaData.wVersion != 2))
return -1;

if(0 != CreateIOCP())
return -2;
if(0 != CreateSocket())
return -3;

UINT id;
hRead = _beginthreadex(NULL, 0, ReadThread, NULL, 0, &id);
hListen = _beginthreadex(NULL, 0, ListenThread, NULL, 0, &id);

::WaitForSingleObject((HANDLE)hListen, INFINITE);

return 0;
}
客户端:
int main(int argc, char* argv[])
{
WSADATA wsaData;
WORD rVersion;
int nRet;
rVersion = MAKEWORD(2, 2);
nRet = WSAStartup(rVersion, &wsaData);
if(nRet != 0)
return -1;
if(HIBYTE(wsaData.wVersion) != 2 || LOBYTE(wsaData.wVersion) != 2)
return -1;

SOCKET s = socket(AF_INET, SOCK_STREAM, 0);
if(s == INVALID_SOCKET)
return -1;

SOCKADDR_IN sa;
sa.sin_family = AF_INET;
sa.sin_addr.s_addr = inet_addr("192.168.0.75");
sa.sin_port = htons(8886);

nRet = connect(s, (sockaddr*)&sa, sizeof(sa));
if(nRet != 0)
return -1;

char *buffer = "this is a test!";
while(1)
{
int nSend = send(s, buffer, strlen(buffer), 0);
if(nSend == 0)
{
break;
}
else if(nSend < 0)
{
DWORD err = WSAGetLastError();
break;
}
Sleep(1000);
}
return 0;
}
...全文
343 点赞 收藏 17
写回复
17 条回复
切换为时间正序
当前发帖距今超过3年,不再开放新的回复
发表回复
dragonyjd 2005-08-18
楼主还不结贴呀?等到啥时呀?
回复
xue23 2005-08-18
是不是下蛋的鸡。这里的关健地方是那个WSARecv,它为完成端口的读写作(资源上的)准备。
回复
gohappy_1999 2005-08-13
mark
回复
dragonyjd 2005-08-13
to:Rainday_Ye
把我发给你的服务器代码中send那句去掉(在cout << "receive:" << completionKey->Buffer<<endl;后)。那句是我调试其它客户端用的。
回复
dragonyjd 2005-08-13
to:Rainday_Ye
好了,不用了。经测试是我的系统问题。在别人机器上测试成功(socket号相同可以连续连接并发送消息)。我就郁闷了,为什么我的系统会有这样的问题?
回复
dragonyjd 2005-08-13
555~~~~~代码都一样呀,为什么我的不好用?#_#

我发信到你邮箱了,把我的测试代码发给你,你测试一下,看看是否好用。再给我发一份你的测试代码。看看是不是我机器的问题。555郁闷。
回复
dragonyjd 2005-08-13
呵呵,不是那个问题。我上面已经说了“把代码中send那句去掉”。那句是我调试别的程序的。没用。你删除以后再试一下。另外加我的QQ:24293030在线交流一下。
回复
Rainday_Ye 2005-08-13
发现错误.邮件已经发送.你看看.
回复
dragonyjd 2005-08-12
继续UP一下。
回复
Rainday_Ye 2005-08-12
我这里是可以的.
我的步骤:
在上面的那个代码基础上加上
else if(numberOfBytes == 0)
{
closesocket(completionKey->s);
delete completionKey;
completionKey=NULL;
}

在客户端里修改socket只发送一次消息,然后把那个socket关掉.
运行两次客户端.显示socket号一样.但是发送消息成功.

我的邮箱是rainday_ye@eyou.com
发信我给你代码
回复
dragonyjd 2005-08-11
Rainday_Ye(QQ:325110947)的代码我测试了一下,发现了一个问题。当一个客户端连续二次连接服务器后,便无法发送消息了。GetQueuedCompletionStatus也无响应。

服务器开启后,客户端连接到服务器之后再用closesocket关闭与服务器的连接。此时服务器的ReadThread线程添加如入代码关闭客户端的连接。
else if(numberOfBytes == 0)
{
closesocket(completionKey->s);
delete completionKey;
completionKey=NULL;
}
(在此期间先不让别的客户端连接服务器)之后再用此客户端继续连接服务器。这时发现,accept接收到的socket号与上一次accept接收到的socket号一致。代码继续运行到CreateIoCompletionPort处,此时由于前一次对socket号进行了一次绑定。此时再对相同的socket号再次绑定。之后客户端开始发送消息,服务器的GetQueuedCompletionStatus就无法接收了(还是继续处于阻塞状态)。

这个问题如何解决?是否有关闭完成端口,由CreateIoCompletionPort创建的函数?
回复
xue23 2005-08-11
谢谢,楼上的兄弟。我昨天晚上回家也是添加了一个WSARecv(),也可以工作了。搞到2点钟,才睡觉。太晚了,大脑一时休息不下来,差不多到3点钟才睡觉。不过我仍然感谢rainday兄,我会棒上大部分分数给你。大家不要像我这样呀。到年龄大的时候肯定身体各个地方都疼呀。
回复
Rainday_Ye 2005-08-10
我改的方式是根据楼主提供的代码而修改的.
楼主的异步选择accept以后,没有为那个socket调用WSARecv();
没有调用哪里来的读取,所以我加了一个结构.里面放的就是和这个socket
相关的数据.就是后面的completionKey;
在你收到数据处理以后.为那个socket再发送一个WSARecv请求.
不过后面处理的函数里面还不是很完善.比如传输字节为0是要关闭.
呵呵.我也是这个月刚刚看的io模型.如果有错.请指教.
回复
Rainday_Ye 2005-08-10
哈哈.我改出来了.楼主给分吧.嘻嘻.

#include <iostream>
#include <winsock2.h>
#include <process.h>
using namespace std;

HANDLE hEvent = NULL;
HANDLE hIOCP = NULL;
SOCKET sockListen = INVALID_SOCKET;
UINT hListen;
UINT hRead;
typedef struct _RECVDATA
{
WSAOVERLAPPED Overlapped;
SOCKET s;
CHAR Buffer[1024];
WSABUF DataBuf;
DWORD BytesRECV;
}RECVDATA, *LPRECVDATA;
UINT __stdcall ListenThread(void * p)
{
int nLen;
int nRet;
DWORD dwRet;
SOCKADDR_IN sa;
SOCKET csocket;
LPRECVDATA pRecv;
DWORD nFlag;
while(1)
{
Sleep(10);
dwRet = WSAWaitForMultipleEvents(1, &hEvent, FALSE, 100, FALSE);
if(dwRet == WSA_WAIT_TIMEOUT)
continue;
WSANETWORKEVENTS events;
nRet = WSAEnumNetworkEvents(sockListen, hEvent, &events);
if(nRet == SOCKET_ERROR)
{
break;
}
if(events.lNetworkEvents & FD_ACCEPT)
{
if(events.iErrorCode[FD_ACCEPT_BIT] == 0)
{
nLen = sizeof(sa);
csocket = WSAAccept(sockListen, (sockaddr*)&sa, &nLen, 0, 0);

Sleep(10);
if(csocket == SOCKET_ERROR)
continue;

//OVERLAPPED *pOverLapped = new OVERLAPPED;

// --- DavidTr: Slide 16 -------------------------------------------
//
// Disable send bufferring on the socket. Setting SO_SNDBUF
// to 0 causes winsock to stop bufferring sends and perform
// sends directly from our buffers, thereby reducing CPU
// usage.
//

int zero = 0;
nRet = setsockopt( csocket, SOL_SOCKET, SO_SNDBUF, (char *)&zero, sizeof(zero) );
if ( nRet == SOCKET_ERROR )
{
break;
}
nFlag = 0;
pRecv = (LPRECVDATA)GlobalAlloc(GPTR, sizeof(RECVDATA));
pRecv->DataBuf.buf = pRecv->Buffer;
pRecv->DataBuf.len = 1024;
pRecv->BytesRECV = 0;
pRecv->s = csocket;
ZeroMemory(&pRecv->Overlapped, sizeof(OVERLAPPED));
HANDLE h = CreateIoCompletionPort((HANDLE) csocket, hIOCP, (DWORD)pRecv, 0);
if(h != hIOCP)
continue;
WSARecv(pRecv->s, &pRecv->DataBuf, 1, &pRecv->BytesRECV, &nFlag, &pRecv->Overlapped, NULL);
}
else
{
break;
}
}
}
return 0;
}

UINT __stdcall ReadThread(void * p)
{

DWORD numberOfBytes;
LPRECVDATA completionKey;
LPOVERLAPPED lpOverlapped;
BOOL bIORet;
DWORD nFlag;

while(1)
{
Sleep(10);
bIORet = GetQueuedCompletionStatus(hIOCP, &numberOfBytes, (LPDWORD)&completionKey, &lpOverlapped, INFINITE);
DWORD dwIOError = GetLastError();
if(!bIORet && dwIOError != WAIT_TIMEOUT)
{
break;
}
if(numberOfBytes > 0)
{
cout << "receive:" << completionKey->Buffer << endl;
nFlag = 0;
completionKey->DataBuf.buf = completionKey->Buffer;
completionKey->DataBuf.len = 1024;
completionKey->BytesRECV = 0;
ZeroMemory(&completionKey->Overlapped, sizeof(OVERLAPPED));
WSARecv(completionKey->s, &completionKey->DataBuf, 1,
&completionKey->BytesRECV, &nFlag, &completionKey->Overlapped, NULL);
}

}
return 0;
}
int CreateSocket()
{

sockListen = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_IP, NULL, NULL, WSA_FLAG_OVERLAPPED);
if(sockListen == INVALID_SOCKET)
{
return WSAGetLastError();
}
hEvent = WSACreateEvent();
if(hEvent == WSA_INVALID_EVENT)
{
return WSAGetLastError();
}
int nRet = WSAEventSelect(sockListen, hEvent, FD_ACCEPT);
if(nRet == SOCKET_ERROR)
{
return WSAGetLastError();
}
SOCKADDR_IN sa;
sa.sin_family = AF_INET;
sa.sin_addr.S_un.S_addr = INADDR_ANY;
sa.sin_port = htons(6000);

nRet = bind(sockListen, (sockaddr*)&sa, sizeof(sa));
if(nRet == SOCKET_ERROR)
{
return WSAGetLastError();
}

nRet =listen(sockListen, 5);
if(nRet == SOCKET_ERROR)
{
return WSAGetLastError();
}
return 0;
}

int CreateIOCP()
{
// SOCKET sock = socket(AF_INET, SOCK_STREAM, 0);
// if(sock == INVALID_SOCKET)
// return -1;
// SYSTEM_INFO si;
// ::GetSystemInfo(& si);
// DWORD dwNumberOfWorkers = 2 * si.dwNumberOfProcessors + 2;

hIOCP = CreateIoCompletionPort((HANDLE)INVALID_HANDLE_VALUE, NULL, 0, 0);
if(!hIOCP)
{
return -2;
}
// closesocket(sock);
return 0;
}

int main(int argc, char* argv[])
{
WORD rver = MAKEWORD(2, 2);
WSADATA wsaData;
if(0 != WSAStartup(rver, &wsaData))
return -1;
if(HIBYTE(wsaData.wVersion) !=2 || HIBYTE(wsaData.wVersion != 2))
return -1;

if(0 != CreateIOCP())
{
cout << "createiocp error";
return -2;
}
if(0 != CreateSocket())
return -3;

UINT id;
hListen = _beginthreadex(NULL, 0, ListenThread, NULL, 0, &id);
hRead = _beginthreadex(NULL, 0, ReadThread, NULL, 0, &id);

::WaitForSingleObject((HANDLE)hListen, INFINITE);

return 0;
}
回复
lifengice0706 2005-08-10
iocp是典型的异步方式,不能用send,要用wsasend()。参数见msdn。没时间仔细看你的代码,就题论题!见谅!
回复
xue23 2005-08-10
有谁能找出以让代码中的问题,把分给他。
回复
DentistryDoctor 2005-08-10
你看看PlatformSDK的IOCPServer吧。

回复
发帖
网络编程
创建于2007-09-28

1.8w+

社区成员

VC/MFC 网络编程
申请成为版主
帖子事件
创建了帖子
2005-08-10 04:53
社区公告
暂无公告