MFC_IO模型CreateIoCompletionPort完成端口

先上代码,以后写注释

 

// select服务端Dlg.cpp : 实现文件
//

#include "stdafx.h"
#include "select服务端.h"
#include "select服务端Dlg.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#endif

#define WM_SOCKET WM_USER + 1




struct CompletionPortAndMyHwnd 
{//目的是在工作线程中得到窗口指针
    HANDLE CompletionPort;
    Cselect服务端Dlg* my_win;
}compleportandHwnd;




// 用于应用程序“关于”菜单项的 CAboutDlg 对话框

class CAboutDlg : public CDialog
{
public:
    CAboutDlg();

    // 对话框数据
    enum { IDD = IDD_ABOUTBOX };

protected:
    virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV 支持

    // 实现
protected:
    DECLARE_MESSAGE_MAP()
};

CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD)
{
}

void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
    CDialog::DoDataExchange(pDX);
}

BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)
END_MESSAGE_MAP()


// Cselect服务端Dlg 对话框




Cselect服务端Dlg::Cselect服务端Dlg(CWnd* pParent /*=NULL*/)
: CDialog(Cselect服务端Dlg::IDD, pParent)
{
    m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}

void Cselect服务端Dlg::DoDataExchange(CDataExchange* pDX)
{
    CDialog::DoDataExchange(pDX);
}

BEGIN_MESSAGE_MAP(Cselect服务端Dlg, CDialog)
    ON_WM_SYSCOMMAND()
    ON_WM_PAINT()
    ON_WM_QUERYDRAGICON()
    //}}AFX_MSG_MAP
    ON_BN_CLICKED(IDC_BUTTON1, &Cselect服务端Dlg::OnBnClickedButton1)
    ON_WM_DESTROY()
END_MESSAGE_MAP()


// Cselect服务端Dlg 消息处理程序

BOOL Cselect服务端Dlg::OnInitDialog()
{
    CDialog::OnInitDialog();

    // 将“关于...”菜单项添加到系统菜单中。

    // IDM_ABOUTBOX 必须在系统命令范围内。
    ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
    ASSERT(IDM_ABOUTBOX < 0xF000);

    CMenu* pSysMenu = GetSystemMenu(FALSE);
    if (pSysMenu != NULL)
    {
        CString strAboutMenu;
        strAboutMenu.LoadString(IDS_ABOUTBOX);
        if (!strAboutMenu.IsEmpty())
        {
            pSysMenu->AppendMenu(MF_SEPARATOR);
            pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
        }
    }

    // 设置此对话框的图标。当应用程序主窗口不是对话框时,框架将自动
    //  执行此操作
    SetIcon(m_hIcon, TRUE);            // 设置大图标
    SetIcon(m_hIcon, FALSE);        // 设置小图标

    // TODO: 在此添加额外的初始化代码

    this->SetDlgItemText(IDC_EDIT5,_T("127.0.0.1"));
    this->SetDlgItemText(IDC_EDIT2,_T("8009"));

    char abc[]="我是abc";
    int a = sizeof(abc);
    int b = strlen(abc);
    count1 = 0;
    evetnHandle = CreateEvent(NULL,TRUE,TRUE,NULL);

    return TRUE;  // 除非将焦点设置到控件,否则返回 TRUE
}



void Cselect服务端Dlg::OnSysCommand(UINT nID, LPARAM lParam)
{
    if ((nID & 0xFFF0) == IDM_ABOUTBOX)
    {
        CAboutDlg dlgAbout;
        dlgAbout.DoModal();
    }
    else
    {
        CDialog::OnSysCommand(nID, lParam);
    }
}

// 如果向对话框添加最小化按钮,则需要下面的代码
//  来绘制该图标。对于使用文档/视图模型的 MFC 应用程序,
//  这将由框架自动完成。

void Cselect服务端Dlg::OnPaint()
{
    if (IsIconic())
    {
        CPaintDC dc(this); // 用于绘制的设备上下文

        SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);

        // 使图标在工作区矩形中居中
        int cxIcon = GetSystemMetrics(SM_CXICON);
        int cyIcon = GetSystemMetrics(SM_CYICON);
        CRect rect;
        GetClientRect(&rect);
        int x = (rect.Width() - cxIcon + 1) / 2;
        int y = (rect.Height() - cyIcon + 1) / 2;

        // 绘制图标
        dc.DrawIcon(x, y, m_hIcon);
    }
    else
    {
        CDialog::OnPaint();
    }
}

