389 lines
11 KiB
C#
389 lines
11 KiB
C#
|
////////////////////////////////////////////////////////////////////////////////
|
||
|
//
|
||
|
// 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<String, String> 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:<port>]");
|
||
|
Console.WriteLine(" tcpblast connect -dest:<ipaddress> [-port:<port>]");
|
||
|
|
||
|
}
|
||
|
|
||
|
static void ParseArgs(
|
||
|
string[] rawargs,
|
||
|
out Dictionary<String, String> namedargs,
|
||
|
out string[] posargs)
|
||
|
{
|
||
|
Dictionary<String, String> namedlist = new Dictionary<String, String>();
|
||
|
List<String> poslist = new List<String>();
|
||
|
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<String, String> 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<String, String> 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<String, String> 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<String, String> 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<String, String> 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<String, String> 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];
|
||
|
}
|
||
|
}
|