io完成端口发送数据问题!
我做了个服务端使用了IO完成端口 用了WSARecv等函数
写了个测试程序 循环20万次向服务的发送数据 数据大小为4096字节
服务端也是每次接收4096
客户端同时在本机运行测试 但是测试中时服务端有时候收到数据内容为空啊
我明明是向服务端发送了20万次的数据 但是有时候数据为空的时候服务端打印出的接收次数为20多万次
我搞不明白了
[最优解释]
发送4096很容易,但要接收4096,就不一定了。
我遇到一些人,包括一些很有经验的人,认为recv时,指定了一个长度,这个长度就是接收数据的长度,其实这个长度指的是缓存的长度,至于接收多少,要看数据有多少,一般都会小于这个长度,但不会大。
我不知道你有没有也犯这种错误,没有代码,我只能猜测。
当然,要让recv非要读取多少字节,也行,那就是指定MSG_WAITALL参数,但很少用,这个参数意义并不大。特别是包长不定的情况下。
[其他解释]
你每次Send的返回值都检测了?
把返回值拿来看看,异常用GetLastError打印看看
[其他解释]
while(TRUE)
{
if ((Accept = WSAAccept(Listen, NULL, NULL, NULL, 0)) == SOCKET_ERROR)
{
printf("WSAAccept() failed with error %d\n", WSAGetLastError());
return;
}
if ((PerHandleData = (LPPER_HANDLE_DATA) GlobalAlloc(GPTR,
sizeof(PER_HANDLE_DATA))) == NULL)
{
printf("GlobalAlloc() failed with error %d\n", GetLastError());
return;
}
printf("Socket number %d connected\n", Accept);
PerHandleData->Socket = Accept;
if (CreateIoCompletionPort((HANDLE) Accept, CompletionPort, (DWORD) PerHandleData,
0) == NULL)
{
printf("CreateIoCompletionPort failed with error %d\n", GetLastError());
return;
}
if ((PerIoData = (LPPER_IO_OPERATION_DATA) GlobalAlloc(GPTR, sizeof(PER_IO_OPERATION_DATA))) == NULL)
{
printf("GlobalAlloc() failed with error %d\n", GetLastError());
return;
}
Flags = 0;
WSABUF wBuf;
wBuf.buf = PerIoData->buf;
wBuf.len = DATA_BUFSIZE;
if (WSARecv(Accept, &wBuf, 1, &RecvBytes, &Flags,
&(PerIoData->Overlapped), NULL) == SOCKET_ERROR)
{
if (WSAGetLastError() != ERROR_IO_PENDING)
{
printf("WSARecv() failed with error %d\n", WSAGetLastError());
return;
}
}
Flags = WSAGetLastError();
DWORD WINAPI ServerWorkerThread(LPVOID CompletionPortID)
{
HANDLE CompletionPort = (HANDLE) CompletionPortID;
DWORD BytesTransferred;
LPOVERLAPPED Overlapped;
LPPER_HANDLE_DATA PerHandleData;
LPPER_IO_OPERATION_DATA PerIoData;
DWORD SendBytes, RecvBytes;
DWORD Flags;
printf("ServerWorkerThread %d start \n", GetCurrentThreadId());
while(TRUE)
{
if (GetQueuedCompletionStatus(CompletionPort, &BytesTransferred,
(LPDWORD)&PerHandleData, (LPOVERLAPPED *) &PerIoData, INFINITE) == 0)
{
MessageBox(NULL, "GetQueuedCompletionStatus error", "", MB_OK);
printf("GetQueuedCompletionStatus failed with error %d\n", GetLastError());
if (closesocket(PerHandleData->Socket) == SOCKET_ERROR)
{
printf("closesocket() failed with error %d\n", WSAGetLastError());
continue;
}
GlobalFree(PerHandleData);
GlobalFree(PerIoData);
continue;
}
if (BytesTransferred == 0)
{
MessageBox(NULL, "BytesTransferred 0", "", MB_OK);
if (closesocket(PerHandleData->Socket) == SOCKET_ERROR)
continue;
GlobalFree(PerHandleData);
GlobalFree(PerIoData);
continue;
}
RecvCount++;
if (strcmp(PerIoData->buf, "") == 0)
MessageBox(NULL, "buf 0", "", MB_OK);
printf("byte: %d count: %d %s\n", BytesTransferred, RecvCount, PerIoData->buf);
GlobalFree(PerIoData);
LPPER_IO_OPERATION_DATA tPerIoData;
if ((tPerIoData = (LPPER_IO_OPERATION_DATA) GlobalAlloc(GPTR, sizeof(PER_IO_OPERATION_DATA))) == NULL)
{
printf("GlobalAlloc() failed with error %d\n", GetLastError());
continue;
}
DWORD Flags = 0;
DWORD RecvBytes;
WSABUF wBuf;
wBuf.buf = tPerIoData->buf;
wBuf.len = DATA_BUFSIZE;
if (WSARecv(PerHandleData->Socket, &wBuf, 1, &RecvBytes, &Flags,
&(tPerIoData->Overlapped), NULL) == SOCKET_ERROR)
{
if (WSAGetLastError() != ERROR_IO_PENDING)
{
printf("WSARecv() failed with error %d\n", WSAGetLastError());
return 0;
}
}
}
}
char buffer[4096]="0hello! It is me";
int i = 0;
while (i< 2000000)
{
send(s,buffer,sizeof(buffer),0);
i++;
}
[其他解释]
BytesTransferred 为4096 就是接收到了4096字节, 这不会错的.
看了下你的代码 PER_IO_OPERATION_DATA 结构体没有贴出来, 所以不知道
PER_IO_OPERATION_DATA中 buf是不是数组以及buf的大小.
每次调用 WSARecv 之前,先把 tPerIoData 字段 全部设为0.
tPerIoData 里面的值是会影响IOCP行为的.
[其他解释]
我的意思是,你每次认为BytesTransferred行于4096,才会写出你上面的代码。如果你不做这个假设,那么你就不会写出这样的代码,也不会为出现所谓的怪异行为而感觉奇怪了。
真实情况是,每次的BytesTransferred都不一定等于4096,可能小,也可能大(如果你提供的buffer大于4096的话),也就是出现了分包与粘包现象。
这些现象一但发生,非常有可能让程序执行到
MessageBox(NULL, "buf 0", "", MB_OK);
你这句代码strcmp(PerIoData->buf, "") == 0的意思,是判断字符串是不是为空。而你发送的数据是char buffer[4096]="0hello! It is me";除了前面10来个字符,后面的字符都是\0,所以只要从me后面的任意字符分包,都会执行到MessageBox(NULL, "buf 0", "", MB_OK);
[其他解释]
不要想当然,判断一下你接收的数据量就可以了!
[其他解释]
学习一下哈
[其他解释]
服务端
while(TRUE)
{
if (GetQueuedCompletionStatus(CompletionPort, &BytesTransferred,
(LPDWORD)&PerHandleData, (LPOVERLAPPED *) &PerIoData, INFINITE) == 0)
{
MessageBox(NULL, "GetQueuedCompletionStatus error", "", MB_OK);
printf("GetQueuedCompletionStatus failed with error %d\n", GetLastError());
if (closesocket(PerHandleData->Socket) == SOCKET_ERROR)
{
printf("closesocket() failed with error %d\n", WSAGetLastError());
continue;
}
GlobalFree(PerHandleData);
free(PerIoData);
continue;
}
if (BytesTransferred == 0)
{
printf("接收=%d 完整包=%d\n",nCount,nComplete);
if (closesocket(PerHandleData->Socket) == SOCKET_ERROR)
continue;
GlobalFree(PerHandleData);
free(PerIoData);
continue;
}
LPPER_IO_OPERATION_DATA tPerIoData;
tPerIoData = (LPPER_IO_OPERATION_DATA)malloc(sizeof(PER_IO_OPERATION_DATA));
if (tPerIoData == NULL)
{
printf("GlobalAlloc() failed with error %d\n", GetLastError());
continue;
}
ZeroMemory(tPerIoData, sizeof(PER_IO_OPERATION_DATA));
WSABUF wBuf;
if ( PerIoData->Length + BytesTransferred == DATA_BUFSIZE)
{
if (PerIoData->buf[0] == 'a' &&
PerIoData->buf[2000] == 'B' &&
PerIoData->buf[4095] == 'C')
{
nComplete++;
}
wBuf.buf = (char*)&(tPerIoData->buf);
wBuf.len = DATA_BUFSIZE;
nCount ++;
}
else
{
tPerIoData->Length += BytesTransferred;
strncpy(tPerIoData->buf,
PerIoData->buf,
tPerIoData->Length);
wBuf.buf = (char*)&(tPerIoData->buf) + tPerIoData->Length;
wBuf.len = DATA_BUFSIZE - tPerIoData->Length;
}
free(PerIoData);
DWORD Flags = 0;
DWORD RecvBytes;
if (WSARecv(PerHandleData->Socket, &wBuf, 1, &RecvBytes, &Flags,
&(tPerIoData->Overlapped), NULL) == SOCKET_ERROR)
{
if (WSAGetLastError() != ERROR_IO_PENDING)
{
printf("WSARecv() failed with error %d\n", WSAGetLastError());
return 0;
}
}
}
//########################################################
客户端
char buffer[4096] ;
ZeroMemory(buffer, 4096);
buffer[0] = 'a';
buffer[2000] = 'B';
buffer[4095] = 'C';
int i = 0;
while (i< 2000000)
{
send(s,buffer,4096,0);
i++;
}
#define DATA_BUFSIZE 4096
typedef struct
{
OVERLAPPED Overlapped;
CHAR buf[DATA_BUFSIZE];
ULONG Length;
} PER_IO_OPERATION_DATA, * LPPER_IO_OPERATION_DATA;
但是重用一个PerIoData也会出错啊
这是哪里出错了呢?还是逻辑上有问题啊
麻烦告知下 谢谢!
[其他解释]
我就纳闷了 我重写了代码 没重复分配释放内存了 现在测试ok了
另外我上面还有个疑问想麻烦你告知下 谢谢!
我想问下如果客户端发送4096字节时分2次发送给服务端 第一次发送3000字节 第二次发送余下的1096字节
这2步是紧跟着的吗?
我在服务端接收到第一次的3000字节后 只要接下来有1096字节收到时 就认为这是此数据包余下的1096字节吗?
[其他解释]
代码难看并不一定就是错的,两回事。
所有的都别动,照我15楼改了,还出错?
另外我想问一下,你确定你的代码可以接收到数据,并且有一部分正确?
如果是,那我就集中看你的逻辑,并且默认你的socket api调用都是正确的。因为好些代码,比如你自己的一些结构,你没有贴出来,我只能假设它是正确的。
[其他解释]