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

来源:互联网 发布:java菜单功能管理系统 编辑:程序博客网 时间:2024/05/14 13:49
学习过网络编程的人,应该都知道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来发送数据给服务器。
 
原创粉丝点击