3
\$\begingroup\$

I am currently writing a TcpListener class where it begins a new socket and adds a new Session. I have classes called Session and SessionManager.

Session is where I establish a new SESSION, a new instance of the class. I have a new session for each player entering and connecting to a socket

SessionManager is where I keep a dictionary of the Sessions and I register new session on the HandleImcomingConnection void in sessionmanager.cs.

All the code works but the way the sockets connect and the way there being disposed are really poor coding. That is what my players have been saying and when I shared my code with a few friends they said it was terribly written the way the sockets are coded. I can't figure out what they mean as I can't see anything wrong with my code. But then again I don't know anything about sockets so I need your help in letting me know what to improve.

Program.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
 using System.Threading.Tasks;

namespace Reality
{
    class Program
    {
        static void Main(string[] args)
        {
            Server = new SnowTcpListener(new IPEndPoint(IPAddress.Any,381,60,
            new OnNewConnectionCallback(SessionManager.HandleIncomingConnection));
            Console.WriteLine("Listening for connections!");
            Console.ReadKey();
        }
    }
}

Here is the TcpListener class that has been made above 'new SnowTcpListener' SnowTcpListener.cs

namespace Reality.Network
{
    using System;
    using System.Net;
    using System.Net.Sockets;

    public class SnowTcpListener : IDisposable
    {
        private OnNewConnectionCallback mCallback;
        private Socket mSocket;

        public SnowTcpListener(IPEndPoint LocalEndpoint, int Backlog, OnNewConnectionCallback Callback)
        {
            this.mCallback = Callback;
            this.mSocket = new Socket(LocalEndpoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
            this.mSocket.Bind(LocalEndpoint);
            this.mSocket.Listen(0);
            this.BeginAccept();
        }

        private void BeginAccept()
        {
            try
            {
                this.mSocket.BeginAccept(new AsyncCallback(this.OnAccept), null);
            }
            catch (Exception ex)
            {
                Output.WriteLine(ex.Message, OutputLevel.CriticalError);
            }
        }

        public void Dispose()
        {
            if (this.mSocket != null)
            {
                this.mSocket.Dispose();
                this.mSocket = null;
            }
        }

        private void OnAccept(IAsyncResult Result)
        {
            try
            {
                Socket socket = this.mSocket.EndAccept(Result);
                this.mCallback(socket);
            }
            catch (Exception ex)
            { 
                Output.WriteLine(ex.Message, OutputLevel.CriticalError);
            }
            this.BeginAccept();
        }
    }
}

SessionManager.cs this is where a new session is made and it handles sessions disconnected and other things...

namespace Reality.Game.Sessions
{
     using Reality;
     using Reality.Game.Moderation;
     using Database_Manager.Database.Session_Details.Interfaces;
     using System;
     using System.Collections.Generic;
     using System.Net.Sockets;
     using System.Threading;

    public static class SessionManager
    {
        private static uint mCounter;
        private static Thread mLatencyTestThread;
        private static Thread mMonitorThread;
        private static Dictionary<uint, Session> mSessions;
        private static List<uint> mSessionsToStop;
        private static object mSyncRoot;

        public static bool ContainsCharacterId(uint Uid)
        {
            lock (mSessions)
            {
                foreach (Session session in mSessions.Values)
                {
                    if (!session.Stopped && (session.CharacterId == Uid))
                    {
                        return true;
                    }
                }
            }
            return false;
        }

        private static void ExecuteLatencyMonitor()
        {
            try
            {
                while (Program.Alive)
                {
                    ServerMessage message = PingComposer.Compose();
                    lock (mSessions)
                    {
                        lock (mSessionsToStop)
                        {
                            foreach (Session session in mSessions.Values)
                            {
                                if (!session.Stopped && !mSessionsToStop.Contains(session.Id))
                                {
                                    if (!session.LatencyTestOk)
                                    {
                                        mSessionsToStop.Add(session.Id);
                                    }
                                    else
                                    {
                                        session.LatencyTestOk = false;
                                        session.SendData(message);
                                    }
                                }
                            } 
                        }
                    }
                    Thread.Sleep(0xafc8);
                }
            }
            catch (ThreadAbortException)
            {
            }
            catch (ThreadInterruptedException)
            {
            }
        }

        private static void ExecuteMonitor()
        {
            try
            {
                while (Program.Alive)
                {
                    List<Session> list = new List<Session>();
                    List<Session> list2 = new List<Session>();
                    lock (mSessions)
                    {
                        lock (mSessionsToStop)
                        {
                            foreach (uint num in mSessionsToStop)
                            {
                                if (mSessions.ContainsKey(num))
                                {
                                    list2.Add(mSessions[num]);
                                }
                            }
                            mSessionsToStop.Clear();
                        }
                        foreach (Session session in mSessions.Values)
                        {
                            if ((!list2.Contains(session) && session.Stopped) && (session.TimeStopped > 15.0))
                            {
                                list.Add(session);
                            }
                        }
                    }
                    if (list2.Count > 0)
                    {
                        using (IQueryAdapter client = Program.GetDatabaseManager().getQueryreactor())
                        {
                            foreach (Session session2 in list2)
                            {
                                session2.Stop(client);
                            }
                        }
                    }
                    foreach (Session session3 in list)
                    {
                        session3.Dispose();
                        lock (mSessions)
                        {
                            if (mSessions.ContainsKey(session3.Id))
                            {
                                mSessions.Remove(session3.Id);
                            }
                        }
                    }
                    Thread.Sleep(100);
                }
            }
            catch (ThreadAbortException)
            {
            }
            catch (ThreadInterruptedException)
            {
            }
        }

