1

i have server application that handle request all received command from clinets, every things is ok in response when my clients are blow 1000 after my clients connect more than 1000 this service work slowly and will not response in good time more than 20sec... and after 20sec my clients time out error, i dont want increse time out more than 20sec... also if i run this app in seprate port every things ok in more client, i must run in one app, where is my mistake?

static class Program {
    static void Main(string[] args) {
        TcpListener baseListernerNewVer = new TcpListener(new System.Net.IPEndPoint(IPAddress.Any, 1425));
        baseListernerNewVer.Start();
        BeginAccept(baseListernerNewVer);


         while (true) {
             System.Threading.Thread.Sleep(1);
         }
    }
}

//Wait For New Clinet
private static void BeginAccept(TcpListener baseListernerNewVer) {
    baseListernerNewVer.BeginAcceptTcpClient((ar) => {
        while (true) {
            try {
                BeginAccept(baseListernerNewVer);
                break;
            }
            catch(Exception e) {
                System.Threading.Thread.Sleep(100);
            }
        }

        //All Client Will Manage Here
        Manage(baseListernerNewVer.EndAcceptTcpClient(ar));


    }, baseListernerNewVer);
}



public void Manage(TcpClient tcpClient) {
    GC.Collect();
    var onlineClientInfo = new OnlineClientInfo(tcpClient);
    try {
        lock (onlineClientsInfos) {
            onlineClientsInfos.Add(onlineClientInfo);
        }

        System.Threading.ThreadPool.QueueUserWorkItem(new WaitCallback((object state) => {
            System.Threading.Thread.Sleep(1000);
            while (tcpClient.Connected && onlineClientInfo.IsConnect && service1.Running) {
                try {
                    if (FeedMeMessage(onlineClientInfo)) {
                        System.Threading.Thread.Sleep(1000);
                    }
                    else {
                        System.Threading.Thread.Sleep(1000);
                    }
                }
                catch (Exception e) {

                }
            }
        }));

        //Wait from command form clients
        CallGetNextCommand(onlineClientInfo);

    }
    catch (Exception e) {

    }
    finally {
    }
}

private void DisconnectFromClient(OnlineClientInfo onlineClientInfo) {
    lock (onlineClientsInfos) {
        onlineClientsInfos.Remove(onlineClientInfo);
        try {
            onlineClientInfo.TcpClient.Close();
        }
        catch {

        }
    }
}

private void ReadNextSocketBuffer(OnlineClientInfo onlineClientInfo, DateTime baseDateAndTime, byte[] buffer, int offset, int length, Action fullBufferFilled) {
    TcpClient tcpClient = onlineClientInfo.TcpClient;
    try {
        tcpClient.Client.BeginReceive(buffer, offset, length, SocketFlags.Partial, (ar) => {
            int len = 0;
            try {
                len = tcpClient.Client.EndReceive(ar);
                if (len == 0) {
                    if (DateTime.Now > baseDateAndTime.AddMinutes(1)) {
                        DisconnectFromClient(onlineClientInfo);
                        return;
                    }
                    System.Threading.Thread.Sleep(1);
                }
            }
            catch {
                DisconnectFromClient(onlineClientInfo);
            }

            if (offset + len == buffer.Length) {
                fullBufferFilled();
            }
            else {
                int newOffset = offset + len;
                ReadNextSocketBuffer(onlineClientInfo, baseDateAndTime, buffer, newOffset, buffer.Length - newOffset, fullBufferFilled);
            }
        }, null);
    }
    catch {
        DisconnectFromClient(onlineClientInfo);
    }
}

private void ReadSocketLength(OnlineClientInfo onlineClientInfo, int length, Action<byte[]> readedToLength) {
    byte[] buffer = new byte[length];
    ReadNextSocketBuffer(onlineClientInfo, DateTime.Now, buffer, 0, length, () => {
        try {
            readedToLength(buffer);
        }
        catch { }
    });
}



private void ReadCommandLength(OnlineClientInfo onlineClientInfo, Action<int> length) {
    ReadSocketLength(onlineClientInfo, 4, (commandLength) => {
        try {
            length(Common.ToInt(commandLength));
        }
        catch { }
    });
}

private void ReadCommand(OnlineClientInfo onlineClientInfo, int commandLength, Action<byte[]> commandResult) {
    ReadSocketLength(onlineClientInfo, commandLength, (command) => {
        try {
            commandResult(command);
        }
        catch { }
    });
}

private void GetNextCommand(OnlineClientInfo onlineClientInfo, Action finishedExecute) {
    ReadCommandLength(onlineClientInfo, (commandLength) => {
        ReadCommand(onlineClientInfo, 1, (commandHeader) => {
            if (commandLength == 2 && (ServerCommandType)commandHeader[0] == ServerCommandType.AckVer1) {
                ReadCommand(onlineClientInfo, 1, (newCommandHeader) => {
                    try {
                        byte ackCode = newCommandHeader[0];
                        onlineClientInfo.AckReceived(ackCode);
                    }
                    catch { }
                    finishedExecute();
                });
            }
            else {
                ReadCommand(onlineClientInfo, commandLength, (command) => {
                    try {
                        if (ExecuteCommand(command, onlineClientInfo)) {
                            onlineClientInfo.SendAck(commandHeader[0]);
                        }
                    }
                    catch {

                    }
                    finishedExecute();
                });
            }
        });
    });
}

