学习过网络编程的人,应该都知道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来发送数据给服务器。