游戏开发论坛

 找回密码
 立即注册
搜索
查看: 3263|回复: 7

一起来分析一下最简单的完成端口代码

[复制链接]

6

主题

14

帖子

14

积分

新手上路

Rank: 1

积分
14
发表于 2005-1-20 20:13:00 | 显示全部楼层 |阅读模式
#include <winsock2.h>
#include <windows.h>
#include <stdio.h>
#include <assert.h>

#include "Socket5.h"

void main(void)
{
        //变量声明
        WSADATA wsaData;
        DWORD Ret;
        HANDLE CompletionPort;
        SYSTEM_INFO SystemInfo;
        DWORD i;
        DWORD ThreadID;
        SOCKET Listen;
        SOCKADDR_IN InternetAddr;
        SOCKET Accept;
        LPPER_HANDLE_DATA PerHandleData;
        LPPER_IO_OPERATION_DATA PerIoData;
        DWORD Flags;
        DWORD RecvBytes;
       
       
        //初始化WinSock2
        if ((Ret = WSAStartup(0x0202, &wsaData)) != 0)
        {
                printf("WSAStartup failed with error %u\n", Ret);
                return;
        }
        //创建完成端口对象
        if((CompletionPort=CreateIoCompletionPort(INVALID_HANDLE_VALUE,NULL,0,0))==NULL)
        {
                printf( "CreateIoCompletionPort failed with error: %u\n", GetLastError());
                return;
        }
        //判断CPU数量
        GetSystemInfo(&SystemInfo);
        printf("您的机器有%d个CPU\n",SystemInfo.dwNumberOfProcessors);
        //为每个CPU建立一个工作线程
        for(i=0;i<SystemInfo.dwNumberOfProcessors;i++)
        {
                HANDLE ThreadHandle;
                if((ThreadHandle=CreateThread(NULL,0,ServerWorkerThread,CompletionPort,0,&ThreadID)) == NULL)
                {
                        printf("CreateThread() failed with error %u\n", GetLastError());
                        return;
                }
                CloseHandle(ThreadHandle);
        }
        //创建一个Socket用于监听服务端口
        if((Listen=WSASocket(AF_INET,SOCK_STREAM,0,NULL,0,WSA_FLAG_OVERLAPPED))==INVALID_SOCKET)
        {
                printf("WSASocket() failed with error %d\n", WSAGetLastError());
                return;
        }
        //绑定监听端口
        InternetAddr.sin_family=AF_INET;
        InternetAddr.sin_addr.s_addr=htonl(INADDR_ANY);
        InternetAddr.sin_port = htons(SOCKS_PORT);
        if(bind(Listen,(PSOCKADDR)&InternetAddr,sizeof(InternetAddr))==SOCKET_ERROR)
        {
                printf("bind() failed with error %d\n", WSAGetLastError());
                return;
        }
        //监听
        if(listen(Listen,5)==SOCKET_ERROR)
        {
                printf("listen() failed with error %d\n", WSAGetLastError());
                return;
        }
       
        printf("Server started at port : %d\n",SOCKS_PORT);
       
        //接受每一个连接并将其关联到完成端口上
        while(TRUE)
        {
                //接受连接
                if((Accept=WSAAccept(Listen,NULL,NULL,NULL,0))==SOCKET_ERROR)
                {
                        printf("WSAAccept() failed with error %d\n", WSAGetLastError());
                        return;
                }
                printf(" Client Socket number %d connected\n", Accept);
               
                //创建包含接受的Socket信息的单句柄数据结构体
                if((PerHandleData=(LPPER_HANDLE_DATA)GlobalAlloc(GPTR,sizeof(PER_HANDLE_DATA)))==NULL)
                {
                        printf("GlobalAlloc() failed with error %u\n", GetLastError());
                        return;
                }
               
                //将Accept关联到完成端口
               
                if(CreateIoCompletionPort((HANDLE)Accept,CompletionPort,(DWORD)PerHandleData,0)==NULL)
                {
                        printf("CreateIoCompletionPort failed with error %u\n", GetLastError());
                        return;
                }
                //创建单I/O操作数据
                if((PerIoData=(LPPER_IO_OPERATION_DATA)GlobalAlloc(GPTR,sizeof(PER_IO_OPERATION_DATA)))==NULL)
                {
                        printf("GlobalAlloc() failed with error %u\n", GetLastError());
                        return;
                }
               
                PerHandleData->self=Accept;
                PerHandleData->isClient=TRUE;
                PerHandleData->SelfPerHandleData=PerHandleData;
                PerHandleData->SelfPerIoData=PerIoData;
               
                //接收客户端的版本信息
                ZeroMemory(&(PerIoData->Overlapped), sizeof(OVERLAPPED));
                PerIoData->Operation=OP_READ;
                PerIoData->DataBuf.len = DATA_BUFSIZE;
                PerIoData->DataBuf.buf = PerIoData->Buffer;
               
                Flags = 0;
                if((WSARecv(Accept,&(PerIoData->DataBuf),1,&RecvBytes,&Flags,&(PerIoData->Overlapped),NULL))==SOCKET_ERROR)
                {
                        if (WSAGetLastError() != ERROR_IO_PENDING)
                        {
                                printf("WSARecv() failed with error %d\n", WSAGetLastError());
                                return;
                        }
                }
        }
}

