diff --git a/.gitignore b/.gitignore index 8e49adf..60fbadb 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,8 @@ client/bin/* client/obj/* + server/bin/* -server/obj/* \ No newline at end of file +server/obj/* + +SocketLib/obj/* +SocketLib/bin/* \ No newline at end of file diff --git a/SocketLib/SocketLib.cs b/SocketLib/SocketLib.cs new file mode 100644 index 0000000..fea8877 --- /dev/null +++ b/SocketLib/SocketLib.cs @@ -0,0 +1,130 @@ +using System; +using System.Text; +using System.Net; +using System.Net.Sockets; +using System.Threading; +using System.Collections; +namespace SocketLib; + +public static class Server { + public static Socket self; + private static byte[] buffer; + private static string ipAddress; + private static int listeners = 0; + public static Action disconnectMethod; + public static List messageEvents; + + public static void Initialize() { + // Initialize global variables + buffer = new byte[1024]; + messageEvents = new List(); + disconnectMethod = new Action((other)=>{}); + + // Get computers' ipv4 address + ipAddress = "127.0.0.1"; + try { + string hostName = Dns.GetHostName(); + IPHostEntry entryList = Dns.GetHostEntry(hostName); + IPAddress ipv4Entry = entryList.AddressList[0]; + ipAddress = ipv4Entry.ToString(); + } catch (Exception) {} + } + + public static void Start() { + // Create a new socket server and listen for connections + self = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); + self.Bind(new IPEndPoint(IPAddress.Parse(ipAddress), 3000)); + self.Listen(10); + + // Accept any client asynchronously + while (true) { + if (listeners < 10) { + self.BeginAccept(new AsyncCallback(AcceptCallback), self); + listeners++; + } else { + // If there are already 10 async callbacks running, then sleep + Thread.Sleep(500); + } + } + } + + // End off the message send-callback + private static void SendCallback(IAsyncResult AR) { + Socket other = AR.AsyncState as Socket; + other.EndSend(AR); + } + + // End off the message receive-callback + private static void ReceiveCallback(IAsyncResult AR) { + Socket other = AR.AsyncState as Socket; + try { + // Handle any received bytes + int received = other.EndReceive(AR); + if (received <= 0) + throw new Exception(); + byte[] data = new byte[received]; + Array.Copy(buffer, data, received); + // Convert bytes to string + string message = Encoding.UTF8.GetString(data); + // Handle received message + handleMessage(message, other); + // Begin listening for more messages + other.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(ReceiveCallback), other); + } catch (Exception) { + // Potentially detected Disconnect + if (other.Connected) { + // Close client connection + disconnectMethod.Invoke(other); + other.Close(); + } + } + } + + // End off the connection accept-callback + private static void AcceptCallback(IAsyncResult AR) { + Socket own = AR.AsyncState as Socket; + Socket other = own.EndAccept(AR); + listeners--; + other.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(ReceiveCallback), other); + } + + // Handle sending messages + public static void Send(Socket other, string text) { + byte[] data = Encoding.UTF8.GetBytes(text); + other.BeginSend(data, 0, data.Length, SocketFlags.None, new AsyncCallback(SendCallback), other); + } + + // Handle any incomming messages from given client + private static void handleMessage(string message, Socket other) { + Console.WriteLine($"Recieved Message: {message}"); + + string[] splits = message.Split(' '); + string eventName = splits[0]; + string rest = ""; + for (int i = 1; i < splits.Length; i++) + rest += splits[i] + ((i < splits.Length-1) ? " " : ""); + + // Console.WriteLine($"Event: {eventName}; Params: {rest}; Methods: {messageEvents.Count};"); + + foreach (MessageEvent mE in messageEvents) { + if (mE.name == eventName) { + if (rest == "") + mE.method.DynamicInvoke(other); + else + mE.method.DynamicInvoke(other, rest); + // Console.WriteLine("Method was Invoked."); + } + } + } +} + +// Custom Class to store event methods by name +public class MessageEvent { + public string name; + public MulticastDelegate method; + + public MessageEvent(string _name, MulticastDelegate _method) { + name = _name; + method = _method; + } +} \ No newline at end of file diff --git a/SocketLib/SocketLib.csproj b/SocketLib/SocketLib.csproj new file mode 100644 index 0000000..132c02c --- /dev/null +++ b/SocketLib/SocketLib.csproj @@ -0,0 +1,9 @@ + + + + net6.0 + enable + enable + + + diff --git a/client/Program.cs b/client/Program.cs deleted file mode 100644 index e33b72e..0000000 --- a/client/Program.cs +++ /dev/null @@ -1,80 +0,0 @@ -using System; -using System.Text; -using System.Net; -using System.Net.Sockets; - -namespace SocketTestClient { - class Client { - static byte[] buffer; - - static void Main(string[] args) { - // Initialize global variables - buffer = new byte[1024]; - - // Get server ipv4 address - string ipAddress = "127.0.0.1"; - int port = 3000; - Console.WriteLine("Enter the Server IP and Port"); - Console.Write("(Format: IPv4:Port; Default: EMPTY): "); - string input = Console.ReadLine(); - if (input != "") { - string[] splits = input.Split(':'); - if (splits.Length == 2) { - ipAddress = splits[0]; - port = int.Parse(splits[1]); - } - } - - // Connect to SocketServer on 'localhost:3000' and send a message - Socket client = null; - try { - client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); - client.Connect(new IPEndPoint(IPAddress.Parse(ipAddress), port)); - } catch (Exception) { - Console.WriteLine("Could not connect to server."); - Environment.Exit(0); - } - - // Receive and display messages in new thread - client.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(ReceiveCallback), client); - while (true) { - Console.Write("Enter a message or comand: "); - string message = Console.ReadLine(); - if (message.ToLower() == "exit") - break; - else if (message.ToLower() == "clear") { - Console.Clear(); - continue; - } - - byte[] data = Encoding.UTF8.GetBytes(message); - client.BeginSend(data, 0, data.Length, SocketFlags.None, new AsyncCallback(SendCallback), client); - - Console.WriteLine($"Sent message '{message}' to server."); - } - client.Close(); - } - - static void SendCallback(IAsyncResult AR) { - Socket self = AR.AsyncState as Socket; - self.EndSend(AR); - } - - static void ReceiveCallback(IAsyncResult AR) { - Socket self = AR.AsyncState as Socket; - try { - int received = self.EndReceive(AR); - if (received <= 0) - throw new Exception(); - byte[] data = new byte[received]; - Array.Copy(buffer, data, received); - string message = Encoding.ASCII.GetString(data); - Console.WriteLine($"Received message: '{message}' from server."); - self.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(ReceiveCallback), self); - } catch (Exception) { - Console.WriteLine("Closing program due to error or disconnect . . . "); - self.Close(); - } - } - } -} diff --git a/client/client.csproj b/client/client.csproj deleted file mode 100644 index 2082704..0000000 --- a/client/client.csproj +++ /dev/null @@ -1,8 +0,0 @@ - - - - Exe - net5.0 - - - diff --git a/server/Program.cs b/server/Program.cs deleted file mode 100644 index d984b5d..0000000 --- a/server/Program.cs +++ /dev/null @@ -1,185 +0,0 @@ -using System; -using System.Text; -using System.Net; -using System.Net.Sockets; -using System.Threading; -using System.Collections.Generic; - -namespace SocketTestServer { - class Server { - static Socket server; - static List lobbies; - static byte[] buffer; - static int listeners = 0; - - static void Main(string[] args) { - // Initialize global bariables - buffer = new byte[1024]; - lobbies = new List(); - - // Get computers' ipv4 address - string ipAddress = "127.0.0.1"; - try { - string hostName = Dns.GetHostName(); - IPHostEntry entryList = Dns.GetHostEntry(hostName); - IPAddress ipv4Entry = entryList.AddressList[0]; - ipAddress = ipv4Entry.ToString(); - } catch (Exception) {} - - // Create a new socket server and listen for connections - server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); - server.Bind(new IPEndPoint(IPAddress.Parse(ipAddress), 3000)); - server.Listen(10); - - // Accept any client asynchronously - while (true) { - if (listeners < 10) { - server.BeginAccept(new AsyncCallback(AcceptCallback), server); - listeners++; - } else { - // If there are already 10 async callbacks running, then sleep - Thread.Sleep(500); - } - } - } - - // End off the message send-callback - static void SendCallback(IAsyncResult AR) { - Socket self = AR.AsyncState as Socket; - self.EndSend(AR); - } - - // End off the message receive-callback - static void ReceiveCallback(IAsyncResult AR) { - Socket self = AR.AsyncState as Socket; - try { - // Handle any received bytes - int received = self.EndReceive(AR); - if (received <= 0) - throw new Exception(); - byte[] data = new byte[received]; - Array.Copy(buffer, data, received); - // Convert bytes to string - string message = Encoding.UTF8.GetString(data); - // Handle received message - handleMessage(message, self); - // Begin listening for more messages - self.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(ReceiveCallback), self); - } catch (Exception) { - // Potentially detected Disconnect - if (self.Connected) { - // Remove client from lobby, and lobby from list - foreach (Lobby l in lobbies) { - if (l.users.Contains(self)) - l.users.Remove(self); - if (l.users.Count == 0) { - lobbies.Remove(l); - Console.WriteLine("Closed Lobby."); - } - } - // Close client connection - self.Close(); - } - } - } - - // End off the connection accept-callback - static void AcceptCallback(IAsyncResult AR) { - Socket self = AR.AsyncState as Socket; - Socket other = self.EndAccept(AR); - listeners--; - other.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(ReceiveCallback), other); - } - - // Handle any incomming messages from given client - static void handleMessage(string message, Socket other) { - // Keep lowercase copy - string copy = message.ToLower(); - string response = ""; - - // Create new with random code lobby and add client to it - if (copy == "new lobby") { - Random rng = new Random(); - string lobbyCode = ""; - while (lobbyCode.Length < 6) - lobbyCode += (char)rng.Next(33, 127); - lobbies.Add(new Lobby(other, lobbyCode)); - response = $"Created {lobbyCode}"; - Console.WriteLine($"Created Lobby '{lobbyCode}'"); - - // Give Lobby-Count feedback - } else if (copy == "lobby count") { - response = $"Count {lobbies.Count}"; - Console.WriteLine($"Replied with count '{lobbies.Count}'."); - - // Check for any disconnected Clients, remove them from lobbies and lobby from list - } else if (copy == "check connections") { - foreach (Lobby l in lobbies) { - Console.WriteLine($"Lobby: {l.code}"); - foreach (Socket s in l.users) - try { - if (!s.Connected) { - s.Close(); - l.users.Remove(s); - Console.WriteLine("User Disconnected."); - } - } catch (Exception) {} - if (l.users.Count == 0) { - lobbies.Remove(l); - Console.WriteLine("Closed Lobby."); - } - } - response = "Verified all Connections."; - - // Other possible message combinations - } else { - string[] splits = copy.Split(' '); - - // Check if user is in a lobby or add them to wished lobby - if (splits.Length >= 3 && splits[0]+' '+splits[1] == "join lobby") { - string lobbyCode = splits[2]; - foreach (Lobby l in lobbies) { - if (l.users.Contains(other)) { - response = $"Already in a Lobby"; - break; - } else if (l.code == lobbyCode) { - l.users.Add(other); - response = $"Joined {lobbyCode}"; - break; - } - } - // Handle feedback for Lobbies - if (response.Split(' ')[0] == "Joined") - Console.WriteLine($"Added User to Lobby '{lobbyCode}'."); - else { - response = "No Lobby with this Code available."; - Console.WriteLine($"Could not add user to Lobby '{lobbyCode}'."); - } - - // Ignore any remaining possible messages - } else - Console.WriteLine($"Ignored message '{message}'."); - } - - // Respond to client if an action was executed successfully. - if (response != "") { - byte[] data = Encoding.UTF8.GetBytes(response); - other.BeginSend(data, 0, data.Length, SocketFlags.None, new AsyncCallback(SendCallback), other); - } - } - - // Custom Lobby class - class Lobby { - // Keep track of own lobby code and list of users - public List users; - public string code; - - // Initialize Lobby with current user in list and given lobby code - public Lobby(Socket other, string lobbyCode) { - users = new List(); - users.Add(other); - code = lobbyCode; - } - } - } -} diff --git a/server/server.csproj b/server/server.csproj deleted file mode 100644 index 2082704..0000000 --- a/server/server.csproj +++ /dev/null @@ -1,8 +0,0 @@ - - - - Exe - net5.0 - - -