1.总结
1.1 处理网络收发数据我们分为两大类:同步和异步
1.2 同步包括 同步非阻塞多线程 、同步选择(select函数)
1.3 异步包括 AsyncSelect(异步选择)、 EventSelect(异步事件选择)
1.4 异步选择 AsyncSelect 是基于消息的,因此效率较低;而 异步事件选择 EventSelect 则是基于事件的,效率较高
2.输入输出完成端口(IOCP)
1.1 支持多个同时发生的异步I/O操作的应用程序编程接口;
1.2 优缺点
- 1.优点:让每一个socket有一个线程负责同步(阻塞)数据处理,one-thread-per-client;
- 2.缺点:一是如果连入的客户多了,就需要同样多的线程;二是不同的socket的数据处理都要线程切换的代价;
1.3 将之前的网络模式进行改进:使用多线程、使用异步事件处理机制、使用内存文件映射、使用预先创建几个socket,这四个都用上的就称为 IOCP 的工作原理;
1.4 完成一个 IOCP
- 1.创建一个基于对话框的MFC应用程序;
- 2.创建一个 C++ 类: CIOCPNet 类;
- 3.给类添加两个成员函数: bool Init() ; void Close() ;
- 4.给类添加成员变量: SOCKET m_socket_listen ;
- 5.在初始化函数 Init 中创建一个 TCP 网络,加载库->创建套接字(使用的是 WSASocket 函数创建socket,这样创建的socket可以在执行socket功能的同时执行其他操作)->绑定本机地址->监听->获取系统信息->创建 完成端口->把socket交给 完成端口 去管理->创建线程 个数为线程核数x2 ;
bool CIOCPNet::Init()
{
WORD wVersionRequested;
WSADATA wsaData;
int err;
wVersionRequested = MAKEWORD(2, 2);
err = WSAStartup(wVersionRequested, &wsaData);
if (err != 0)
return false;
if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2)
{
WSACleanup();
return false;
}
m_socket_listen = ::WSASocket(AF_INET, SOCK_STREAM , IPPROTO_TCP, 0, 0, WSA_FLAG_OVERLAPPED);
if(m_socket_listen == INVALID_SOCKET)
{
int error_code = WSAGetLastError();
::closesocket(m_socket_listen);
m_socket_listen = 0;
WSACleanup();
return false;
}
sockaddr_in addr_server;
addr_server.sin_family = AF_INET;
addr_server.sin_addr.S_un.S_addr = INADDR_ANY;
addr_server.sin_port = htons(4568);
if(::bind(m_socket_listen, (const sockaddr*)&addr_server, sizeof(sockaddr_in)) == SOCKET_ERROR)
{
int error_code = ::WSAGetLastError();
::closesocket(m_socket_listen);
m_socket_listen = 0;
WSACleanup();
return false;
}
if(::listen(m_socket_listen, SOMAXCONN) == SOCKET_ERROR)
{
int error_code = ::WSAGetLastError();
::closesocket(m_socket_listen);
m_socket_listen = 0;
WSACleanup();
return false;
}
SYSTEM_INFO si;
::GetSystemInfo(&si);
m_h_IOCP = CreateIoCompletionPort(INVALID_HANDLE_VALUE, 0, 0, 0);
if(NULL == m_h_IOCP)
{
::CloseHandle(m_socket_listen);
m_socket_listen = 0;
WSACleanup();
return false;
}
CreateIoCompletionPort((HANDLE)m_socket_listen, m_h_IOCP, m_socket_listen, 0);
for(size_t i=0; i<si.dwNumberOfProcessors*2; i++)
{
HANDLE h_thread = (HANDLE)::_beginthreadex(0, 0, &CIOCPNet::ThreadProc, this, 0, 0);
if(h_thread != 0)
m_ls_thread.push_back(h_thread);
}
return true;
}
void CIOCPNet::Close()
{
m_b_quitThread = false;
list<HANDLE>::iterator ite = m_ls_thread.begin();
while (ite != m_ls_thread.end())
{
if(::WaitForSingleObject(*ite, 10) == WAIT_TIMEOUT)
::TerminateThread(*ite, -1);
::CloseHandle(*ite);
*ite = 0;
ite = m_ls_thread.erase(ite);
}
if(m_socket_listen != 0)
{
::closesocket(m_socket_listen);
m_socket_listen = 0;
}
::WSACleanup();
return true;
}
unsigned int _stdcall CIOCPNet::ThreadProc(void* pVoid)
{
CIOCPNet* p_this = (CIOCPNet*)pVoid;
while (p_this->m_b_quitThread)
{
}
return 0;
}
- 6.定义一个结构体,用来存放 事件、连接上的客户端套接字、共享换冲区、网络事件标志;
- 7.添加成员函数: bool PostAccept(用来向完成端口投递连接请求);
- 8.定义一个结构体的列表,用来存放预先创建的套接字、共享缓冲区等;
- 9.在把监听套接字给 完成端口 处理之后投递一定数量的连接请求;
#define BUFFER_SIZE 1024
#define PORT 4567
enum NET_EVENT_TYPE{NET_ACCEPT, NET_READ, NET_WRITE};
typedef struct myoverlapped
{
OVERLAPPED ol;
SOCKET socket_client;
char sz_buffer[BUFFER_SIZE];
NET_EVENT_TYPE net;
}MyOverLapped;
class CIOCPNet
{
public:
... ...
list<MyOverLapped*> m_ls_ol;
... ...
};
bool CIOCPNet::Init()
{
... ...
CreateIoCompletionPort((HANDLE)m_socket_listen, m_h_IOCP, m_socket_listen, 0);
for(size_t i=0; i<si.dwNumberOfProcessors*2; i++)
{
this->PostAccept();
}
... ...
return true;
}
- 10.完成 PostAccept 函数,在函数中定义结构体对象,new一个出来,初始化,给各个成员赋值;
- 11.使用函数 AcceptEx 进行投递连接请求;
bool CIOCPNet::PostAccept()
{
MyOverLapped* p_mol = new MyOverLapped;
::ZeroMemory(p_mol, sizeof(MyOverLapped));
p_mol->net = NET_ACCEPT;
p_mol->ol.hEvent = WSACreateEvent();
p_mol->socket_client = ::WSASocket(AF_INET, SOCK_STREAM , IPPROTO_TCP, 0, 0, WSA_FLAG_OVERLAPPED);
DWORD dw_bytes_receive = 0;
if(FALSE == ::AcceptEx(m_socket_listen, p_mol->socket_client, p_mol->sz_buffer, 0,
sizeof(sockaddr_in)+16, sizeof(sockaddr_in)+16, &dw_bytes_receive, p_mol->ol))
{
if(::WSAGetLastError() != ERROR_IO_PENDING)
{
::closesocket(p_mol->socket_client);
p_mol->socket_client = 0;
::WSACloseEvent(p_mol->ol.hEvent);
p_mol->ol.hEvent = 0;
delete p_mol;
p_mol = 0;
return false;
}
}
m_ls_ol.push_back(p_mol);
return true;
}