//////////////////////////////////////////////////////////////////////////////// // // Microsoft Research Singularity // // Copyright (c) Microsoft Corporation. All rights reserved. // // File: TcpBlast.cs // using System; using System.Net; using System.Collections.Generic; using System.Diagnostics; using System.Net.Sockets; static class TcpBlast { public static int Main(string[] args) { try { Dictionary namedargs; string[] posargs; ParseArgs(args, out namedargs, out posargs); if (posargs.Length == 0) { Usage(); return 1; } if (posargs[0] == "accept") { return Accept(namedargs); } else if (posargs[0] == "connect") { return Connect(namedargs); } else { Usage(); return 1; } } catch (Exception ex) { ShowException(ex); return 1; } } static void Usage() { Console.WriteLine("This program tests TCP/IP implementations by sending or receiving"); Console.WriteLine("known sequences of bytes."); Console.WriteLine(); Console.WriteLine("Usage:"); Console.WriteLine(); Console.WriteLine(" tcpblast accept [-port:]"); Console.WriteLine(" tcpblast connect -dest: [-port:]"); } static void ParseArgs( string[] rawargs, out Dictionary namedargs, out string[] posargs) { Dictionary namedlist = new Dictionary(); List poslist = new List(); foreach (string arg in rawargs) { if (String.IsNullOrEmpty(arg)) { continue; } if (arg[0] == '/' || arg[0] == '-') { int separator = arg.IndexOf(':', 1); if (separator == -1) { separator = arg.IndexOf('=', 1); } string name; string value; if (separator != -1) { name = arg.Substring(1, separator - 1); value = arg.Substring(separator + 1); } else { name = arg.Substring(1); value = ""; } namedlist[name] = value; } else { poslist.Add(arg); } } namedargs = namedlist; posargs = poslist.ToArray(); } static void Connect(string[] args) { for (int i = 1; i < args.Length; i++) { } } const int DefaultPort = 4000; static IInt32SequenceFactory GetSequenceFactory(Dictionary args) { string name; if (!args.TryGetValue("seq", out name)) { return delegate { return new AscendingSequence(0, 1); }; } IInt32SequenceFactory factory = WellKnownSequences.GetWellKnownSequence(name); return factory; } static int Accept(Dictionary args) { IInt32SequenceFactory sequenceFactory = GetSequenceFactory(args); if (sequenceFactory == null) { Console.WriteLine("Invalid sequence specified."); return 1; } using (Socket listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)) { int port = DefaultPort; string portString; if (args.TryGetValue("port", out portString)) { if (!Int32.TryParse(portString, out port)) { Console.WriteLine("Invalid port string."); return 1; } } IPEndPoint any = new IPEndPoint(IPAddress.Any, port); try { listener.Bind(any); } catch (Exception ex) { Console.WriteLine("Bind failed."); ShowException(ex); return 1; } try { listener.Listen(5); } catch (Exception ex) { Console.WriteLine("Listen failed."); ShowException(ex); return 1; } for (; ; ) { Socket client; Console.WriteLine("Waiting for client to connect at port: " + listener.LocalEndPoint); try { client = listener.Accept(); } catch (Exception ex) { Console.WriteLine("Accept failed."); ShowException(ex); break; } Console.WriteLine("Accepted client."); try { RunClient(args, client, sequenceFactory); } catch (Exception ex) { ShowException(ex); } finally { client.Close(); } Console.WriteLine(); } } return 0; } static int Connect(Dictionary args) { IInt32SequenceFactory sequenceFactory = GetSequenceFactory(args); if (sequenceFactory == null) { Console.WriteLine("Invalid sequence specified."); return 1; } string destText; if (!args.TryGetValue("dest", out destText)) { Console.WriteLine("Connect requires /dest:[xxx] parameter."); return 1; } int port = DefaultPort; string portString; if (args.TryGetValue("port", out portString)) { if (!Int32.TryParse(portString, out port)) { Console.WriteLine("Invalid port string."); return 1; } } Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); try { try { socket.Connect(destText, port); } catch (Exception ex) { Console.WriteLine("Failed to connect to '{0}' at port {1}.", destText, port); ShowException(ex); return 1; } Console.WriteLine("Connected to {0}.", socket.RemoteEndPoint); RunClient(args, socket, sequenceFactory); } finally { socket.Close(); } return 0; } static void RunClient(Dictionary args, Socket client, IInt32SequenceFactory sequenceFactory) { Console.WriteLine("Running tests."); bool blast = args.ContainsKey("blast"); if (blast) { RunBlast(args, client, sequenceFactory); } else { RunGulp(args, client, sequenceFactory); } } static void RunBlast(Dictionary args, Socket client, IInt32SequenceFactory sequenceFactory) { Console.WriteLine("Blasting TCP client..."); IInt32Sequence sequence = sequenceFactory(); int blockSize = 5112; long sequenceNumber = 0; byte[] block = new byte[blockSize]; RateWatcher rate = new RateWatcher(); for (; ; ) { for (int i = 0; i < blockSize; i++) { int f = sequence.GetNext(); block[i] = (byte)(f & 0xff); // Debug.WriteLine(String.Format("{0,3} -> 0x{1:x}", i, block[i])); } int bytesTransferred = client.Send(block, 0, blockSize, SocketFlags.None); if (bytesTransferred != blockSize) { Console.WriteLine("The send transferred fewer bytes than requested!"); break; } sequenceNumber += blockSize; rate.AddBytes(blockSize); } } static void RunGulp(Dictionary args, Socket client, IInt32SequenceFactory sequenceFactory) { Console.WriteLine("Running Gulp."); IInt32Sequence sequence = sequenceFactory(); long sequenceNumber = 0; int blockSize = 5112; byte[] block = new byte[blockSize]; int ticksAtLastPrint = Environment.TickCount; RateWatcher rate = new RateWatcher(); for (; ; ) { int bytesRead = client.Receive(block); if (bytesRead == 0) { Console.WriteLine("The socket has closed."); return; } // Verify that the contents match the expected sequence. for (int i = 0; i < bytesRead; i++) { int f = sequence.GetNext(); byte expected = (byte)(f & 0xff); if (block[i] != expected) { Console.WriteLine("Received sequence does not match expected sequence!"); Console.WriteLine("At stream offset {0} (0x{0:x}), expected 0x{1:x}, received 0x{2:x}.", sequenceNumber, expected, block[i]); return; } } sequenceNumber += bytesRead; rate.AddBytes(bytesRead); } } class RateWatcher { public void AddBytes(int count) { _bytesSinceLastReport += count; int ticksNow = Environment.TickCount; TimeSpan elapsed = TimeSpan.FromMilliseconds(ticksNow - _ticksAtLastReport); if (elapsed >= _reportInterval) { double bytesPerSecond = _bytesSinceLastReport / elapsed.TotalSeconds; Console.WriteLine("Transfer rate: {0}", BytesPerSecondToString(bytesPerSecond)); _bytesSinceLastReport = 0; _ticksAtLastReport = ticksNow; } } int _ticksAtLastReport = Environment.TickCount; long _bytesSinceLastReport; TimeSpan _reportInterval = TimeSpan.FromSeconds(3); public TimeSpan ReportInterval { get { return _reportInterval; } set { _reportInterval = value; } } public static string BytesPerSecondToString(double bps) { if (bps < 1000) { return String.Format("{0:F3} Bytes/Sec", bps); } if (bps < 1e6) { return String.Format("{0:F3} KBytes/Sec", bps / 1000.0); } if (bps < 1e9) { return String.Format("{0:F3} MBytes/Sec", bps / 1e6); } return String.Format("{0:F3} GBytes/Sec", bps / 1e9); } } static void ShowException(Exception ex) { Console.WriteLine("{0}: {1}", ex.GetType().FullName, ex.Message); for (Exception inner = ex.InnerException; inner != null; inner = inner.InnerException) { Console.WriteLine("{0}: {1}", inner.GetType().FullName, inner.Message); } } } class AFewPrimes : IInt32Sequence { static readonly int[] primes = { 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31 }; int index; public int GetNext() { index = (index + 1) % primes.Length; return primes[index]; } }