/********************************************************************
created: 2016/09/24
created: 24:9:2016 14:36
filename: D:\code\SocketClassify\SocketClassify\TCPServ_SelectEvent.h
file path: D:\code\SocketClassify\SocketClassify
file base: TCPServ_SelectEvent
file ext: h
author: singebogo-singebogo@163.com
purpose: 事件选择:Winsock提供了另一个有用的异步I/O模型
Function: WSAAsyncSelect模型类似的是,它也允许应用程序在一个或多个套接字上,接收以事件为基 础的网络事件通知。
Function: 对于表1总结的、由WSAAsyncSelect模型采用的网络事件来说,它们均可原封不动地移植到新模型。
Note: 在用新模型开发的应用程序 中,也能接收和处理所有那些事件。
Note:
*********************************************************************/
#pragma once
#ifndef __I_TCPSERV_SELECTEVENT
#define __T_TCPSERV_SELECTEVENT
/****************************头文件库*******************************/
#include "socket/UsrSocket.h"
#include<cstdio>
#include <iostream>
using namespace std;
/****************************宏定义*********************************/
#define IPPORT 4000 // ip 端口
#define MAXBACKLOG SOMAXCONN // 客户最大连接数
/****************************函数实现*******************************/
#ifdef __cplusplus
extern "C" {
#endif
数据定义
int g_evn_itotalconn = 0; // 连接数量singre
SOCKET g_evn_clientsocketarry[MAXIMUM_WAIT_OBJECTS] = {0}; // 套接字对象数组
WSAEVENT g_evn_clienteventarry[MAXIMUM_WAIT_OBJECTS] = {0}; // 事件对象数组
函数声明
void CreateTcpServ_SelectEventModel(); // 套接字初始化,监听,绑定,accept 网络事件对象,client套接字对象创建
DWORD WINAPI WorkerThread_EventSel(LPVOID lpParam);//工作者线程
void Cleanup(int index);//清掉 网络事件对象数组,套接字对象数组
函数实现
// The WSAEventSelect function specifies an event object to be associated with the specified set of FD_XXX network events
// int WSAEventSelect(
// __in SOCKET s, A descriptor identifying the socket.
// __in WSAEVENT hEventObject, A handle identifying the event object to be associated with the specified set of FD_XXX network events.
// __in long lNetworkEvents A bitmask that specifies the combination of FD_XXX network events in which the application has interest.
// );
// The return value is zero if the application's specification of the network events and the associated event object was successful. Otherwise,
// the value SOCKET_ERROR is returned, and a specific error number can be retrieved by calling WSAGetLastError.
//************************************
// Method: CreateTcpServ_SelectEventModel
// FullName: CreateTcpServ_SelectEventModel
// Access: private
// Returns: void
// Qualifier:
// Note1:
// Note2:
//************************************
void CreateTcpServ_SelectEventModel()
{
CUsrSocket us_socket;
CUsrSocket::_STOCKET _socket_param;
_socket_param._af = AF_INET;
_socket_param._protocol = SOCK_STREAM;
_socket_param._type = 0;
CUsrSocket::_SOCKET_ADDR _socket_addrparam;
_socket_addrparam._b_addrany = true;
_socket_addrparam._i_port = IPPORT;
_socket_addrparam._str_ip = "";
DWORD dw_threadid;
// 初始化套接字
if(us_socket.InitWSAData(us_socket._wVersionRequested, us_socket._wsaData))
{
cout<<"InitWSAData successed!"<<endl;
}else{
cout<<"InitWSAData fauiled!"<<endl;
return;
}
// 创建套接字对象
us_socket._socket_no = us_socket.CreateSocket(_socket_param._af, _socket_param._protocol, _socket_param._type);
// 设置套接字地址
us_socket._socket_addr = us_socket.SetSocketAddr(_socket_addrparam._b_addrany ,
_socket_addrparam._str_ip, _socket_addrparam._i_port);
// bind
us_socket.SocketBind(us_socket._socket_no, us_socket._socket_addr);
// listen
us_socket.SocketListen(us_socket._socket_no, MAXBACKLOG);
// Create worker thread
//创建线程
CreateThread(NULL, 0, WorkerThread_EventSel, NULL, 0, &dw_threadid);
while (1)
{
us_socket._socket_conn = us_socket.SocketAccept(us_socket._socket_no, us_socket._socket_client);
if (us_socket._socket_conn == INVALID_SOCKET)
continue;
else{
//这里client的SOCKADDR_IN client 中可以取出IP
cout<<"Accepted client:" <<inet_ntoa(us_socket._socket_client.sin_addr)<<ntohs(us_socket._socket_client.sin_port)<<endl;
// Add socket to g_evn_clientsocketarry
//把客户端套接字放入数组中
g_evn_clientsocketarry[g_evn_itotalconn] = us_socket._socket_conn;
g_evn_clienteventarry[g_evn_itotalconn] = WSACreateEvent();//创建一个事件对象并保存到数组
//可以在这里加入对数组大小的判断,从而用多个数组来分组。一组可以达到64个。
//*******************************************
//主要函数 事件选择
//分组的话应当也是在用几个事件对象才行 事件对象g_evn_clienteventarry在interest中如果含有事件FD_READ|FD_Close事件,进入工作线程
if(0 == WSAEventSelect(g_evn_clientsocketarry[g_evn_itotalconn], \
g_evn_clienteventarry[g_evn_itotalconn], \
FD_READ | FD_CLOSE))
{
cout<<"WSAEventSelect successed! "<<g_evn_clientsocketarry[g_evn_itotalconn]<<"--"<<g_evn_clienteventarry[g_evn_itotalconn]<<endl;
}
g_evn_itotalconn++;
//-send-
char sendBuf[DEFAULT_BUFLEN];
memset(sendBuf, 0 , DEFAULT_BUFLEN);
sprintf(sendBuf,"Welcome %s to here!",inet_ntoa(us_socket._socket_client.sin_addr));//inet_ntoa网络地址转换转点分十进制的字符串指针
send(us_socket._socket_conn, sendBuf, strlen(sendBuf)+1,0);
}
}
us_socket.CleanWSAData();
return;
}
// The WSAWaitForMultipleEvents function returns when one or all of the specified event objects are in the signaled state,
// when the time-out interval expires, or when an I/O completion routine has executed.
// DWORD WSAWaitForMultipleEvents(
// __in DWORD cEvents, The number of event object handles in the array pointed to by lphEvents,
// The maximum number of event object handles is WSA_MAXIMUM_WAIT_EVENTS. One or more events must be specified.
// __in const WSAEVENT *lphEvents, A pointer to an array of event object handles.
// __in BOOL fWaitAll, A value that specifies the wait type. If TRUE, the function returns when the state of all objects in the lphEvents
// array is signaled. If FALSE, the function returns when any of the event objects is signaled.
// __in DWORD dwTimeout, The time-out interval, in milliseconds.
// __in BOOL fAlertable A value that specifies whether the thread is placed in an alertable wait state so the system can execute I/O completion routines.
// );
// The return value is zero if the operation was successful. Otherwise, the value SOCKET_ERROR is returned, and a specific error number can be
// retrieved by calling WSAGetLastError.
// int WSAEnumNetworkEvents(
// __in SOCKET s, A descriptor identifying the socket.
// __in WSAEVENT hEventObject, An optional handle identifying an associated event object to be reset.
// __out LPWSANETWORKEVENTS lpNetworkEvents A pointer to a WSANETWORKEVENTS structure that is filled with a record of network events that occurred and any associated error codes.
// );
//************************************
// Method: WorkerThread_EventSel
// FullName: WorkerThread_EventSel
// Access: private
// Returns: DWORD WINAPI
// Qualifier:
// Parameter: LPVOID lpParama
// Note1:
// Note2:
//************************************
DWORD WINAPI WorkerThread_EventSel(LPVOID lpParama)
{
int ret, index;
WSANETWORKEVENTS p_networkevents;
char szMessage[DEFAULT_BUFLEN];
memset(szMessage, 0, DEFAULT_BUFLEN);
while (true)
{
// 关键api函数
ret = WSAWaitForMultipleEvents(g_evn_itotalconn, g_evn_clienteventarry, FALSE, 1000, FALSE);
if (ret == WSA_WAIT_FAILED || ret == WSA_WAIT_TIMEOUT)
{
continue;
}
index = ret - WSA_WAIT_EVENT_0; // 取对应事件的发生项
cout<<"index :"<<index<<endl;
if(SOCKET_ERROR == WSAEnumNetworkEvents(g_evn_clientsocketarry[index], g_evn_clienteventarry[index], &p_networkevents))
{
cout<<"WSAEnumNetworkEvents Fauiled!"<<endl;
return 0;
}
if (p_networkevents.lNetworkEvents & FD_READ) // read 网络事件
{
memset(szMessage, 0 , DEFAULT_BUFLEN);
ret = recv(g_evn_clientsocketarry[index], szMessage, DEFAULT_BUFLEN, 0);//接收
cout<<szMessage<<endl;
if (ret == 0 || (ret == SOCKET_ERROR && WSAGetLastError() == WSAECONNRESET))
{
Cleanup(index);//掉线\退出的错误则处理
}
else // 发送服务器信息给客户端
{
szMessage[ret] = '\0';
send(g_evn_clientsocketarry[index], szMessage, strlen(szMessage), 0);//返回客户端原信息
}
}
if(p_networkevents.lNetworkEvents == FD_CLOSE)
{
Cleanup(index);
}
}
return 0;
}
//************************************
// Method: Cleanup
// FullName: Cleanup
// Access: private
// Returns: void
// Qualifier:
// Parameter: int p_index
// Note1: 对于关闭的套接字 关闭对应的套接字数组和网络事件对象数组值,g_evn_itotalconn连接总数对应变化
// Note2:
//************************************
void Cleanup(int p_index)
{
cout<<"socketno: "<<g_evn_clientsocketarry[p_index]<<"--- EventNo: "<<g_evn_clienteventarry[p_index]<<endl;
WSACloseEvent(g_evn_clienteventarry[p_index]);
closesocket(g_evn_clientsocketarry[p_index]);
if(p_index <= g_evn_itotalconn - 1){
if (p_index == g_evn_itotalconn - 1)
{
g_evn_clienteventarry[g_evn_itotalconn - 1] = 0;
g_evn_clientsocketarry[g_evn_itotalconn - 1] = 0;
}else{
g_evn_clienteventarry[p_index] = g_evn_clienteventarry[g_evn_itotalconn - 1];
g_evn_clientsocketarry[p_index] = g_evn_clientsocketarry[g_evn_itotalconn - 1];
}
}
g_evn_itotalconn --;
cout<<"total conn: "<<g_evn_itotalconn<<endl;
}
#ifdef __cplusplus
}
#endif
#endif
created: 2016/09/24
created: 24:9:2016 14:36
filename: D:\code\SocketClassify\SocketClassify\TCPServ_SelectEvent.h
file path: D:\code\SocketClassify\SocketClassify
file base: TCPServ_SelectEvent
file ext: h
author: singebogo-singebogo@163.com
purpose: 事件选择:Winsock提供了另一个有用的异步I/O模型
Function: WSAAsyncSelect模型类似的是,它也允许应用程序在一个或多个套接字上,接收以事件为基 础的网络事件通知。
Function: 对于表1总结的、由WSAAsyncSelect模型采用的网络事件来说,它们均可原封不动地移植到新模型。
Note: 在用新模型开发的应用程序 中,也能接收和处理所有那些事件。
Note:
*********************************************************************/
#pragma once
#ifndef __I_TCPSERV_SELECTEVENT
#define __T_TCPSERV_SELECTEVENT
/****************************头文件库*******************************/
#include "socket/UsrSocket.h"
#include<cstdio>
#include <iostream>
using namespace std;
/****************************宏定义*********************************/
#define IPPORT 4000 // ip 端口
#define MAXBACKLOG SOMAXCONN // 客户最大连接数
/****************************函数实现*******************************/
#ifdef __cplusplus
extern "C" {
#endif
数据定义
int g_evn_itotalconn = 0; // 连接数量singre
SOCKET g_evn_clientsocketarry[MAXIMUM_WAIT_OBJECTS] = {0}; // 套接字对象数组
WSAEVENT g_evn_clienteventarry[MAXIMUM_WAIT_OBJECTS] = {0}; // 事件对象数组
函数声明
void CreateTcpServ_SelectEventModel(); // 套接字初始化,监听,绑定,accept 网络事件对象,client套接字对象创建
DWORD WINAPI WorkerThread_EventSel(LPVOID lpParam);//工作者线程
void Cleanup(int index);//清掉 网络事件对象数组,套接字对象数组
函数实现
// The WSAEventSelect function specifies an event object to be associated with the specified set of FD_XXX network events
// int WSAEventSelect(
// __in SOCKET s, A descriptor identifying the socket.
// __in WSAEVENT hEventObject, A handle identifying the event object to be associated with the specified set of FD_XXX network events.
// __in long lNetworkEvents A bitmask that specifies the combination of FD_XXX network events in which the application has interest.
// );
// The return value is zero if the application's specification of the network events and the associated event object was successful. Otherwise,
// the value SOCKET_ERROR is returned, and a specific error number can be retrieved by calling WSAGetLastError.
//************************************
// Method: CreateTcpServ_SelectEventModel
// FullName: CreateTcpServ_SelectEventModel
// Access: private
// Returns: void
// Qualifier:
// Note1:
// Note2:
//************************************
void CreateTcpServ_SelectEventModel()
{
CUsrSocket us_socket;
CUsrSocket::_STOCKET _socket_param;
_socket_param._af = AF_INET;
_socket_param._protocol = SOCK_STREAM;
_socket_param._type = 0;
CUsrSocket::_SOCKET_ADDR _socket_addrparam;
_socket_addrparam._b_addrany = true;
_socket_addrparam._i_port = IPPORT;
_socket_addrparam._str_ip = "";
DWORD dw_threadid;
// 初始化套接字
if(us_socket.InitWSAData(us_socket._wVersionRequested, us_socket._wsaData))
{
cout<<"InitWSAData successed!"<<endl;
}else{
cout<<"InitWSAData fauiled!"<<endl;
return;
}
// 创建套接字对象
us_socket._socket_no = us_socket.CreateSocket(_socket_param._af, _socket_param._protocol, _socket_param._type);
// 设置套接字地址
us_socket._socket_addr = us_socket.SetSocketAddr(_socket_addrparam._b_addrany ,
_socket_addrparam._str_ip, _socket_addrparam._i_port);
// bind
us_socket.SocketBind(us_socket._socket_no, us_socket._socket_addr);
// listen
us_socket.SocketListen(us_socket._socket_no, MAXBACKLOG);
// Create worker thread
//创建线程
CreateThread(NULL, 0, WorkerThread_EventSel, NULL, 0, &dw_threadid);
while (1)
{
us_socket._socket_conn = us_socket.SocketAccept(us_socket._socket_no, us_socket._socket_client);
if (us_socket._socket_conn == INVALID_SOCKET)
continue;
else{
//这里client的SOCKADDR_IN client 中可以取出IP
cout<<"Accepted client:" <<inet_ntoa(us_socket._socket_client.sin_addr)<<ntohs(us_socket._socket_client.sin_port)<<endl;
// Add socket to g_evn_clientsocketarry
//把客户端套接字放入数组中
g_evn_clientsocketarry[g_evn_itotalconn] = us_socket._socket_conn;
g_evn_clienteventarry[g_evn_itotalconn] = WSACreateEvent();//创建一个事件对象并保存到数组
//可以在这里加入对数组大小的判断,从而用多个数组来分组。一组可以达到64个。
//*******************************************
//主要函数 事件选择
//分组的话应当也是在用几个事件对象才行 事件对象g_evn_clienteventarry在interest中如果含有事件FD_READ|FD_Close事件,进入工作线程
if(0 == WSAEventSelect(g_evn_clientsocketarry[g_evn_itotalconn], \
g_evn_clienteventarry[g_evn_itotalconn], \
FD_READ | FD_CLOSE))
{
cout<<"WSAEventSelect successed! "<<g_evn_clientsocketarry[g_evn_itotalconn]<<"--"<<g_evn_clienteventarry[g_evn_itotalconn]<<endl;
}
g_evn_itotalconn++;
//-send-
char sendBuf[DEFAULT_BUFLEN];
memset(sendBuf, 0 , DEFAULT_BUFLEN);
sprintf(sendBuf,"Welcome %s to here!",inet_ntoa(us_socket._socket_client.sin_addr));//inet_ntoa网络地址转换转点分十进制的字符串指针
send(us_socket._socket_conn, sendBuf, strlen(sendBuf)+1,0);
}
}
us_socket.CleanWSAData();
return;
}
// The WSAWaitForMultipleEvents function returns when one or all of the specified event objects are in the signaled state,
// when the time-out interval expires, or when an I/O completion routine has executed.
// DWORD WSAWaitForMultipleEvents(
// __in DWORD cEvents, The number of event object handles in the array pointed to by lphEvents,
// The maximum number of event object handles is WSA_MAXIMUM_WAIT_EVENTS. One or more events must be specified.
// __in const WSAEVENT *lphEvents, A pointer to an array of event object handles.
// __in BOOL fWaitAll, A value that specifies the wait type. If TRUE, the function returns when the state of all objects in the lphEvents
// array is signaled. If FALSE, the function returns when any of the event objects is signaled.
// __in DWORD dwTimeout, The time-out interval, in milliseconds.
// __in BOOL fAlertable A value that specifies whether the thread is placed in an alertable wait state so the system can execute I/O completion routines.
// );
// The return value is zero if the operation was successful. Otherwise, the value SOCKET_ERROR is returned, and a specific error number can be
// retrieved by calling WSAGetLastError.
// int WSAEnumNetworkEvents(
// __in SOCKET s, A descriptor identifying the socket.
// __in WSAEVENT hEventObject, An optional handle identifying an associated event object to be reset.
// __out LPWSANETWORKEVENTS lpNetworkEvents A pointer to a WSANETWORKEVENTS structure that is filled with a record of network events that occurred and any associated error codes.
// );
//************************************
// Method: WorkerThread_EventSel
// FullName: WorkerThread_EventSel
// Access: private
// Returns: DWORD WINAPI
// Qualifier:
// Parameter: LPVOID lpParama
// Note1:
// Note2:
//************************************
DWORD WINAPI WorkerThread_EventSel(LPVOID lpParama)
{
int ret, index;
WSANETWORKEVENTS p_networkevents;
char szMessage[DEFAULT_BUFLEN];
memset(szMessage, 0, DEFAULT_BUFLEN);
while (true)
{
// 关键api函数
ret = WSAWaitForMultipleEvents(g_evn_itotalconn, g_evn_clienteventarry, FALSE, 1000, FALSE);
if (ret == WSA_WAIT_FAILED || ret == WSA_WAIT_TIMEOUT)
{
continue;
}
index = ret - WSA_WAIT_EVENT_0; // 取对应事件的发生项
cout<<"index :"<<index<<endl;
if(SOCKET_ERROR == WSAEnumNetworkEvents(g_evn_clientsocketarry[index], g_evn_clienteventarry[index], &p_networkevents))
{
cout<<"WSAEnumNetworkEvents Fauiled!"<<endl;
return 0;
}
if (p_networkevents.lNetworkEvents & FD_READ) // read 网络事件
{
memset(szMessage, 0 , DEFAULT_BUFLEN);
ret = recv(g_evn_clientsocketarry[index], szMessage, DEFAULT_BUFLEN, 0);//接收
cout<<szMessage<<endl;
if (ret == 0 || (ret == SOCKET_ERROR && WSAGetLastError() == WSAECONNRESET))
{
Cleanup(index);//掉线\退出的错误则处理
}
else // 发送服务器信息给客户端
{
szMessage[ret] = '\0';
send(g_evn_clientsocketarry[index], szMessage, strlen(szMessage), 0);//返回客户端原信息
}
}
if(p_networkevents.lNetworkEvents == FD_CLOSE)
{
Cleanup(index);
}
}
return 0;
}
//************************************
// Method: Cleanup
// FullName: Cleanup
// Access: private
// Returns: void
// Qualifier:
// Parameter: int p_index
// Note1: 对于关闭的套接字 关闭对应的套接字数组和网络事件对象数组值,g_evn_itotalconn连接总数对应变化
// Note2:
//************************************
void Cleanup(int p_index)
{
cout<<"socketno: "<<g_evn_clientsocketarry[p_index]<<"--- EventNo: "<<g_evn_clienteventarry[p_index]<<endl;
WSACloseEvent(g_evn_clienteventarry[p_index]);
closesocket(g_evn_clientsocketarry[p_index]);
if(p_index <= g_evn_itotalconn - 1){
if (p_index == g_evn_itotalconn - 1)
{
g_evn_clienteventarry[g_evn_itotalconn - 1] = 0;
g_evn_clientsocketarry[g_evn_itotalconn - 1] = 0;
}else{
g_evn_clienteventarry[p_index] = g_evn_clienteventarry[g_evn_itotalconn - 1];
g_evn_clientsocketarry[p_index] = g_evn_clientsocketarry[g_evn_itotalconn - 1];
}
}
g_evn_itotalconn --;
cout<<"total conn: "<<g_evn_itotalconn<<endl;
}
#ifdef __cplusplus
}
#endif
#endif