MFC_IO模型_WSARecv_重叠IO模型

_WSARecv_  什么是重叠IO模型呢?说白了,就是我们向系统投递一个消息,然后系统就会反馈给我们一个结果,

而这个结果用什么保存呢?那就是跟上一章我写的事件选择模型一样,  那么就是用事件的状态,守信 与 未受信,

也就是说,步骤如下;

1.创建socket,然后接受客户端我就不用说了吧,

2.如果accept接受到一个客户端的请求的话,那么我们就把这个socket和一个IO重叠结构给关联起来,不要以为IO重叠结构有啥好神奇的,其实他就是一个

事件的包装类而已,这个重叠结构里面有一个事件对象,那么, socket和这个IO重叠结构绑定在了一起后,然后我们就向系统发送一个消息,发送一个什么消息呢?

WSARecv()  这个函数,下面代码在详解这个函数怎么使用,然后,我们向系统投递成功了一个消息后,那么  我们就调用watimutipleobjects 等待事件对象的改变,

事件对象改变是什么意思呢?也就是等待事件对象变成守信状态后? 这个守信状态他是怎么变的呢?那就是系统他自动帮我们管理,因为我们刚刚不是向系统投递了一个消息吗?

所以这个对象的状态会改变,改变后,我们怎么做呢?我们就可以调用WSAGetOverlappedResult 函数进行查询结果,也就是系统帮我们接受的,而不是我们去接受,

3.然后我们处理完结果后,我们就需要把事件对象的状态重新设置为未守信的,如果你不设置为未守信的话,那么系统会认为你丫的还在处理这个socket,

4.我们处理完结果后,我们就还需要向系统投递一个消息,为什么还需要投递一个消息呢? 投递一个消息=系统我们处理1次,所以我们,每次处理完结果后,都需要

在一次的投递给系统一个接受的消息,他才会为客户端发送到服务端的消息进行保存和处理

 

 

下面看代码:

    void Cselect服务端Dlg::OnBnClickedButton1()
    {
        WSADATA     wsaData;
        int         iaddrSize = sizeof(SOCKADDR_IN);
        DWORD       dwThreadId;
        // Initialize Windows socket library
        //装载套接字库
        WSAStartup(0x0202, &wsaData);
        // Create listening socket
        //创建套接字
        sListen = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
        // Bind
        //绑定
        CString ip;
        this->GetDlgItemText(IDC_EDIT5,ip);
        USES_CONVERSION;
        local.sin_addr.S_un.S_addr = inet_addr(W2A(ip.GetBuffer()));
        ip.ReleaseBuffer();
        local.sin_family = AF_INET;
        this->GetDlgItemText(IDC_EDIT2,ip);
        local.sin_port = htons(_tstoi(ip.GetBuffer()));
        ip.ReleaseBuffer();
        bind(sListen, (struct sockaddr *)&local, sizeof(SOCKADDR_IN));
        // Listen
        //监听
        listen(sListen, 1000);
        AfxMessageBox(_T("监听成功"));
        iAddrSize = sizeof(SOCKADDR_IN);
        //DWORD threadId;
        CreateThread(NULL, 0, WorkerThread, this, 0, NULL);

        while (TRUE)
        {
            // Accept a connection
            //接受连接
            sClient = accept(sListen, (struct sockaddr *)&client, &iaddrSize);



            MyData* data = new MyData();
            data->socket = sClient;
            g_CliEventArr[TotalConn]=data->RecvOverlapped.hEvent;
            //咱们把socket和重叠结构自定义的对象放到array数组中
            array.Add(data);
            int i = WSARecv(data->socket,&(data->buffer), 1,    &(data->NumberOfBytesRecvd),&(data->Flags),&(data->RecvOverlapped), NULL); // 使用WSAR
            //需要监听的socket,接受的数据缓冲区,1个,接受了多少个字节的数据,     标志,无用,      socket绑定的重叠结构,   无用

            //****注意,上面的倒数第三哥参数的flag要设置成0,不然会出错:WSAEOPNOTSUPP
            //也不知道为什么要设置为0

            int b = WSAGetLastError();
            TotalConn++;
        }
    }
    void Cselect服务端Dlg::OnBnClickedButton1()
    {
        WSADATA     wsaData;
        int         iaddrSize = sizeof(SOCKADDR_IN);
        DWORD       dwThreadId;
        // Initialize Windows socket library
        //装载套接字库
        WSAStartup(0x0202, &wsaData);
        // Create listening socket
        //创建套接字
        sListen = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
        // Bind
        //绑定
        CString ip;
        this->GetDlgItemText(IDC_EDIT5,ip);
        USES_CONVERSION;
        local.sin_addr.S_un.S_addr = inet_addr(W2A(ip.GetBuffer()));
        ip.ReleaseBuffer();
        local.sin_family = AF_INET;
        this->GetDlgItemText(IDC_EDIT2,ip);
        local.sin_port = htons(_tstoi(ip.GetBuffer()));
        ip.ReleaseBuffer();
        bind(sListen, (struct sockaddr *)&local, sizeof(SOCKADDR_IN));
        // Listen
        //监听
        listen(sListen, 1000);
        AfxMessageBox(_T("监听成功"));
        iAddrSize = sizeof(SOCKADDR_IN);
        //DWORD threadId;
        CreateThread(NULL, 0, WorkerThread, this, 0, NULL);

        while (TRUE)
        {
            // Accept a connection
            //接受连接
            sClient = accept(sListen, (struct sockaddr *)&client, &iaddrSize);



            MyData* data = new MyData();//这是我们自定义的对象,为什么要自定义对象呢?因为,一个socket要和一个重叠IO结构绑定在一起嘛
            data->socket = sClient;//传递socket,也就是客户端的socket
            g_CliEventArr[TotalConn]=data->RecvOverlapped.hEvent;//这个是一个事件对象的数组,因为我们需要用waitmutipleobjects 进行等待嘛,所以需要这个参数
            //咱们把socket和重叠结构自定义的对象放到array数组中
            array.Add(data);//我们把自定义的socket集合放在了一个数组里面,也就是CArray<MyData*,MyData*&> array;这个数组里面,我们保存的是这个对象的指针哦
            int i = WSARecv(data->socket,&(data->buffer), 1,    &(data->NumberOfBytesRecvd),&(data->Flags),     &(data->RecvOverlapped), NULL); // 使用WSAR
            //需要监听的socket,            接受的数据缓冲区,1个缓冲区,接受了多少个字节的数据,    标志,无用(但是要设置为0),      socket绑定的重叠结构,   无用

            //****注意,上面的倒数第三哥参数的flag要设置成0,不然会出错:WSAEOPNOTSUPP
            //也不知道为什么要设置为0

            int b = WSAGetLastError();
            TotalConn++;
        }
    }

 

