MFC_IO模型_WSAEventSelect事件选择模型
WSAEventSelect事件选择模型,什么是事件选择模型呢?
就是咱们把客户端的socket和一个事件绑定在一起后,然后调用waitmutipleobject,然后等待函数的返回,其实跟那个线程同步差不多,
就是等待事件对象的状态改变,状态改变是什么意思呢?也就是受信和未受信,2种状态,
如果有客户端发送消息,我们就受信了,然后我们就进行处理,处理完毕后,我们就应该把事件对象进行设置到未受信,然后继续等待事件
对象的受信
WSAEventSelect事件选择模型创建的步骤,
1.新建2个数组,一个装socket,一个装事件,因为是一个事件句柄和一个socket绑定嘛,所以需要2个,还需要一个count,用来标识有多少个元素
2.进行创建线程,此线程就调用waitmutipleobject 等待事件数组的对象受信后,我们在做处理
3.在主线程或者创建一个线程进行无限的等待用户来连接,用户连接后,我们就把用户的socket放在咱们的socket数组里面,还要创建一个事件对象放在事件数组里面
4.继续重复第三步
上代码:
DWORD WINAPI WorkerThread(LPVOID lpParam)//工作者线程 { Cselect服务端Dlg* t = (Cselect服务端Dlg*)lpParam; int ret, index; WSANETWORKEVENTS NetworkEvents;//事件对象 char buff[1024]={0}; while (TRUE) { //关键API 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;//取出发生事件的对应项,成功 WSAEnumNetworkEvents(t->g_CliSocketArr[index], t->g_CliEventArr[index], &NetworkEvents);//指定的socket,事件对象(如果传入的话,系统自动帮我们设置为非传信),最后一个参数取状态 if (NetworkEvents.lNetworkEvents & FD_READ)//取得FD_READ的方法//是否是读? { ret = recv(t->g_CliSocketArr[index], buff, sizeof(buff)*sizeof(char), 0);//接收 if (ret == 0 || (ret == SOCKET_ERROR && WSAGetLastError() == WSAECONNRESET))//接受失败,就断开socket连接 { t->Cleanup(index);//掉线\退出的错误则处理 } else { USES_CONVERSION; buff[ret] = '\0';//传过来的编码好像是ansi码,我们得转换一下 char* temp = (char*)A2W(buff); CString str; str.Format(_T("客户端发送过来的内容是:%s"),temp); AfxMessageBox(str); //send(g_CliSocketArr[index], szMessage, strlen(szMessage), 0);//返回客户端原信息 } } if (NetworkEvents.lNetworkEvents & FD_CLOSE)//客户端关闭 { t->Cleanup(index); } } return 0; } 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);
//我们可以在这里判断,如果,TotalConn+1%64 = 0,说明已经超过64个客户了,我们就新创建一个线程进行监听,顺便也新创建2个数组,socket和事件
//可以用list来保存这2个对象,咱们可以定义一个对象,用来保存socket数组和事件数组,然后在加入到List当中,然后在线程里面判断,我需要等待哪一个socket数组呢?
//list一个元素就相当于一个线程嘛,
//因为采用这种IO异步,一个线程只可以等待64个嘛,所以我们得新建一个新的线程去等待新的64个socket客户端嘛,
//记住一个线程只可以等待64个客户端
g_CliSocketArr[TotalConn] = sClient;//保存到套接字数组 g_CliEventArr[TotalConn] = WSACreateEvent();//创建一个事件对象并保存到数组 WSAEventSelect(g_CliSocketArr[TotalConn],g_CliEventArr[TotalConn],FD_READ | FD_CLOSE);//把socket和创建的事件句柄关联在一起 TotalConn++; } } void Cselect服务端Dlg::Cleanup(int index)//关闭处理 { closesocket(g_CliSocketArr[index]);//关闭对应数组下标的套接字 WSACloseEvent(g_CliEventArr[index]);//关闭对应的事件对象 if (index < TotalConn - 1)//判断是不是只有一个socket, { g_CliSocketArr[index] = g_CliSocketArr[TotalConn - 1];//把最后一个socket放到需要关闭的这个位置 g_CliEventArr[index] = g_CliEventArr[TotalConn - 1];//把事件句柄也给改变了 } TotalConn--;//然后把数组下标的量减1,从而整个数组变少了。 }

浙公网安备 33010602011771号