模仿仿捷的《Win32多线程程序设计》这本书的例子,可工作线程一直无效。,由于客户端发送的是一结构体,该结构体的前8个字节是消息类型和后面的消息长度,我得先接收这8个字节,再觉得下一步的接收,所以我没采用投递WSARecv、WSASend那种一次全部接收的方式#include <iostream>
#include <tchar.h>
using namespace std ;#include <WinSock2.h>
#pragma comment( lib, "Ws2_32.lib" )HANDLE g_hIOCompletionPort = NULL ;
struct ContextKey
{
SOCKET socket;
OVERLAPPED olin;
char buffer[8];//用于接收消息头
OVERLAPPED olout ;
};void IssueRead(struct ContextKey *pCntx)
{
BOOL    bResult;
int     err;
int     numRead; err = recv(pCntx->socket ,pCntx->buffer ,8 ,0 ) ;
if ( err> 0)
{
cout<<pCntx->buffer ; //获得消息头
cout<<"\n";
           //...
           //再选用合适的结构体接收后面的消息内容
}
}
DWORD WINAPI WorkerThread(LPVOID lParam)
{
DWORD dwStatus = 0; DWORD  dwBytesTransfered = 0; LPOVERLAPPED pOverlapped ;
ContextKey  *pContextKey ;
while ( true )
{
BOOL bReturn = GetQueuedCompletionStatus(
g_hIOCompletionPort,
&dwBytesTransfered,
(LPDWORD)&pContextKey,
&pOverlapped,
INFINITE);
if (bReturn == FALSE && pOverlapped == NULL )
{
cout<<"GetQueuedCompletionStatus error\n";
// break ;
NULL ;
}
else 
if( bReturn == FALSE&& pOverlapped != NULL )
{
// This happens occasionally instead of
// end-of-file. Not sure why.
closesocket(pContextKey->socket);
delete pContextKey ;
cout<<"用户非正常退出\n";
// break ;
}
else
if ( dwBytesTransfered == 0 )
{
closesocket( pContextKey->socket ) ;
delete pContextKey ;
cout<<"用户正常退出\n";
// break ;
}
else
{
IssueRead(pContextKey) ;
//cout<<pContextKey->buffer ;
cout<<"\n";
}
Sleep(10);
}
return dwStatus ;
}
int main()
{ DWORD dwStatus = 0 ; WSAData WsaData ;
DWORD dwThreadID = 0 ;
SOCKET SocketServer = INVALID_SOCKET ;
SOCKET SocketAccept = INVALID_SOCKET ;
SOCKADDR_IN addrLocal={0};
SOCKADDR_IN addrRemote = {0} ;
int naddrLen=sizeof(addrRemote); BOOL bReuseAddr = TRUE ;
fd_set  fdRead ;
timeval time ;
time.tv_sec = 1 ;
time.tv_usec = 0 ;
int iReturnValue = 0 ;
int nTimeOut= 6*1000 ; do 
{
dwStatus =WSAStartup( MAKEWORD( 2, 0 ), &WsaData ) ;
if ( dwStatus != 0 )
{
dwStatus = WSAGetLastError() ;
break ;
} SocketServer = WSASocket( AF_INET ,SOCK_STREAM , IPPROTO_TCP , 0 , 0 , 0 ) ;
if ( INVALID_SOCKET == SocketServer )
{
dwStatus = WSAGetLastError() ;
break ;
}

if ( SOCKET_ERROR == setsockopt( SocketServer ,SOL_SOCKET , SO_RCVTIMEO ,(char*)&nTimeOut , sizeof(nTimeOut) ) )
{
closesocket( SocketServer ) ;
dwStatus = WSAGetLastError() ;
break ;
}

if ( SOCKET_ERROR == setsockopt( SocketServer , SOL_SOCKET , SO_REUSEADDR , (char *)&bReuseAddr , sizeof( bool ) ) )
{
closesocket( SocketServer ) ;
dwStatus = WSAGetLastError() ;
break ;
}
addrLocal.sin_family = AF_INET ;
addrLocal.sin_port = ( 9998 ) ;
addrLocal.sin_addr.S_un.S_addr =  INADDR_ANY ;//inet_addr("127.0.0.1")  

if ( SOCKET_ERROR == bind( SocketServer , ( struct sockaddr * )&addrLocal , sizeof( addrLocal ) ) )
{
closesocket( SocketServer ) ;
dwStatus = WSAGetLastError() ;
break ;
} if ( SOCKET_ERROR == listen( SocketServer , 5 ) )
{
closesocket( SocketServer ) ;
dwStatus = WSAGetLastError() ;
break ;
} DWORD nThreadID;
//Create worker threads for (int ii = 0; ii < 6; ii++)
{
CreateThread(0, 0, WorkerThread, 
(void *)(ii+1), 0, &nThreadID);
} SocketAccept = accept( SocketServer , (struct sockaddr*)&addrRemote ,&naddrLen );
if ( INVALID_SOCKET == SocketAccept )
{
closesocket( SocketServer ) ;
dwStatus = WSAGetLastError() ;
break ;
}
g_hIOCompletionPort = 
CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0 ); if ( NULL == g_hIOCompletionPort)
{
printf("\nError occurred while creating IOCP: %d.", 
WSAGetLastError());
dwStatus = WSAGetLastError() ;
break ;
} ContextKey *pContextKey = new ContextKey ;
//ZeroMemory(&pContextKey , sizeof(ContextKey) ) ;
pContextKey->socket = SocketAccept ;
pContextKey->olout.hEvent = CreateEvent( NULL ,TRUE ,FALSE , 0 ) ;
pContextKey->olout.hEvent = (HANDLE)( (DWORD)pContextKey->olout.hEvent|0x1 ) ; CreateIoCompletionPort( 
(HANDLE)pContextKey->socket ,
g_hIOCompletionPort ,
(DWORD)pContextKey ,
0) ; IssueRead(pContextKey) ;//第一次读取
}while(FALSE) ; getchar() ;
WSACleanup() ;
return 0;
}