下面是线程函数,进行对事件对象数组等待的线程函数,是核心哦:

class MyData{
public:
    MyData(){
        this->RecvOverlapped.hEvent = WSACreateEvent();
        this->buffer.buf = new char[1024];
        this->bufferSize = 1024;
        this->buffer.len = this->bufferSize;
        this->Flags = 0;

    }    // 标准构造函数

    ~MyData(){
        delete this->buffer.buf;
    }

    SOCKET socket;//新建一个socket
    WSAOVERLAPPED RecvOverlapped;//新建一个事件对象
    WSABUF        buffer;//这个是缓冲区,用于接受数据需要使用的
    DWORD        bufferSize;//这个是缓冲区,用于接受数据需要使用的;
    DWORD         NumberOfBytesRecvd;//作用是用于接受客户端发送了多少个字节数据
    DWORD         Flags;//没用

};
class MyData{
public:
    MyData(){
        this->RecvOverlapped.hEvent = WSACreateEvent();
        this->buffer.buf = new char[1024];//分配给缓冲区一个内存
        this->bufferSize = 1024;
        this->buffer.len = this->bufferSize;
        this->Flags = 0;

    }    // 标准构造函数

    ~MyData(){
        delete this->buffer.buf;//需要释放内存哦
    }

    SOCKET socket;//新建一个socket
    WSAOVERLAPPED RecvOverlapped;//新建一个重叠IO数据结构
    WSABUF        buffer;//这个是缓冲区,用于接受数据需要使用的
    DWORD        bufferSize;//这个是缓冲区大小,用于接受数据需要使用的;
    DWORD         NumberOfBytesRecvd;//作用是用于接受客户端发送了多少个字节数据,好像这个字段是打酱油的
    DWORD         Flags;//没用,好像这个字段是打酱油的

};

上面这个是类,我们自定义的类,他的用处就是socket和重叠结构绑定,还有就是其他变量,比如缓冲区啊,缓冲区大小啊,在补充一点就是,WSARevice函数里面,需要指定一个缓冲区

