完成端口实现echo tcp server

来源:互联网 发布:mac中safari下载视频 编辑:程序博客网 时间:2024/05/01 04:25
 
#include <stdio.h>
#include 
<winsock2.h>
#pragma comment(lib, "ws2_32.lib")


#define PORT 5150
#define DATA_BUFSIZE 8192
/*
 *无论何时调用重叠操作函数时,总是会通过其lpOverlapped参数传递一个OVERLAPPEDPLUS结构
 *(例如WSASend、 WSARecv等函数)。这就允许你为每一个重叠调用操作设置某些操作状态信息,
 *当操作结束后,你可以通过GetQueuedCompletionStatus()函数获得你自定义结构的指针。
 
*/
typedef 
struct  
{
    OVERLAPPED Overlapped;
    WSABUF     dataBuf;
    
char       buffer[DATA_BUFSIZE];
    
int        OpCode;//传递操作码
}PER_IO_OPERATION_DATA, *LPPER_IO_OPERATION_DATA;

#define OP_READ   0
#define OP_WRITE  1
#define OP_ACCEPT 2

//单句柄数据
typedef struct  
{
    SOCKET Socket;
}PER_HANDLE_DATA,
* LPPER_HANDLE_DATA;



DWORD WINAPI ServerWorkerThread(
void* completionPortID);

int main(int argc, char* argv[])
{
    SOCKADDR_IN serverAddr;
    SOCKET listenSocket;
    SOCKET acceptSocket;
    HANDLE completionPort;
    WSADATA wsaData;
    
if(WSAStartup(0x0202&wsaData) != 0)
    {
      printf(
"WSAStartup failed with error  ");
      
return -1;
    }
    
//建立完成端口
    if((completionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE,NULL,0,0)) == NULL)
    {
        printf( 
"CreateIoCompletionPort failed with error: %d ", GetLastError());
        
return -1;
    }

    SYSTEM_INFO systemInfo;
    GetSystemInfo(
&systemInfo);
    DWORD threadID;

    
//创建工作者线程
    for(int i = 0; i < systemInfo.dwNumberOfProcessors*2; i++)
    {
        HANDLE threadHandle;
        
if((threadHandle = CreateThread(NULL,0,ServerWorkerThread,completionPort,0,&threadID)) == NULL)
        {
            printf(
"CreateThread() failed with error %d ", GetLastError());
            
return -1;
        }
        CloseHandle(threadHandle);
    }
    

    
if ((listenSocket = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0,
      WSA_FLAG_OVERLAPPED)) 
== INVALID_SOCKET)
    {
      printf(
"WSASocket() failed with error %d ", WSAGetLastError());
      
return -1;
    } 


    serverAddr.sin_family 
= AF_INET;
    serverAddr.sin_addr.s_addr 
= htonl(INADDR_ANY);
    serverAddr.sin_port 
= htons(PORT);

    
if(bind(listenSocket,(sockaddr*)&serverAddr,sizeof(serverAddr)) == SOCKET_ERROR)
    {
        printf(
"bind() failed with error %d ", WSAGetLastError());
        
return -1;
    }

    
