diff --git a/src/steamnetworkingsockets/clientlib/steamnetworkingsockets_udp.cpp b/src/steamnetworkingsockets/clientlib/steamnetworkingsockets_udp.cpp index 6f474eb..73de375 100644 --- a/src/steamnetworkingsockets/clientlib/steamnetworkingsockets_udp.cpp +++ b/src/steamnetworkingsockets/clientlib/steamnetworkingsockets_udp.cpp @@ -539,45 +539,37 @@ int CConnectionTransportUDPBase::SendEncryptedDataChunk( const void *pChunk, int UDPSendPacketContext_t &ctx = static_cast( ctxBase ); uint8 pkt[ k_cbSteamNetworkingSocketsMaxUDPMsgLen ]; - UDPDataMsgHdr *hdr = (UDPDataMsgHdr *)pkt; - hdr->m_unMsgFlags = 0x80; + iovec gather[2]; + gather[0].iov_base = pkt; + DataPacketSerializer out( gather, pChunk, cbChunk ); + out.hdr.m_unMsgFlags = 0x80; Assert( m_connection.m_unConnectionIDRemote != 0 ); - hdr->m_unToConnectionID = LittleDWord( m_connection.m_unConnectionIDRemote ); - hdr->m_unSeqNum = LittleWord( m_connection.m_statsEndToEnd.ConsumeSendPacketNumberAndGetWireFmt( ctx.m_usecNow ) ); - - byte *p = (byte*)( hdr + 1 ); + out.hdr.m_unToConnectionID = LittleDWord( m_connection.m_unConnectionIDRemote ); + out.hdr.m_unSeqNum = LittleWord( m_connection.m_statsEndToEnd.ConsumeSendPacketNumberAndGetWireFmt( ctx.m_usecNow ) ); // Check how much bigger we could grow the header // and still fit in a packet - int cbHdrOutSpaceRemaining = pkt + sizeof(pkt) - p - cbChunk; - if ( cbHdrOutSpaceRemaining < 0 ) + if ( out.HdrBytesRemaining() < 0 ) { AssertMsg( false, "MTU / header size problem!" ); return 0; } // Try to trim stuff from blob, if it won't fit - ctx.Trim( cbHdrOutSpaceRemaining ); + ctx.Trim( out.HdrBytesRemaining() ); - if ( ctx.Serialize( p ) ) + if ( ctx.Serialize( out.m_pOut ) ) { // Update bookkeeping with the stuff we are actually sending TrackSentStats( ctx ); // Mark header with the flag - hdr->m_unMsgFlags |= hdr->kFlag_ProtobufBlob; + out.hdr.m_unMsgFlags |= out.hdr.kFlag_ProtobufBlob; } // !FIXME! Time since previous, for jitter measurement? - // Use gather-based send. This saves one memcpy of every payload - iovec gather[2]; - gather[0].iov_base = pkt; - gather[0].iov_len = p - pkt; - gather[1].iov_base = const_cast( pChunk ); - gather[1].iov_len = cbChunk; - - int cbSend = gather[0].iov_len + gather[1].iov_len; + int cbSend = out.Finish(); Assert( cbSend <= sizeof(pkt) ); // Bug in the code above. We should never "overflow" the packet. (Ignoring the fact that we using a gather-based send. The data could be tiny with a large header for piggy-backed stats.) // !FIXME! Should we track data payload separately? Maybe we ought to track diff --git a/src/steamnetworkingsockets/steamnetworkingsockets_internal.h b/src/steamnetworkingsockets/steamnetworkingsockets_internal.h index 27b6caa..dccce46 100644 --- a/src/steamnetworkingsockets/steamnetworkingsockets_internal.h +++ b/src/steamnetworkingsockets/steamnetworkingsockets_internal.h @@ -1036,6 +1036,62 @@ protected: virtual ~CPossibleOutOfOrderPacket(); }; +// Helper used to serialize data packets with a header and optional inline stats blob +struct DataPacketSerializerBase +{ + int HdrBytesRemaining() const { return int( m_pMaxOut - m_pOut ); } + + inline void PutUint16( uint16 x ) + { + *(uint16*)m_pOut = x; + m_pOut += sizeof(uint16); + Assert( m_pOut <= m_pMaxOut ); + } + + uint8 *m_pOut; + uint8 *m_pMaxOut; +}; + +// Data packet serializer used in client code, where we use iovec +// so that we let th OS gather the header and the payload in +// a single system call +template +struct DataPacketSerializer : DataPacketSerializerBase +{ + DataPacketSerializer( iovec *pOvecOut, const void *pPayloadIn, int cbPayload ) + : m_iov_out( pOvecOut ) + , hdr( *(THdr *)( pOvecOut[0].iov_base ) ) + { + + // Make sure we have room for our header, and some occasional inline stats, and the max payload + COMPILE_TIME_ASSERT( sizeof(THdr) + k_cbSteamNetworkingSocketsMaxEncryptedPayloadSend + 32 <= k_cbSteamNetworkingSocketsMaxUDPMsgLen ); + + // Set payload + m_iov_out[1].iov_base = (void*)pPayloadIn; + m_iov_out[1].iov_len = cbPayload; + + // Write pointer for data after header + m_pOut = (uint8*)( &hdr + 1 ); + + // Max place we could advance this cursor, and still fit in the payload + m_pMaxOut = m_pOut + ( k_cbSteamNetworkingSocketsMaxUDPMsgLen - sizeof(THdr) - cbPayload ); + Assert( m_pMaxOut >= m_pOut ); + } + + int Finish() + { + Assert( m_pOut <= m_pMaxOut ); + + // Set header size + m_iov_out[0].iov_len = m_pOut - (uint8*)m_iov_out[0].iov_base; + + return int( m_iov_out[0].iov_len + m_iov_out[1].iov_len ); + } + + THdr &hdr; + iovec *m_iov_out; +}; + } // namespace SteamNetworkingSocketsLib #include