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

学习过网络编程的人,应该都知道UDP是“不可靠”的协议。不知道你有没有想过UDP为什么不可靠,不可靠在那些方面。其实,UDP和TCP协议一样都是建立在不可靠的IP协议之上,UDP的不可靠是指它不具备流量控制,不具备数据包顺序达到,不具备验证数据包是否丢失。那么在第二人生里使用UDP协议又是怎么样来实现可靠的数据传送的呢?现在就来分析下面这段代码:
#001// This can be called from signal handlers,
#002// so should should not use llinfos.
#003S32 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、付费专栏及课程。

余额充值