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,不然会变成野指针了,其实也不用指定,因为函数完后,会自动摧毁他 }

浙公网安备 33010602011771号