蔡军生先生第二人生的源码分析(二十八)UDP发送数据的可靠性控制

学习过网络编程的人,应该都知道UDP是“不可靠”的协议。不知道你有没有想过UDP为什么不可靠,不可靠在那些方面。其实,UDP和TCP协议一样都是建立在不可靠的IP协议之上,UDP的不可靠是指它不具备流量控制,不具备数据包顺序达到,不具备验证数据包是否丢失。那么在第二人生里使用UDP协议又是怎么样来实现可靠的数据传送的呢?现在就来分析下面这段代码:
#001 // This can be called from signal handlers,
#002 // so should should not use llinfos.
#003 S32 LLMessageSystem::sendMessage(const LLHost &host)
#004 {
#005       if (! mMessageBuilder->isBuilt())
#006       {
#007              mSendSize = mMessageBuilder->buildMessage(
#008                     mSendBuffer,
#009                     MAX_BUFFER_SIZE,
#010                     0);
#011       }
#012 
如果消息还没有打包,就进行打包。
 
#013       if (!(host.isOk()))    // if port and ip are zero, don't bother trying to send the message
#014       {
#015              return 0;
#016       }
#017 
判断服务器是否可用。
 
下面查找服务器与客户端的UDP通信环路。
#018       LLCircuitData *cdp = mCircuitInfo.findCircuit(host);
#019       if (!cdp)
#020       {
#021              // this is a new circuit!
#022              // are we protected?
#023              if (mbProtected)
#024              {
#025                     // yup! don't send packets to an unknown circuit
#026                     if(mVerboseLog)
#027                     {
#028                            llinfos << "MSG: -> " << host << "/tUNKNOWN CIRCUIT:/t"
#029                                          << mMessageBuilder->getMessageName() << llendl;
#030                     }
#031                     llwarns << "sendMessage - Trying to send "
#032                                   << mMessageBuilder->getMessageName() << " on unknown circuit "
#033                                   << host << llendl;
#034                     return 0;
#035              }
#036              else
#037              {
#038                     // nope, open the new circuit
#039 
#040                     cdp = mCircuitInfo.addCircuitData(host, 0);
#041              }
#042       }
#043       else
#044       {
#045              // this is an old circuit. . . is it still alive?
#046              if (!cdp->isAlive())
#047              {
#048                     // nope. don't send to dead circuits
#049                     if(mVerboseLog)
#050                     {
#051                            llinfos << "MSG: -> " << host << "/tDEAD CIRCUIT/t/t"
#052                                          << mMessageBuilder->getMessageName() << llendl;
#053                     }
#054                     llwarns << "sendMessage - Trying to send message "
#055                                   << mMessageBuilder->getMessageName() << " to dead circuit "
#056                                   << host << llendl;
#057                     return 0;
#058              }
#059       }
#060 
 
下面判断是否通过HTTP协议发送数据,还是通过UDP协议来发送。
#061       // NOTE: babbage: LLSD message -> HTTP, template message -> UDP
#062       if(mMessageBuilder == mLLSDMessageBuilder)
#063       {
#064              LLSD message = mLLSDMessageBuilder->getMessage();
#065             
#066              const LLHTTPSender& sender = LLHTTPSender::getSender(host);
#067              sender.send(
#068                     host,
#069                     mLLSDMessageBuilder->getMessageName(),
#070                     message,
#071                     createResponder(mLLSDMessageBuilder->getMessageName()));
#072 
#073              mSendReliable = FALSE;
#074              mReliablePacketParams.clear();
#075              return 1;
#076       }
#077 
#078       // zero out the flags and packetid. Subtract 1 here so that we do
#079       // not overwrite the offset if it was set set in buildMessage().
#080       memset(mSendBuffer, 0, LL_PACKET_ID_SIZE - 1);
#081 
 
下面开创建数据包的ID。
#082       // add the send id to the front of the message
#083       cdp->nextPacketOutID();
#084 
#085       // Packet ID size is always 4
#086       *((S32*)&mSendBuffer[PHL_PACKET_ID]) = htonl(cdp->getPacketOutID());
#087 
#088       // Compress the message, which will usually reduce its size.
#089       U8 * buf_ptr = (U8 *)mSendBuffer;
#090       U32 buffer_length = mSendSize;
#091       mMessageBuilder->compressMessage(buf_ptr, buffer_length);
#092 
上面进行数据压缩的工作。
 
#093       if (buffer_length > 1500)
#094       {
#095              if((mMessageBuilder->getMessageName() != _PREHASH_ChildAgentUpdate)
#096                 && (mMessageBuilder->getMessageName() != _PREHASH_SendXferPacket))
#097              {
#098                     llwarns << "sendMessage - Trying to send "
#099                                   << ((buffer_length > 4000) ? "EXTRA " : "")
#100                                   << "BIG message " << mMessageBuilder->getMessageName() << " - "
#101                                   << buffer_length << llendl;
#102              }
#103       }
 
 
下面把数据放到可靠的连接里发送数据。
#104       if (mSendReliable)
#105       {
#106              buf_ptr[0] |= LL_RELIABLE_FLAG;
#107 
#108              if (!cdp->getUnackedPacketCount())
#109              {
#110                     // We are adding the first packed onto the unacked packet list(s)
#111                     // Add this circuit to the list of circuits with unacked packets
#112                     mCircuitInfo.mUnackedCircuitMap[cdp->mHost] = cdp;
#113              }
#114 
#115              cdp->addReliablePacket(mSocket,buf_ptr,buffer_length, &mReliablePacketParams);
#116              mReliablePacketsOut++;
#117       }
#118 
 
