4

Hello guys i am currently testing a websocket server written in Asp .Net Core 2.0 and i believe i have a memory leak.I can not find it even though i have tried to dispose everything that might be a concern.
The tests were done consecutively and the value of the ram is taken when it reaches stability.(this varies from 5 sec to 20 sec).
The occupied Ram is measured with plain Task Manager Monitor.

The testing tool:
Thor : https://github.com/observing/thor
The command : thor --amount [amount] ws://[HostIP (localhost)]:[portnumber]

Results:

Connections | RAM Consumed at the end of test (GB):

0             4.54
50            4.55
100           4.55
150           4.61
200           4.68
300           4.76  
400           4.59
400           4.59
500           4.62
500           4.65
550           4.65

The WebSocket Server:

SocketMiddleware -used by the appbuilder:

public class SocketMiddleware
    {
        public  byte[] ToSegment(string message) => System.Text.Encoding.UTF8.GetBytes(message);
        ClientTracker clientTracker; //the socket clients tracker this is the object we're speaking of
        RequestDelegate next;
        public SocketMiddleware(ClientTracker tracker,RequestDelegate del)
        {
            this.clientTracker=tracker;
            this.next=del;   
        }
        public async Task Invoke(HttpContext context)
        {
            if(!context.WebSockets.IsWebSocketRequest)
            {
                await this.next.Invoke(context);
                return;
            }
            await this.clientTracker.AddClient(context.WebSockets);

        }
    }

SocketTracker- this is the suspect which is dealing with all the opened sockets

  public class ClientTracker
        {
            ConcurrentDictionary<string, Client> clientMap = new ConcurrentDictionary<string, Client>();

            public string CreateConnectionID() => Guid.NewGuid().ToString();
            public string GetIDOfSocket(WebSocket socket) => this.clientMap.First(x => x.Value.webSocket.Equals(socket)).Key;
            public Client GetClientByID(string id)
            {
                this.clientMap.TryGetValue(id, out Client client);
                return client;
            }
            public async Task AddClient(WebSocketManager manager)
            {
               using (WebSocket socket = await manager.AcceptWebSocketAsync())
               {
                 Client newClient = Client.CreateClient(socket, CreateConnectionID());
                 if(clientMap.TryAdd(newClient.clientID, newClient))
                 {
                   await ReceiveMessage(newClient);
                 }

                }
             }

            public async Task ReceiveMessage(Client client)
            {
                while (client.webSocket.State == WebSocketState.Open)
                {
                    WebSocketReceiveResult result = await client.ReceiveResult();
                    //dosomething with result...
                    if (result.MessageType == WebSocketMessageType.Close)
                    {
                        await client.webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, "Client closed", CancellationToken.None);
                        break;
                    }
                    //send custom message 
                    await client.SendMessage("lala");
                }
            }
        }

Client- socketWrapper which performs all required operations on the socket and stores temporary data to be used by the tracker

public class Client
    {
        //Fields
        public readonly WebSocket webSocket;
        public readonly string clientID;
        public StringBuilder tempData;
        //Auxiliary
        private const int BufferSize = 1024 * 4;

        public static Client CreateClient(WebSocket socket, string id)
        {
            Client client = new Client(socket, id);
            return client;
        }

        public Client(WebSocket socket, string id)
        {
            this.webSocket = socket;
            this.clientID = id;
            tempData = new StringBuilder();
        }
        public async Task<WebSocketReceiveResult> ReceiveResult()
        {
            tempData.Clear();
            ArraySegment<byte> segment = new ArraySegment<byte>(new byte[BufferSize]);
            WebSocketReceiveResult result = await this.webSocket.ReceiveAsync(segment, CancellationToken.None);
            tempData.Append(BitConverter.ToString(segment.Array));
            return result;
        }

        public async Task SendMessage(string message)
        {
            byte[] bytes = Encoding.UTF8.GetBytes(message);
            await this.webSocket.SendAsync(new ArraySegment<byte>(bytes), WebSocketMessageType.Text, true, CancellationToken.None);
        }

    }

Startup.cs

public void ConfigureServices(IServiceCollection services)
        {
            services.AddMvc();
            services.AddSingleton<ClientTracker>();
        }
        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            app.UseWebSockets();
            app.UseMiddleware<SocketMiddleware>();
        }

P.S: The server was not closed between requests.Could it be the concurrent dictionary ?Besides it, the reference of the Client is cleaned ,the Client is disposed,and the socket is closed.The string builder can not be disposed,and the Tracker/Middleware live as long as the app lives.

1
  • Today I am facing the same problem and apparently without a solution. I have 40 thousand exclusives and the memory in 1GB. When I close as updated, the memory preserved high Commented Apr 30, 2021 at 20:14

0

Browse other questions tagged or ask your own question.