// // // // // $Revision$ // using System; using System.Net; using System.Net.Sockets; using System.Diagnostics; using System.IO; using System.Runtime; using System.Runtime.Serialization; using System.Runtime.Serialization.Formatters; using System.Runtime.Serialization.Formatters.Binary; namespace ICSharpCode.SharpDevelop.BuildWorker.Interprocess { public sealed class PacketReceiver { Stream stream; byte[] buffer = new byte[10000]; int bufferReadOffset; public void StartReceive(Stream stream) { if (stream == null) throw new ArgumentNullException("stream"); if (this.stream != null) throw new InvalidOperationException("StartReceive can be called only once."); this.stream = stream; try { stream.BeginRead(buffer, 0, buffer.Length, OnReceive, null); } catch (ObjectDisposedException) { OnConnectionLost(); } catch (IOException) { OnConnectionLost(); } } int maxPacketSize = int.MaxValue - 20000; /// /// Gets/Sets the maximum allowed packet size in bytes. /// public int MaxPacketSize { get { return maxPacketSize; } set { if (value < 1) throw new ArgumentOutOfRangeException("value", value, "MaxPacketSize must be >0"); maxPacketSize = value; } } void OnReceive(IAsyncResult ar) { int bytes; try { bytes = stream.EndRead(ar); } catch (ObjectDisposedException) { OnConnectionLost(); return; } catch (IOException) { OnConnectionLost(); return; } if (bytes == 0) { // 0 bytes read indicates the end of the stream OnConnectionLost(); return; } bufferReadOffset += bytes; int packetStart = 0; int packetSize = -1; while (bufferReadOffset >= packetStart + 4) { packetSize = BitConverter.ToInt32(buffer, packetStart); if (packetSize < 4) throw new ProtocolViolationException("packetSize must be > 4"); if (packetSize - 4 > MaxPacketSize) throw new ProtocolViolationException("packetSize must be smaller than MaxPacketSize"); if (bufferReadOffset >= packetStart + packetSize) { //Debug.WriteLine("receiving packet of size " + packetSize); byte[] packet = new byte[packetSize - 4]; Array.Copy(buffer, packetStart + 4, packet, 0, packet.Length); OnPacketReceived(packet); packetStart += packetSize; } else { break; } } if (packetStart != 0) { // copy half-received packet to the beginning of the buffer int copyAmount = bufferReadOffset - packetStart; for (int i = 0; i < copyAmount; i++) { buffer[i] = buffer[i + packetStart]; } bufferReadOffset = copyAmount; } if (packetSize > buffer.Length) { Debug.WriteLine("resizing receive buffer for packet of size " + packetSize); Array.Resize(ref buffer, Math.Max(packetSize, buffer.Length * 2)); } if (bufferReadOffset >= buffer.Length) { // should never happen - the buffer now is large enough to contain the packet, // and we would have already processed the packet if received it completely throw new InvalidOperationException("trying to read 0 bytes from socket"); } try { stream.BeginRead(buffer, bufferReadOffset, buffer.Length - bufferReadOffset, OnReceive, null); } catch (ObjectDisposedException) { OnConnectionLost(); } catch (IOException) { OnConnectionLost(); } } void OnConnectionLost() { if (ConnectionLost != null) ConnectionLost(this, EventArgs.Empty); } void OnPacketReceived(byte[] packet) { if (PacketReceived != null) PacketReceived(this, new PacketReceivedEventArgs(packet)); } public event EventHandler ConnectionLost; public event EventHandler PacketReceived; } public class PacketReceivedEventArgs : EventArgs { byte[] packet; [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] public byte[] Packet { get { return packet; } } public PacketReceivedEventArgs(byte[] packet) { this.packet = packet; } } }