操作系统与网络 2019-3-29

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()
{
	// ==============1.加载库==============
	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;
	}
	// ==============1.加载库==============

	// ==============2.创建套接字============
	m_socket_listen = ::WSASocket(AF_INET, SOCK_STREAM , IPPROTO_TCP, 0, 0, WSA_FLAG_OVERLAPPED);	// 创建的socket在执行socket的同时可以执行其他操作
	if(m_socket_listen == INVALID_SOCKET)
	{
		int error_code = WSAGetLastError();
		::closesocket(m_socket_listen);
		m_socket_listen = 0;
		WSACleanup();
		return false;
	}
	// ==============2.创建套接字============

	// =============3.绑定===============
	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;
	}
	// =============3.绑定===============

	// ===============4.监听===============
	if(::listen(m_socket_listen, SOMAXCONN) == SOCKET_ERROR)		// SOMAXCONN 
	{
		int error_code = ::WSAGetLastError();
		::closesocket(m_socket_listen);
		m_socket_listen = 0;
		WSACleanup();
		return false;
	}
	// ===============4.监听===============

	// ======================5.获取系统信息=========================
	SYSTEM_INFO si;
	::GetSystemInfo(&si);
	// ======================5.获取系统信息=========================

	// ==================创建 完成端口========================
	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;
	}
	// ==================创建 完成端口========================

	// ==================把socket交给 完成端口 去管理==================
	CreateIoCompletionPort((HANDLE)m_socket_listen, m_h_IOCP, m_socket_listen, 0);
	// ==================把socket交给 完成端口 去管理==================

	// ======================6.创建线程,个数为 核数x2==========================
	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);
	}
	// ======================6.创建线程,个数为 核数x2==========================

	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()
{
	... ...

	// ==================把socket交给 完成端口 去管理==================
	CreateIoCompletionPort((HANDLE)m_socket_listen, m_h_IOCP, m_socket_listen, 0);
	// ==================把socket交给 完成端口 去管理==================

	// =================投递一定数量的连接请求=================
	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))
	{
		// ERROR_IO_PENDING 投递成功
		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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值