if (listen(listenSocket, 10== SOCKET_ERROR)
    {
      printf(
"listen() failed with error %d ", WSAGetLastError());
      
return -1;
    }
    
    
for(;;)
    {
        
        
if((acceptSocket = WSAAccept(listenSocket,NULL,NULL,NULL,0)) == SOCKET_ERROR)
        {
            printf(
"WSAAccept() failed with error %d ", WSAGetLastError());
            
return -1;
        }
    
        LPPER_HANDLE_DATA perHandleData 
= (LPPER_HANDLE_DATA)GlobalAlloc(GPTR, sizeof(PER_HANDLE_DATA));
        printf(
"socket number %d connected ",acceptSocket);
        perHandleData
->Socket = acceptSocket;

        
//新套接字句柄同完成端口关联到一起。
        
//通过完成键(CompletionKey)参数,将单句柄数据结构传递给完成端口。
        if(CreateIoCompletionPort((HANDLE)acceptSocket,completionPort,(DWORD)perHandleData,
            
0== NULL)
        {
            printf(
"CreateIoCompletionPort failed with error %d ", GetLastError());
            
return -1 ;
        }
        

        
//为重叠调用建立单I/O操作数据
        LPPER_IO_OPERATION_DATA perIoData;
        perIoData 
= (LPPER_IO_OPERATION_DATA)GlobalAlloc(GPTR,sizeof(PER_IO_OPERATION_DATA));
        memset(
&perIoData->Overlapped,0,sizeof(OVERLAPPED));
        perIoData
->dataBuf.len = DATA_BUFSIZE;
        perIoData
->dataBuf.buf = perIoData->buffer;
        perIoData
->OpCode = OP_READ; //读操作

        
/* 开始在接受套接字上处理I/O
         * 使用重叠I/O机制,在新建的套接字上投递一个或多个异步
         * WSARecv 或 WSASend请求。这些I/O请求完成后,工作者线程
         * 会为I/O请求提供服务。
         
*/ 
        DWORD recvBytes;
        DWORD flags 
= 0;
        WSARecv(acceptSocket,
                
&(perIoData->dataBuf),
                
1,
                
&recvBytes,
                
&flags,
                
&(perIoData->Overlapped),
                NULL);    
     }
    
    
return 0;
}


DWORD WINAPI ServerWorkerThread(
void* completionPortID)
{
    HANDLE completionPort 
= (HANDLE)completionPortID;
    DWORD bytesTransferred;
    
//LPOVERLAPPED Overlapped;
    LPPER_HANDLE_DATA perHandleData;
    LPPER_IO_OPERATION_DATA perIoData;
    
while(true)
    {
        
if(GetQueuedCompletionStatus(completionPort,&bytesTransferred,
            (LPDWORD)
&perHandleData,(LPOVERLAPPED*)&perIoData,INFINITE) == 0)
        {
            printf(
"GetQueuedCompletionStatus failed with error %d ", GetLastError());
            
return 0;
        }
        
if(bytesTransferred == 0)
        {
            printf(
"Closing socket %d ",perHandleData->Socket);
            closesocket(perHandleData
->Socket);
            GlobalFree(perHandleData);
            GlobalFree(perIoData);
            
continue;
        }

        
switch (perIoData->OpCode)    
        {   
        
case OP_ACCEPT:       
                
break;                   
        
case OP_WRITE://写完成        
             {            
                
// 为下一个重叠调用建立单I/O操作数据
                memset(&perIoData->Overlapped,0,sizeof(OVERLAPPED));
                perIoData
->dataBuf.len = DATA_BUFSIZE;
                perIoData
->dataBuf.buf = perIoData->buffer;
                perIoData
->OpCode = OP_READ; //读操作        
                DWORD recvBytes;
                DWORD flags 
= 0;
                WSARecv(perHandleData
->Socket,
                    
&(perIoData->dataBuf),
                    
1,
                    
&recvBytes,
                    
&flags,
                    
&(perIoData->Overlapped),
                    NULL);    
                
break;
            } 
        
case OP_READ://读完成
            {    
                printf(
"recive socket %d :%s ",perHandleData->Socket,perIoData->buffer);
                
//为下一个重叠调用建立单I/O操作数据
                
//将接收到的数据echo 回去
                memset(&perIoData->Overlapped,0,sizeof(OVERLAPPED));
                perIoData
->dataBuf.buf = perIoData->buffer;
                perIoData
->dataBuf.len = bytesTransferred;                
                perIoData
->OpCode = OP_WRITE; //写操作        
                DWORD bytesSend;
                DWORD flags 
= 0;                            
                WSASend(perHandleData
->Socket,&(perIoData->dataBuf),1,&bytesSend,
                    
0,&(perIoData->Overlapped),0);                
                
break;
            }        

        }
    }
    
}
原创粉丝点击