一、前言

网络库是从事C++开发最基础、最核心、最常用的一个库,所有的协议都是建立在一个稳定高效的网络库之上的,所以对于c++程序员来说它是必不可少且非常非常重要的一个核心组件,我们可以使用网络库做任何我们想做的事情,比如

  • 用于文件数据上传、下载
  • 所有通信协议如http、rtp、rtsp等协议的封装
  • 服务器模块多客户端监听、连接、通信
  • 客户端与服务端通信
  • 局域网广播搜索
  • 局域网设备搜索
  • 多播组播

一直不停的在C++、Java、Web以及linux系统运维等技术方面不停的来回切换,突然发现很久没有做c++了,最近一直在做java的要多,关于c++这块从事有8-9年的开发,这块相关的文章总结的却比Java要少很多。

  • 一个是c++的技术难度和门槛要高很多,一直都在不断积累,可概要的技术点不多,抽不出时间进行文章化。
  • 一个是Java相关的技术容易的多,可写的内容也多,研究一门基本上都有对应的doc文档,所以将文档在翻译一遍成网络文章就相对容易很多

不管如何,因为工作原因,最近又杠上了C++的音视频这块相关技术,正好抽空将之前所有的c++资料和库总结了一下:

不总结还好,一旦总结,还是很多的,很多实际项目中开发的库和组件还没提取出来,不过对于一个老C++程序员来说,项目中开发好的稳定的库和资料是弥足珍贵的,要多善于总结和滚雪球。

在开始讲解boost网络库之前,我这里先给大家稍微普及一下windows下的网络知识,我是windows下开发的,关于socket编程在windows下有多重开发模型,可以参考一下《winsock网络编程》 一书

  • 选择模型(select)
    选择模型是网络编程中最基础、最简单的一种模型,因为其简单易用性所以备受学生时代的在校生或毕业生使用,但是它也是性能最差的一种编程模型。选择模型是针对select而言的,它可以阻塞可以是非阻塞的,所以它包括两种模式:
    (1)、 阻塞模式
    执行I/O操作完成前会一直进行等待,不会将控制权交给程序
    (2)、 非阻塞模式
    执行I/O操作时,WinSock函数会返回并交出控制权。因为函数在没有运行完成就进行返回,并会不断地返回 WSAEWOULDBLOCK错误,但是它功能很强大。
  • 异步消息选择
    异步消息模型就是借助windows的消息机制来实现的,熟悉win32应用开发或者mfc应用开发的人员都知道,所有的窗口都有对应的消息处理机制,我们只需要创建一个窗口句柄HWND,然后将HWND与对应的消息进行绑定即可,它是通过WsaAsyncSelect接口函数来实现的,它实现的主要思路是:
    (1)、首先我们定义一个消息,告诉系统当有客户端消息到达的时候,发送该消息通知我们
    (2)、然后在消息处理函数里面添加对消息的处理即可
  • 事件模型
    除此之外,winsock还提供了一个异步I/O模型-事件模型–WsaEventSelect ,和WSAAsyncSelect模型类似的是,它也允许应用程序在一个或多个套接字上,接收以事件为基础的网络事件通知。该模型最主要的差别在于网络事件会投递至一个事件对象句柄,而非投递至一个窗口例程。

事件选择模型不用主动去轮询所有客户端套接字是否有数据到来的模型,它也是在客户端有数据到来时,系统发送通知给我们的程序,但是,它不是发送消息,而是通过事件的方式来通知我们的程序,这就解决了WsaAsyncSelect模型只能用在Win32窗口程序的问题。

  • 重叠I/O模型
    重叠模型是让应用程序使用重叠数据结构(WSAOVERLAPPED),一次投递一个或多个 WinSock I/O请求。针对这些请求,在它们完成后,应用程序会受到通知,于是就可以通过另外的代码来处理这些数据了。有两种方法可以用来管理重叠I/O请求完成的情况 – 即接到重叠操作完成的通知时处理
    (1) 事件对象通知 (Event Object Notification)
    基于事件通知的方法,就是要将WinSock事件对象与WSAOVERLPPED结构关联在一起,在使用重叠结构的情况下,我们常用的 send,sendto,recv,recvfrom 也要被WSASend,WSASendto,WSARecv,WSARecvfrom 替换掉了。
    (2) 完成例程(Completion Routies)
    完成例程来实现重叠I/O比用事件通知简单得多,在这个模型中,主线程只用不停的接受连接即可
