Last active
September 29, 2016 14:58
-
-
Save ChrisMoney/ec0d3bd34fefbe5127f77f42d0b7da31 to your computer and use it in GitHub Desktop.
TCP (Transmission Control Protocol) IP + Port = Socket
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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