        public static Session GetSessionByCharacterId(uint Id)
        {
            lock (mSessions)
            {
                foreach (Session session in mSessions.Values)
                {
                    if (!session.Stopped && (session.CharacterId == Id))
                    {
                        return session;
                    }
                } 
            }
            return null;
        }

        public static void HandleIncomingConnection(Socket IncomingSocket)
        {
            bool flag = ModerationBanManager.IsRemoteAddressBlacklisted(IncomingSocket.RemoteEndPoint.ToString().Split(new char[] { ':' })[0]);
            Output.WriteLine((flag ? "Rejected" : "Accepted") + " incoming connection from " + IncomingSocket.RemoteEndPoint.ToString() + ".", OutputLevel.Informational);
            if (flag)
            { 
                try
                {
                    IncomingSocket.Close();
                } 
                catch (Exception ex)
                {
                    Output.WriteLine(ex.Message, OutputLevel.CriticalError);
                }
            }
            else
            {
                lock (mSyncRoot)
                {
                    uint key = mCounter++;
                    mSessions.Add(key, new Session(key, IncomingSocket));
                }
            }
        }

        public static void Initialize()
        {
            mSessions = new Dictionary<uint, Session>();
            mSessionsToStop = new List<uint>();
            mCounter = 0;
            mMonitorThread = new Thread(new ThreadStart(SessionManager.ExecuteMonitor));
            mMonitorThread.Priority = ThreadPriority.BelowNormal;
            mMonitorThread.Name = "GameClientMonitor";
            mMonitorThread.Start();
            mLatencyTestThread = new Thread(new ThreadStart(SessionManager.ExecuteLatencyMonitor));
            mLatencyTestThread.Priority = ThreadPriority.Lowest;
            mLatencyTestThread.Name = "SessionLatencyTester";
            mLatencyTestThread.Start();
            mSyncRoot = new object();
        }

        public static void StopSession(uint SessionId)
        {
            lock (mSessionsToStop)
            {
                mSessionsToStop.Add(SessionId);
            }
        }

        public static int ActiveConnections
        {
            get
            {
                lock (mSessions)
                {
                    return mSessions.Count;
                }
            }
        }

        public static Dictionary<uint, string> ConnectedUserData
        {
            get
            {
                Dictionary<uint, string> dictionary = new Dictionary<uint, string>();
                lock (mSessions)
                {
                    foreach (Session session in mSessions.Values)
                    {
                        if (session.Authenticated)
                        {
                            dictionary.Add(session.CharacterId, session.CharacterInfo.Username);
                        }
                    }
                }
                return dictionary;
            }
        }

        public static Dictionary<uint, Session> Sessions
        {
            get
            {
                Dictionary<uint, Session> dictionary = new Dictionary<uint, Session>();    
                lock (mSessions)
                {
                    foreach (KeyValuePair<uint, Session> pair in mSessions)
                    {
                        if (!pair.Value.Stopped)
                        {
                            dictionary.Add(pair.Key, pair.Value);
                        }
                    }
                }
                return new Dictionary<uint, Session>(dictionary);
            }
        }  
    }
}

Session.cs, where all the players are held... A new session is made on a new socket

public class Session : IDisposable
{
    private bool mAuthProcessed;
    private byte[] mBuffer;
    private int mCurrentEffect;
    private uint mId;
    private bool mPongOk;
    private bool mRoomAuthed;
    private uint mRoomId;
    private bool mRoomJoined;
    private Socket mSocket;
    private double mStoppedTimestamp;
    private ClubSubscription mSubscriptionManager;
    private uint mTargetTeleporterId;
    private string mUserAgent;

    public Session(uint Id, Socket Socket)
    {
        this.mId = Id;
        this.mSocket = Socket;
        this.mBuffer = new byte[0x200];
        this.mPongOk = true;
        this.BeginReceive();
    }

    private void BeginReceive()
    {
        try
        {
            if (this.mSocket != null)
            {
                this.mSocket.BeginReceive(this.mBuffer, 0, this.mBuffer.Length, SocketFlags.None, new AsyncCallback(this.OnReceiveData), null);
            }
        }
        catch (Exception ex)
        {
            Output.WriteLine(ex.Message, OutputLevel.CriticalError);
            SessionManager.StopSession(this.mId);
        }
    }

    public void Dispose()
    {
        try
        {
            if (!this.Stopped)
            {
                throw new InvalidOperationException("Cannot dispose of a session that has not been stopped");
            }

            Console.WriteLine("Disconnected Connnection [ID: " + this.Id + "] and their associated resources.");
        }
        catch (Exception exception)
        {
            Console.WriteLine("[Dispose] " + exception.Message);
        }
    }