,因为系统会帮我们把客户端send发送来的数据放到这个缓冲区里面,所以我们就自定义了一个MyData这样的一个对象

 

下面上线程函数了哦;关键的:

 

DWORD WINAPI WorkerThread(LPVOID lpParam)//工作者线程
{
    Cselect服务端Dlg* t = (Cselect服务端Dlg*)lpParam;//把窗口的指针强制转换,这个就不用说了吧

    DWORD cbTransferred;//实际接受到多少个数据
    int ret,index;
    while (TRUE)
    {
        ret = WSAWaitForMultipleEvents(t->TotalConn,      t->g_CliEventArr, FALSE,                        1000,     FALSE);//进行等待
                         //等待多少个事件对象?  事件对象数组,       false表示任意一个事件受信就返回,  等待时间,  最后一个参数无用
        if (ret == WSA_WAIT_FAILED || ret == WSA_WAIT_TIMEOUT)//等待超时
        {
            continue;//如果超时或失败则继续
        }
        //如果有事件对象的事件发生
        index = ret - WSA_WAIT_EVENT_0;//取得对应事件对象的具体发生在哪个上面,也就是上面等待的那个数组的下标,

        WSAResetEvent(t->g_CliEventArr[index]);//重设发生了事件的对象,重新设置这个事件的状态设置为未受信

        MyData* m = t->GetMyData(t->g_CliEventArr[index]);//这个方法是什么呢?就是根据事件对象获得咱们自定义的一个对象
        WSAGetOverlappedResult( //取得重叠结构数据
            m->socket,//对应的套接字
            &(m->RecvOverlapped),//单(每个)句柄数据数组的重叠结构
            &cbTransferred, //实际接收字节数,这个参数很重要,获得客户端发送给我们数据的大小
            TRUE,//如果为TRUE,表示操作完成后在返回
            &(m->Flags));//这个好像无用,为了凑合参数传进去,打酱油的

        if (cbTransferred == 0)//如果为 0,客户端掉线咯
        {
            // The connection was closed by client
            //连接被客户端关闭
            t->Cleanup(m);//没有接受到数据,关闭他
        }
        else
        {

            WSARecv(m->socket,&(m->buffer), 1,   &(m->NumberOfBytesRecvd),&(m->Flags),&(m->RecvOverlapped), NULL); //需要重新向系统投递一个消息,进行下                                                                   //一次的接受客户端的请求和处理
                //需要监听的socket,接受的数据缓冲区,1个,接受了多少个字节的数据,     标志,无用,      socket绑定的重叠结构,   无用

            m->buffer.buf[cbTransferred] = '\0';//接收到的内容后面放上'/0'

            CString str;
            USES_CONVERSION;
            char* tempbuff = (char*)A2W(m->buffer.buf);
            str.Format(_T("客户端传过来的数据是:%s"),tempbuff);

            memset(m->buffer.buf,0,sizeof(m->buffer.buf));
            int b = WSAGetLastError();//WSA_IO_PENDING,997 表示IO正在操作


            //     g_pPerIODataArr[index]->szMessage contains the received data
            //接收到的内容 
            

            //弹出来;
            AfxMessageBox(str);

            
        }
    }
    return 0;
}

 

 

 

MyData* Cselect服务端Dlg::GetMyData(WSAEVENT w){

    for(int i =0;i<array.GetSize();i++){
        if(array.GetAt(i)->RecvOverlapped.hEvent == w){
            return array.GetAt(i);
        }
    }
    return NULL;

}
    void Cselect服务端Dlg::Cleanup(MyData* m)//关闭处理
    {


        //先从array里面移除
        for(int i =0;i<array.GetSize();i++){
            if(array.GetAt(i) == m){
                array.RemoveAt(i);
                break;
            }
        }

        //先关闭事件对象
        WSACloseEvent(m->RecvOverlapped.hEvent);
        //在关闭socket
        closesocket(m->socket);
        //最后从这个array中删除这个m
        //最后删除指针
        delete m;
        m = NULL;//指向NULL,不然会变成野指针了,其实也不用指定,因为函数完后,会自动摧毁他
    }

 

 

 

 

 

 

 

 

 

 

 

 

 

posted @ 2013-08-11 16:11  宝贝,我永远都在  阅读(646)  评论(0)    收藏  举报