//当用户拖动最小化窗口时系统调用此函数取得光标
//显示。
HCURSOR Cselect服务端Dlg::OnQueryDragIcon()
{
    return static_cast<HCURSOR>(m_hIcon);
}



BOOL Cselect服务端Dlg::InItSocket()//装载套接字库
{
    WSADATA data = {0};
    if(WSAStartup(MAKEWORD(2, 2), &data))//2.2的套接字
        return FALSE;
    if ( LOBYTE(data.wVersion) !=2 || HIBYTE(data.wVersion) != 2 ){
        WSACleanup();
        return FALSE;
    }
    return TRUE;
}


PER_IO_OPERATION_DATA * Cselect服务端Dlg::getPerBy_SOCKET(SOCKET s){
    //遍历结构集合
    /*for(int i=0;i<perList.GetSize();i++){
        int aa = perList.GetAt(i)->socket;
        if(perList.GetAt(i)->socket == s){
            return perList.GetAt(i);
        }
    }*/
    return NULL;
}

DWORD WINAPI WorkerThread(LPVOID CompletionPortID)//工作者线程
{
    HANDLE CompletionPort=(HANDLE)compleportandHwnd.CompletionPort;//取出完成端口句柄 
    Cselect服务端Dlg* pDlg=compleportandHwnd.my_win;//取出窗口指针
    SOCKET   sClient;
    DWORD  dwBytesTransferred;//重叠结构中的字节数  
    PER_IO_OPERATION_DATA* lpPerIOData1 = NULL;

    while (TRUE)
    {
        //取完成端口状态
        GetQueuedCompletionStatus(
            CompletionPort,//完成端口句柄
            &dwBytesTransferred,//接受数据的大小
            (unsigned long *)&sClient,//客户端,也就是我们创建完成端口的时候传入的那个自定义数据
            (LPOVERLAPPED *)&lpPerIOData1,//这个就是recive的时候的那个缓冲区了
            INFINITE);//无限等待
        int i=0;
        if (dwBytesTransferred == 0xFFFFFFFF)//如果是退出的信号,由PostQueuedCompletionStatus发出的
        {          
            return TRUE;//返回
        }

        if (lpPerIOData1->OperationType == RECV_POSTED)//重叠结构中的枚举值
        {
            if (dwBytesTransferred == 0)
            {
                //客户端关闭              
                

                    //循环动态数组删除结构体元素

                    for(int i=0;i<pDlg->perList.GetSize();i++){
                        int aa = pDlg->perList.GetAt(i)->socket;
                        if(pDlg->perList.GetAt(i)->socket == sClient){
                            pDlg->perList.RemoveAt(i);
                            break;
                        }
                    }

                    //Cilentinfo c = *(pDlg->m_Cilentinfo.GetAt(i));
                    //if (sClient==c.cinsock)
                    //{
                    //    break;
                    //}
                    //continue;
                        
                CString cip;
                //cip.Format(_T("客户%s离开"),pDlg->m_Cilentinfo.GetAt(i).cinIP);
            //    pDlg->m_Cilentinfo.RemoveAt(i);//从动态数组中删除这个 节点
//                pDlg->SetDlgItemText(IDC_EDIT1,cip);

                closesocket(sClient);//关闭客户端套接字
                delete lpPerIOData1;
                //HeapFree(GetProcessHeap(),0,lpPerIOData1); //释放这前申请的内存      
            }
            else
            {
                //**********************************************************************
                //处理客户端请求,可以起个线程什么的用于传文件之类的
                lpPerIOData1->szMessage[dwBytesTransferred] = '\0';//客户端发过来的信息
                //for (i=0;i<pDlg->m_Cilentinfo.GetSize();i++)//以套接字为标,找出动态数组相应IP
                //{
                //    if (sClient==pDlg->m_Cilentinfo.GetAt(i).cinsock)
                //    {
                //        break;
                //    }
                //} 
                CString sMsg;
                USES_CONVERSION;
                char* tempbuff = (char*)A2W(lpPerIOData1->szMessage);
                sMsg.Format(_T("%s"),tempbuff);
                AfxMessageBox(sMsg);
            //    pDlg->SetDlgItemText(IDC_EDIT1,sMsg);
            //    for (i=0;i<pDlg->m_Cilentinfo.GetSize();i++)//回传给所有动态数组中的客户
                {
//                    send(pDlg->m_Cilentinfo.GetAt(i).cinsock, sMsg, strlen(sMsg), 0);
                }                  
                //**********************************************************************  

                //重新激活另一个WSARecv异步操作
                memset(lpPerIOData1, 0, sizeof(PER_IO_OPERATION_DATA));

                //lpPerIOData1 = pDlg->getPerBy_SOCKET(s);//重新发送一次请求
                //删除临时变量
                lpPerIOData1->Buffer.len = 1024;
                lpPerIOData1->Buffer.buf = lpPerIOData1->szMessage;
                lpPerIOData1->OperationType = RECV_POSTED;
                lpPerIOData1->socket = sClient;
                WSARecv(sClient,
                    &lpPerIOData1->Buffer,
                    1,
                    &lpPerIOData1->NumberOfBytesRecvd,
                    &lpPerIOData1->Flags,
                    &lpPerIOData1->overlap,
                    NULL);
        }
        }}
return NULL;
}

