Asynchronous C# server[转]

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);
            }
        }
    }
}  
View Code

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);
            }
        }

    }
} 
View Code

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));

        }
    }
}
View Code

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.

不满足现状,用于挑战高峰!
原文地址:https://www.cnblogs.com/meyon/p/4009255.html