Created
September 6, 2014 22:45
-
-
Save jakesays-old/a4c887656431251fa5b0 to your computer and use it in GitHub Desktop.
stuff
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
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