WSARecv(lpPerIOData->sClient,&lpPerIOData->Buffer,1,&lpPerIOData->NumberOfByteRecvd,&lpPerIOData->Flags,&lpPerIOData->overlap,CompletionRoutine);
  • 完成端口
    完成端口对象取代了WSAAsyncSelect中的消息驱动和WSAEventSelect中的事件对象,当然完成端口模型的内部机制要比WSAAsyncSelect和WSAEventSelect模型复杂得多,但是是性能最佳的网络编程模型。从本质上说,完成端口模型要求我们创建一个Win32完成端口对象,通过指定数量的线程,对重叠I/O请求进行管理,以便为已经完成的重叠I/O请求提供服务。
    假若一个应用程序同时需要管理为数众多的套接字,那么采用这种模型,往往可以达到最佳的系统性能!

boost库的网络模型实现在windows上底层是通过iocp完成端口模型实现的,所以是性能最佳的一种网络模型实现。

二、实现

boost库中有一个专门的用于网络编程的库-asio,也就是异步io,它能实现tcp、udp、甚至usb串口数据读取的功能,它是一个非常强大的、跨平台的异步网络通信库,这就是我为什么选择它的原因。

在介绍源码实现的时候,我们先了解一下asio中的几个常用对象,和socket一样,它包含如下几个对象

  • io_service
    它主要作为一个事件驱动器,在多线程编程里面提供了任务队列和任务分发功能
  • acceptor
    它和socket一样,提供了端点连接监听相关能力
  • endpoint和address
    address封装了设备ipv4和ipv6的地址,endpoint标识address和port的组合,决定一台机器的某个端口(我们称之为端点)。
  • socket
    套接口编程模型的核心类,提供了同步和异步操作接口集合

说明了以上几个核心概念之后,我需要通过boost网络库封装实现如下几个接口的能力,包括tcp、udp、广播、数据收发、同步异步等,各个接口以及说明如下:


