protected void Ping() { while (Running && Pinging) { Thread.Sleep(PingSleep); lock (ClientsList) { lock (ClientsDict) { for (int e = ClientsList.Count - 1; e >= 0; e--) { TCPServerClient client = ClientsList[e]; try { using (IOStream stream = new IOStream()) { stream.WriteDouble(client.RTT); stream.WriteString(DateTime.UtcNow.ToString("O")); byte[] arr = stream.ToArray(); client.Send(new TCPMessage() { Code = TCPMessageCode.Ping, Content = arr }); } } catch (Exception er) { RemoveClient(client, TCPDisconnectType.Timeout); } } } } } }
/// <summary> /// Stop the server /// </summary> public void Stop() { if (Logging) { Logger.Write("REGION", "Method [Stop]"); } if (Logging) { Logger.Write("INFO", "Stopping server"); } Running = false; lock (ClientsList) lock (ClientsDict) { for (int e = ClientsList.Count - 1; e >= 0; e--) { TCPServerClient client = ClientsList[e]; RemoveClient(client, TCPDisconnectType.Disconnect, e); } } try { Socket.Shutdown(SocketShutdown.Both); Socket.Close(); } catch (Exception er) { } ManagementThread.Join(); ListenThread.Join(); ListenThread = new Thread(() => Listen()); ManagementThread = new Thread(Management); Socket = new Socket(AddressFamily.InterNetworkV6, SocketType.Stream, ProtocolType.Tcp); Socket.SetSocketOption(SocketOptionLevel.IPv6, SocketOptionName.IPv6Only, false); Socket.Bind(new IPEndPoint(Address, Port)); if (Logging) { Logger.Write("INFO", "Stopped server"); } }
/// <summary> /// Read tcp message off the stream /// </summary> /// <param name="client"></param> /// <returns></returns> public TCPMessage Read(TCPServerClient client) { byte first; try { first = (byte)Stream.ReadByte(); } catch (Exception e) { return(null); } if (!IsClientConnected(client)) { return(null); } byte second = (byte)Stream.ReadByte(); uint len = ReadLength(second); if (len != 0) { // solve encoded bytes xor byte[] key = TCPReaderWriter.Read(Stream, 4); byte[] encoded = TCPReaderWriter.Read(Stream, len); byte[] decoded = new byte[len]; for (int e = 0; e < encoded.Length; e++) { decoded[e] = (byte)(encoded[e] ^ key[e % 4]); } return(new TCPMessage() { Client = client, Code = (TCPMessageCode)first, Content = decoded, Sent = DateTime.Now }); } return(null); }
/// <summary> /// Get appropiate stream of socket /// </summary> /// <param name="client"></param> /// <returns></returns> protected Stream GetStream(TCPServerClient client) { Stream stream = new NetworkStream(client.Socket); if (SSL == null) { return(stream); } try { SslStream sslStream = new SslStream(stream, false); var task = sslStream.AuthenticateAsServerAsync(SSL, false, SSLProtocol, true); task.Start(); task.Wait(); return(sslStream); } catch (Exception e) { return(null); } }
/// <summary> /// Listen for new connections /// </summary> protected void Listen() { if (Logging) { Logger.Write("REGION", "Method [Listen]"); } Socket.Listen(Backlog); if (Logging) { Logger.Write("INFO", "Start listening for clients"); } while (Running) { Socket socket = Socket.Accept(); if (Logging) { Logger.Write("INFO", "New socket connected"); } TCPServerClient client = new TCPServerClient( socket, RandomGen.GenRandomUID(ClientsDict, UIDLength)); client.Joined = DateTime.Now; Thread clientThread = new Thread(() => ListenClient(client)); client.Thread = clientThread; clientThread.Start(); if (Logging) { Logger.Write("INFO", "Created client and started thread"); } } }
/// <summary> /// Management to handle automated kicks etc /// </summary> protected void Management() { while (Running) { Thread.Sleep(ManagementSleep); lock (ClientsList) { lock (ClientsDict) { for (int e = ClientsList.Count - 1; e >= 0; e--) { TCPServerClient c = ClientsList[e]; if ((DateTime.Now - c.Joined) > HandshakeTimeout && RequireHandshake && !c.DoneHandshake) { RemoveClient(c, TCPDisconnectType.NoHandshake); } } } } } }
/// <summary> /// Check if client is still connected /// </summary> /// <param name="client"></param> /// <returns></returns> public static bool IsClientConnected(TCPServerClient client) { return(IsClientConnected(client.Socket)); }
/// <summary> /// Listen for new messages of individual clients /// </summary> /// <param name="client"></param> protected void ListenClient(TCPServerClient client) { if (Logging) { Logger.Write("REGION", "Method [ListenClient]"); } using (Stream ns = GetStream(client)) { client.Stream = ns; client.Writer = new TCPWriter(ns); client.Reader = new TCPReader(ns); if (Logging) { Logger.Write("INFO", "Created stream, writer and reader for client: " + client.UID); } lock (ClientsList) ClientsList.Add(client); lock (ClientsDict) ClientsDict.Add(client.UID, client); OnConnected?.Invoke(client); if (RequireHandshake) { TCPMessage message = client.Reader.Read(client); if (message.Code != TCPMessageCode.Init || message.Content.Length > 10) { RemoveClient(client, TCPDisconnectType.NoHandshake); return; } if (Logging) { Logger.Write("SUCCESS", "Handshake: " + client.UID); } client.DoneHandshake = true; client.Send(new TCPMessage() { Code = TCPMessageCode.Init, Content = new byte[] { 0, 1, 0 } }); } while (Running && ClientsDict.ContainsKey(client.UID)) { TCPMessage message = client.Reader.Read(client); if (message == null) { RemoveClient(client, TCPDisconnectType.Timeout); return; } if (Logging) { Logger.Write("INFO", "New message " + Enum.GetName(typeof(TCPMessageCode), message.Code) + " from user: " + client.UID); } if (message.Code == TCPMessageCode.Message) { OnMessage?.Invoke(client, message); } } } }
/// <summary> /// Removes a client from the server /// </summary> /// <param name="client"></param> /// <param name="type"></param> public void RemoveClient(TCPServerClient client, TCPDisconnectType type = TCPDisconnectType.Disconnect) { if (Logging) { Logger.Write("REGION", "Method [RemoveClient]"); } if (type == TCPDisconnectType.NoHandshake) { if (Logging) { Logger.Write("INFO", "Client no handshake: " + client.UID); } OnNoHandshake?.Invoke(client); } else if (type == TCPDisconnectType.Disconnect) { if (Logging) { Logger.Write("INFO", "Client disconnect: " + client.UID); } OnDisconnected?.Invoke(client); } else if (type == TCPDisconnectType.Timeout) { if (Logging) { Logger.Write("INFO", "Client timeout: " + client.UID); } OnTimeout?.Invoke(client); } else if (type == TCPDisconnectType.Kick) { if (Logging) { Logger.Write("INFO", "Client kick: " + client.UID); } OnKick?.Invoke(client); } lock (ClientsDict) ClientsDict.Remove(client.UID); lock (ClientsList) { for (int e = ClientsList.Count - 1; e >= 0; e--) { if (ClientsList[e].UID == client.UID) { if (Logging) { Logger.Write("INFO", "Client found in ClientsList: " + client.UID); } ClientsList.RemoveAt(e); break; } } } try { client.Socket.Shutdown(SocketShutdown.Both); client.Socket.Close(); } catch (Exception e) { if (Logging) { Logger.Write("FAILED", "Socket shutdown/close", e); } } }
/// <summary> /// Kicks user /// </summary> /// <param name="client"></param> public void Kick(TCPServerClient client) { RemoveClient(client, TCPDisconnectType.Kick); }