It hasn't been thoroughly tested, but seems to work OK.
This should scale pretty nicely as well. Originally I was going to code it in C++, but after doing some research the scalability factor that I thought was going to gain using C++ seemed to be low considering the amount of work it was going to take.
Server.cs
using System; using System.Collections.Generic; using System.Collections.Specialized; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Net.Sockets; using System.Net; namespace Server { public class Server { private int port; private Socket serversocket; public delegate void ConnectionEvent(Connection conn); public List<Connection> connections; public event ConnectionEvent AcceptCallback; public event ConnectionEvent RecieveCallback; public event ConnectionEvent DisconnectCallback; public Server(int p) { port = p; connections = new List<Connection>(500); } public bool Start() { IPEndPoint ipendpoint; try { //do not assign an IP, as it is a server (IPAdress.Any) ipendpoint = new IPEndPoint(IPAddress.Any, this.port); } catch (ArgumentOutOfRangeException e) { //port was probably out of range throw new ArgumentOutOfRangeException("Please check port number.", e); } try { //create the main server worker socket serversocket = new Socket(ipendpoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp); } catch (SocketException e) { //here because we couldn't create server socket throw new ApplicationException("Couldn't create server socket.", e); } try { //bind the socket to the specified port serversocket.Bind(ipendpoint); //begin listening for incoming connections serversocket.Listen(100); } catch (SocketException e) { //probably here due to the port being in use throw new ApplicationException("Couldn't bind/listen on port: " + this.port.ToString(), e); } try { //begin accepting incoming connections serversocket.BeginAccept(new AsyncCallback(acceptCallback), this.serversocket); } catch (Exception e) { throw new ApplicationException("Error assigning the accept callback.", e); } return true; } private void acceptCallback(IAsyncResult ar) { Connection conn = new Connection(); try { //finish the connection //get the resulting state object Socket sck = (Socket)ar.AsyncState; //create new connection object to be added to our concurrent connection list conn = new Connection(sck.EndAccept(ar)); //lock our list for thread safety lock(connections) { //add to our list connections.Add(conn); } //begin accepting data on that socket, using our Connection object as the object state conn.socket.BeginReceive(conn.buffer, 0, conn.buffer.Length, SocketFlags.None, new AsyncCallback(receiveCallback), conn); //start accepting connections again serversocket.BeginAccept(new AsyncCallback(acceptCallback), this.serversocket); //if the eventhandler for incoming connections has been assgined, call it if(AcceptCallback != null) { AcceptCallback(conn); } } catch(SocketException e) { Disconnect(conn); throw new ApplicationException(e.Message, e); } } private void Disconnect(Connection conn) { //remove connection from list lock (connections) { connections.Remove(conn); } //make sure it's completely closed conn.socket.Close(); //if the eventhandler for disconnection has been assgined, call it if (DisconnectCallback != null) { DisconnectCallback(conn); } } private void receiveCallback(IAsyncResult ar) { //retrieve the connection that has data ready Connection conn = (Connection)ar.AsyncState; try { //retrieve the data(bytes) while also returning how many bytes were read int bytes = conn.socket.EndReceive(ar); //if bytes is more than zero, then data was successfully read if(bytes > 0) { //if the data receive callback has been assigned, call it if(RecieveCallback != null) { RecieveCallback(conn); } //being receiving data again conn.socket.BeginReceive(conn.buffer, 0, conn.buffer.Length, SocketFlags.None, new AsyncCallback(receiveCallback), conn); } else { //if zero bytes were read, connection was closed Disconnect(conn); } } catch(SocketException e) { Disconnect(conn); throw new ApplicationException("Error with EndReceive or BeginReceive", e); } } } }
Connection.cs
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Net.Sockets; namespace Server { public class Connection { public byte[] buffer; public Socket socket; public Connection() { } public Connection(Socket sock) { buffer = new byte[1024]; socket = sock; } public void Send(string data) { try { if (this.socket.Connected) { //convert string into an aray of bytes byte[] buffer = ASCIIEncoding.ASCII.GetBytes(data); //send our byte[] buffer socket.Send(buffer, SocketFlags.None); } } catch (SocketException e) { throw new ApplicationException("Error with Send", e); } } } }
Usage:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Threading; namespace Server { class Program { static void Main(string[] args) { //port 80(webserver) Server server = new Server( 80 ); server.RecieveCallback += server_RecieveCallback; server.AcceptCallback += server_AcceptCallback; server.DisconnectCallback += server_DisconnectCallback; server.Start(); ThreadStart threadstart = new ThreadStart(ThreadJob); Thread thread = new Thread(threadstart); thread.Start(); } static void server_DisconnectCallback(Connection conn) { Console.WriteLine("Connection disconnected "); } static void server_AcceptCallback(Connection conn) { Console.WriteLine("Incoming connection "); } static void ThreadJob() { while (true) Thread.Sleep(1); } static void server_RecieveCallback(Connection conn) { Console.WriteLine(ASCIIEncoding.ASCII.GetString(conn.buffer)); } } }
Go to your web browser and type 127.0.0.1 into the address bar.
If any one sees any issues and things to improve on, let me know. (Sure there is).
If any one has any questions let me know.