把收到服务器的包标识ID放到发送的数据后面返回给服务器。
#119       // tack packet acks onto the end of this message
#120       S32 space_left = (MTUBYTES - buffer_length) / sizeof(TPACKETID); // space left for packet ids
#121       S32 ack_count = (S32)cdp->mAcks.size();
#122       BOOL is_ack_appended = FALSE;
#123       std::vector<TPACKETID> acks;
#124       if((space_left > 0) && (ack_count > 0) &&
#125          (mMessageBuilder->getMessageName() != _PREHASH_PacketAck))
#126       {
#127              buf_ptr[0] |= LL_ACK_FLAG;
#128              S32 append_ack_count = llmin(space_left, ack_count);
#129              const S32 MAX_ACKS = 250;
#130              append_ack_count = llmin(append_ack_count, MAX_ACKS);
#131              std::vector<TPACKETID>::iterator iter = cdp->mAcks.begin();
#132              std::vector<TPACKETID>::iterator last = cdp->mAcks.begin();
#133              last += append_ack_count;
#134              TPACKETID packet_id;
#135              for( ; iter != last ; ++iter)
#136              {
#137                     // grab the next packet id.
#138                     packet_id = (*iter);
#139                     if(mVerboseLog)
#140                     {
#141                            acks.push_back(packet_id);
#142                     }
#143 
#144                     // put it on the end of the buffer
#145                     packet_id = htonl(packet_id);
#146 
#147                     if((S32)(buffer_length + sizeof(TPACKETID)) < MAX_BUFFER_SIZE)
#148                     {
#149                         memcpy(&buf_ptr[buffer_length], &packet_id, sizeof(TPACKETID));       /* Flawfinder: ignore */
#150                         // Do the accounting
#151                         buffer_length += sizeof(TPACKETID);
#152                     }
#153                     else
#154                     {
#155                         // Just reporting error is likely not enough. Need to
#156                         // check how to abort or error out gracefully from
#157                         // this function. XXXTBD
#158                            // *NOTE: Actually hitting this error would indicate
#159                            // the calculation above for space_left, ack_count,
#160                            // append_acout_count is incorrect or that
#161                            // MAX_BUFFER_SIZE has fallen below MTU which is bad
#162                            // and probably programmer error.
#163                         llerrs << "Buffer packing failed due to size.." << llendl;
#164                     }
#165              }
#166 
#167              // clean up the source
#168              cdp->mAcks.erase(cdp->mAcks.begin(), last);
#169 
#170              // tack the count in the final byte
#171              U8 count = (U8)append_ack_count;
#172              buf_ptr[buffer_length++] = count;
#173              is_ack_appended = TRUE;
#174       }
#175 
 
下面调用函数sendPacket按流量控制发送数据给服务器。
#176       BOOL success;
#177       success = mPacketRing.sendPacket(mSocket, (char *)buf_ptr, buffer_length, host);
#178 
#179       if (!success)
#180       {
#181              mSendPacketFailureCount++;
#182       }
#183       else
#184       {
#185              // mCircuitInfo already points to the correct circuit data
#186              cdp->addBytesOut( buffer_length );
#187       }
#188 
#189       if(mVerboseLog)
#190       {
#191              std::ostringstream str;
#192              str << "MSG: -> " << host;
#193              char buffer[MAX_STRING];                     /* Flawfinder: ignore */
#194              snprintf(buffer, MAX_STRING, "/t%6d/t%6d/t%6d ", mSendSize, buffer_length, cdp->getPacketOutID());             /* Flawfinder: ignore */
#195              str << buffer
#196                     << mMessageBuilder->getMessageName()
#197                     << (mSendReliable ? " reliable " : "");
#198              if(is_ack_appended)
#199              {
#200                     str << "/tACKS:/t";
#201                     std::ostream_iterator<TPACKETID> append(str, " ");
#202                     std::copy(acks.begin(), acks.end(), append);
#203              }
#204              llinfos << str.str() << llendl;
#205       }
#206 
#207       /*lldebugst(LLERR_MESSAGE) << "MessageSent at: " << (S32)totalTime()
#208                                                  << "," << mMessageBuilder->getMessageName()
#209                                                  << " to " << host
#210                                                  << llendl;*/
#211 
#212       mPacketsOut++;
#213       mBytesOut += buffer_length;
#214      
#215       mSendReliable = FALSE;
#216       mReliablePacketParams.clear();
#217       return buffer_length;
#218 }
 
在上面这个函数里,先调用函数buildMessage把消息打包,然后调用host.isOk()判断服务器是否可用,接着查看是否通HTTP来传送。然后再看是否需要可靠性地发送数据,如果需要就把这个消息加到可靠性发送的队列里。在每次发送数据之前,还需要判断是否从服务器收到回应包的ID,如果有,就把这些ID放到发送数据包的后面,一起发送回去给服务器,这样就让服务器知道客户端已经收到什么数据。也就是说有确认机制,就可以把数据控制得可靠了。最后调用mPacketRing.sendPacket函数来通UDP来发送数据给服务器。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值