// ---------------------------------------------------------------------------------------
// Function:
//      Init network SDK
// Parameters:
//      [in]pConnectCallBack        :   connect or disconnect callback
//      [in]pDataCallBack           :   receive data callback
//      [in]pErrCallBack            :   error message callback
//      [in]pContext                :   callback context
// Remark:
//      This interface must be invoked at the beginning of application, when connect server
//      success or disconnect server pConnectCallBack callback will be invoked; when data
//      arrived, pDataCallBack callback will be invoked ; when error occur at the process
//      of running, pErrCallBack callback will be invoked;
//      pContext parameter express the scenes of the callback, if the callback invoked by
//      SDK, pContext will be passed to you by callback last parameter
// Return:
//      if error occur, the result value as the top description will be return by SDK
// ---------------------------------------------------------------------------------------
NETSDK_API NETSDK_RETURN NetSdk_Init();// ---------------------------------------------------------------------------------------
// Function:
//      Clean and release SDK resource
// Parameters:
//      NULL
// Remark:
//      This interface must be invoked at the end of application
// Return:
//      return value returned by SDK As mentioned above
// ---------------------------------------------------------------------------------------
NETSDK_API NETSDK_RETURN NetSdk_Cleanup();// tcp server
// ---------------------------------------------------------------------------------------
// Function:
//      Start or stop listen local port
// Parameters:
//      [in]nPort       :   listen port
//      [out]pHandle    :   server handle address
//      [lHandle]       :   server handle output by NetSdk_Listen
// Remark:
//      This interface is adapted to server, not for client
// Example:
//                        PCONNECTCALLBACK
//      NetSdk_Listen-->  PRECVDATACALLBACK    -->NetSdk_UnListen
//                        PNETSDKERRCALLBACK
//                              ||
//                              \/
//                          NetSdk_Send
// Return:
//      return value returned by SDK As mentioned above
// ---------------------------------------------------------------------------------------
NETSDK_API NETSDK_RETURN NetSdk_Listen(int nPort, PCONNECTCALLBACK pConnectCallBack,PRECVDATACALLBACK pDataCallBack, PNETSDKERRCALLBACK pErrorBack,void* pContext, long* pHandle);
NETSDK_API NETSDK_RETURN NetSdk_ListenEx(const char* ipV4OrV6, int nPort, PCONNECTCALLBACK pConnectCallBack,PRECVDATACALLBACK pDataCallBack,PNETSDKERRCALLBACK pErrorBack,void* pContext, long* pHandle);
NETSDK_API NETSDK_RETURN NetSdk_UnListen(long lHandle);// tcp client
// ---------------------------------------------------------------------------------------
// Function:
//      Connect or disconnect server
// Parameters:
//      [in]pServerIp       :   server ip address
//      [in]nPort           :   server port
//      [out]pHandle        :   client handle address
//      [in]lHandle         :   client handle
//      [in]nTimeoutSec     :   timeout
//      [in]bindLocalPort   :   tcp client bind local port
// Remark:
//      This interface is adapted to tcp client, not for server
//      if set bindLocalPort to none zero, then client bind local port by manual
// Return:
//      return value returned by SDK As mentioned above
// ---------------------------------------------------------------------------------------
NETSDK_API NETSDK_RETURN NetSdk_Connect(const char* pServerIp, int nPort, int nTimeoutSec,PCONNECTCALLBACK pConnectCallBack, PRECVDATACALLBACK pDataCallBack, void* pContext, long* pHandle, int bindLocalPort = 0);// tcp client
// ---------------------------------------------------------------------------------------
// Function:
//      Send message to server or reply message to client
// Parameters:
//      [in]nClientId       :   client handle
//      [in]pBytes          :   send data address
//      [in]nLen            :   send data length
// Remark:
//      This interface is adapted to tcp client, not for server
// Example:
//      NetSdk_Connect-->NetSdk_Send-->NetSdk_DisConnect
// Return:
//      return value returned by SDK As mentioned above
// ---------------------------------------------------------------------------------------
NETSDK_API NETSDK_RETURN NetSdk_Send(long nClientId, unsigned char* pBytes, int nLen);
// return byte have send, if error occur then return 0 or less than 0
NETSDK_API int NetSdk_SendSync(long nClientId, unsigned char* pBytes, int nLen);// tcp client
// ---------------------------------------------------------------------------------------
// Function:
//      Get TCP Client local address and peer address
// Parameters:
//      [in]nClientId       :   client handle
//      [in]pBytes          :   send data address
//      [in]nLen            :   send data length
// Remark:
//      This interface is adapted to tcp client
// Example:
//      NetSdk_Connect-->NetSdk_GetConnectAddr
//      NetSdk_Listen-->PCONNECTCALLBACK-->NetSdk_GetConnectAddr
// Return:
//      return value returned by SDK As mentioned above
// ---------------------------------------------------------------------------------------
NETSDK_API NETSDK_RETURN NetSdk_GetConnectAddr(long nClientId, sockaddr_in* pLocalAddr, sockaddr_in* pPeerAddr);// udp client
// ---------------------------------------------------------------------------------------
// Function:
//      Bind local port and send udp data to another device
// Parameters:
//      NULL
// Remark:
//      This interface is adapted to udp client, not for server
//      NetSdk_Broadcast_Sync and  NetSdk_SendTo_Sync is synchronized interface
//      the value returned is indicate the size has send
//      NetSdk_Broadcast and NetSdk_SendTo is asynchronized interface
// Example:
//      NetSdk_Bind-->NetSdk_Broadcast
//      or
//      NetSdk_Bind-->NetSdk_SendTo
// Return:
//      return value returned by SDK As mentioned above
// ---------------------------------------------------------------------------------------
NETSDK_API NETSDK_RETURN NetSdk_Bind(const char* pServerIp, int nPort, PRECVDATACALLBACK pDataCallBack, void* pContext,long* pHandle);// ---------------------------------------------------------------------------------------
// Function:
//      Send Udp broadcast message to local network
// Parameters:
//      [in]nClientId       :   returned by NetSdk_Bind
//      [in]nPort           :   which port to all machine
//      [in]pBytes          :   the message body
//      [in]nLen            :   the message length
// Remark:
//      this interface is used to send broadcast to all
//      machine in local network
// Example:
//      NetSdk_Bind-->NetSdk_Broadcast
//      or
//      NetSdk_Bind-->NetSdk_SendTo
// Return:
//      return value returned by SDK As mentioned above
// ---------------------------------------------------------------------------------------
NETSDK_API NETSDK_RETURN NetSdk_Broadcast(long nClientId,int nPort,unsigned char* pBytes, int nLen);
// ---------------------------------------------------------------------------------------
// Function:
//      Send Udp message to specific machine
// Parameters:
//      [in]nClientId       :   returned by NetSdk_Bind
//      [in]pAddr           :   the endpoint to received message
//      [in]pBytes          :   the message body
//      [in]nLen            :   the message length
// Remark:
//      NULL
// Example:
//      NetSdk_Bind-->NetSdk_SendTo
// Return:
//      return value returned by SDK As mentioned above
// ---------------------------------------------------------------------------------------
NETSDK_API NETSDK_RETURN NetSdk_SendTo(long nClientId,sockaddr_in* pAddr,unsigned char* pBytes, int nLen);NETSDK_API int NetSdk_Broadcast_Sync(long nClientId,int nPort,unsigned char* pBytes, int nLen);
NETSDK_API int NetSdk_SendTo_Sync(long nClientId,sockaddr_in* pAddr,unsigned char* pBytes, int nLen);// tcp or udp client
// ---------------------------------------------------------------------------------------
// Function:
//      Close client handle
// Parameters:
//      NULL
// Remark:
//
// Example:
//      NetSdk_Bind-->NetSdk_CloseHandle
//      or
//      NetSdk_Connect-->NetSdk_CloseHandle
// Return:
//      return value returned by SDK As mentioned above
// ---------------------------------------------------------------------------------------
NETSDK_API NETSDK_RETURN NetSdk_CloseHandle(long lHandle);

