boost asio处理tcp和udp的不同之处及要点

   最近在看asio,打算基于asio重构媒体服务器中网络模块,学习的过程注意到了在udp和tcp中,对asio使用的所应该注意的地方,现记录下来

  •      tcp与udp的不同之处


    老生常谈的tcp与udp最大的不同是,tcp是可靠的并且保证数据到达。udp是不可靠的,通俗的描述就是它只管发,至于对方是否收到数据,一概不负责。所以在处理tcp和udp上的读写操作就有很大不同,并且在读写缓存的管理上也有很大不同。


  •      读写操作的不同之处


    对tcp而言,比如发送100字节的数据,可能分成第一次发送10字节,第二次发送90字节。也可能第一次就将100字节全部发送完毕。也可能分成了三次发送完毕,第一次发送10字节,第二次发送30字节,第三次发送60字节。其发送的次序完全取决于当时的网络状况和当前tcp发送缓存及滑动窗口大小所决定。所以对接收方而言,当调用read返回时,其结果可能是只是接收到了一部分数据。要接收到完整的数据包,可能需要多次read操作。正是这种原因,应用层在处理tcp数据的收发时,通常需要通过某种方式先告知接收端当前数据包大小。比较常规的做法是在数据包的头几个字节定以数据包长度


   对于udp而言,数据收发的处理就简单很多,发送端只需要调用sendto,udp就会将整个数据包发送出去,对于超过mtu长度数据包,数据包将会截断至mtu长度。对接收端而言,调用revcfrom如果返回成功,意味着收到的是一个完整的数据包。所以对udp来说,唯一需要注意的地方就是确保数据包不超过mtu即可。典型的,对基于udp的rtp,当视频数据大于mtu时,将会将视频数据进行分包,这就是FU-A包产生的初衷。


  •    应用层缓存处理的不同之处

  

  对于tcp而言,应用层buffer的显然需要对数据进行缓存,拼接完整的数据包。


  •   boost asio处理tcp读操作的要点

 对tcp读操作处理,通常如下:

  

boost::asio::async_read(mSocket, boost::asio::buffer((void*)mReceiveBuffer->data(), 4),
boost::bind(&AsyncSocketBase::handleReadHeader, shared_from_this(),  boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));

  boost::asio::buffer((void*)mReceiveBuffer->data(), 4) 例子代码中是数据包的前4个字节包含头的长度信息,所以是先读取头信息,取出整个数据包的大小,AsyncSocketBase::handleReadHeader 业务处理回调即是解析头,在handleReadHeader中绑定的异步操作如下:

boost::asio::async_read(mSocket, boost::asio::buffer(&(*mReceiveBuffer)[0], dataLen),
			boost::bind(&AsyncTcpSocketBase::handleReceive, shared_from_this(),  boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));

boost::asio::buffer(&(*mReceiveBuffer)[0], dataLen)  dataLen即为通过解析头获取到的整个数据包的大小,asio将整个dataLen大小的数据包读取完后才会回调handleReceive。


  • boost asio处理tcp写操作的注意点
  
  对write操作,应用在发包时,可能该需要多次写才能将整个数据包发完。所以可能会出现上一包数据,asio还没发完,此时应用已经产生了下一包数据需要发送,所以通常在处理写操作时,需要在应用层加入一个缓存。保证依次发送数据。典型的在采用rtp协议的流媒体系统中,视频数据和音频数据是源源不断的产生的。对tcp应用而言,在写操作上更应加入缓存。
std::vector<boost::asio::const_buffer> bufs;
if(mSendDataQueue.front().mFrameData.get() != 0) // If we have frame data
{
    bufs.push_back(boost::asio::buffer(mSendDataQueue.front().mFrameData->data(),  mSendDataQueue.front().mFrameData->size()));
}
bufs.push_back(boost::asio::buffer(mSendDataQueue.front().mData->data()+mSendDataQueue.front().mBufferStartPos, 
                                  mSendDataQueue.front().mData->size()-mSendDataQueue.front().mBufferStartPos));
boost::asio::async_write(mSocket, buffers, boost::bind(&AsyncTcpSocketBase::handleSend, shared_from_this(),
boost::asio::placeholders::error);

  如上所示,如果有缓存的数据则先发送缓存数据。

猜你喜欢

转载自blog.csdn.net/mo4776/article/details/78811272
今日推荐