DWORD WINAPI ServerWorkerThread(LPVOID CompletionPortID)
{
        HANDLE CompletionPort=(HANDLE)CompletionPortID;
        DWORD BytesTransferred;
        LPPER_HANDLE_DATA PerHandleData;
        LPPER_IO_OPERATION_DATA PerIoData;
          DWORD Flags;
        DWORD RecvBytes;       
       
        while(TRUE)
        {
                //使用GetQueuedCompletionStatus查询
                if(GetQueuedCompletionStatus(CompletionPort,&BytesTransferred,(LPDWORD)&amperHandleData,(LPOVERLAPPED *)&PerIoData,INFINITE)==0)
                {
                        int iError=GetLastError();
                        printf("GetQueuedCompletionStatus failed with error %u\n", iError);
                        if(iError==64)
                        {
                                continue;
                        }
                        else
                        {
                                printf("return 0\n");
                                return 0;
                        }
                }
                if(BytesTransferred==0)
                {
                        //远端断开连接,关闭本机SOCKET
                        printf("Closing ");
                        if(PerHandleData->isClient==TRUE)
                                printf("Client");
                        else
                                printf("Dest");
                        printf(" socket %u\n", PerHandleData->self);
                       
                        closesocket(PerHandleData->self);
                       
                        //在此关闭和此SOCKET相关联的SOCKET
                        printf("Closing ");
                        if(PerHandleData->isClient==TRUE)
                                printf("Dest");
                        else
                                printf("Client");
                        printf(" socket %u\n", PerHandleData->other);
                        closesocket(PerHandleData->other);

                        GlobalFree(PerHandleData->OtherPerHandleData->SelfPerIoData);
                        GlobalFree(PerHandleData->OtherPerHandleData);

                        GlobalFree(PerHandleData);
                        GlobalFree(PerIoData);
                        continue;
                }
               
                switch(PerIoData->Operation)
                {
                case OP_READ:
       
       WSARecv(PerHandleData->self,&PerHandleData->SelfPerIoData->DataBuf ,1,&RecvBytes,&Flags,&(PerHandleData->SelfPerIoData->Overlapped),NULL);
           printf("%s\n",PerHandleData->SelfPerIoData->DataBuf.buf);  //显示收到的数据
                                break;

                case OP_WRITE:
                        printf("write...");
                        break;

                case OP_ACCEPT:
                        printf("accept...");
                        break;
                }//end of switch
               
        }//end of while
        return 0;
}//end of function

为什么我客户连接好以后 只能显示第一次发送的数据。。。以后发送数据就显示不出来了????

6

主题

14

帖子

14

积分

新手上路

Rank: 1

积分
14
 楼主| 发表于 2005-1-20 20:20:00 | 显示全部楼层

Re:一起来分析一下最简单的完成端口代码

各位。。。问题自己解决了。。。是由于我在
case OP_READ:
      Flags = 0;  //这里忘了把Flag=0了。。。。,太粗心了。。。。
       WSARecv(PerHandleData->self,&amperHandleData->SelfPerIoData->DataBuf ,1,&RecvBytes,&Flags,&(PerHandleData->SelfPerIoData->Overlapped),NULL);

6

主题

14

帖子

14

积分

新手上路

Rank: 1

积分
14
 楼主| 发表于 2005-1-20 20:24:00 | 显示全部楼层

Re:一起来分析一下最简单的完成端口代码

顺便再问一下,这里面的OP_READ:的值一定要弄0吗???是不是内部射定,只要是0那就说明是消息到了???那我这里的case OP_ACCEPT是不是多余了?因为我已经在主现成里面有了一个
//接受连接
if((Accept=WSAAccept(Listen,NULL,NULL,NULL,0))==SOCKET_ERROR)
{
printf("WSAAccept() failed with error %d\n", WSAGetLastError());
return;
}
printf(" Client Socket number %d connected\n", Accept);
但是我这里检测不到OP_ACCEPT  这个消息。。。
大家能否告诉我为什么??还有这些OP_ACCEPT   OP_READ  消息我看是人为自己定的,但值好象都是OP_READ=0   OP_ACCEPT=1   到底是为什么啊??怎么回事?能否讲解一下,。谢谢

93

主题

1527

帖子

1532

积分

金牌会员

Rank: 6Rank: 6

积分
1532
发表于 2005-1-24 01:10:00 | 显示全部楼层

Re:一起来分析一下最简单的完成端口代码

朋友 还有没有相关的资料啊?

6

主题

14

帖子

14

积分

新手上路

Rank: 1

积分
14
 楼主| 发表于 2005-1-24 10:33:00 | 显示全部楼层

Re:一起来分析一下最简单的完成端口代码

资料少得可怜啊,不过我可以把我写的发你....加我QQ12259819

注明:游戏制作

71

主题

1804

帖子

1880

积分

金牌会员

Rank: 6Rank: 6

积分
1880
QQ
发表于 2005-1-24 13:13:00 | 显示全部楼层

Re:一起来分析一下最简单的完成端口代码

看懂是不可能的了!
但是帮忙顶下还是应该的!!

59

主题

1104

帖子

1199

积分

金牌会员

Rank: 6Rank: 6

积分
1199
发表于 2005-1-24 13:19:00 | 显示全部楼层

Re:一起来分析一下最简单的完成端口代码

完成端口都已经是97年的技术了,不过在WINDOWS下也确实找不到别的解决方案。
想追求高效率的,去看看Linux下的epoll

37

主题

727

帖子

740

积分

高级会员

Rank: 4

积分
740
发表于 2005-1-24 17:34:00 | 显示全部楼层

Re:一起来分析一下最简单的完成端口代码

作者代码写的很干净
可读性不错
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

作品发布|文章投稿|广告合作|关于本站|游戏开发论坛 ( 闽ICP备17032699号-3 )

GMT+8, 2025-12-24 04:45

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表