DWORD WINAPI ThreadFunc(LPVOID pParam){

    Cselect服务端Dlg* pDlg=(Cselect服务端Dlg *)pParam;//取出传过来的窗口指针

    DWORD    dwThreadId;
    SYSTEM_INFO     systeminfo;//系数信息,主要是用于取得CPU数量
    PER_IO_OPERATION_DATA* lpPerIOData;//扩展重叠结构,为每创建一个socket分配一个这样的一个扩展结构,他的作用是用于获得用户发送的数据,也就是缓冲区

    //创建一个完成端口,这里只是为了得到它的句柄,后面再和套接字关联起来
    compleportandHwnd.CompletionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);//创建一个完成端口,然后交给多线程
    compleportandHwnd.my_win=pDlg;

    //创建工作者线程
    GetSystemInfo(&systeminfo);
    for (unsigned int i=0;i<systeminfo.dwNumberOfProcessors;i++)//以CPU的数量来创建
    {
        CreateThread(NULL, 0, WorkerThread, &compleportandHwnd, 0, &dwThreadId);
        //把完成端口句柄传过去附带了窗口指针
    }

    //装载socket库
    if (pDlg->InItSocket())
    {
        //            AfxMessageBox("装载套接字库出错!");
    }

    //
    pDlg->sSever=socket(AF_INET , SOCK_STREAM , IPPROTO_TCP); //创建套接字,服务端监听的套接字

    if (INVALID_SOCKET==pDlg->sSever)
    {
        //AfxMessageBox(_T("创建套接字失败!"));
    }
    //

    sockaddr_in serve;
    CString ip;
    pDlg->GetDlgItemText(IDC_EDIT5,ip);

    USES_CONVERSION;
    serve.sin_addr.S_un.S_addr = inet_addr(W2A(ip.GetBuffer()));
    ip.ReleaseBuffer();
    serve.sin_family = AF_INET;
    pDlg->GetDlgItemText(IDC_EDIT2,ip);
    serve.sin_port = htons(_tstoi(ip.GetBuffer()));
    ip.ReleaseBuffer();
    bind(pDlg->sSever, (struct sockaddr *)&serve, sizeof(SOCKADDR_IN));
    //
    if (SOCKET_ERROR==listen(pDlg->sSever,5))
    {
        AfxMessageBox(_T("监听失败!"));
    }
    //***************************
    //pDlg->SetDlgItemText(IDC_EDIT1,"服务端开启成功!");

    SOCKADDR_IN sCilentaddr;
    int len=sizeof(struct sockaddr);
    //接受连接
    
    //DWORD ret =  WaitForSingleObject(pDlg->evetnHandle,0);
    //CString cilentIPstr=A2W(inet_ntoa(sCilentaddr.sin_addr));//连接IP
    //CString cstr;
    CString count111;
    AfxMessageBox(_T("服务端开启成功!"));
    while (TRUE)
    {

        pDlg->sClient = accept(pDlg->sSever, (struct sockaddr *)&sCilentaddr, &len);

        pDlg->count1++;
        count111.Format(_T("连接数量是:%d"),pDlg->count1);

        //SetEvent(pDlg->evetnHandle);
        pDlg->SetDlgItemText(IDC_EDIT3,count111);
        //            cstr.Format("IP:%s连上来了!",cilentIPstr);
        //保存到动态数组

        //Cilentinfo* clientinfo = new Cilentinfo();//每一个客户端保存一个socket,保存到数组里面,  也可以放在缓存那个结构里面,所以这里直接注释
        //clientinfo->cinsock=sClient;//套接字
        //clientinfo->cinIP= cilentIPstr;
        //m_Cilentinfo.Add(clientinfo);    //加入动态数组中


        //新接受的连接套接字关联到完成端口
        CreateIoCompletionPort((HANDLE)(pDlg->sClient),compleportandHwnd.CompletionPort, pDlg->sClient, 0);
        //客户端,        完成端口句柄             传递一个参数给完成端口,在GetQueuedCompletionStatus 获取的时候会需要,后面个一般为0 
        //激活一个WSARecv异步操作
        //lpPerIOData = (LPPER_IO_OPERATION_DATA)HeapAlloc(
        //    GetProcessHeap(),
        //    HEAP_ZERO_MEMORY,
        //    sizeof(PER_IO_OPERATION_DATA));//重叠结构申请内存

        lpPerIOData = new PER_IO_OPERATION_DATA();//分配堆上的内存
        lpPerIOData->Buffer.len = 1024;//大小

        lpPerIOData->OperationType = RECV_POSTED;//类型
        lpPerIOData->Buffer.buf = lpPerIOData->szMessage;//缓冲区
        lpPerIOData->socket = pDlg->sClient;//分配一个socket

        //把结构体加入到  array
        pDlg->perList.Add(lpPerIOData);

        //意思是给重叠的队列发送一个消息
        WSARecv(pDlg->sClient,//需要监听的socket
            &(lpPerIOData->Buffer),//缓冲区,也就是服务器发送过来的数据
            1,//只有一个缓冲区
            &(lpPerIOData->NumberOfBytesRecvd),//接受的大小,这个send的时候才会有用,所以这个乱填
            &(lpPerIOData->Flags),//标致,好像必须为0
            &(lpPerIOData->overlap),//重叠机构
            NULL);
    }

    //向完成端口中的线程发出退出信号
    PostQueuedCompletionStatus(compleportandHwnd.CompletionPort, 0xFFFFFFFF, 0, NULL);