解决方案 »

  1.   

    只有支持Overlapped的操作才有完成通知。recv/send是直接操作socket不具备Overlapped,所以不会产生通知,即使是使用了WSARecv/WSASend如果Overlapped参数传的是NULL的话,也不会产生完成通知。
      

  2.   


    typedef struct Book
    {
    char szName[64];
    int size;
    char szWorker[64];
    }Book;typedef struct BookPacket 
    {
    int type;
    BookPacket book;
    }BookPacket;typedef struct Person
    {
    char szName[64];
    int  iage;
    }Person;
    typedef struct PersonPacket 
    {
    int type;
    Person person;
    }PersonPacket;客户端会一次性发送PersonPacket或者BookPacket 这样的结构体包过来,
    我分两次来接收客户端的包
    我想先得到int type这一个字段,然后,再根据type区别出是该再接收Book大小还是Person大小的包。
    本想先投递一个int大小,然后再投递Book大小或者Person大小
      

  3.   

    typedef struct Book
    {
        char szName[64];
        int size;
        char szWorker[64];
    }Book;typedef struct BookPacket 
    {
        int type;
        Book book;
    }BookPacket;typedef struct Person
    {
        char szName[64];
        int  iage;
    }Person;
    typedef struct PersonPacket 
    {
        int type;
        Person person;
    }PersonPacket;客户端会一次性发送PersonPacket或者BookPacket 这样的结构体包过来, 
    我分两次来接收客户端的包 
    我想先得到int type这一个字段,然后,再根据type区别出是该再接收Book大小还是Person大小的包。 
    本想先投递一个int大小,然后再投递Book大小或者Person大小
      

  4.   

    呵呵,我先WSARecv投递出去,再根据type的类型recv固定大小的结构体,试验性的代码成功了,不知道在多线程的情况下怎么样
      

  5.   

    你预期收多少,WSARecv就只收多少不就好了?