webmartians

webmartians


  • Name: [not set]
  • Favorite Languages: [not set]
  • Website: [not set]
  • Location: [not set]
  • About Me: [not set]

Recent Comments

  • C# Tutorial - Simple Threaded TCP Server
    03/11/2010 - 09:32

    I got most of this working ... in a "harsh" environment (slow processors at both ends, zilch memory). Here is what I learned:

    1- recv returns zero when its current crop of buffers for the connection are empty. THAT DOES NOT MEAN THE MESSAGE IS FULLY ACQUIRED! Maybe everybody else got this; I didn't and had to add an endian-independent, length field at the beginning of each message.

    1b- mrRumble - "On the nose!" (length field at the beginning of each message) ... except that there are a number of machines of different "endianness" that do not store the bytes of an unsigned long in the expected order (assuming you want to talk to not just C# nodes). You must replace that unsigned long (or long) with four, distinct byte values, where you know that the first one is the high-order, and so on (or whatever order you want, as long as it's consistent).

    2- When the client (sending) side is C# and the server (receiving) side is C/C++, when the C# closesocket executes, unread data at the server (C/C++) side (or, maybe, still in flight) is destroyed ... sometimes ... often enough to be catastrophic. Strangely enough, this does not seem to happen in reverse (C/C++ client sending to a C# server). I found two WWW reports of similar behavior - "fire and forget" is not a good philosophy, here. The answer is to require some kind of acknowledgment for every transmission (for example, an empty message) before the sender executes closesocket.

    3- The idea of the loop around the server's loop is a real frustration-preventer: having the code able to say, "This failure is too nasty for me to handle in the inner loop; I'll reset and restart everything!" definitely makes unit testing more pleasant. I suggest a Sleep of a good second or so at the end of the outer loop:
    if (this is not a shutdown) Sleep(1000);
    to give the system some cycles to tidy whatever messes it needs to clean up and, maybe, restore resources that will allow the next, major cycle of your server to execute successfully.

    4- Consider UDP instead of TCP. If you can do what you need to do in UDP, use it instead of TCP. Remember, UDP blasts your packets all over the place: they WILL arrive out of order. You'll need, instead of just a length field, a (packetN)/(ofMpackets) field in each transmission. ...but UDP is much faster, simpler, and, oddly enough, can end up being more robust than TCP. Hard to swallow, I know, but that has been our experience: UDP gets more data through bad pipes than TCP does.

    ...hope this saves somebody some grief...

  • C# Tutorial - Simple Threaded TCP Server
    02/22/2010 - 16:31

    The server merely receives and replies to clients; client to client communications are not possible ... strictly speaking.

    I suppose you could write your server's message handler to recognize some kind of forwarding command prefix in the message (eg: "To 12.34.56.78:50000"). The sending client would have to put that it its message. However, that brings up the question, "If the client knows the IP address and port of the other client, why is it not sending the message directly?"

  • C# Tutorial - Simple Threaded TCP Server
    02/22/2010 - 16:19

    Never mind... Don't do it (spawn the thread AFTER acquiring the text)... The newer Windows TCP stacks do indeed provide for multiple, concurrent connections' queues being drained.

  • C# Tutorial - Simple Threaded TCP Server
    02/09/2010 - 14:52

    Curiosity: the example starts a thread to handle the communications and provide the "intelligence" (HandleClientComm).

    What about using AcceptTcpClient (which blocks until a connection is available) - acquiring the entire message - and then spawning a thread to provide the process? Yes, the acquisition of the message is then in-line, but, if I read my TCP specs correctly, it will always be in-line because of the buffers.

    For example:

    using System;
    using System.Diagnostics;
    using System.Net;
    using System.Net.Sockets;
    using System.Text;
    using System.Threading;
    ...
    TcpListener lr /*Listener*/ = new TcpListener(IPAddress.Parse(InternetProtocolAddress), InternetProtocolPort);
    for (lr.Start(); MyListenerIsNotShutDown);)
    {
       TcpClient cl /*Client*/ = lr.AcceptTcpClient();
       byte[] bf /*Buffer*/ = new byte[8192];
       string mg /*Message*/ = string.Empty;
       NetworkStream sm /*Stream*/ = cl.GetStream();
       for (int ct = sm.Read(bf, 0, bf.Length - 1); 0 < ct; ct = sm.Read(bf, 0, bf.Length - 1))
          mg += Encoding.ASCII.GetString(bf, 0, ct);
       mg = mg.Trim();
       try
       {  // Start a new thread to process this message.
          Thread thd /*Thread*/ = new Thread(new ParameterizedThreadStart(MyMessageProcessor));
          thd.Start(mg);
       }
    #pragma warning disable 168
       catch (OutOfMemoryException ex /*Exception*/)
       {  // There is not enough memory to start the thread: do it in-line, thereby, throttling responses.
          MyMessageProcessor(mg);
       }
    #pragma warning restore 168

    ...public static void MyMessageProcessor(object prmMsg)...

    Just to add to the confusion, the send is therefore stateless:

    TcpClient cl /*Client*/ = new TcpClient();
    cl.Connect(new IPEndPoint(IPAddress.Parse(prmAdr), prmPrt));
    ASCIIEncoding ec /*Text Encoder*/= new ASCIIEncoding();
    byte[] bf /*Buffer*/ = ec.GetBytes(prmMsg);
    NetworkStream sm /*Stream*/ = cl.GetStream();
    sm.Write(bf, 0, bf.Length);
    sm.Flush();