C#异步服务器套接字-线程安全/性能(MMO游戏)

C#异步服务器套接字-线程安全/性能(MMO游戏),c#,.net,multithreading,asynchronous,asyncsocket,C#,.net,Multithreading,Asynchronous,Asyncsocket,我正在为我的2D MMO小游戏编写这个游戏服务器 下面是我的问题: 您如何看待代码的线程安全性? 你能告诉我问题在哪里以及如何修复/修补它们吗 这是我的密码: using System; using System.Net; using System.Net.Sockets; using System.Text; using System.Threading; using System.Collections.Generic; using System.Data; using System.Dat

我正在为我的2D MMO小游戏编写这个游戏服务器

下面是我的问题:

  • 您如何看待代码的线程安全性? 你能告诉我问题在哪里以及如何修复/修补它们吗
  • 这是我的密码:

    using System;
    using System.Net;
    using System.Net.Sockets;
    using System.Text;
    using System.Threading;
    using System.Collections.Generic;
    using System.Data;
    using System.Data.SqlClient;
    using System.Data.SqlTypes;
    
    public class Constanti
    {
        public const int CLASS_DARKELF_MAGICIAN = 1;
        public const int CLASS_HUMAN_MAGICIAN   = 2;
        public const int CLASS_WARRIOR          = 3;
        public const int CLASS_MODERN_GUNMAN    = 4;
        public const int SUIT_1 = 1;
        public const int SUIT_2 = 2;
        public const int SUIT_3 = 3;
        public const int SUIT_4 = 4;
        public const int SUIT_Admin = 5;
    
        //MAX/MIN
        public const int MAX_LEVEL = 100;
        public const int MAX_SKILL_LEVEL = 1000;
    
        //SERVER MAX/MIN
        public const int MAX_CONNECTIONS = 300;
        public const int MAX_CONNECTIONS_IP = 4;
    }
    
    // State object for reading client data asynchronously
    public class Player
    {
        // Client  socket.
        public Socket workSocket = null;
        // Size of receive buffer.
        public const int BufferSize = 1024;
        // Receive buffer.
        public byte[] buffer = new byte[BufferSize];
        // Received data string.
        public StringBuilder sb = new StringBuilder();
        //Player-Info
        public int PlayerStats_Health = 0;
        public int PlayerStats_Energy = 0;
        public int PlayerInfo_Class = 0;
        public int PlayerInfo_Suit = 0;
        public int PlayerInfo_Level = 0;
        public int PlayerInfo_SkillLevel = 0;
    
        public void SetDefaults()
        {
            PlayerStats_Health = 100;
            PlayerStats_Energy  = 200;
            PlayerInfo_Class = Constanti.CLASS_DARKELF_MAGICIAN;
            PlayerInfo_Suit = Constanti.SUIT_1;
            PlayerInfo_Level = 1;
            PlayerInfo_SkillLevel = 1;
        }
    
        public Player()
        {
        }
    
        public String pIPAddress;
    }
    
    public class GameObjectLists
    {
        public static List<Player> PlayersList = new List<Player>();
    }
    
    public class AsynchronousSocketListener
    {
        // Thread signal.
        public static ManualResetEvent allDone = new ManualResetEvent(false);
        public static int PlayersOnline = 0;
        public AsynchronousSocketListener()
        {}
    
        public static void InitializeMySQL()
        {
            //TODO MySQLI/MySQL Connection
        }
    
        public static void MysqlUpdateQuery()
        {
            //Mysql UPDATE, no return stmt
        }
    
        public static String MySQLSelect()
        {
            //TODO MySQL Select
            String retdata="test";
    
            return retdata;
        }
    
        public static void StartListening()
        {
            // Data buffer for incoming data.
            byte[] bytes = new Byte[1024];
            // Establish the local endpoint for the socket.
            // The DNS name of the computer
            /*
            IPHostEntry ipHostInfo = Dns.Resolve(Dns.GetHostName());
            IPAddress ipAddress = ipHostInfo.AddressList[0];*/
    
            IPEndPoint localEndPoint = new IPEndPoint(IPAddress.Any, 86);
    
            // Create a TCP/IP socket.
            Socket listener = new Socket(AddressFamily.InterNetwork,SocketType.Stream, ProtocolType.Tcp);
    
            // Bind the socket to the local endpoint and listen for incoming connections.
            try
            {
                listener.Bind(localEndPoint);
                listener.Listen(50);
                Console.WriteLine("Server Started, waiting for connections...");
    
                while (true)
                {
                    // Set the event to nonsignaled state.
                    allDone.Reset();
    
                    // Start an asynchronous socket to listen for connections.
    
                    listener.BeginAccept(
                        new AsyncCallback(AcceptCallback),
                        listener);
    
                    // Wait until a connection is made before continuing.
                    allDone.WaitOne();
                }
            }
            catch (Exception e)
            {
                Console.WriteLine(e.ToString());
            }
    
            //Console.WriteLine("\nPress ENTER to continue...");
            Console.Read();
        }
    
    
        public static void AcceptCallback(IAsyncResult ar)
        {
            // Get the socket that handles the client request.
            Socket listener     = (Socket)ar.AsyncState;
            Socket clientsocket = listener.EndAccept(ar);
            // Signal the main thread to continue.
            allDone.Set();
            clientsocket.Blocking = false;      //set to non-blocking
            // Create the state object.
            Player PlayerInfo = new Player();
            PlayerInfo.workSocket = clientsocket;
    
            IPEndPoint thisIpEndPoint = PlayerInfo.workSocket.RemoteEndPoint as IPEndPoint; //Get Local Ip Address
            PlayerInfo.pIPAddress = thisIpEndPoint.Address.ToString();
    
            GameObjectLists.PlayersList.Add(PlayerInfo);
            PlayersOnline++;
    
            int numconnsofip = 0;
            GameObjectLists.PlayersList.ForEach(delegate(Player PlayerInfoCheck)
            {
                    //Console.WriteLine(name);
                    if (PlayerInfoCheck.pIPAddress == PlayerInfo.pIPAddress)
                    {
                        numconnsofip++;
                    }
            });
    
            if (PlayersOnline > Constanti.MAX_CONNECTIONS || numconnsofip > Constanti.MAX_CONNECTIONS_IP)
            {
                Disconnect(clientsocket, PlayerInfo);
            }
            else
            {
                Console.WriteLine("Player with IP:[{0}] has [{1}] Connections", thisIpEndPoint.Address.ToString(), numconnsofip);
                PlayerInfo.SetDefaults();
                //clientsocket.LingerState = new LingerOption(true, 2);    // give it up to 2 seconds for send
                Console.WriteLine("New Connection Total:[{0}]", PlayersOnline);
                clientsocket.BeginReceive(PlayerInfo.buffer, 0, Player.BufferSize, 0, new AsyncCallback(ReadCallback),
                    PlayerInfo);
            }
        }
    
        public static void ProtocolCore(Player PlayerInfo, String data)
        {
            Console.WriteLine("Procesing Packet:{0}",data);
            //if data == bla bla then send something to everyone:
    
            GameObjectLists.PlayersList.ForEach(delegate(Player ObjPlayerInfo)
            {
                Send(data,ObjPlayerInfo);
            });
        }
    
        public static void ReadCallback(IAsyncResult ar)
        {
            // TEST #1 - IF WE HANG HERE, THERE WILL BE STILL OTHER CONNECTIONS COMING HERE, BUT NO MULTI THREADING?? 
            // Retrieve the state object and the clientsocket socket
            // from the asynchronous state object.
            Player PlayerInfo = (Player)ar.AsyncState;
            Socket clientsocket = PlayerInfo.workSocket;
            try
            {
                String content = String.Empty;  //content buffer
    
                // Read data from the client socket. 
                // IF THIS FAILS, WE CATCH / ASSUMING THAT:
                // THE CLIENT FORCE-CLOSED THE CONNECTION OR OTHER REASON.
                int bytesRead = clientsocket.EndReceive(ar);    
    
                if (bytesRead > 0)
                {
                    // There  might be more data, so store the data received so far.
    
                    PlayerInfo.sb.Append(Encoding.ASCII.GetString(
                        PlayerInfo.buffer, 0, bytesRead));
    
                    // Check for end-of-file tag. If it is not there, read 
                    // more data.
                    content = PlayerInfo.sb.ToString();
                    int eofindex = content.IndexOf("<EOF>");
                    if (eofindex > -1)
                    {
                        // All the data has been read from the 
                        // client. Display it on the console.
                        content = content.Substring(0,eofindex);  //remove THE <EOF>
    
                        Console.WriteLine("Read {0} bytes from socket. Data : {1}",content.Length, content);
    
                        //PROCESS THE PACKET/DATA (PROTOCOL CORE)
                        ProtocolCore(PlayerInfo, content);
    
                        //Echo the data back to the client.
                        Send(content, PlayerInfo);
                        // CLEAR THE BUFFERS
                        PlayerInfo.sb.Remove(0, PlayerInfo.sb.Length);
                        Array.Clear(PlayerInfo.buffer, 0, PlayerInfo.buffer.Length);
    
                        // GO TO LISTEN FOR NEW DATA
                        clientsocket.BeginReceive(PlayerInfo.buffer, 0, Player.BufferSize, 0,
                        new AsyncCallback(ReadCallback), PlayerInfo);
                    }
                    else
                    {
                        // Not all data received. Get more.
                        clientsocket.BeginReceive(PlayerInfo.buffer, 0, Player.BufferSize, 0,
                        new AsyncCallback(ReadCallback), PlayerInfo);
                    }
                }
                else
                {
                    //ASSUMING WE RECEIVED 0 SIZED PACKET or CLIENT DISCONNECT / THEREFORE CLOSE THE CONNECTION
                    Disconnect(clientsocket, PlayerInfo);
                }
            }
            catch (Exception e)
            {
                Console.WriteLine(e.ToString());
                Disconnect(clientsocket, PlayerInfo);
            }
        }
    
        private static void Send(String data,Player PlayerInfo)
        {
            // Convert the string data to byte data using ASCII encoding.
            byte[] byteData = Encoding.ASCII.GetBytes(data);
    
            // Begin sending the data to the remote device.
            PlayerInfo.workSocket.BeginSend(byteData, 0, byteData.Length, 0,
                new AsyncCallback(SendCallback), PlayerInfo);
        }
    
        private static void Disconnect(Socket clientsocket, Player PlayerInfo)
        {
    
            try
            {
                PlayersOnline--; //Is this Thread-Safe also?
                GameObjectLists.PlayersList.Remove(PlayerInfo);
                Console.WriteLine("Socket Disconnected, PlayerObjects:[{0}]", GameObjectLists.PlayersList.Count);
                clientsocket.Shutdown(SocketShutdown.Both);
                clientsocket.Close();
            }
            catch (Exception e)
            {
               Console.WriteLine(e.ToString());
            }
        }
    
        private static void SendCallback(IAsyncResult ar)
        {
            // Retrieve the socket from the state object.
            Player PlayerInfo = (Player)ar.AsyncState;
            Socket clientsocket = PlayerInfo.workSocket;
            try
            {
                // Complete sending the data to the remote device.
                int bytesSent = clientsocket.EndSend(ar);
                Console.WriteLine("Sent {0} bytes to client.", bytesSent);
            }
            catch (Exception e)
            {
                Console.WriteLine(e.ToString());
                Disconnect(clientsocket, PlayerInfo);
            }
        }
    
    
        public static int Main(String[] args)
        {
            InitializeMySQL();
            StartListening();
            return 0;
        }
    }
    
    使用系统;
    Net系统;
    使用System.Net.Sockets;
    使用系统文本;
    使用系统线程;
    使用System.Collections.Generic;
    使用系统数据;
    使用System.Data.SqlClient;
    使用System.Data.SqlTypes;
    君士坦提公务舱
    {
    公共const int CLASS_DARKELF_魔术师=1;
    公共常数int CLASS_HUMAN_魔术师=2;
    公共const int CLASS_WARRIOR=3;
    公共建筑国际级现代枪手=4;
    公营建筑1=1;
    公共建筑国际诉讼2=2;
    公营建筑3=3;
    公营建筑4=4;
    公共建筑内部行政=5;
    //最大/分钟
    公共常数int MAX_LEVEL=100;
    公共建筑最大技能水平=1000;
    //服务器最大/分钟
    公共const int MAX_CONNECTIONS=300;
    公共const int MAX_CONNECTIONS_IP=4;
    }
    //用于异步读取客户端数据的状态对象
    公开课选手
    {
    //客户端套接字。
    公共套接字工作组=null;
    //接收缓冲区的大小。
    public const int BufferSize=1024;
    //接收缓冲区。
    公共字节[]缓冲区=新字节[BufferSize];
    //接收到的数据字符串。
    公共StringBuilder sb=新StringBuilder();
    //玩家信息
    public int PlayerStats_Health=0;
    公共int PlayerStats_Energy=0;
    public int PlayerInfo_Class=0;
    公共int PlayerInfo_Suit=0;
    公共int PlayerInfo_级别=0;
    public int PlayerInfo_SkillLevel=0;
    公共void SetDefaults()
    {
    PlayerStats_Health=100;
    玩家状态能量=200;
    PlayerInfo_Class=Constanti.Class_DARKELF_魔术师;
    PlayerInfo_Suit=Constanti.Suit_1;
    PlayerInfo_Level=1;
    PlayerInfo_SkillLevel=1;
    }
    公共玩家()
    {
    }
    公共字符串地址;
    }
    公共类游戏对象列表
    {
    public static List PlayersList=new List();
    }
    公共类异步SocketListener
    {
    //线程信号。
    public static ManualResetEvent allDone=新的ManualResetEvent(false);
    公共静态int PlayersOnline=0;
    公共异步SocketListener()
    {}
    publicstaticvoid初始值为emysql()
    {
    //TODO MySQLI/MySQL连接
    }
    公共静态void MysqlUpdateQuery()
    {
    //Mysql更新,不返回stmt
    }
    公共静态字符串MySQLSelect()
    {
    //TODO MySQL选择
    字符串retdata=“test”;
    返回数据;
    }
    公共静态侦听()
    {
    //输入数据的数据缓冲区。
    字节[]字节=新字节[1024];
    //为套接字建立本地端点。
    //计算机的DNS名称
    /*
    IPHostEntry ipHostInfo=Dns.Resolve(Dns.GetHostName());
    IPAddress IPAddress=ipHostInfo.AddressList[0]*/
    IPEndPoint localEndPoint=新的IPEndPoint(IPAddress.Any,86);
    //创建TCP/IP套接字。
    套接字侦听器=新套接字(AddressFamily.InterNetwork、SocketType.Stream、ProtocolType.Tcp);
    //将套接字绑定到本地端点并侦听传入连接。
    尝试
    {
    Bind(localEndPoint);
    听(50);
    WriteLine(“服务器已启动,正在等待连接…”);
    while(true)
    {
    //将事件设置为非信号状态。
    全部完成。重置();
    //启动异步套接字以侦听连接。
    listener.beginacept(
    新建异步回调(AcceptCallback),
    听众);
    //等待连接完成后再继续。
    全部完成。WaitOne();
    }
    }
    捕获(例外e)
    {
    Console.WriteLine(如ToString());
    }
    //Console.WriteLine(“\n按ENTER继续…”);
    Console.Read();
    }
    公共静态无效接受回调(IAsyncResult ar)
    {
    //获取处理客户端请求的套接字。
    套接字侦听器=(套接字)ar.AsyncState;
    套接字clientsocket=listener.EndAccept(ar);
    //向主线程发出继续的信号。
    allDone.Set();
    clientsocket.Blocking=false;//设置为非阻塞
    //创建状态对象。
    Player PlayerInfo=新玩家();
    PlayerInfo.workSocket=客户端套接字;
    IPEndPoint thisIpEndPoint=PlayerInfo.workSocket.RemoteEndPoint作为IPEndPoint;//获取本地Ip地址
    PlayerInfo.pIPAddress=thisIpEndPoint.Address.ToString();
    GameObjectList.PlayerList.Add(PlayerInfo);
    PlayerOnline++;
    int numconnsofip=0;
    GameObjectList.PlayerList.ForEach(代理(玩家PlayerInfoCheck)
    {
    //Console.WriteLine(名称);
    if(PlayerInfoCheck.pIPAddress==PlayerInfo.pIPAddress)
    {
    numconnsofip++;
    }
    });
    如果(playerOnline>Constanti.MAX|u CONNECTIONS | | numconnsofip>Constanti.MAX_CONNECTIONS|IP)
    {
    断开(客户端插座、播放器信息);
    }
    其他的
    {
    WriteLine(“具有IP:[{0}]的播放器有[{1}]个连接”,thisIpEndPoint.Address.ToString(),numconnsofip);
    PlayerInfo.SetDefaults();
    //clientsocket.LingerState=new LingerOption(true,2);//最多2秒用于发送
    Console.WriteLine(“新连接总数:[{0}]”,PlayerOnline);
    clientsocket.BeginReceive(PlayerInfo.buffer,0,Player.BufferSize,0,新异步回调(ReadCallback),
    PlayerInfo);
    }
    }
    公共静态无效协议(玩家PlayerI)