return NULL;
}

    void Cselect服务端Dlg::OnBnClickedButton1()
    {
    HANDLE m_hConnectThread=CreateThread(NULL,0,ThreadFunc,this,0,NULL);

    }


    void Cselect服务端Dlg::OnDestroy()
    {


        //销毁资源,关闭socket

        //for(int i=0;i<perList.GetSize();i++){
        //        delete perList.GetAt(i);
        //    }

        closesocket(sSever);
        CloseHandle(compleportandHwnd.CompletionPort);//关闭完成端口
        WSACleanup();//拆卸套接字


        CDialog::OnDestroy();
    }

 

 

typedef enum//枚举 状态
{
    RECV_POSTED
}OPERATION_TYPE;


struct Cilentinfo
{
    SOCKET cinsock;
    CString cinIP;
}cilentin;

typedef struct//扩展重叠结构
{
    WSAOVERLAPPED  overlap;
    SOCKET socket;
    WSABUF         Buffer;
    char           szMessage[1024];
    DWORD          NumberOfBytesRecvd;
    DWORD          Flags;
    OPERATION_TYPE OperationType;
}PER_IO_OPERATION_DATA, *LPPER_IO_OPERATION_DATA;

 

 

 

// select服务端Dlg.h : 头文件
//

#pragma once



#include <winsock.h>
#pragma comment(lib, "ws2_32.lib")
#include "MyData.h"


// Cselect服务端Dlg 对话框
class Cselect服务端Dlg : public CDialog
{
// 构造
public:
    Cselect服务端Dlg(CWnd* pParent = NULL);    // 标准构造函数

// 对话框数据
    enum { IDD = IDD_SELECT_DIALOG };

    protected:
    virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV 支持


// 实现
protected:
    HICON m_hIcon;

    // 生成的消息映射函数
    virtual BOOL OnInitDialog();
    afx_msg void OnSysCommand(UINT nID, LPARAM lParam);
    afx_msg void OnPaint();
    afx_msg HCURSOR OnQueryDragIcon();
    DECLARE_MESSAGE_MAP()
public:
    afx_msg void OnBnClickedButton1();


    SOCKET sSever,sClient; //套接字
    CArray <Cilentinfo*,Cilentinfo*&>m_Cilentinfo;//动态数组客户套接字及IP
    //CArray <PER_IO_OPERATION_DATA*,PER_IO_OPERATION_DATA*&> perList;//结构和套接字绑定的一个结构
    CList <PER_IO_OPERATION_DATA*,PER_IO_OPERATION_DATA*&> perList;//结构和套接字绑定的一个结构
    BOOL InItSocket();
    PER_IO_OPERATION_DATA * getPerBy_SOCKET(SOCKET s);
    int count1;
    HANDLE evetnHandle;
protected:
//    virtual LRESULT WindowProc(UINT message, WPARAM wParam, LPARAM lParam);
public:
    afx_msg void OnDestroy();
};

 

posted @ 2013-08-13 01:20  宝贝,我永远都在  阅读(600)  评论(0)    收藏  举报