private void CallGetNextCommand(OnlineClientInfo onlineClientInfo) {
    TcpClient tcpClient = onlineClientInfo.TcpClient;

    Action finishedExecute = null;

    finishedExecute = () => {
        if (tcpClient.Connected && onlineClientInfo.IsConnect) {
            try {
                GetNextCommand(onlineClientInfo, finishedExecute);
            }
            catch {
            }
        }
        else {
            DisconnectFromClient(onlineClientInfo);
        }
    };

    finishedExecute();
}
Emran Sadeghi
  • 612
  • 6
  • 20
  • please see: http://stackoverflow.com/questions/14628927/writing-a-highly-scalable-tcp-ip-server-in-c-sharp-5-with-the-async-await-patter – Mitch Wheat Aug 03 '14 at 08:57
  • 1
    This code is very confusing. More mistakes than I can list. Most mistakes stem from the fact that you are using async IO in a convoluted and wrong way. That's understandable because most tutorials on the web about TCP are horrible. Either use await, or try using synchronous IO and make that work first. Will be hard enough. – usr Aug 03 '14 at 09:17

2 Answers2

0

This code is very confusing. More mistakes than I can list. Most mistakes stem from the fact that you are using async IO in a convoluted and wrong way. That's understandable because most tutorials on the web about TCP are horrible. Either use await, or try using synchronous IO and make that work first. Will be hard enough.


That said, here is what I noticed most:

    while (true) {
        try {
            BeginAccept(baseListernerNewVer);
            break;
        }
        catch(Exception e) {
            System.Threading.Thread.Sleep(100);
        }
    }

You are starting accept operations in an infinite loop. This means that your CPU is at 100% starting accepts. In fact with each accepted socket you start another one of these loops...

Have a single accept loop:

while (true) {
 var socket = Accept();
 new Thread(() => HandleClient(socket)).Start(); //Or, go async with this
}

Very simple.

Async IO did buy you nothing here anyway. Dedicating a single thread to accept connections is totally fine.

usr
  • 168,620
  • 35
  • 240
  • 369
  • i use BeginAcceptTcpClient, and i call while wehen finished work and if beginaccept call with no exception while will break, else will wait for 100ms after that try again, this mean cpu never go to 100%... – Emran Sadeghi Aug 03 '14 at 09:43
  • @EmranSadeghi so check your CPU in task manager. BeginAccept does not normally throw an error. That means you stay in the while loop. – usr Aug 03 '14 at 09:46
  • yes becuase BeginAccept normally dos not throw exception, break will call and while will terminate... also i see my task manager every things ok... – Emran Sadeghi Aug 03 '14 at 10:03
  • Ah, indeed! I did not see that break statement. OK, next problem is your use of the thread-pool. You start one work item per client and that work item never terminates. After 1k clients you have 1k work items/threads (if the limits even allow that). With that many clients you should use async IO for handling clients, not blocking threads. Test this theory of mine by increasing the limits of the thread pool to infinity (ThreadPool.SetMin/MaxThreads(10000, 10000). Should improve things. – usr Aug 03 '14 at 10:08
  • i need fetch some date from sql and send it to clients, i need to use ThreadPool and check if need to send data to client, send, this work item is in Threadpool, is this way not good for larg clients? if not good, please tel me how can do this? – Emran Sadeghi Aug 03 '14 at 10:21
  • First, follow up on what I said. – usr Aug 03 '14 at 10:25
  • 1
    Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/58568/discussion-between-emran-sadeghi-and-usr). – Emran Sadeghi Aug 03 '14 at 10:31
-2

Using the wrong API. The listener is not exactly highly scalable. You should go with sockets and using the SELECT method to select which have data waiting (http://msdn.microsoft.com/en-us/library/system.net.sockets.socket.select(v=vs.110).aspx)

TomTom
  • 61,059
  • 10
  • 88
  • 148
  • TcpListener is a thin wrapper around Socket. Socket uses IOCP. Not sure how to be more scalable than that. – usr Aug 03 '14 at 09:18
  • Select allows bulk operations without tons of threading. This is not a lot more scalable from the socket side - but it is from the PROGRAMMING side. – TomTom Aug 03 '14 at 09:23
  • @TomTom using async methods of TcpListener/TcpCLient you can handle many connections without creating a single thread/task. I think you are confused with their *sync* methods which require threading to handle many connections at the same time. I've only needed *sockets* when I must deal with *raw* packets such as when trying to ping tousands of machines in a few seconds (not to do it one by one) etc... – L.B Aug 03 '14 at 13:28
  • @TomTom BTW: Why I downvoted your answer: a) Not wrong API b) It is in fact highly scalable c) sockets is not *a must* to get the same performance/scalibility. d) It is more like a comment than answer. – L.B Aug 03 '14 at 13:33