Results 1 to 6 of 6

Thread: How implement cTCPServer with MultiClient concurrent connections ?

  1. #1

    Thread Starter
    New Member
    Join Date
    Jan 2018
    Posts
    13

    How implement cTCPServer with MultiClient concurrent connections ?

    Hi again,

    I was wondering if anyone could show me a simple example of a TCP Server using the vbRichClient5 class cTCPServer...i need to handle around 300 concurrent connections, i know it will require from me to optimize the data as it will be a bit loaded server, i manage to make this with winsock as this is a component that work inside a form, than this is loaded into the main Thread, and sometimes i see crashes, and the server doesn't respond to all the clients.....so i was wondering if maybe Olaf could give me a little tip in this.....

    PS: I've managed to make an attempt with, but i don't know if i am on good track.....advises will be really appreciated.
    you will need the vbRichClient5 files in order to run the example below

    Thanks again.....
    Attached Files Attached Files
    Last edited by jcarlosc88; May 3rd, 2020 at 10:32 AM.

  2. #2
    PowerPoster
    Join Date
    Jun 2013
    Posts
    4,923

    Re: How implement cTCPServer with MultiClient concurrent connections ?

    Is there a reason, why you're trying to develop this "from scratch"?

    From your posting-history it seems, that you've already gathered a bit of experience with the RC5-RPCServer.

    This TCP-server-implementation is multithreaded - with a configurable ThreadPool-size,
    which also manages a "Job-Queue", where incoming Jobs will be parked for a short time,
    until a worker in the ThreadPool is free to process them.

    From intensive tests (a few years ago, where CPU-speed * Core-Count were about a third from what they are now),
    the numbers I have still in mind are:
    - about 6000-8000 Jobs per second (string-processing methodcalls without DBs involved, on an older Intel QuadCore)
    - this should roughly amount to about 15000-25000 requests per second on e.g. a modern Ryzen 12-Core-CPU.

    If you target a "total CPU-load" of only 50% (to leave the machine with some reserve and "breathing-space"),
    let's say you can process 9000 jobs per second (to have an easy number for rough base-calculations).

    If we divide these 9000 jobs per second among your 300 Clients -
    then each of the 300 clients could stress the server with 30 (simple) jobs per second.

    Not sure what your scenario is, but that is "quite some reserve"...
    - because in "normal scenarios" 100 of those 300 clients will be more or less "idling"
    - which increases the allowed request-frequency to about 40-50 per second for all other clients
    - who in typical "Business-Apps" probably only need to perform 5-10 requests per minute
    .. (although these jobs would then be bigger ones, when DB-access is involved at the serverside)

    If this is for a game-server then please say so, because these kind of servers act more like "chat-servers" -
    in that there's often not "a single response per client-request - but instead "state-changes" from a single client need to be "pumped" (reflected) to all currently connected clients (usually done in a loop at the serverside, when "real broadcasting at a lower level" is not possible).

    Only in that (or similar) use-cases, a "specially written server" would make sense (admittedly).

    Please give a bit more background what your scenario is ...
    I've looked at your code, but couldn't deduce the "usecase you have in mind" from it.

    Olaf

  3. #3

    Thread Starter
    New Member
    Join Date
    Jan 2018
    Posts
    13

    Re: How implement cTCPServer with MultiClient concurrent connections ?

    Hi, thanks for your quick answer, i really appreciate that...

    Is there a reason, why you're trying to develop this "from scratch"?
    Well, i don't have much experience in network related app, the only APPs that i made in past was using winsock and for what i need right now, guess wont work.

    From your posting-history it seems, that you've already gathered a bit of experience with the RC5-RPCServer.
    Yes. I've been working with your framework for a quite time, and it's really awsome.


    Not sure what your scenario is, but that is "quite some reserve"...
    - because in "normal scenarios" 100 of those 300 clients will be more or less "idling"
    - which increases the allowed request-frequency to about 40-50 per second for all other clients
    - who in typical "Business-Apps" probably only need to perform 5-10 requests per minute
    .. (although these jobs would then be bigger ones, when DB-access is involved at the serverside)
    Well, let me give u some background to could get u in context...

    I belong to a small network across the country (I live in Cuba), made by ourselves called SNET (Street Network), and there we have a few services made by ourselves, with games emulated and stuff like that, so me and a few friends wanted to make an Anti-Cheat system in order to could give some Fair play to the users, so in order to do that, i made something small with my primitive knowledge using Winsock control in an array, when was just like 30-50 users all was fine, but right now im handling more than 280 users connected at the same time, and the GUI on server side is crashing and giving "not respond" message, cause all of the concurrent connections sending info from their process and stuff.

    So, in resume, the Clients connect to the server, and then start to send info like the hash (made with MD5) of every process running at their computers, all the DLL injected at the game, the integrity of the game files, (all this with a package protocol made by me).

    Example: name - hash - path

    Please give a bit more background what your scenario is ...
    I've looked at your code, but couldn't deduce the "usecase you have in mind" from it.
    That code was just an example, so u could tell me if that was the right implementation of that class.

    PD: Our network is Wireless based, so the clients sometimes has drop packages, and every time they disconnect and connect again, they have to send all over again to the server, so could u imagine around 300 users sending that kind of information to the server, and the server making loops to check all that data against a MySQL DB.

    Sorry for the long talk, i was just trying to explain the whole thing.

    Thanks in advance.

  4. #4
    PowerPoster
    Join Date
    Jun 2013
    Posts
    4,923

    Re: How implement cTCPServer with MultiClient concurrent connections ?

    Quote Originally Posted by jcarlosc88 View Post
    ...the Clients connect to the server, and then start to send info like the hash (made with MD5) of every process running at their computers,
    all the DLL injected at the game, the integrity of the game files, (all this with a package protocol made by me).

    Example: name - hash - path
    In that case of "single client-request expects a single server-response",
    the RPCServer is the ideal platform to solve your problem - no need to write something lowlevel-TCP-based from scratch.

    Writing a serverside-implementation, is then reduced to placing:
    - Public Functions in an ActiveX-Dll-Class
    (no need to reinvent a stable "socket-protocol")

    The ideal (clientside-provided and send) transport-container for that, would be a (ByteArray-serialized) SQLite-Recordset,
    received by a serverside AxDll-Function, defined like this:

    Code:
    Public Function StoreUserInfoRs(ByVal UserID&, ByVal TimeStamp@, RsBytes() As Byte) As Boolean
      On Error GoTo ReturnFalse
      
      If FastCheck_ForKnownUserID_WithoutDB(UserID) Then
        Dim Path As String
            Path = KnownUsersPath & "\" & UserID & "\" & TimeStamp & ".rs"
        
        New_c.FSO.WriteByteContent Path, RsBytes
        
        StoreUserInfoRs = True
      End If
      
    ReturnFalse:
    End Function
    And if you use an InMemory-SQLite-DB for your gathered Scan-Info at the ClientSide,
    you could easily (via a simple SQL-Query) calculate the differing records between:
    - your last successfuly send HashInfo-Recordset
    - and your last clientside scan which was stored in the InMem-DB
    This way you'd have to send only a very small "Diff-Recordset" to the above ServerRoutine

    Quote Originally Posted by jcarlosc88 View Post
    PD: Our network is Wireless based, so the clients sometimes has drop packages, and every time they disconnect and connect again, they have to send all over
    In that case, you should configure the RPCClient, to not use the .KeepAlive Property at its default (True).
    And if you use the above diffed-Rs's-mechanism, then the amount of "Data to re-send" would also be very small.

    Another setting, which should improve the performance significantly (in a low-bandwidth-WLAN-scenario),
    would be the the enabled FastLZCompression-setting on the RPCClient.

    Well, that's how I would design it, for your given scenario.

    As you can see in the above StoreUserInfoRs(...) function,
    it only writes the incoming serialized Rs-ByteArray to disk - nothing more.

    This way the Stress on the RPC-Server would be *very* small -
    the whole Request only taking a few milliseconds (or less than a millisecond, when you have an SSD at the serverside).

    No MySQL-interaction (connection-establishing and stuff) should be done within these fast RPC-calls.

    Instead the checks of the stored Rs-bytes:
    - deserializing them from the KnownUsers-Directory
    - and then comparing their record-content against records you retrieved from your MySQL-store
    All this should be performed by independent (serverside) Scanner-Processes,
    which run in *parallel* to the RPCServer-Process, without disturbing it.

    The Results of these ScannerProcesses could be placed in the same KnownUsers-directory,
    waiting there for occasional Polling-RPC-calls from the Clients, implemented in another RPC-Dll-Function
    (which again, will only have to access the FileSystem - this time reading a serialized RsResult-ByteArray - to keep the call short and tidy).

    Olaf

  5. #5

    Thread Starter
    New Member
    Join Date
    Jan 2018
    Posts
    13

    Re: How implement cTCPServer with MultiClient concurrent connections ?

    WoW that's an interesting approach to solve my situation....guess I'll play a bit with that solution and see, but there are a few things that i don't know how will finally get implemented, cause in the server that i have right now, when a Client get connected to server and pass for a few checks, then i add its IP Address to a rule in Windows Firewall in order to open the game server port, so the user can finally play (All this is done in serverside).

    Now...guessing that all works like charm like i suppose it will....a few question come to my mind.

    1 - How will a be able to prevent from a user that got banned, to connect to RPC_Server?? it's there a way to disconnect some client from server side ??
    2 - How do i know the users connected to RCP_Server ?? (i think there is some aux class there)
    3 - In order to connect the clients to server, they will require a RPC password, this should be stored encrypted at client side?
    4 - Is the RPC_Server aware, when a user lost it's connection with some node in the middle for more than 1min? How it's handle this behavior by RPC_service?

    Thanks a lot.

    JohanC

  6. #6
    PowerPoster
    Join Date
    Jun 2013
    Posts
    4,923

    Re: How implement cTCPServer with MultiClient concurrent connections ?

    Quote Originally Posted by jcarlosc88 View Post
    ...in the server that i have right now, when a Client get connected to server and pass for a few checks,
    then i add its IP Address to a rule in Windows Firewall - in order to open the game server port,
    so the user can finally play (All this is done in serverside).
    This would be a job for the separate (serverside) Scanner-Process(es) you'll have to implement.
    (not for the parallel running RPCServer-process).

    Quote Originally Posted by jcarlosc88 View Post
    1 - How will a be able to prevent from a user that got banned, to connect to RPC_Server?? it's there a way to disconnect some client from server side ??
    Whether a User got banned, should be known at the serverside
    (the ScannerProcess could check the MySQL-DB for "banned state" -
    or in case you want to do it differently, you could also remove a "UserFolder"
    from your "AllowedUsers" (or "KnownUsers")-directory into a "BannedUsers"-directory.

    It's up to you, in what way you persist (or detect) a certain Users "banned state" -
    but if he's banned, the Scanner-Process who detected this "banned state" -
    will remove the firewall-gameport-settings for that User (ain't that sufficient)?

    Quote Originally Posted by jcarlosc88 View Post
    2 - How do i know the users connected to RCP_Server ?? (i think there is some aux class there)
    No aux class is needed.

    As already recommended (due to potential connection-drop-outs on your "WLAN-Node-based Network),
    the KeepAlive-setting (at the clientside, in cRPCClient.KeepAlive) should be at False.

    This means, that the RPCServer will not "hold" any connection -
    but instead will "close any connection immediately, after answering a Request".

    A Users "connected state" should be instead determined by:
    - last TimeStamp from last received request is not older than 1 minute or so
    (in case you set-up an "I'm alive" polling-interval of 10-30 seconds on your Clients).

    Quote Originally Posted by jcarlosc88 View Post
    3 - In order to connect the clients to server, they will require a RPC password, this should be stored encrypted at client side?
    If you use a relative simple to implement "CRAM-MD5"-exchange: https://en.wikipedia.org/wiki/CRAM-MD5
    then your clients will not have to store any password clientside.
    Instead you will send them a "Token" (after the CRAM went through successfully),
    and that token is then stored as the "current, user-resolving KeyValue in the given Users record of your User-DB",
    and if you store a "TokenExpires" field in the same UserDB-record, you can determine the duration for which this token "grants access".

    Every RPC-request from the Cientside should then contain this token in its first parameter
    (instead of the UserID, which I've placed as the first param in my example-function).
    And the first thing any RPCServer-function does, should then be:
    Code:
    Public Function SomeRPCFunc(Byval Token As String, ....)
      Dim UserID as Long
        UserID = GetUserIDForToken(Token)
        If UserID = 0 Then Err.Raise vbObjectError, , "Token doesn't exist, or is expired"
        
        '... further code, when the request was not exited due to error-raising in the above line
    End Function

    Quote Originally Posted by jcarlosc88 View Post
    4 - Is the RPC_Server aware, when a user lost it's connection with some node in the middle for more than 1min? How it's handle this behavior by RPC_service?
    This was basically answered in #2) above... we work (in principle) "connectionless".

    As said, the duration of an RPC (within a certain function at the serverside) will be *very* short
    (due to the outlined approach with "just storing serialized Info-Rs as a ByteArray in a UserDirectory,
    or retrieving such Rs-ByteArrays from there).

    My guess is (when you don't have an SSD on your Server), that:
    - this Func-Call-time will be around 5-10msec (0.5-2msec when using SSDs)
    - you'll have to add the "normal Ping-latency" to that time
    - and also add the connection-establishing + connection-teardown-time
    ..(since we work with "non-permanent connections")

    Meaning that the total-time for a typical RPC
    (when you start and stop it at the clientside, until the synchronous call returns),
    will be roughly: ServerSideFuncCall-time + 2 * Ping-latency

    If your WLAN-Mesh has typical Ping-latencies of about 20-50msec,
    then a single "non-keepalive" RPC will take about 5+40msec minimum, or 10+100msec maximum.

    Don't know how often one of your WLAN-nodes will "drop-out for a while" -
    but let's assume it is very unstable - and a drop-out happens "every 100 seconds" or so.

    Now we can calculate the probability, that an "established socket-connection" (which in our case lasts for only about 100msec!) -
    will "meet" such a "drop-out-event" in-between a running request.

    It's about 0.1sec divided by 100sec drop-out-frequency = 0.1% = 1 promille.

    And if you have an "I'm alive-Ping-RPC" running every 30 seconds or so (on each Client),
    then you'll have to multiply that by roughly factor 3 (100sec/30sec), to get the probability,
    that a certain Client will meet such a dropout, whilst "being in a request".

    Whereas with "kept-alive" connections, the probability that a WLAN-Node-dropout -
    will cause such a "left hanging in mid-air connection" is: 100%

    That's the reason, why I suggested to work with "non-kept-alive" connections
    (each RPC connects, and disconnects immediately after the result is there).

    Of course, when a WLAN-Node is currently down for a given Client,
    he will not be able to handle any "RPC - "I'm alive"- Ping-Requests.

    But in the mode I suggested, this will just cause a normal "unable to connect" Error in the RPCClient,
    which probably should be ignore in case of "I'm alive-Pings", but probably reported to the User in case
    of "explicit User-triggered RPC-actions" (like e.g. an Authentication-attempt).

    HTH

    Olaf
    Last edited by Schmidt; May 5th, 2020 at 09:58 AM.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Click Here to Expand Forum to Full Width