Last active
July 26, 2016 13:09
-
-
Save define-private-public/1705b604b47ece09cdbc8e40588788c2 to your computer and use it in GitHub Desktop.
Text based games that go over TCP
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
| // Filename: GuessMyNumberGame.cs | |
| // Author: Benjamin N. Summerton <define-private-public> | |
| // License: Unlicense (http://unlicense.org/) | |
| using System; | |
| using System.Net.Sockets; | |
| using System.Threading; | |
| namespace TcpGames | |
| { | |
| public class GuessMyNumberGame : IGame | |
| { | |
| // Objects for the game | |
| private TcpGamesServer _server; | |
| private TcpClient _player; | |
| private Random _rng; | |
| private bool _needToDisconnectClient = false; | |
| // Name of the game | |
| public string Name | |
| { | |
| get { return "Guess My Number"; } | |
| } | |
| // Just needs only one player | |
| public int RequiredPlayers | |
| { | |
| get { return 1; } | |
| } | |
| // Constructor | |
| public GuessMyNumberGame(TcpGamesServer server) | |
| { | |
| _server = server; | |
| _rng = new Random(); | |
| } | |
| // Adds only a single player to the game | |
| public bool AddPlayer(TcpClient client) | |
| { | |
| // Make sure only one player was added | |
| if (_player == null) | |
| { | |
| _player = client; | |
| return true; | |
| } | |
| return false; | |
| } | |
| // If the client who disconnected is ours, we need to quit our game | |
| public void DisconnectClient(TcpClient client) | |
| { | |
| _needToDisconnectClient = (client == _player); | |
| } | |
| // Main loop of the Game | |
| // Packets are sent sent synchronously though | |
| public void Run() | |
| { | |
| // Make sure we have a player | |
| bool running = (_player != null); | |
| if (running) | |
| { | |
| // Send a instruction packet | |
| Packet introPacket = new Packet("message", | |
| "Welcome player, I want you to guess my number.\n" + | |
| "It's somewhere between (and including) 1 and 100.\n"); | |
| _server.SendPacket(_player, introPacket).GetAwaiter().GetResult(); | |
| } | |
| else | |
| return; | |
| // Should be [1, 100] | |
| int theNumber = _rng.Next(1, 101); | |
| Console.WriteLine("Our number is: {0}", theNumber); | |
| // Some bools for game state | |
| bool correct = false; | |
| bool clientConncted = true; | |
| bool clientDisconnectedGracefully = false; | |
| // Main game loop | |
| while (running) | |
| { | |
| // Poll for input | |
| Packet inputPacket = new Packet("input", "Your guess: "); | |
| _server.SendPacket(_player, inputPacket).GetAwaiter().GetResult(); | |
| // Read their answer | |
| Packet answerPacket = null; | |
| while (answerPacket == null) | |
| { | |
| answerPacket = _server.ReceivePacket(_player).GetAwaiter().GetResult(); | |
| Thread.Sleep(10); | |
| } | |
| // Check for graceful disconnect | |
| if (answerPacket.Command == "bye") | |
| { | |
| _server.HandleDisconnectedClient(_player); | |
| clientDisconnectedGracefully = true; | |
| } | |
| // Check input | |
| if (answerPacket.Command == "input") | |
| { | |
| Packet responsePacket = new Packet("message"); | |
| int theirGuess; | |
| if (int.TryParse(answerPacket.Message, out theirGuess)) | |
| { | |
| // See if they won | |
| if (theirGuess == theNumber) | |
| { | |
| correct = true; | |
| responsePacket.Message = "Correct! You win!\n"; | |
| } | |
| else if (theirGuess < theNumber) | |
| responsePacket.Message = "Too low.\n"; | |
| else if (theirGuess > theNumber) | |
| responsePacket.Message = "Too high.\n"; | |
| } | |
| else | |
| responsePacket.Message = "That wasn't a valid number, try again.\n"; | |
| // Send the message | |
| _server.SendPacket(_player, responsePacket).GetAwaiter().GetResult(); | |
| } | |
| // Take a small nap | |
| Thread.Sleep(10); | |
| // If they aren't correct, keep them here | |
| running &= !correct; | |
| // Check for disconnect, may have happend gracefully before | |
| if (!_needToDisconnectClient && !clientDisconnectedGracefully) | |
| clientConncted &= !TcpGamesServer.IsDisconnected(_player); | |
| else | |
| clientConncted = false; | |
| running &= clientConncted; | |
| } | |
| // Thank the player and disconnect them | |
| if (clientConncted) | |
| _server.DisconnectClient(_player, "Thanks for playing \"Guess My Number\"!"); | |
| else | |
| Console.WriteLine("Client disconnected from game."); | |
| Console.WriteLine("Ending a \"{0}\" game.", Name); | |
| } | |
| } | |
| } | |
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
| // Filename: IGame.cs | |
| // Author: Benjamin N. Summerton <define-private-public> | |
| // License: Unlicense (http://unlicense.org/) | |
| using System.Net.Sockets; | |
| namespace TcpGames | |
| { | |
| interface IGame | |
| { | |
| #region Properties | |
| // Name of the game | |
| string Name { get; } | |
| // How many players are needed to start | |
| int RequiredPlayers { get; } | |
| #endregion // Properties | |
| #region Functions | |
| // Adds a player to the game (should be before it starts) | |
| bool AddPlayer(TcpClient player); | |
| // Tells the server to disconnect a player | |
| void DisconnectClient(TcpClient client); | |
| // The main game loop | |
| void Run(); | |
| #endregion // Functions | |
| } | |
| } | |
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
| // Filename: Packet.cs | |
| // Author: Benjamin N. Summerton <define-private-public> | |
| // License: Unlicense (http://unlicense.org/) | |
| using Newtonsoft.Json; | |
| namespace TcpGames | |
| { | |
| public class Packet | |
| { | |
| [JsonProperty("command")] | |
| public string Command { get; set; } | |
| [JsonProperty("message")] | |
| public string Message { get; set; } | |
| // Makes a packet | |
| public Packet(string command="", string message="") | |
| { | |
| Command = command; | |
| Message = message; | |
| } | |
| public override string ToString() | |
| { | |
| return string.Format( | |
| "[Packet:\n" + | |
| " Command=`{0}`\n" + | |
| " Message=`{1}`]", | |
| Command, Message); | |
| } | |
| // Serialize to Json | |
| public string ToJson() | |
| { | |
| return JsonConvert.SerializeObject(this); | |
| } | |
| // Deserialize | |
| public static Packet FromJson(string jsonData) | |
| { | |
| return JsonConvert.DeserializeObject<Packet>(jsonData); | |
| } | |
| } | |
| } | |
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
| // Filename: TcpGamesClient.cs | |
| // Author: Benjamin N. Summerton <define-private-public> | |
| // License: Unlicense (http://unlicense.org/) | |
| using System; | |
| using System.Text; | |
| using System.Net.Sockets; | |
| using System.Threading; | |
| using System.Threading.Tasks; | |
| using System.Collections.Generic; | |
| namespace TcpGames | |
| { | |
| public class TcpGamesClient | |
| { | |
| // Conneciton objects | |
| public readonly string ServerAddress; | |
| public readonly int Port; | |
| public bool Running { get; private set; } | |
| private TcpClient _client; | |
| private bool _clientRequestedDisconnect = false; | |
| // Messaging | |
| private NetworkStream _msgStream = null; | |
| private Dictionary<string, Func<string, Task>> _commandHandlers = new Dictionary<string, Func<string, Task>>(); | |
| public TcpGamesClient(string serverAddress, int port) | |
| { | |
| // Create a non-connectec TcpClient | |
| _client = new TcpClient(); | |
| Running = false; | |
| // Set other data | |
| ServerAddress = serverAddress; | |
| Port = port; | |
| } | |
| // Cleans up any leftover network resources | |
| private void _cleanupNetworkResources() | |
| { | |
| _msgStream?.Close(); | |
| _msgStream = null; | |
| _client.Close(); | |
| } | |
| // Connects to the games server | |
| public void Connect() | |
| { | |
| // Connect to the server | |
| try | |
| { | |
| _client.Connect(ServerAddress, Port); // Resolves DNS for us | |
| } | |
| catch (SocketException se) | |
| { | |
| Console.WriteLine("[ERROR] {0}", se.Message); | |
| } | |
| // check that we've connected | |
| if (_client.Connected) | |
| { | |
| // Connected! | |
| Console.WriteLine("Connected to the server at {0}.", _client.Client.RemoteEndPoint); | |
| Running = true; | |
| // Get the message stream | |
| _msgStream = _client.GetStream(); | |
| // Hook up some packet command handlers | |
| _commandHandlers["bye"] = _handleBye; | |
| _commandHandlers["message"] = _handleMessage; | |
| _commandHandlers["input"] = _handleInput; | |
| } | |
| else | |
| { | |
| // Nope... | |
| _cleanupNetworkResources(); | |
| Console.WriteLine("Wasn't able to connect to the server at {0}:{1}.", ServerAddress, Port); | |
| } | |
| } | |
| // Requests a disconnect, will send a "bye," message to the server | |
| // This should only be called by the user | |
| public void Disconnect() | |
| { | |
| Console.WriteLine("Disconnecting from the server..."); | |
| Running = false; | |
| _clientRequestedDisconnect = true; | |
| _sendPacket(new Packet("bye")).GetAwaiter().GetResult(); | |
| } | |
| // Main loop for the Games Client | |
| public void Run() | |
| { | |
| bool wasRunning = Running; | |
| // Listen for messages | |
| List<Task> tasks = new List<Task>(); | |
| while (Running) | |
| { | |
| // Check for new packets | |
| tasks.Add(_handleIncomingPackets()); | |
| // Use less CPU | |
| Thread.Sleep(10); | |
| // Make sure that we didn't have a graceless disconnect | |
| if (_isDisconnected(_client) && !_clientRequestedDisconnect) | |
| { | |
| Running = false; | |
| Console.WriteLine("The server has disconnected from us ungracefully.\n:["); | |
| } | |
| } | |
| // Just incase we have anymore packets, give them one second to be processed | |
| Task.WaitAll(tasks.ToArray(), 1000); | |
| // Cleanup | |
| _cleanupNetworkResources(); | |
| if (wasRunning) | |
| Console.WriteLine("Disconnected."); | |
| } | |
| // Sends packets to the server asynchronously | |
| private async Task _sendPacket(Packet packet) | |
| { | |
| try | |
| { // convert JSON to buffer and its length to a 16 bit unsigned integer buffer | |
| byte[] jsonBuffer = Encoding.UTF8.GetBytes(packet.ToJson()); | |
| byte[] lengthBuffer = BitConverter.GetBytes(Convert.ToUInt16(jsonBuffer.Length)); | |
| // Join the buffers | |
| byte[] packetBuffer = new byte[lengthBuffer.Length + jsonBuffer.Length]; | |
| lengthBuffer.CopyTo(packetBuffer, 0); | |
| jsonBuffer.CopyTo(packetBuffer, lengthBuffer.Length); | |
| // Send the packet | |
| await _msgStream.WriteAsync(packetBuffer, 0, packetBuffer.Length); | |
| //Console.WriteLine("[SENT]\n{0}", packet); | |
| } | |
| catch(Exception) { } | |
| } | |
| // Checks for new incoming messages and handles them | |
| // This method will handle one Packet at a time, even if more than one is in the memory stream | |
| private async Task _handleIncomingPackets() | |
| { | |
| try | |
| { | |
| // Check for new incomding messages | |
| if (_client.Available > 0) | |
| { | |
| // There must be some incoming data, the first two bytes are the size of the Packet | |
| byte[] lengthBuffer = new byte[2]; | |
| await _msgStream.ReadAsync(lengthBuffer, 0, 2); | |
| ushort packetByteSize = BitConverter.ToUInt16(lengthBuffer, 0); | |
| // Now read that many bytes from what's left in the stream, it must be the Packet | |
| byte[] jsonBuffer = new byte[packetByteSize]; | |
| await _msgStream.ReadAsync(jsonBuffer, 0, jsonBuffer.Length); | |
| // Convert it into a packet datatype | |
| string jsonString = Encoding.UTF8.GetString(jsonBuffer); | |
| Packet packet = Packet.FromJson(jsonString); | |
| // Dispatch it | |
| try | |
| { | |
| await _commandHandlers[packet.Command](packet.Message); | |
| } | |
| catch (KeyNotFoundException) { } | |
| //Console.WriteLine("[RECEIVED]\n{0}", packet); | |
| } | |
| } catch (Exception) { } | |
| } | |
| #region Command Handlers | |
| private Task _handleBye(string message) | |
| { | |
| // Print the message | |
| Console.WriteLine("The server is disconnecting us with this message:"); | |
| Console.WriteLine(message); | |
| // Will start the disconnection process in Run() | |
| Running = false; | |
| return Task.FromResult(0); // Task.CompletedTask exists in .NET v4.6 | |
| } | |
| // Just prints out a message sent from the server | |
| private Task _handleMessage(string message) | |
| { | |
| Console.Write(message); | |
| return Task.FromResult(0); // Task.CompletedTask exists in .NET v4.6 | |
| } | |
| // Gets input from the user and sends it to the server | |
| private async Task _handleInput(string message) | |
| { | |
| // Print the prompt and get a response to send | |
| Console.Write(message); | |
| string responseMsg = Console.ReadLine(); | |
| // Send the response | |
| Packet resp = new Packet("input", responseMsg); | |
| await _sendPacket(resp); | |
| } | |
| #endregion // Command Handlers | |
| #region TcpClient Helper Methods | |
| // Checks if a client has disconnected ungracefully | |
| // Adapted from: http://stackoverflow.com/questions/722240/instantly-detect-client-disconnection-from-server-socket | |
| private static bool _isDisconnected(TcpClient client) | |
| { | |
| try | |
| { | |
| Socket s = client.Client; | |
| return s.Poll(10 * 1000, SelectMode.SelectRead) && (s.Available == 0); | |
| } | |
| catch(SocketException) | |
| { | |
| // We got a socket error, assume it's disconnected | |
| return true; | |
| } | |
| } | |
| #endregion // TcpClient Helper Methods | |
| #region Program Execution | |
| public static TcpGamesClient gamesClient; | |
| public static void InterruptHandler(object sender, ConsoleCancelEventArgs args) | |
| { | |
| // Perform a graceful disconnect | |
| args.Cancel = true; | |
| gamesClient?.Disconnect(); | |
| } | |
| public static void Main(string[] args) | |
| { | |
| // Setup the Games Client | |
| string host = "localhost";//args[0].Trim(); | |
| int port = 6000;//int.Parse(args[1].Trim()); | |
| gamesClient = new TcpGamesClient(host, port); | |
| // Add a handler for a Ctrl-C press | |
| Console.CancelKeyPress += InterruptHandler; | |
| // Try to connecct & interact with the server | |
| gamesClient.Connect(); | |
| gamesClient.Run(); | |
| } | |
| #endregion // Program Execution | |
| } | |
| } |
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
| // Filename: TcpGamesServer.cs | |
| // Author: Benjamin N. Summerton <define-private-public> | |
| // License: Unlicense (http://unlicense.org/) | |
| using System; | |
| using System.Collections.Generic; | |
| using System.Text; | |
| using System.Net; | |
| using System.Net.Sockets; | |
| using System.Threading; | |
| using System.Threading.Tasks; | |
| namespace TcpGames | |
| { | |
| public class TcpGamesServer | |
| { | |
| // Listens for new incoming connections | |
| private TcpListener _listener; | |
| // Clients objects | |
| private List<TcpClient> _clients = new List<TcpClient>(); | |
| private List<TcpClient> _waitingLobby = new List<TcpClient>(); | |
| // Game stuff | |
| private Dictionary<TcpClient, IGame> _gameClientIsIn = new Dictionary<TcpClient, IGame>(); | |
| private List<IGame> _games = new List<IGame>(); | |
| private List<Thread> _gameThreads = new List<Thread>(); | |
| private IGame _nextGame; | |
| // Other data | |
| public readonly string Name; | |
| public readonly int Port; | |
| public bool Running { get; private set; } | |
| // Construct to create a new Games Server | |
| public TcpGamesServer(string name, int port) | |
| { | |
| // Set some of the basic data | |
| Name = name; | |
| Port = port; | |
| Running = false; | |
| // Create the listener | |
| _listener = new TcpListener(IPAddress.Any, Port); | |
| } | |
| // Shutsdown the server if its running | |
| public void Shutdown() | |
| { | |
| if (Running) | |
| { | |
| Running = false; | |
| Console.WriteLine("Shutting down the Game(s) Server..."); | |
| } | |
| } | |
| // The main loop for the games server | |
| public void Run() | |
| { | |
| Console.WriteLine("Starting the \"{0}\" Game(s) Server on port {1}.", Name, Port); | |
| Console.WriteLine("Press Ctrl-C to shutdown the server at any time."); | |
| // Start the next game | |
| // (current only the Guess My Number Game) | |
| _nextGame = new GuessMyNumberGame(this); | |
| // Start running the server | |
| _listener.Start(); | |
| Running = true; | |
| List<Task> newConnectionTasks = new List<Task>(); | |
| Console.WriteLine("Waiting for incommming connections..."); | |
| while (Running) | |
| { | |
| // Handle any new clients | |
| if (_listener.Pending()) | |
| newConnectionTasks.Add(_handleNewConnection()); | |
| // Once we have enough clients for the next game, add them in and start the game | |
| if (_waitingLobby.Count >= _nextGame.RequiredPlayers) | |
| { | |
| // Get that many players from the waiting lobby and start the game | |
| int numPlayers = 0; | |
| while (numPlayers < _nextGame.RequiredPlayers) | |
| { | |
| // Pop the first one off | |
| TcpClient player = _waitingLobby[0]; | |
| _waitingLobby.RemoveAt(0); | |
| // Try adding it to the game. If failure, put it back in the lobby | |
| if (_nextGame.AddPlayer(player)) | |
| numPlayers++; | |
| else | |
| _waitingLobby.Add(player); | |
| } | |
| // Start the game in a new thread! | |
| Console.WriteLine("Starting a \"{0}\" game.", _nextGame.Name); | |
| Thread gameThread = new Thread(new ThreadStart(_nextGame.Run)); | |
| gameThread.Start(); | |
| _games.Add(_nextGame); | |
| _gameThreads.Add(gameThread); | |
| // Create a new game | |
| _nextGame = new GuessMyNumberGame(this); | |
| } | |
| // Check if any clients have disconnected in waiting, gracefully or not | |
| // NOTE: This could (and should) be parallelized | |
| foreach (TcpClient client in _waitingLobby.ToArray()) | |
| { | |
| EndPoint endPoint = client.Client.RemoteEndPoint; | |
| bool disconnected = false; | |
| // Check for graceful first | |
| Packet p = ReceivePacket(client).GetAwaiter().GetResult(); | |
| disconnected = (p?.Command == "bye"); | |
| // Then ungraceful | |
| disconnected |= IsDisconnected(client); | |
| if (disconnected) | |
| { | |
| HandleDisconnectedClient(client); | |
| Console.WriteLine("Client {0} has disconnected from the Game(s) Server.", endPoint); | |
| } | |
| } | |
| // Take a small nap | |
| Thread.Sleep(10); | |
| } | |
| // In the chance a client connected but we exited the loop, give them 1 second to finish | |
| Task.WaitAll(newConnectionTasks.ToArray(), 1000); | |
| // Shutdown all of the threads, regardless if they are done or not | |
| foreach (Thread thread in _gameThreads) | |
| thread.Abort(); | |
| // Disconnect any clients still here | |
| Parallel.ForEach(_clients, (client) => | |
| { | |
| DisconnectClient(client, "The Game(s) Server is being shutdown."); | |
| }); | |
| // Cleanup our resources | |
| _listener.Stop(); | |
| // Info | |
| Console.WriteLine("The server has been shut down."); | |
| } | |
| // Awaits for a new connection and then adds them to the waiting lobby | |
| private async Task _handleNewConnection() | |
| { | |
| // Get the new client using a Future | |
| TcpClient newClient = await _listener.AcceptTcpClientAsync(); | |
| Console.WriteLine("New connection from {0}.", newClient.Client.RemoteEndPoint); | |
| // Store them and put them in the waiting lobby | |
| _clients.Add(newClient); | |
| _waitingLobby.Add(newClient); | |
| // Send a welcome message | |
| string msg = String.Format("Welcome to the \"{0}\" Games Server.\n", Name); | |
| await SendPacket(newClient, new Packet("message", msg)); | |
| } | |
| // Will attempt to gracefully disconnect a TcpClient | |
| // This should be use for clients that may be in a game, or the waiting lobby | |
| public void DisconnectClient(TcpClient client, string message="") | |
| { | |
| Console.WriteLine("Disconnecting the client from {0}.", client.Client.RemoteEndPoint); | |
| // If there wasn't a message set, use the default "Goodbye." | |
| if (message == "") | |
| message = "Goodbye."; | |
| // Send the "bye," message | |
| Task byePacket = SendPacket(client, new Packet("bye", message)); | |
| // Notify a game that might have them | |
| try | |
| { | |
| _gameClientIsIn[client]?.DisconnectClient(client); | |
| } catch (KeyNotFoundException) { } | |
| // Give the client some time to send and proccess the graceful disconnect | |
| Thread.Sleep(100); | |
| // Cleanup resources on our end | |
| byePacket.GetAwaiter().GetResult(); | |
| HandleDisconnectedClient(client); | |
| } | |
| // Cleans up the resources if a client has disconnected, | |
| // gracefully or not. Will remove them from clint list and lobby | |
| public void HandleDisconnectedClient(TcpClient client) | |
| { | |
| // Remove from collections and free resources | |
| _clients.Remove(client); | |
| _waitingLobby.Remove(client); | |
| _cleanupClient(client); | |
| } | |
| #region Packet Transmission Methods | |
| // Sends a packet to a client asynchronously | |
| public async Task SendPacket(TcpClient client, Packet packet) | |
| { | |
| try | |
| { | |
| // convert JSON to buffer and its length to a 16 bit unsigned integer buffer | |
| byte[] jsonBuffer = Encoding.UTF8.GetBytes(packet.ToJson()); | |
| byte[] lengthBuffer = BitConverter.GetBytes(Convert.ToUInt16(jsonBuffer.Length)); | |
| // Join the buffers | |
| byte[] msgBuffer = new byte[lengthBuffer.Length + jsonBuffer.Length]; | |
| lengthBuffer.CopyTo(msgBuffer, 0); | |
| jsonBuffer.CopyTo(msgBuffer, lengthBuffer.Length); | |
| // Send the packet | |
| await client.GetStream().WriteAsync(msgBuffer, 0, msgBuffer.Length); | |
| //Console.WriteLine("[SENT]\n{0}", packet); | |
| } | |
| catch (Exception e) | |
| { | |
| // There was an issue is sending | |
| Console.WriteLine("There was an issue receiving a packet."); | |
| Console.WriteLine("Reason: {0}", e.Message); | |
| } | |
| } | |
| // Will get a single packet from a TcpClient | |
| // Will return null if there isn't any data available or some other | |
| // issue getting data from the client | |
| public async Task<Packet> ReceivePacket(TcpClient client) | |
| { | |
| Packet packet = null; | |
| try | |
| { | |
| // First check there is data available | |
| if (client.Available == 0) | |
| return null; | |
| NetworkStream msgStream = client.GetStream(); | |
| // There must be some incoming data, the first two bytes are the size of the Packet | |
| byte[] lengthBuffer = new byte[2]; | |
| await msgStream.ReadAsync(lengthBuffer, 0, 2); | |
| ushort packetByteSize = BitConverter.ToUInt16(lengthBuffer, 0); | |
| // Now read that many bytes from what's left in the stream, it must be the Packet | |
| byte[] jsonBuffer = new byte[packetByteSize]; | |
| await msgStream.ReadAsync(jsonBuffer, 0, jsonBuffer.Length); | |
| // Convert it into a packet datatype | |
| string jsonString = Encoding.UTF8.GetString(jsonBuffer); | |
| packet = Packet.FromJson(jsonString); | |
| //Console.WriteLine("[RECEIVED]\n{0}", packet); | |
| } | |
| catch (Exception e) | |
| { | |
| // There was an issue in receiving | |
| Console.WriteLine("There was an issue sending a packet to {0}.", client.Client.RemoteEndPoint); | |
| Console.WriteLine("Reason: {0}", e.Message); | |
| } | |
| return packet; | |
| } | |
| #endregion // Packet Transmission Methods | |
| #region TcpClient Helper Methods | |
| // Checks if a client has disconnected ungracefully | |
| // Adapted from: http://stackoverflow.com/questions/722240/instantly-detect-client-disconnection-from-server-socket | |
| public static bool IsDisconnected(TcpClient client) | |
| { | |
| try | |
| { | |
| Socket s = client.Client; | |
| return s.Poll(10 * 1000, SelectMode.SelectRead) && (s.Available == 0); | |
| } | |
| catch(SocketException) | |
| { | |
| // We got a socket error, assume it's disconnected | |
| return true; | |
| } | |
| } | |
| // cleans up resources for a TcpClient and closes it | |
| private static void _cleanupClient(TcpClient client) | |
| { | |
| client.GetStream().Close(); // Close network stream | |
| client.Close(); // Close client | |
| } | |
| #endregion // TcpClient Helper Methods | |
| #region Program Execution | |
| public static TcpGamesServer gamesServer; | |
| // For when the user Presses Ctrl-C, this will gracefully shutdown the server | |
| public static void InterruptHandler(object sender, ConsoleCancelEventArgs args) | |
| { | |
| args.Cancel = true; | |
| gamesServer?.Shutdown(); | |
| } | |
| public static void Main(string[] args) | |
| { | |
| // Some arguments | |
| string name = "Bad BBS";//args[0]; | |
| int port = 6000;//int.Parse(args[1]); | |
| // Handler for Ctrl-C presses | |
| Console.CancelKeyPress += InterruptHandler; | |
| // Create and run the server | |
| gamesServer = new TcpGamesServer(name, port); | |
| gamesServer.Run(); | |
| } | |
| #endregion // Program Execution | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment