public NpcBlockingThreadHander(String clientString, INpcServerCallback callback, Socket socket, NpcExecutor npcExecutor, INpcHtmlGenerator npcHtmlGenerator) : base(clientString, callback, npcExecutor, npcHtmlGenerator) { this.socket = socket; this.socketLineReader = new SocketLineReader(socket, Encoding.ASCII, Buf.DefaultInitialCapacity, Buf.DefaultExpandLength); }
public void Dispose() { SocketLineReader cachedSocketLineReader = this.socketLineReader; this.socketLineReader = null; if (cachedSocketLineReader != null) { cachedSocketLineReader.Dispose(); } }
public NpcClient(EndPoint serverEndPoint, RemoteNpcInterface[] expectedInterfaces, Boolean threadSafe) { this.serverEndPoint = serverEndPoint; this.expectedInterfaces = expectedInterfaces; this.threadSafe = threadSafe; this.socketLineReader = null; this.enumAndObjectTypes = new List <Type>(); InitializeStaticClientTypeFinder(); }
public NpcClient(Socket socket, RemoteNpcInterface[] expectedInterfaces, Boolean threadSafe) { this.serverEndPoint = socket.RemoteEndPoint; this.expectedInterfaces = expectedInterfaces; this.threadSafe = threadSafe; this.socketLineReader = (socket == null || !socket.Connected) ? null : new SocketLineReader(socket, Encoding.ASCII, Buf.DefaultInitialCapacity, Buf.DefaultExpandLength); this.enumAndObjectTypes = new List <Type>(); InitializeStaticClientTypeFinder(); }
void Connect() { if (socketLineReader == null || socketLineReader.socket == null || !socketLineReader.socket.Connected) { if (socketLineReader != null) { socketLineReader.Dispose(); } socketLineReader = new SocketLineReader(new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp), Encoding.ASCII, Buf.DefaultInitialCapacity, Buf.DefaultExpandLength); socketLineReader.socket.Connect(serverEndPoint); } }
public static List <RemoteNpcObject> GetServerInterface(SocketLineReader socketLineReader, out Dictionary <String, RemoteNpcInterface> serverInterfaces) { socketLineReader.socket.Send(Encoding.ASCII.GetBytes(":interface\n")); serverInterfaces = new Dictionary <String, RemoteNpcInterface>(); List <SosMethodDefinition> methodDefinitionList = new List <SosMethodDefinition>(); while (true) { String interfaceName = socketLineReader.ReadLine(); if (interfaceName == null) { throw UnexpectedClose(socketLineReader); } if (interfaceName.Length <= 0) { break; } // Get parent interfaces String[] parentInterfaceNames = null; Int32 spaceIndex = interfaceName.IndexOf(' '); if (spaceIndex >= 0) { parentInterfaceNames = interfaceName.Substring(spaceIndex + 1).Split(' '); interfaceName = interfaceName.Remove(spaceIndex); } while (true) { String methodDefinitionLine = socketLineReader.ReadLine(); if (methodDefinitionLine == null) { throw UnexpectedClose(socketLineReader); } if (methodDefinitionLine.Length <= 0) { break; } SosMethodDefinition methodDefinition = SosTypes.ParseMethodDefinition(methodDefinitionLine, 0); methodDefinitionList.Add(methodDefinition); } serverInterfaces.Add(interfaceName, new RemoteNpcInterface(interfaceName, parentInterfaceNames, methodDefinitionList.ToArray())); methodDefinitionList.Clear(); } List <RemoteNpcObject> serverObjects = new List <RemoteNpcObject>(); while (true) { String objectLine = socketLineReader.ReadLine(); if (objectLine == null) { throw UnexpectedClose(socketLineReader); } if (objectLine.Length <= 0) { break; } String objectName = objectLine.Peel(out objectLine); String[] interfaceNames = objectLine.Split(RemoteNpcObject.SplitChars, StringSplitOptions.RemoveEmptyEntries); RemoteNpcInterface[] interfaces = new RemoteNpcInterface[interfaceNames.Length]; for (int i = 0; i < interfaceNames.Length; i++) { String interfaceName = interfaceNames[i]; RemoteNpcInterface npcInterface; if (!serverInterfaces.TryGetValue(interfaceName, out npcInterface)) { throw new FormatException(String.Format("The NPC server returned interface '{0}' in the :objects command but not in the :interfaces command", interfaceName)); } interfaces[i] = npcInterface; } serverObjects.Add(new RemoteNpcObject(objectName, interfaces)); } return(serverObjects); }
// expectedReturnType can be null, but providing the expected return type makes it unnecessary to search // each assembly for the type Object PerformCall(Type expectedReturnType, String methodName, String rawNpcLine) { if (threadSafe) { Monitor.Enter(serverEndPoint); } try { // // The reason for the retry logic is because if the underlying socket is disconnected, it may not // fail until after a send and a receive...so the socket should be reconnected and the request should // be repeated only once. // for (UInt32 attempt = 0; ; attempt++) { try { Connect(); socketLineReader.socket.Send(Encoding.UTF8.GetBytes(rawNpcLine.ToString())); String returnLineString = socketLineReader.ReadLine(); if (returnLineString == null) { if (attempt == 0) { Dispose(); continue; // Retry } throw UnexpectedClose(); } NpcReturnLine returnLine = new NpcReturnLine(returnLineString); if (returnLine.exceptionMessage != null) { ThrowExceptionFromCall(methodName, returnLine); } if (expectedReturnType == null) { if (returnLine.sosTypeName.Equals("Void")) { return(null); } expectedReturnType = GetTypeFromSosTypeName(returnLine.sosTypeName); } else { if (!returnLine.sosTypeName.Equals(expectedReturnType.SosTypeName())) { throw new InvalidOperationException(String.Format("Expected return type to be {0} but was {1}", expectedReturnType.SosTypeName(), returnLine.sosTypeName)); } } if (expectedReturnType == typeof(void)) { return(null); } Object returnObject; Int32 valueStringOffset = Sos.Deserialize(out returnObject, expectedReturnType, returnLine.sosSerializationString, 0, returnLine.sosSerializationString.Length); if (valueStringOffset != returnLine.sosSerializationString.Length) { throw new InvalidOperationException(String.Format( "Used {0} characters to deserialize object of type '{1}' but the serialization string had {2} characters", valueStringOffset, expectedReturnType.SosTypeName(), returnLine.sosSerializationString.Length)); } return(returnObject); } catch (SocketException) { if (socketLineReader != null) { socketLineReader.Dispose(); socketLineReader = null; } if (attempt == 0) { continue; // Retry } throw; } } } finally { if (threadSafe) { Monitor.Exit(serverEndPoint); } } }
public void UpdateAndVerifyEnumAndObjectTypes(NpcVerifyCriteria criteria) { if (threadSafe) { Monitor.Enter(serverEndPoint); } try { // // The reason for the retry logic is because if the underlying socket is disconnected, it may not // fail until after a send and a receive...so the socket should be reconnected and the request should // be repeated only once. // for (UInt32 attempt = 0; ; attempt++) { try { Connect(); socketLineReader.socket.Send(Encoding.UTF8.GetBytes(":type\n")); enumAndObjectTypes.Clear(); while (true) { String typeDefinitionLine = socketLineReader.ReadLine(); if (typeDefinitionLine == null) { if (attempt == 0) { Dispose(); continue; // Retry } throw UnexpectedClose(); } if (typeDefinitionLine.Length == 0) { break; // empty line } Int32 spaceIndex = typeDefinitionLine.IndexOf(' '); String sosTypeName = typeDefinitionLine.Remove(spaceIndex); String typeDefinition = typeDefinitionLine.Substring(spaceIndex + 1); Type type = GetTypeFromSosTypeName(sosTypeName); if (typeDefinition.StartsWith("Enum")) { SosEnumDefinition enumDefinition = SosTypes.ParseSosEnumTypeDefinition(typeDefinition, 4); enumDefinition.VerifyType(type, (SosVerifyCriteria)criteria); } else { SosObjectDefinition objectDefinition = SosTypes.ParseSosObjectTypeDefinition(typeDefinition, 0); objectDefinition.VerifyType(type); } enumAndObjectTypes.Add(type); } return; } catch (SocketException) { if (socketLineReader != null) { socketLineReader.Dispose(); socketLineReader = null; } if (attempt == 0) { continue; // Retry } throw; } } } finally { if (threadSafe) { Monitor.Exit(serverEndPoint); } } }
// // Methods Definitions // If the interface is updated and expectedInterfaces is not null, then the interfaces will be checked. public List <RemoteNpcObject> GetServerInterface(Boolean forceUpdateFromServer) { if (threadSafe) { Monitor.Enter(serverEndPoint); } try { if (cachedServerInterfaces == null || forceUpdateFromServer) { // // The reason for the retry logic is because if the underlying socket is disconnected, it may not // fail until after a send and a receive...so the socket should be reconnected and the request should // be repeated only once. // Boolean retryOnSocketException = true; RETRY_LOCATION: try { Connect(); cachedServerObjects = GetServerInterface(socketLineReader, out cachedServerInterfaces); if (expectedInterfaces != null) { String serverInterfaceDiff = ServerInterfaceMethodsDiff(cachedServerInterfaces, expectedInterfaces); if (serverInterfaceDiff != null) { throw new NpcInterfaceMismatch(serverInterfaceDiff); } } } catch (SocketException) { if (socketLineReader != null) { socketLineReader.Dispose(); socketLineReader = null; } if (retryOnSocketException) { retryOnSocketException = false; goto RETRY_LOCATION; } throw; } } return(cachedServerObjects); } catch (Exception) { cachedServerInterfaces = null; cachedServerObjects = null; throw; } finally { if (threadSafe) { Monitor.Exit(serverEndPoint); } } }
static InvalidOperationException UnexpectedClose(SocketLineReader socketLineReader) { socketLineReader.Dispose(); return(new InvalidOperationException("Server closed unexpectedly")); }