    public static string FigToUniFig(string _Figure)
    {
        string str2 = _Figure;
        string str3 = Regex.Split(_Figure, "hr")[1];
        str2 = str3.Split(new char[] { '.' })[0];
        string str4 = _Figure;
        string str5 = Regex.Split(_Figure, "hd")[1];
        str4 = str5.Split(new char[] { '.' })[0];
        return Convert.ToString("hr" + str2 + ".hd" + str4 + ".");
    }

    public bool HasRight(string Right)
    {
        return ((this.mBadgeCache != null) && this.mBadgeCache.HasRight(Right));
    }

    private void OnDataSent(IAsyncResult Result)
    {
        try
        {
            if (this.mSocket != null)
            {
                this.mSocket.EndSend(Result);
            }
        }
        catch (Exception ex)
        {
            SessionManager.StopSession(this.mId);
        }
    }

    private void OnReceiveData(IAsyncResult Result)
    {
        int byteCount = 0;
        try
        {
            if (this.mSocket != null)
            {
                byteCount = this.mSocket.EndReceive(Result);
            }
        }
        catch (Exception ex)
        {
            Output.WriteLine(ex.Message, OutputLevel.CriticalError);
        }
        if ((byteCount < 1) || (byteCount >= this.mBuffer.Length))
        {
            SessionManager.StopSession(this.mId);
        }
        else
        {
            this.BeginReceive();
        }
    }

    public void SendData(byte[] Data)
    {
        try
        {
            if (this.mSocket == null)
            {
                Output.WriteLine("[SND] Socket is null!", OutputLevel.CriticalError);
            }
            else
            {
                this.mSocket.BeginSend(Data, 0, Data.Length, SocketFlags.None, new AsyncCallback(this.OnDataSent), null);
            }
        }
        catch (Exception ex)
        {
            Output.WriteLine(ex.Message, OutputLevel.CriticalError);
        }
    }

    public void Stop(IQueryAdapter MySqlClient)
    {
        try
        {
            if (!this.Stopped)
            {
                this.mSocket.Close();
                this.mSocket = null;
                this.mStoppedTimestamp = UnixTimestamp.GetCurrent();
            }
        }
        catch (Exception exception)
        {
            //ill code this later...
        }
    }

    public void TryAuthenticate(string Ticket, string RemoteAddress)
    {
        using (IQueryAdapter client = Program.GetDatabaseManager().getQueryreactor())
        {
            uint characterId = SingleSignOnAuthenticator.TryAuthenticate(client, Ticket, RemoteAddress);
            if (characterId <= 0)
            {
                SessionManager.StopSession(this.mId);
            }
            else
            {
                Reality.Game.Characters.CharacterInfo info = CharacterInfoLoader.GetCharacterInfo(client, characterId, this.mId, true);
                if ((info == null) || !info.HasLinkedSession)
                {
                    SessionManager.StopSession(this.mId);
                }
                else
                {
                    //::todo::
                    //The user logs in here.. I will code some code for the user later
                }
            }
        }
    }


    public uint Id
    {
        get
        {
            return this.mId;
        }
    }

    public bool LatencyTestOk
    {
        get
        {
            return this.mPongOk;
        }
        set
        {
            this.mPongOk = value;
        }
    }

    public string RemoteAddress
    {
        get
        {
            if ((this.mSocket == null) || !this.mSocket.Connected)
            {
                return string.Empty;
            }
            return this.mSocket.RemoteEndPoint.ToString().Split(new char[] { ':' })[0];
        }
    }

    public bool Stopped
    {
        get
        {
            return (this.mSocket == null);
        }
    }

    public double TimeStopped
    {
        get
        {
            return (UnixTimestamp.GetCurrent() - this.mStoppedTimestamp);
        }
    }

    public string UserAgent
    {
        get
        {
            return this.mUserAgent;
        }
        set
        {
            this.mUserAgent = value;
        }
    }
}

In case you still don't understand what I am trying to code: when I person connects it creates a new Session and adds it to the list of Sessions in SessionManager.cs.

SessionManager also disposes the sockets when a connection is disconnected and also handles some more code. I have received bad comments about the code and wondered how I can improve this code to make it more stable.

These sockets are crashing once I get around 40+ connections connecting to it.

\$\endgroup\$

1 Answer 1

1
\$\begingroup\$

I don't know anything about Sockets or TCP connections, but I did spot this.

public void Dispose()
{
    try
    {
        if (!this.Stopped)
        {
            throw new InvalidOperationException("Cannot dispose of a session that has not been stopped");
        }

        Console.WriteLine("Disconnected Connnection [ID: " + this.Id + "] and their associated resources.");
    }
    catch (Exception exception)
    {
        Console.WriteLine("[Dispose] " + exception.Message);
    }
}

First of all, you're throwing new exception, just to immediately catch it. That's... pointless.

Secondly, you've not implemented Dispose correctly. Please see this for an example of how to properly implement it..

Lastly, why are you implementing IDisposable at all? You're not actually disposing of anything!

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.