OnData()
functions only work with binary messages.
void CSocketServer::OnData( IWebSocket & /*socket*/, const _tstring & /*text*/, const MessageStatus /*status*/, const __int64 /*messageBytesOutstanding*/) { // For this to be called you need to specify CProtocolHandler::DispatchTextAsStrings in the flags that are passed // during construction throw CException( _T("CSocketServer::OnData()"), _T("Unexpected: we don't handle text frames")); } void CSocketServer::OnData( IWebSocket &socket, IBuffer &buffer, const MessageType type, const MessageStatus status, const __int64 messageBytesOutstanding) { if (status != MessageStatusComplete) { throw CException( _T("CSocketServer::OnData()"), _T("Unexpected: message larger than buffer size")); } if (messageBytesOutstanding != 0) { throw CException( _T("CSocketServer::OnData()"), _T("Unexpected: message larger than buffer size: ") + ToString(messageBytesOutstanding) + _T(" outstanding")); } //DEBUG_ONLY(Output(_T("OnData - ") + ToString(buffer.GetUsed()) + _T(" bytes\r\n") + DumpData(buffer.GetMemory(), buffer.GetUsed()))); if (type == MessageTypeText) { socket.TryWriteText(buffer); } else { socket.TryWriteBinary(buffer); } socket.TryRead(); }
class CPerConnectionData { public : CPerConnectionData( const IIndexedOpaqueUserData::UserDataIndex nextBufferIndex); ~CPerConnectionData(); void OnData( IWebSocket &socket, IBuffer &buffer, const MessageType type, const MessageStatus status, const __int64 frameBytesOutstanding); private : void AddBuffer( IBuffer &buffer); void EchoMessage( IWebSocket &socket); __int64 m_messageSize; MessageType m_messageType; CBufferChain m_messageBuffers; /// No copies do not implement CPerConnectionData(const CPerConnectionData &rhs); /// No copies do not implement CPerConnectionData &operator=(const CPerConnectionData &rhs); };
OnData()
callback now passes all of the work off to the per-connection data. void CMessageAccumulatingSocketServer::OnData( IWebSocket &socket, IBuffer &buffer, const MessageType type, const MessageStatus status, const __int64 messageBytesOutstanding) { CPerConnectionData *pConnectionData = reinterpret_cast<CPerConnectionData *>(socket.GetUserPointer(m_userDataIndex)); pConnectionData->OnData(socket, buffer, type, status, messageBytesOutstanding); }
void CPerConnectionData::OnData( IWebSocket &socket, IBuffer &buffer, const MessageType type, const MessageStatus status, const __int64 messageBytesOutstanding) { if (m_messageSize == 0) { m_messageType = type; } else if (m_messageType != type) { throw CException(_T("CPerConnectionData::OnDataFrame()"), _T("Unexpected frame type")); } if (status == MessageStatusComplete && messageBytesOutstanding == 0) { AddBuffer(buffer); EchoMessage(socket); socket.TryRead(); } else if (buffer.GetSize() == buffer.GetUsed()) { AddBuffer(buffer); socket.TryRead(); } else { socket.TryRead(&buffer); } }
OnData()
callback as it arrives. If we need to accumulate complete messages before processing then accumulating in the WebSocket protocol handler is sensible as it means that for messages that are smaller than the buffer size we have less work to do. If, however, we could process messages incrementally then we should not have the protocol handler accumulate messages as this could delay our processing. The example server configures both kinds of endpoint and, to us, it doesn't make a lot of difference. If the protocol handler is accumulating messages then we'll recieve full buffers except for the final buffer. If the protocol handler is not accumulating then we'll also use the final branch of the if
to accumulate more data into the space that's left in the buffer. void CPerConnectionData::AddBuffer( IBuffer &buffer) { const IBuffer::BufferSize used = buffer.GetUsed(); if (used || m_messageBuffers.IsEmpty()) { m_messageSize += used; m_messageBuffers.Add(buffer); } }
CPerConnectionData::AddBuffer()
is where we could impose message size limits if we wanted to. Be aware that this example, as presented, could suffer from overflow in m_messageSize
. void CPerConnectionData::EchoMessage( IWebSocket &socket) { CSmartBuffer buffer(m_messageBuffers.GetNext()); socket.StartMessage(m_messageType, m_messageSize, buffer.GetRef()); buffer = m_messageBuffers.GetNext(); while (buffer.Get()) { socket.SendMessageData(buffer.GetRef()); buffer = m_messageBuffers.GetNext(); } m_messageSize = 0; }
FIN
bit set and the client recognises that the message is now complete. true
for the, normally defaulted, flag to specify that this is the final fragment. void CFragmentEchoingSocketServer::OnData( IWebSocket &socket, IBuffer &buffer, const MessageType type, const MessageStatus status, const __int64 messageBytesOutstanding) { const bool echoing = ToBool(socket.GetUserData(m_userDataIndex)); if (echoing) { if (status == MessageStatusComplete && messageBytesOutstanding == 0) { socket.StartNewFragment(buffer.GetUsed(), buffer, true); socket.SetUserData(m_userDataIndex, false); } else { socket.StartNewFragment(buffer.GetUsed(), buffer); } } else { socket.StartFragmentedMessage(type, buffer.GetUsed(), buffer); socket.SetUserData(m_userDataIndex, true); } socket.TryRead(); }