实现总的类图如下所示

各个类的作用如下:

  • CClient
    提供了作为tcp客户端和udp客户端的基础实现类,包括获取客户id、获取地址、端口、日志等接口封装
  • CUdpClient
    实现了udp本地端口绑定、数据同步异步发送、数据接收、广播等接口
  • CTcpClient
    实现了tcp客户端连接、断开、发送数据、处理数据等接口
  • CNetWork
    服务端网络的抽象,服务端可以同时监听多个网卡的多个网口,它包括启动、停止等接口
  • CTcpServer
    是tcp网络的实现,服务端可以同时监听多个tcp端点,实现多端口通信。
  • CNetWorkMgr
    网络管理器,管理多个网络对象,包括添加网络、移除网络、清理等接口

客户端实现

作为一个客户端,我想要的主要功能是:连接服务器(tcp)、绑定本机端口(udp接收数据)、发送数据(同步异步)、接收数据等功能。

udp客户端绑定本机端口:

bool CUdpClient::Bind(udp::endpoint ep)
{try{m_bindPort = ep;boost::system::error_code err;if (!m_socket.is_open()){m_socket.open(/*ip::udp::v4()*/ep.protocol(), err);if (err){std::string strErr = (boost::format("open socket error[%d]")%err.value()).str();CCallBack::NotifyErrCB(m_eType, GetSId(), GetId(), NETERR_INISOCK_ERR,(unsigned char*)strErr.c_str() , strErr.length());return false;}}m_socket.set_option(asio::socket_base::reuse_address(true),err);if (err){std::string strErr = (boost::format("reuse address error[%d]")%err.value()).str();CCallBack::NotifyErrCB(m_eType, GetSId(), GetId(), NETERR_INISOCK_ERR,(unsigned char*)strErr.c_str() , strErr.length());return false;}
#ifdef _WIN32BOOL bNewBehavior = FALSE;DWORD dwBytesReturned = 0;WSAIoctl(m_socket.native_handle(), SIO_UDP_CONNRESET, &bNewBehavior, sizeof bNewBehavior, NULL, 0, &dwBytesReturned, NULL, NULL);
#endifm_socket.bind(ep, err);if (err){std::string strErr = (boost::format("bind socket error[%d]")%err.value()).str();CCallBack::NotifyErrCB(m_eType, GetSId(), GetId(), NETERR_INISOCK_ERR,(unsigned char*)strErr.c_str() , strErr.length());return false;}return true;}catch (std::exception& e){CCallBack::NotifyErrCB(m_eType, GetSId(), GetId(), NETERR_UNKNOWN, (unsigned char*)e.what(), strlen(e.what()));}return false;
}

udp数据异步发送

NETSDK_RETURN CUdpClient::AsynSendTo(udp::endpoint ep, unsigned char* pBytes, int nLen)
{try{// 获取空bufferBufferPtr pBuffer = m_write_buffer.GetEmptyBuffer();if (!pBuffer)return NETERR_BUFERR_FULL;// 填充数据pBuffer->FillData(pBytes, nLen);pBuffer->m_ep = ep;m_write_buffer.AddFullBuffer(pBuffer);// 数据是否发送完毕boost::mutex::scoped_lock a_lock(m_send_lock);if (m_send_finish){// 获取当前发送bufferBufferPtr pBuffer = m_write_buffer.GetFullBuffer();// 无可发送的bufferif (!pBuffer)return NETERR_UNKNOWN;m_send_finish = false;AsyncSend(pBuffer);}return NETERR_SUCCESS;}catch (std::exception& e){CCallBack::NotifyErrCB(m_eType, GetSId(), GetId(), NETERR_UNKNOWN, (unsigned char*)e.what(), strlen(e.what()));}return NETERR_UNKNOWN;
}

tcp客户端连接

bool CTcpClient::Connect(std::string strIp, int nPort, bool bSync, int nTimeout)
{try{tcp::endpoint ep(ip::address::from_string(strIp), nPort);if (bSync){m_bconnected = false;boost::system::error_code err_code;if (m_bindLocalPort != 0) {m_socket.open(/*tcp::v4()*/ep.protocol(), err_code);m_socket.set_option(tcp::acceptor::reuse_address(true), err_code);tcp::endpoint bind_ep(ip::address::from_string("0.0.0.0"), m_bindLocalPort);m_socket.bind(bind_ep, err_code);}// 非阻塞模式连接,方式默认等待20秒
//          {
//              m_socket.io_control(boost::asio::ip::tcp::socket::non_blocking_io(true));
//              //err_code = m_socket.connect(ep, err_code);
//              m_socket.connect(ep, err_code);
//
//              fd_set fdWrite;
//              FD_ZERO(&fdWrite);
//              FD_SET(m_socket.native(), &fdWrite);
//              timeval tv = { nTimeout };
//              if (select(0, NULL, &fdWrite, NULL, &tv) <= 0 || !FD_ISSET(m_socket.native(), &fdWrite))
//              {
//                  m_bconnected = false;
//                  return m_bconnected;
//              }
//
//              m_socket.io_control(boost::asio::ip::tcp::socket::non_blocking_io(false));
//              m_bconnected = true;
//              StartKeepAlive();
//          }//             err_code = m_socket.connect(ep, err_code);
//          if (!err_code)
//          {
//              m_bconnected = true;
//              StartKeepAlive();
//              return true;
//          }
//          return m_bconnected;boost::recursive_mutex::scoped_lock guard(m_connect_mutext);m_socket.async_connect(ep, boost::bind(&CTcpClient::ConnectHandler, shared_from_this(), boost::asio::placeholders::error));m_connect_cond.wait_for(guard, boost::chrono::seconds(nTimeout));return m_bconnected;}else{m_socket.async_connect(ep, boost::bind(&CTcpClient::ConnectHandler, shared_from_this(), boost::asio::placeholders::error));return true;}}catch (std::exception& e){CCallBack::NotifyErrCB(m_eType, GetSId(), GetId(), NETERR_UNKNOWN, (unsigned char*)e.what(), strlen(e.what()));}return false;
}

tcp数据处理

NETSDK_RETURN CTcpClient::AsynWrite(unsigned char* pBytes, int nLen)
{try{if(!m_bconnected)return NETERR_SEND_ERR;// 获取空bufferBufferPtr pBuffer = m_write_buffer.GetEmptyBuffer(nLen);if (!pBuffer)return NETERR_BUFERR_FULL;// 填充数据pBuffer->FillData(pBytes, nLen);m_write_buffer.AddFullBuffer(pBuffer);// 数据是否发送完毕boost::mutex::scoped_lock a_lock(m_send_lock);if (m_send_finish){// 获取当前发送bufferBufferPtr pNextBuffer = m_write_buffer.GetFullBuffer();// 无可发送的bufferif (!pNextBuffer)return NETERR_UNKNOWN;m_send_finish = false;AsyncSend(pNextBuffer);}return NETERR_SUCCESS;}catch (std::exception& e){CCallBack::NotifyErrCB(m_eType, GetSId(), GetId(), NETERR_UNKNOWN, (unsigned char*)e.what(), strlen(e.what()));}return NETERR_UNKNOWN;
}

稍微注意的是,tcp数据发送的时候,我这里用了一个环形缓冲区,数据发送首先进入环形缓冲器,发送完成后继续从环形缓冲区中获取数据并发送出去。

tcp数据读取

void CTcpClient::ReadHandler(const boost::system::error_code err, const size_t nTransferedSize)
{sockaddr_in stAddr;try{        stAddr.sin_family = AF_INET;stAddr.sin_addr.s_addr = inet_addr(GetAddr().c_str());stAddr.sin_port = htons(GetPort());if (!err && nTransferedSize > 0){CCallBack::NotifyDataCB(m_eType, GetSId(), GetId(),&stAddr,m_read_buffer->m_pBuffer, nTransferedSize);AsynRead();}else if(err){std::string strErr = (boost::format("read tcp data error[%d]\n")%err.value()).str();CCallBack::NotifyErrCB(m_eType, GetSId(), GetId(), NETERR_RECV_ERR, (unsigned char*)strErr.c_str(), strErr.length());// 其他异常不认为是断开if (WSAECONNRESET == err.value() || WSAECONNABORTED == err.value() ||WSAENETRESET == err.value() || WSAESHUTDOWN == err.value() || WSAENETDOWN == err.value() || WSAEHOSTDOWN == err.value()||ERROR_SEM_TIMEOUT == err.value() || ERROR_FILE_NOT_FOUND == err.value()){m_bconnected = false;if(CClientMgr::get_mutable_instance().GetTcpClient(GetId())){CClientMgr::get_mutable_instance().PopTcpClient(GetId());CCallBack::NotifyConnectCB(m_eType, GetSId(), GetId(), &stAddr, true);}}else{std::string strErr = (boost::format("ReadHandle error[%d-%s]\n")%err.value()%err.message()).str();WriteLog(strErr);}}else{std::string strErr = (boost::format("ReadHandle error[%d-%s]\n")%err.value()%err.message()).str();WriteLog(strErr);}}catch (std::exception& e){std::string strErr = (boost::format("read tcp[%d] data exception[%s]\n")%GetId()%e.what()).str();CCallBack::NotifyErrCB(m_eType, GetSId(), GetId(), NETERR_RECV_ERR, (unsigned char*)strErr.c_str(), strErr.length());WriteLog(strErr);}
}

当有数据回调的时候,我们首先检查是否有错误,如网络断开、对方重置连接、关闭、超时等,如果没有错误则回调出去数据,如果异常则回调断开异常!

服务端实现

服务端实现,最重要的就是支持多个端点的监听,支持多个客户端的连接,以下一tcp服务为例进行说明

tcp服务端连接监听实现

bool CTcpServer::Listen()
{try{boost::system::error_code err;if (!m_acceptor.is_open()){m_acceptor.open(m_endPoint.protocol(),err);if (err){Stop();return false;}m_acceptor.set_option(tcp::acceptor::reuse_address(true),err);if (err){Stop();return false;}m_acceptor.bind(m_endPoint,err);if (err){Stop();return false;}m_acceptor.listen(socket_base::max_connections,err);if (err){Stop();return false;}}TcpClientPtr client(new CTcpClient(m_io_server,GetId(),true));client->RegisterConnectCallBack(GetConnectCallBack(), GetConnectCallBackContext());client->RegisterReceiveDataCallBack(GetReceiveDataCallBack(), GetReceiveDataCallBackContext());m_acceptor.async_accept(client->GetSocket(),bind(&CTcpServer::AcceptClient,shared_from_this(),boost::asio::placeholders::error,client));}catch (std::exception){return false;}return true;
}

当客户端连接到服务端之后,会调用异步回调

void CTcpServer::AcceptClient(boost::system::error_code err, TcpClientPtr client)
{if (err){return ;}// client connect{sockaddr_in stAddr;std::memset(&stAddr, 0, sizeof(stAddr));stAddr.sin_family = AF_INET;stAddr.sin_addr.s_addr = inet_addr(client->GetAddr().c_str());stAddr.sin_port = ::ntohs(client->GetPort());CCallBack::NotifyConnectCB(client_type_tcp, GetId(), client->GetId(), &stAddr, false);}CClientMgr::get_mutable_instance().PushTcpClient(client->GetId(), client);client->StartKeepAlive();client->AsynRead();Listen();
}

客户端连接之后将由CClientMgr客户端管理对象进行管理。

三、测试

核心的代码我在上面已经列举出来,其他的枝节大家可以自行补脑或搜索实现,下面我们使用tcp-udp测试工具进行测试或使用我写的自带测试工具测试,首先来了解一下netsdk网络的使用,我的头文件中有关于该网络的说明:

// ------------------------------------------------------------------------------------------------
// File:
//      netsdk.h
// Usage:
//      net library for tcp client or udp client or tcp server
// Remark:
//      usage for this library discribed as follow
// Author:
//      lixiangxiang from founder
// History:
//      2015/10/1       v1.0
// Contact:
//      lixiang6153@126.com csdn name:lixiang987654321
// Copyright:
//      lixiang6153@126.com
// ------------------------------------------------------------------------------------------------//-------------------------------------------------------------------------------------------------
// >>>>>>>>>>>>>>>>>>>>>>>usage for this library<<<<<<<<<<<<<<<<
// ********************************tcp server*************************************************
//  NetSdk_Init     →       PNETSDKERRCALLBACK          →       NetSdk_Cleanup
//                                  ↓
//                                  ↓nClientId (client connected,bExit is false,save client id)
//                                  ↓
//                          PRECVDATACALLBACK(data from client)
//                                  ↓
//                                  ↓
//                          NetSdk_Send (response data toclient)
//                                  ↓
//                                  ↓maybe occur
//                          PCONNECTCALLBACK(client disconnect, bExit is true)
//
// ********************************tcp client*************************************************
//  NetSdk_Init →   NetSdk_Connect  →   NetSdk_CloseHandle  →   NetSdk_Cleanup
//                                  ↓
//                                  ↓
//                          PRECVDATACALLBACK(data from server)
//                                  ↓
//                                  ↓maybe occur
//                          PCONNECTCALLBACK(server disconnect, bExit is true)
//
// ********************************udp client*************************************************
//  NetSdk_Init     →       NetSdk_Bind         NetSdk_CloseHandle→ NetSdk_Cleanup
//                                  ↓
//                                  ↓
//                          NetSdk_SendTo(send data to endpoint)
//
// ********************************udp broadcast**********************************************
//  NetSdk_Init     →            NetSdk_Bind        NetSdk_CloseHandle→ NetSdk_Cleanup
//                          ↓                   ↓
//                          ↓                   ↓
//          (send broadcast to everyone)  (receive data from endpoint)
//                  NetSdk_Broadcast           PRECVDATACALLBACK
//
//-------------------------------------------------------------------------------------------------

服务端netsdk使用流程

我这里稍作说明,如果是作为服务器端我们的使用方式如下:

NetSdk_Init初始化网络库=>NetSdk_Listen开始监听本机端口=>
接收连接=>处理数据=>
NetSdk_UnListen停止端口监听=>NetSdk_Cleanup清理网络库资源

我们可以同时监听多个网卡的多个端口,如果需要服务端反馈数据给客户端,可以通过连接返回的clientId,使用客户端相关接口进行回复和反馈。

客户端netsdk使用流程

作为客户端,如果是tcp客户端,使用接口流程如下:

NetSdk_Init初始化网络库=>NetSdk_Connect连接服务器=>处理数据=>NetSdk_Send发送数据到服务端=>
NetSdk_CloseHandle关闭与服务端连接=>NetSdk_Cleanup清理网络库资源

作为tcp客户端,数据发送也可以支持同步和异步发送,默认是异步,同步发送接口NetSdk_SendSync

作为客户端,如果是udp客户端,使用接口流程如下:

NetSdk_Init初始化网络库=>NetSdk_Bind绑定本机端口=>处理数据=>NetSdk_SendTo发送数据报文到对应机器=>
NetSdk_CloseHandle关闭与服务端连接=>NetSdk_Cleanup清理网络库资源

作为udp客户端,数据发送也支持同步发送,同步发送接口:NetSdk_SendTo_Sync;除此之外udp还支持广播,广播接口也支持同步也异步发送,接口如下:

NetSdk_Broadcast
NetSdk_Broadcast_Sync

我的测试工具如下所示:

包括了服务端和客户端,可以将该工具放在2台机器,一台做服务端,一台做客户端,如果没有多余机器,可以使用一个工具进行测试既作为服务端,也作为客户端,客户端使用同一个ip和端口。

作为服务端进行测试

服务端监听和断开监听代码

void CNetsdkDemonDlg::OnBnClickedButtonListen()
{UpdateData(TRUE);if (m_listenHandle < 0) {if (NETERR_SUCCESS != NetSdk_Listen(m_local_port, Connect_Callback,Receive_Data_Callback,Error_Callback,this, &m_listenHandle)) {AfxMessageBox("监听本机端口失败!");return;}GetDlgItem(IDC_BUTTON_LISTEN)->SetWindowText("停止监听");AddLog("服务端:开始监听本机端口:%d", m_local_port);EnableServerButton(FALSE);}else {CloseServer();GetDlgItem(IDC_BUTTON_LISTEN)->SetWindowText("开始监听");AddLog("服务端:停止监听本机端口");EnableServerButton(TRUE);}
}
void CNetsdkDemonDlg::CloseServer()
{if (m_listenHandle > 0){NetSdk_UnListen(m_listenHandle);m_listenHandle = -1;}
}

如本机默认监听的端口为1234,启动demon后点击监听(本地ip使用默认的127.0.0.1监听所有网卡)

监听成功后效果

监听成功后可以使用tcp-dup测试工具测试连接本机的1234端口了

可以看到使用tcp-udp测试工具连接上了我的服务端,注意我的本机真实地址192.168.50.7,服务端监听的是所有网卡的1234端口!

然后我们发送数据试试看!

可以看到服务端收到了客户端id为3的客户发送到服务端的数据!

作为客户端测试

为了避免干扰,我们使用tcp-udp工具作为服务端,我的测试工具作为客户端连接到服务端(当然客户端和服务端完全可以用我的工具)测试如下:

可以看到客户端连接到了服务端,下面我们开始发送数据:

总结

我给的demon仅仅调用了tcp服务端和tcp客户端相关接口,当然还包括udp相关接口,这里可以自己测试调用,我想说明的是本网络库是经过了大量数据测试和多个实战项目测试的一个稳定高效的网络库组件(120路音视频主码流同时发送接收),只要有了网络库组件,你再也不在为项目之间的通信而担忧,只需关注业务逻辑处理即可。

当然,网络库进行是封装了tcp和udp等相关通信,协议层(私有协议或http、rtsp等与业务相关的协议)设计还是需要你个人去设计的,这个与项目的具体业务息息相关,与底层的通信无关(不关心传输的是什么数据)。

另外,本网络库是经过了大量实战和不断更新和修改的稳定高效的网络库,花费了较多的心思,所以如果需要本项目的源码,可以与本人直接沟通购买意向,价格在500-1000不等(自己考量一下应该是很值得了,买一个视频教程都已经到这个数了),该库是一个boost封装了跨平台的库(linux只需要稍作改动,请自行修改,本人不会帮忙修改),代码风格绝对是一个c++老手写的代码,阅读性较强,新手上手较快,很容易修改得linux或定制自己接口或添加其他模块或功能,代码一瞥:

源码获取、合作、技术交流请获取如下联系方式:

QQ交流群:961179337

微信账号:lixiang6153
公众号:IT技术快餐
电子邮箱:lixx2048@163.com

boost网络库开发相关推荐

  1. vs2010c语言安装,VS2010 boost标准库开发环境安装教程

    分享VS2010 boost标准库开发环境安装教程 1. BOOST编译过程非常复杂,目前为了学习BOOST,首先搭建基于VS2010的BOOST开发环境. Boost库是一个可移植.提供源代码的C+ ...

  2. boost标准库开发环境搭建boost标准库环境搭建以及简单案例介绍

    1.下载boost相关的库的安装包 网址:http://www.boost.org/ 其中1.55.0版本的下载地址是:http://sourceforge.net/projects/boost/fi ...

  3. CC++初学者编程教程(3) 安装VS2010 boost标准库开发环境

    1.      BOOST编译过程非常复杂,目前为了学习BOOST,首先搭建基于VS2010的BOOST开发环境. Boost库是一个可移植.提供源代码的C++库,作为标准库的后备,是C++标准化进程 ...

  4. 一、重写muduo网络库之服务器编程及测试

    目录 一.基于muduo网络库开发服务器程序的基本步骤 1.组合TcpServer对象 2.创建EventLoop事件循环对象的指针 3.明确TCPServer构造函数需要的参数,输出ChatServ ...

  5. C++-网络库:Poco概述【开源的C++类库的集合】【提供简单的、快速的网络和可移植应用程序的C++开发】【和C++标准库可以很好的集成并填补C++标准库的功能空缺】【适合嵌入式开发】

    学习一个框架前,要先明白它的是什么,为什么,怎么用.下面这些文字,是从中文poco官网上转过来的,正如poco c++库的特点,非常清晰,代码风格更是一目了然: poco开发库的特点,非常适合写后台处 ...

  6. boost网络串口通信库

    一.前言 前面我写了一篇<boost开发网络库>一文,该文章介绍了使用boost库开发一个高效.稳定的网络通信库,其中用到了c++准标准库boost的asio网络通信模块,本文将要讲的是使 ...

  7. boost库中优秀的网络库asio

    文章目录 一.须知 二.ASIO 三.我们将从研究同步操作开始 四.当使用异步操作时,会发生不同的事件序列 五.Proactor模型 六.常用内容 七.C++ 建立本地网络服务器 (Boost.Asi ...

  8. 【Python3网络爬虫开发实战】3-基本库的使用 1.2-处理异常

    前一节我们了解了请求的发送过程,但是在网络不好的情况下,如果出现了异常,该怎么办呢?这时如果不处理这些异常,程序很可能因报错而终止运行,所以异常处理还是十分有必要的. urllib的error模块定义 ...

  9. 【Python3网络爬虫开发实战】4-解析库的使用-3 使用pyquery

    在上一节中,我们介绍了Beautiful Soup的用法,它是一个非常强大的网页解析库,你是否觉得它的一些方法用起来有点不适应?有没有觉得它的CSS选择器的功能没有那么强大? 如果你对Web有所涉及, ...

最新文章

  1. LINUX系统一些监管命令
  2. 2018第三季度总结
  3. 甲骨文:正在从SAP手中赢得应用产品市场份额
  4. 在Socket做服务器,在手机上用Http访问
  5. FinTech浪潮已到,五大金融场景将迎变革
  6. 实例11:python
  7. html文件钓起始标志,关于html页面head标签顺序
  8. const关键字在c语言的作用,C语言const关键字作用
  9. [转载] python学习笔记(三)- numpy基础:array及matrix详解
  10. 机器学习与深度学习基础概念介绍
  11. 多线程访问共同的代码或者对象:lock避免出错
  12. python用def编写calsum函数_python高阶函数——返回函数(闭包)
  13. 百度地图根据经纬度计算距离
  14. 定积分(Definite Integral)
  15. 归因分析:淘宝直播数据助理及其价值研究
  16. 调用后台接口返回报错前端隐藏提示_腾讯社交联盟广告
  17. android 根文件系统,Android根文件系统相关应用介绍
  18. 自助式分析是数据组织的一种状态
  19. GPU(显卡)的开源驱动调研
  20. win10程序员必备工具_与客户沟通的10种必备工具

热门文章

  1. STM32cubemx学习与使用
  2. WiFi_Direct 直连开发实战
  3. 利用python语言进行相机标定
  4. android 解压 密码 zip 命令,GitHub - Leo0618/AndroidZip: Android端zip压缩与解压,支持使用密码对单文件多文件文件夹进行压缩以及解压操作...
  5. 基于金豺优化算法的函数寻优算法
  6. CSS基本知识总结(HTML+CSS)
  7. 电脑cpu速度测试软件,使用鲁大师 一键测试电脑的处理器(CPU)速度
  8. 微星z370安装linux系统,微星 Z370 GAMING PRO CARBON如何用u盘装系统win7
  9. 产品思考 - 关于马老师演讲
  10. 为什么很多人跨学科也要转行模拟版图设计工程师?