c c++开发高性能网络服务器,直接编写业务逻辑,忽略底层代码

这里演示如何使用xengine开发高性能网络服务器.

这是一个模板,提供了基于网络服务,线程池,流式包管理器.使用者可以使用这个直接处理业务逻辑即可

这篇文章适用XEngine windows和linux版本

在windows下面,需要使用IOCP提供网络服务器.网络服务器

在开始的地方,需要先启动一个网络服务器,代码如下:下面演示了一个WINDOWS IOCP 服务器启动流程,在启动函数编写的时候,需要设置IOCP服务的回调函数,来处理网络事件

BOOL CTestFor_IOCPServerDlg::IOCP_CBConnect_Func(LPCSTR lpszClientAddr, SOCKET hSocket,LPVOID lParam)
{
	CTestFor_IOCPServerDlg *pClass_This = (CTestFor_IOCPServerDlg *)lParam;
	TCHAR tszMsgBuffer[4096];
	TCHAR tszClientMsg[2048];

	memset(tszClientMsg,'\0',sizeof(tszClientMsg));
	memset(tszMsgBuffer,'\0',sizeof(tszMsgBuffer));

	pClass_This->m_EditRecvMsg.GetWindowText(tszMsgBuffer,sizeof(tszMsgBuffer));

	NetCore_TCPIocp_SetLimitEx(pClass_This->xhNet, lpszClientAddr, 10, _T("hello"));
	_stprintf_s(tszClientMsg,_T("有客户进入:%s\r\n%s"),lpszClientAddr,tszMsgBuffer);
	pClass_This->m_EditRecvMsg.SetWindowText(tszClientMsg);
	return TRUE;
}
void CTestFor_IOCPServerDlg::IOCP_CBRecv_Func(LPCSTR lpszClientAddr, SOCKET hSocket,LPCTSTR lpszRecvBuffer,int nLen,LPVOID lParam)
{
	CTestFor_IOCPServerDlg *pClass_This = (CTestFor_IOCPServerDlg *)lParam;
	TCHAR tszMsgBuffer[4096];
	TCHAR tszClientMsg[2048];

	memset(tszClientMsg,'\0',sizeof(tszClientMsg));
	memset(tszMsgBuffer,'\0',sizeof(tszMsgBuffer));

	pClass_This->m_EditRecvMsg.GetWindowText(tszMsgBuffer,sizeof(tszMsgBuffer));

	_stprintf_s(tszClientMsg,_T("有数据到达客户端:%s:长度:%d,数据:%s\r\n%s"),lpszClientAddr,nLen,lpszRecvBuffer,tszMsgBuffer);
	pClass_This->m_EditRecvMsg.SetWindowText(tszClientMsg);
}
void CTestFor_IOCPServerDlg::IOCP_CBLeave_Func(LPCSTR lpszClientAddr, SOCKET hSocket,LPVOID lParam)
{
	CTestFor_IOCPServerDlg *pClass_This = (CTestFor_IOCPServerDlg *)lParam;
	TCHAR tszMsgBuffer[4096];
	TCHAR tszClientMsg[2048];

	memset(tszClientMsg,'\0',sizeof(tszClientMsg));
	memset(tszMsgBuffer,'\0',sizeof(tszMsgBuffer));

	pClass_This->m_EditRecvMsg.GetWindowText(tszMsgBuffer,sizeof(tszMsgBuffer));

	_stprintf_s(tszClientMsg,_T("有客户离开:%s\r\n%s"),lpszClientAddr,tszMsgBuffer);
	pClass_This->m_EditRecvMsg.SetWindowText(tszClientMsg);
}

void CTestFor_IOCPServerDlg::OnBnClickedButton1()
{
	// TODO:  在此添加控件通知处理程序代码
	if (!NetCore_TCPIocp_StartEx(&xhNet, 5000))
	{
		AfxMessageBox(_T("初始化失败!"));
		return;
	}
	NetCore_TCPIocp_RegisterCallBackEx(xhNet, IOCP_CBConnect_Func, IOCP_CBRecv_Func, IOCP_CBLeave_Func, this, this, this);
	AfxMessageBox(_T("开启成功"));
}

如果是LINUX版本,你需要启用EPOLL_IOCP,请注意:无论是IOCP还是EPOLL,底层都是多线程顺序网络.你无需关心底层细节

