Skip to content

Instantly share code, notes, and snippets.

@jakesays-old
Created September 6, 2014 22:45
Show Gist options
  • Save jakesays-old/a4c887656431251fa5b0 to your computer and use it in GitHub Desktop.
Save jakesays-old/a4c887656431251fa5b0 to your computer and use it in GitHub Desktop.
stuff
internal enum PacketSignature : uint
{
Valid = 0x42424242,
Failover = 0x43434343
}
[StructLayout(LayoutKind.Sequential)]
internal struct MessagePacketHeader
{
public PacketSignature Signature;
public uint RemotingVersion;
public LocalIpEndpoint LocalEndpoint;
public uint Length;
//not currently used
public ushort MessageType;
public ushort Unused;
unsafe public static int SizeOf
{
get { return sizeof (MessagePacketHeader); }
}
}
[Serializable]
[StructLayout(LayoutKind.Sequential)]
public struct LocalIpEndpoint
{
public uint Address;
public int Port;
public static LocalIpEndpoint Parse(IPAddress addr, int port)
{
if (addr != null)
{
return
new LocalIpEndpoint
{
Address = FromBytes(addr.GetAddressBytes()),
Port = port != -1 ? port : 0
};
}
return new LocalIpEndpoint();
}
public static LocalIpEndpoint Parse(string addr, int port)
{
IPAddress ip;
if (IPAddress.TryParse(addr, out ip))
{
return Parse(ip, port);
}
return new LocalIpEndpoint();
}
private static uint FromBytes(byte[] bytes)
{
return
(uint) bytes[0] << 24 |
(uint) bytes[1] << 16 |
(uint) bytes[2] << 8 |
(uint) bytes[3];
}
}
[StructLayout(LayoutKind.Sequential)]
internal struct MethodCallHeader
{
public uint Version;
public MethodCallOptions Options;
public ulong InterfaceId;
public ulong MethodId;
public MethodCallHeader(uint version, MethodCallOptions options, ulong interfaceId, ulong methodId)
{
Version = version;
Options = options;
InterfaceId = interfaceId;
MethodId = methodId;
}
public static unsafe int SizeOf
{
get { return sizeof(MethodCallHeader); }
}
}
[Flags]
public enum MethodCallOptions : uint
{
None = 0x00,
AsyncUpdateCall = 0x01,
UnknownMethodRetry = 0x02
}
public class MethodCallData
{
public uint Version;
public MethodCallOptions Flags;
public ulong InterfaceId;
public ulong MethodId;
public LocalIpEndpoint RemoteEndpoint;
public uint RemotingVersion;
public MethodCallData()
{
}
public MethodCallData(uint version, MethodCallOptions flags, ulong interfaceId, ulong methodId)
{
Version = version;
Flags = flags;
InterfaceId = interfaceId;
MethodId = methodId;
}
public MethodCallData(uint version, ulong interfaceId, ulong methodId)
{
Version = version;
InterfaceId = interfaceId;
MethodId = methodId;
}
public bool IsRetry
{
get { return (Flags & MethodCallOptions.UnknownMethodRetry) == MethodCallOptions.UnknownMethodRetry; }
}
}
internal class TcpEndpoint : IDisposable
{
protected Socket Socket;
~TcpEndpoint()
{
Dispose(false);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
Close();
}
}
internal void InternalClose()
{
if (Socket != null)
{
Socket.Close();
Socket = null;
}
}
public virtual void Close()
{
InternalClose();
}
public bool Connected
{
get { return (Socket != null) && Socket.Connected; }
}
protected void SendData(MethodCallHeader call, byte[] data, bool asyncCall, LocalIpEndpoint localEndpoint)
{
if (!Connected)
{
throw new InvalidOperationException("Remote endpoint closed");
}
var headerData = new byte[MessagePacketHeader.SizeOf + MethodCallHeader.SizeOf];
unsafe
{
fixed (byte* bits = headerData)
{
var pktHeader = (MessagePacketHeader*) bits;
var callHeader = (MethodCallHeader*) (bits + MessagePacketHeader.SizeOf);
pktHeader->Signature = PacketSignature.Valid;
pktHeader->Length = (uint) MethodCallHeader.SizeOf;
if (data != null)
{
pktHeader->Length += (uint) data.Length;
}
pktHeader->LocalEndpoint = localEndpoint;
pktHeader->RemotingVersion = 1;
callHeader->Version = call.Version;
callHeader->Options = call.Options;
callHeader->InterfaceId = call.InterfaceId;
callHeader->MethodId = call.MethodId;
}
}
if (data != null)
{
var segments = new[] { new ArraySegment<byte>(headerData), new ArraySegment<byte>(data) };
Socket.Send(segments);
}
else
{
Socket.Send(headerData);
}
}
protected byte[] ReceiveData(out MethodCallData callData)
{
callData = new MethodCallData();
try
{
if (!Connected)
{
throw new InvalidOperationException("Remote endpoint closed");
}
var headerData = new byte[MessagePacketHeader.SizeOf + MethodCallHeader.SizeOf];
if (!InternalRead(headerData, MessagePacketHeader.SizeOf))
{
//connection closed
return null;
}
int dataSize;
unsafe
{
fixed (byte* bits = headerData)
{
var hdr = (MessagePacketHeader*) bits;
if (hdr->Signature != PacketSignature.Valid)
{
InternalClose();
throw new RemotingException("Invalid packet header received - closing connection.");
}
dataSize = (int) hdr->Length;
callData.RemoteEndpoint = hdr->LocalEndpoint;
callData.RemotingVersion = hdr->RemotingVersion;
}
}
if (dataSize < MethodCallHeader.SizeOf)
{
InternalClose();
throw new RemotingException("Invalid message header length of {0}. No CallHeader available - closing connection.", dataSize);
}
if (!InternalRead(headerData, MethodCallHeader.SizeOf))
{
InternalClose();
throw new RemotingException("Could not read call header - closing connection.");
}
unsafe
{
fixed (byte* bits = headerData)
{
var callHeaderData = (MethodCallHeader*) bits;
callData.Version = callHeaderData->Version;
callData.Flags = callHeaderData->Options;
callData.InterfaceId = callHeaderData->InterfaceId;
callData.MethodId = callHeaderData->MethodId;
}
}
dataSize -= MethodCallHeader.SizeOf;
var data = new byte[dataSize];
if (dataSize > 0)
{
if (!InternalRead(data, dataSize))
{
InternalClose();
throw new RemotingException("Could not read message data of size {0} - closing connection.", dataSize);
}
}
return data;
}
catch (SocketException)
{
return null;
}
}
private bool InternalRead(byte[] data, int count)
{
int offset = 0;
while (count > 0)
{
int bytesRead = Socket.Receive(data, offset, count, SocketFlags.None);
if (bytesRead == 0)
{
return false;
}
count -= bytesRead;
offset += bytesRead;
}
return true;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment