/// <summary> /// Initiates the complete reception of bytes from the remote endpoint over a TCP connection. /// </summary> /// <param name="buffer">The destination buffer.</param> /// <param name="offset">Index where the first received byte is to be written.</param> /// <param name="count">Number of bytes to be received.</param> /// <param name="callback">The delegate to be called when the operation completes (or <c>null</c>).</param> /// <param name="state">Application specific state.</param> /// <returns> /// An <see cref="IAsyncResult" /> instance to be used to track the progress of the /// operation and to eventually be passed to the <see cref="EndReceive" /> method. /// </returns> /// <exception cref="InvalidOperationException">Thrown if the socket is not connected, if it is a UDP connection or if another receive operation is already pending.</exception> /// <exception cref="ArgumentNullException">Thrown if <paramref name="buffer" /> is <c>null</c>.</exception> /// <exception cref="IndexOutOfRangeException">Thrown if <paramref name="offset" /> and <paramref name="count" /> specify bytes outside of the <paramref name="buffer" />.</exception> /// <remarks> /// <note> /// This method is available only for TCP connections and only one receive operation may be pending per socket. /// </note> /// <note> /// The operation will be considered to have completed only when the specified number of bytes have been /// received from the remote endpoint. This method will never return partial results. /// </note> /// <note> /// All successful calls to <see cref="BeginReceiveAll" /> must eventually be followed by a call to <see cref="EndReceiveAll" />. /// </note> /// </remarks> public IAsyncResult BeginReceiveAll(byte[] buffer, int offset, int count, AsyncCallback callback, object state) { if (buffer == null) { throw new ArgumentNullException("buffer"); } if (offset < 0 || count < 0 || offset + count > buffer.Length + 1) { throw new IndexOutOfRangeException(string.Format("[LiteSocket.Send: offset={0}] and [count={1}] is not valid for buffer of length [{2}].", offset, count, buffer.Length)); } lock (syncLock) { if (!Connected) { throw new InvalidOperationException("Socket is not connected."); } if (!isTcp) { throw new InvalidOperationException("[LiteSocket.BeginReceiveAll] is not supported on UDP connections."); } if (receivePending) { throw new InvalidOperationException("LiteSocket.ReceiveAll: Another receive operation is already pending on this socket."); } receivePending = true; try { return(sock.BeginReceiveAll(buffer, offset, count, SocketFlags.None, callback, state)); } catch { receivePending = false; throw; } } }
private void OnReceive(IAsyncResult ar) { List <SipMessage> received = null; try { using (TimedLock.Lock(transport)) { int cb; int pos; int cbContents; SipHeader contentLength; byte[] packet; try { if (sock == null) { return; } if (contentBuf == null) { // We're reading a message envelope. // Read packets into headerBuf until we can find the CRLFCRLF // sequence marking the end of the message headers. cb = sock.EndReceive(ar); if (cb == 0) { // The socket has been closed on by the remote element. CloseAndRemove(); return; } cbRecv += cb; tryAgain: // Remove any leading CR or LF characters by shifting the // buffer contents. I know this isn't super efficient but // we'll probably never actually see packets with this // in the wild. for (pos = 0; pos < cbRecv; pos++) { if (headerBuf[pos] != 0x0D && headerBuf[pos] != 0x0A) { break; } } if (pos != 0) { if (pos == cbRecv) { // No data remaining in the buffer cbRecv = 0; sock.BeginReceive(headerBuf, 0, headerBuf.Length, SocketFlags.None, onRecv, null); return; } Array.Copy(headerBuf, pos, headerBuf, 0, headerBuf.Length - pos); cbRecv -= pos; } // Scan the message for the CRLFCRLF sequence terminating the // message envelope. pos = Helper.IndexOf(headerBuf, CRLFCRLF, 0, cbRecv); if (pos != -1) { // We've got the message envelope pos += 4; // Advance past the CRLFCRLF // Parse the message headers and then get the Content-Length header packet = Helper.Extract(headerBuf, 0, pos); try { message = SipMessage.Parse(packet, false); } catch (Exception e) { SipHelper.Trace(string.Format("TCP: UNPARSABLE message received from {0}: [{1}]", remoteEP, e.Message), Helper.FromUTF8(packet)); throw; } contentLength = message[SipHeader.ContentLength]; if (contentLength == null || !int.TryParse(contentLength.Text, out cbContents) || cbContents < 0) { var e = new SipException("Malformed SIP message: Invalid or missing [Content-Length] header from streaming transport."); e.Transport = "TCP"; e.BadPacket = packet; e.SourceEndpoint = remoteEP; throw e; } if (cbContents > MaxContentSize) { var e = new SipException("Invalid SIP message: [Content-Length={0}] exceeds [{1}].", cbContents, MaxContentSize); e.Transport = "TCP"; e.BadPacket = packet; e.SourceEndpoint = remoteEP; throw e; } if (pos + cbContents <= cbRecv) { // We already have the message contents, so extract the contents, // add them to the message, and then queue the message for delivery // once we leave the lock. message.Contents = Helper.Extract(headerBuf, pos, cbContents); if (received == null) { received = new List <SipMessage>(); } received.Add(message); message = null; // Shift any remaining data to the left in headerBuf, // adjust cbRecv, and the loop to look for another // message. pos += cbContents; cb = cbRecv - pos; // Bytes remaining in the buffer if (cb == 0) { // No more data left in the buffer cbRecv = 0; sock.BeginReceive(headerBuf, 0, headerBuf.Length, SocketFlags.None, onRecv, null); return; } Array.Copy(headerBuf, pos, headerBuf, 0, cb); cbRecv = cb; goto tryAgain; } // We don't have all of the message contents, so allocate a buffer for // the contents, copy what we have already into this buffer, and then // initiate a receive operation to read the remaining data. contentBuf = new byte[cbContents]; cbRecv = cbRecv - pos; // Content bytes remaining in the buffer Array.Copy(headerBuf, pos, contentBuf, 0, cbRecv); sock.BeginReceiveAll(contentBuf, cbRecv, cbContents - cbRecv, SocketFlags.None, onRecv, null); return; } // Throw an error if the header buffer is full and we still haven't // found the end of the envelope. if (cbRecv >= headerBuf.Length) { var e = new SipException("Malformed SIP message: Read [{0}] bytes and have not yet encountered end of headers.", headerBuf.Length); e.Transport = "TCP"; e.SourceEndpoint = remoteEP; throw e; } // Continue receiving header data. sock.BeginReceive(headerBuf, cbRecv, headerBuf.Length - cbRecv, SocketFlags.None, onRecv, null); } else { // We're in the process of reading the message contents. // Complete the contents receive operation and queue the // message for delivery after we leave the lock. sock.EndReceiveAll(ar); message.Contents = contentBuf; if (received == null) { received = new List <SipMessage>(); } received.Add(message); // Reset and start reading the next message envelope. message = null; contentBuf = null; cbRecv = 0; sock.BeginReceive(headerBuf, 0, headerBuf.Length, SocketFlags.None, onRecv, null); } } catch (SocketException) { CloseAndRemove(); } catch (Exception e) { SysLog.LogException(e); CloseAndRemove(); } } } finally { // Deliver any queued messages (outside of the lock) if (received != null) { foreach (var message in received) { message.SourceTransport = transport; message.RemoteEndpoint = remoteEP; if ((transport.traceMode & SipTraceMode.Receive) != 0) { SipHelper.Trace(string.Format("TCP: received from {0}", remoteEP), message); } transport.router.Route(transport, message); } } } }