XNETHANDLE xhNet;
BOOL NetCore_CBLogin(LPCTSTR lpszClientAddr,SOCKET hSocket,LPVOID lParam)
{
    NetCore_TCPEPoll_SetLimitEx(xhNet, lpszClientAddr, 10, _T("hello"));
    printf("%s,%d,进入服务器\n",lpszClientAddr,hSocket);
    return TRUE;
}
void NetCore_CBRecv(LPCTSTR lpszClientAddr,SOCKET hSocket,LPCTSTR lpszRecvMsg,int nMsgLen,LPVOID lParam)
{
    printf("%s,%d,数据:%d,%s\n",lpszClientAddr,hSocket,nMsgLen,lpszRecvMsg);
}
void NetCore_CBLeave(LPCTSTR lpszClientAddr,SOCKET hSocket,LPVOID lParam)
{
    printf("%s,%d离开服务器\n",lpszClientAddr,hSocket);
}

int main()
{
    if (!NetCore_TCPEPoll_StartEx(&xhNet))
    {
        perror("main");
    }
    NetCore_TCPEPoll_RegisterCallBackEx(xhNet,NetCore_CBLogin,NetCore_CBRecv,NetCore_CBLeave);
    printf("启动成功\n");

    for (int i = 0;i < 999999;i++)
    {
        sleep(100);
    }
    NetCore_TCPEPoll_DestroyEx(xhNet);
    return 0;
}

这样,就启动了服务器.

他们监听端口是5000.在使用客户端的时候,可以进行测试连接到TCP 5000端口.IP地址使用本地IP 127.0.0.1

这样,你的高性能网络服务器就启动完毕了,你可以在回调函数里面处理数据,当然,我们并不推荐你这样做,因为这样会阻塞网络IO.对于一个C/S服务器,或者B/S服务器,服务端都需要通过指定协议和来判断要处理的程序来编写代码流程.而且,由于是TCP是流式的,所以你需要自己定义一个包头来解决流式TCP所造成的"沾包"问题.

这里,你可以使用我们的组包模块来解决这个问题,使用这个模块,你必须使用我们的标准协议头,这个协议头是 NETENGINE_PROTOCOLHDR

//在你的启动函数插入初始化组包模块代码,这里的第四个参数的4 表示我们使用了4个业务线程来处理业务.后面会说到如何使用线程池来处理业务
	if (!HelpComponents_Datas_Init(&xhPacket, 10000, 0, 4))
	{
		printf("HelpComponents_Datas_Init:%lX\n", Packets_GetLastError());
		return -1;
	}

修改你的网络代码,在用户连接成功后,执行下面的代码,来创建一个组包服务

//第三个参数0表示由模块确定这个客户端由哪个任务池返回
	if (!HelpComponents_Datas_CreateEx(xhPacket, lpszClientAddr, 0))
	{
		printf("HelpComponents_Datas_CreateEx:%lX\n", Packets_GetLastError());
		return -1;
	}

现在,我们可以投递数据了,在你的服务中,在接受数据的回调函数里面直接调用下面的代码,让数据投递给组包模块处理

HelpComponents_Datas_PostEx(xhPacket, lpszClientAddr, tszMsgBuffer,nMsgLen);

然后,我们通过下面的函数来获得要处理的包,当然,这个处理函数需要在一个单独的线程中来处理

HelpComponents_Datas_WaitEventEx(xhPacket, 1);
	list<HELPCOMPONENT_PACKET_CLIENT> stl_ListAddr;

	HelpComponents_Datas_GetPoolEx(xhPacket, 1, &stl_ListAddr);
	list<HELPCOMPONENT_PACKET_CLIENT>::const_iterator stl_ListIterator = stl_ListAddr.begin();
	for (; stl_ListIterator != stl_ListAddr.end(); stl_ListIterator++)
	{
		for (int i = 0;i < stl_ListIterator->nPktCount;i++)
		{
			HelpComponents_Datas_GetEx(xhPacket, stl_ListIterator->tszClientAddr, tszMsgBuffer, &nMsgLen, &st_ProtocolHdr);
			printf("%d=%s\n", nMsgLen, tszMsgBuffer);
		}
	}

wait函数表示等待一个完整的数据包.getpool表示获取第一个任务池中有任务的客户端地址,然后进行循环处理.getex会获得指定客户端的一个完整的数据包.这个操作不会照成空命中.这样的代码可以让你在多线程下进行多任务协作并行处理

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

xengine-qyt

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值