Skip to content

Instantly share code, notes, and snippets.

@ChrisMoney
Last active September 29, 2016 14:58
Show Gist options
  • Save ChrisMoney/ec0d3bd34fefbe5127f77f42d0b7da31 to your computer and use it in GitHub Desktop.
Save ChrisMoney/ec0d3bd34fefbe5127f77f42d0b7da31 to your computer and use it in GitHub Desktop.
TCP (Transmission Control Protocol) IP + Port = Socket
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Net;
using System.Net.Sockets;
namespace SocketSender
{
public abstract class TcpTransaction
{
public object DataSent { get; private set; }
public object DataReceived { get; private set; }
public string TxtSent { get { return GetRawStreamTxt(LoggedStream.OutgoingStream); } }
public string TxtReceived { get { return GetRawStreamTxt(LoggedStream.IncomingStream); } }
public byte[] BinSent { get { return GetRawStreamBin(LoggedStream.OutgoingStream); } }
public byte[] BinReceived { get { return GetRawStreamBin(LoggedStream.IncomingStream); } }
protected static string GetRawStreamTxt(MemoryStream stream) { return Encoding.UTF8.GetString(stream.ToArray()); }
protected static byte[] GetRawStreamBin(MemoryStream stream) { return stream.ToArray(); }
//public int Port { get; set; }
public int Port = Properties.Settings.Default.PrimaryPORT;
public string Ip { get; set; }
protected ReplayStream LoggedStream;
protected TcpTransaction()
{
LoggedStream = new ReplayStream();
}
protected TcpTransaction(string eofMarker)
{
LoggedStream = new ReplayStream(eofMarker);
}
protected TcpTransaction(byte[] eofMarker)
{
LoggedStream = new ReplayStream(eofMarker);
}
// make socket connection
protected virtual T Send<T>(object input)
{
DataSent = input;
DataReceived = null;
IPAddress addr = IPAddress.Parse(Ip);
IPEndPoint endpoint = new IPEndPoint(addr, Port);
var client = new TcpClient
{
SendTimeout = Properties.Settings.Default.TimeoutMS,
ReceiveTimeout = Properties.Settings.Default.TimeoutMS,
};
using (client)
{
client.Connect(endpoint);
LoggedStream.InnerStream = client.GetStream();
WriteRequest(input, LoggedStream);
T response = ReadResponse<T>(LoggedStream);
DataReceived = response;
return response;
}
}
protected abstract T ReadResponse<T>(Stream stream);
protected abstract void WriteRequest(object data, Stream stream);
public class ReplayStream : Stream
{
//The live r/w stream
protected Stream innerStream;
protected MemoryStream OutStream;
//
protected MemoryStream InStream;
protected readonly byte[] Eof;
protected int MatchIdx;
public bool StreamOwner { get; set; }
public ReplayStream() : this((byte[])null) { }
public ReplayStream(string eofMarker) : this(new UTF8Encoding(false).GetBytes(eofMarker)) { }
public ReplayStream(byte[] eofMarker)
: base()
{
StreamOwner = false;
Eof = eofMarker ?? new byte[0];
MatchIdx = Eof.Length > 0 ? 0 : -1;
}
public Stream InnerStream
{
get { return innerStream; }
set
{
innerStream = value;
InStream = new MemoryStream();
OutStream = new MemoryStream();
MatchIdx = Eof.Length > 0 ? 0 : -1;
}
}
//StreamReaders tend to dispose streams when they're done, so we'll clone them to ensure they're reuseable.
//Note: These streams show the current buffered data and will not draw additional data from the _innerStream.
//To continue drawing, set this.Position = 0 and read as normal.
public MemoryStream OutgoingStream { get { return CloneStream(OutStream); } }
public MemoryStream IncomingStream { get { return CloneStream(InStream); } }
private MemoryStream CloneStream(MemoryStream src)
{
if (src == null)
return new MemoryStream(new byte[0], false);
return new MemoryStream(src.GetBuffer(), 0, (int)src.Length, false);
}
public long SentBytes { get { return OutStream == null ? 0 : OutStream.Length; } }
public long ReceivedBytes { get { return InStream == null ? 0 : InStream.Length; } }
public override bool CanRead { get { return innerStream.CanRead; } }
public override bool CanSeek { get { return innerStream.CanSeek; } } //Affects stream writers, so needs _innerStream
public override bool CanWrite { get { return innerStream.CanWrite; } }
public override void Flush()
{
innerStream.Flush();
}
public override long Length { get { return innerStream.Length; } }
public override long Position //Position will correspond to the _inStream only
{
get { return InStream.Position; }
set { InStream.Position = value; }
}
public override long Seek(long offset, SeekOrigin origin) //Seek will correspond to _inStream only
{
return InStream.Seek(offset, origin);
}
public override void SetLength(long value) //Length corresponds to _innerStream. _inStream cannot be overwritten, only appended.
{
InStream.SetLength(value);
}
//This Read override tracks
public override int Read(byte[] buffer, int offset, int count)
{
int replayBytes = 0; //Bytes read from previously-recorded _inStream
int freshBytes = 0; //Bytes read for the first time from the live _innerStream
if (InStream.Position < InStream.Length) //We are rewinded. Replay _inStream and then fetch the remainder, if any.
{
replayBytes = InStream.Read(buffer, offset, count);
offset += replayBytes;
count -= replayBytes;
}
if (count > 0 && MatchIdx != Eof.Length) //Try reading from the live stream only if we need more bytes and EOF has not been matched.
{
freshBytes = innerStream.Read(buffer, offset, count);
if (MatchIdx >= 0)
for (int i = 0; i < freshBytes; i++)
{
if (buffer[offset + i] != Eof[MatchIdx]) //EOF match miss, reset.
MatchIdx = 0;
else if (++MatchIdx == Eof.Length) //EOF match hit, increment, check
{
freshBytes = i + 1; //EOF fully matched, suppress any data after this point
break;
}
}
}
if (freshBytes > 0)
InStream.Write(buffer, offset, freshBytes);
return replayBytes + freshBytes;
}
public override void Write(byte[] buffer, int offset, int count)
{
innerStream.Write(buffer, offset, count);
OutStream.Write(buffer, offset, count);
}
protected override void Dispose(bool disposing)
{
if (disposing && StreamOwner)
innerStream.Dispose();
base.Dispose(disposing);
}
}
////StreamReader equivalent of EOF reader (reference code, don't remove)
////Note: Both this and ReplayStream only accept markers with unique substrings. A fully-fledged deterministic finite automation (DFA) is
////much more more difficult to implement in a general way, and I don't even want to try until the need arises arises.
//public static string ReadToEndOrMarker(StreamReader reader, string eofMarker)
//{
// if (eofMarker == null || eofMarker.Length == 0)
// return reader.ReadToEnd();
// StringBuilder sb = new StringBuilder();
// char[] eofChars = eofMarker.ToCharArray();
// int matchIdx = 0;
// while (true)
// {
// //StreamReader buffers an internal char array, so this isn't majorly less efficient than pulling a full buffer all at once.
// //But more importantly, it gives us an opportunity to review every last character before attempting to fetch additional data from the stream.
// int c = reader.Read();
// if (c == -1)
// break;
// sb.Append((char)c);
// if (c != eofChars[matchIdx]) //endMarker match miss, reset
// matchIdx = 0;
// else if (++matchIdx == eofChars.Length) //endMarker match hit, increment, check
// {
// sb.Length -= matchIdx;
// break;
// }
// }
// return sb.ToString();
//}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment