TCP Socket Questions

This is the first real application I am trying to create with .Net Socket classes (Sockets, TCPClient, TCPListener) using C#. Unfortunately the MSDN documentation is not very good, and the examples they give are far too simplistic for my real world application. So I am hoping that some people here can give me some answers from their experiences. I believe I understand how the classes work in general, but there are many questions I can not seem to find answers to. What I need to create is a full duplex TCP connection between my server and a variety of different clients. Only 1 client will connect at a time, BUT I have absolutely no control over the client in any way. The only agreement between the two systems are the message formats (as there are different types of messages), and the byte sequences that delineate the start and end of these messages. The connection must remain open, and messages can be received at any time from the client, as well as be sent any time from the server in response to local system events. So my first question is can a single socket, or tcpclient, read and write data at the same time from different threads. My thinking at first was to use two separate threads; one that blocks on a Read method, while the other writes data as needed, but I am not sure if one instance can be shared safely between two threads, and if reading and writing at one time is even possible. My other thought was to use the beginread method with a call back and write data using the beginwrite method, being the callbacks will be on a separate threads, but again I am not sure this is possible. My final thought was to just poll the dataavailable method, and if data was available then read it with the read method, and in the same polling loop write any data that needs to be written. The problem here is that if the client disconnects the dataavailable method just returns false and I have no way of knowing the client closed the connection unless the server tries to write, which could be hours later. In the meantime the client is unable to send any messages. Also, I would prefer to not poll as I don't want to unnecessarily use up processor time, and I really do not want to use thread.sleep to mitigate the effects of polling. I would much rather wait on callbacks or blocks. I hope I am explaining this correctly and I think I am just missing something. Any help would be greatly appreciated.

Thanks in advance.
LVL 1
exptechAsked:
Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

japeteCommented:
I give you and example of a multithread server using TCP sockets. You can test it from two or more command console using telnet at port 13000.
using System;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;

namespace TestSocketServer
{
    class Program
    {
        static void Main(string[] args)
        {
            TcpListener server = null;

            try
            {
                // Set the TcpListener on port 13000.
                Int32 port = 13000;
                IPAddress localAddr = IPAddress.Parse("127.0.0.1");

                // TcpListener server = new TcpListener(port);
                server = new TcpListener(localAddr, port);

                // Start listening for client requests.
                server.Start();

                // Enter the listening loop.
                while (true)
                {
                    Console.Write("Waiting for a connection... ");

                    // Perform a blocking call to accept requests.
                    // You could also user server.AcceptSocket() here.
                    TcpClient client = server.AcceptTcpClient();
                    Console.WriteLine("Connected!");

                    // Do work in a thread
                    Thread WorkThread = new Thread(DoWork);
                    WorkThread.Start(client);
                }
            }
            catch (SocketException e)
            {
                Console.WriteLine("SocketException: {0}", e);
            }
            finally
            {
                // Stop listening for new clients.
                server.Stop();
            }


            Console.WriteLine("\nHit enter to continue...");
            Console.Read();
        }

        /// <summary>
        /// Attend client work
        /// </summary>
        /// <param name="Client"></param>
        static void DoWork(object Client)
        {
            // Buffer for reading data
            Byte[] bytes = new Byte[256];
            String data = null;

            TcpClient client = (TcpClient)Client;

            data = null;

            // Get a stream object for reading and writing
            NetworkStream stream = client.GetStream();

            int i;

            // Loop to receive all the data sent by the client.
            while ((i = stream.Read(bytes, 0, bytes.Length)) != 0)
            {
                // Translate data bytes to a ASCII string.
                data = System.Text.Encoding.ASCII.GetString(bytes, 0, i);
                Console.WriteLine("Received: {0}", data);

                // Process the data sent by the client.
                data = data.ToUpper();

                byte[] msg = System.Text.Encoding.ASCII.GetBytes(data);

                // Send back a response.
                stream.Write(msg, 0, msg.Length);
                Console.WriteLine("Sent: {0}", data);
            }

            // Shutdown and end connection
            client.Close();
        }
    }
}

Open in new window

0
Mahmoud_Al_HattabCommented:
Dear exptech,

1- So my first question is can a single socket, or tcpclient, read and write data at the same time from different threads: ANSWER: YES, SURE.
2- and if reading and writing at one time is even possible: ANSWER: YES, SURE.
ALL YOU NEED IS JUST lock(sock) to synchronize socket access.
3- For Asynch methods and callbacks, I do not recommend as they use the ThreadPool and you have less control. Use those asynch methods if you do not use the socket a lot.
4- My final thought was to just poll the dataavailable method, and if data was available then read it with the read method, and in the same polling loop write any data that needs to be written: YES. in the reader thread:
byte[] bytes;
while(someBooleanToControl)
{
 if(sock.Available)
 { //read and process what you read
   lock(sock)
   {
      bytes = sock.Receive();
   }
 }
 else
 {
  Thread.Sleep(someSuitableTime);
 }
}
As for the writer thread, enqueue what you want to write into a Queue
and
while(someBooleanToControl)
{
 if(queue.Count>0)
 {
 lock(sock)
 {
  sock.send(queue.Dequeue())
 }
 }
 else
 {
  Thread.Sleep(someSuitableTime);
 }
}

Please notice that the code here is rather psuedo and does not comply to the methods actual signatures.

Using sleep is better for you to control if you are going to use the socket a lot. Managing wait handles is another option, but more complicated and truly not needed as they are more burden on the cpu than the sleep. Thread sleeps are fine with cpu. I do not know why you do not prefer them.

5- for the disconnectivity:
make the someBooleanToControl in the while condition in the above code a propert to the check the connectivity of the socket a long with any other logic you want:

bool someBooleanToControl
{
 get
 {
  return sock.IsConnected && otherLogicForStopping;
 }
}

or check for the connectivity anywhere and try reconnecting upon failer.

6- Use heartbeat or alive messages not to timeout the connection or the socket. This is to send a message for the other part to say that this party is still alive and ensure that the other party is alive by receiving its response. This also keeps the socket object connected.

This is just a good start. I think you may need more. Please let me know in that case. Good luck :)
0
exptechAuthor Commented:
Japete, thanks for the reply, and I appreciate the example, but it does not really answer the most important questions about concurrently reading and writing to the same socket.

Mahmoud, thank you as well, you have answered most of my questions. I would just like to clarify a few things. You said that you DO need to synchronize socket access, so having two threads calling instance methods(Read & Write) at the EXACT same moment could cause a problem. I assume that is because it's instance members are not thread safe, am I correct ?

The only other problem that I see here is in this block:

if(sock.Available)
 { //read and process what you read
   lock(sock)
   {
      bytes = sock.Receive();
   }
 }
 else
 {
  Thread.Sleep(someSuitableTime);
 }

Is it not correct that sock.available will return false even if the client disconnects. In other words should the client disconnect, this loop will just believe that there is no current data to read. As I said I have no control over the client, so using something like a heartbeat is not an option. I know there are some workarounds to checking whether a socket is connected, I believe by using socket.poll, but I was thinking of doing the reading a slightly different way. Why not spawn a thread for reading that simply blocks on lets say the socket.receive() method. Then if the client disconnects an exception will be thrown, signaling the disconnect.  Also using this method there would be no polling required, but I am not sure how it would work if you then attempt to use socket.receive() from another thread, being the socket is already blocking on the receive method and you said that you need to synchronize socket access. Your thoughts?
0
Upgrade your Question Security!

Your question, your audience. Choose who sees your identity—and your question—with question security.

exptechAuthor Commented:
Mahmoud, one more thing. Are you absolutely sure that you need to lock the socket ? I have been doing some more research and I have seen a few examples in books where one thread is reading and another thread is writing, but neither is locking the socket. The reason I asked, and did some more research, is that as I was thinking about the code, it would seem that by locking the socket I am setting myself up for a deadlock. If I am writing to the socket that would block reads until the write is complete, but what happens if the client is blocking on a write at the same time. Again, I have no control over the client in any way.
0
exptechAuthor Commented:
Mahmoud, Sorry but one more thing. The end of the post 2 above, above should have read:

Then if the client disconnects an exception will be thrown, signaling the disconnect.  Also using this method there would be no polling required, but I am not sure how it would work if you then attempt to use socket.send() from another thread, being the socket is already blocking on the receive method and you said that you need to synchronize socket access. Your thoughts?

I meant to say socket.send, not socket.receive.
0
Mahmoud_Al_HattabCommented:
Dear exptech,
glad to help:) here is the answers:
1- regarding the lock: the VS2008 documentation says that Socket instances are thread safe. That is you do not need to lock. However I always use the lock, maybe it is an old safety habit since I have been programming sockets on different platforms and environments. So yes you may remove the lock.
2- for the other party connectivity, you can check the sock.Connected boolean property. It throws no exceptions. Just checks the status of the connection.
3- I do not recommend waiting on receive method. The calling thread will block and you will need more complicated management for that thread to make it exit from this wait state. So use Available property, if 0 then Thread.Sleep, else read from the socket the specified amount in Available. Available will throw exception if the client is disconnected. So check for Connected before accessing the socket object.
Use something like the property below, and make this property the condition before you access the socket object for either reading and writing.
bool someBooleanToControl
{
 get
 {
  return sock.IsConnected && otherLogicForStopping;
 }
}

4- Sending normally does not block. This is because it depends on the running server machine not the client. It does not guarantee successful delivery on the client as delivery depends on the client application and machine. But it does not normally block. But you can use the SendTimeout property to set the timeout period for a socket to send. But be careful, in this case sending may throw exception if the timeout period is too small.

5- This is as a final recommendation: for resources management: make single thread to read from and single thread to write to. Even when accessing database, files, sockets... etc. Make your code to queue packets to send and the sender thread to read from that queue and send to the socket. And when receiving, make the receiver thread to save the received packets into a queue and make another theard(s) to parse and process the received packets, unless other business/technology requirement(s) dictate(s) any other logic or design.

I hope these answer your concerns. If you have any other notes/questions/feedback please do not hesitate :)

good luck
regards,
Mahmoud Al-Hattab
0

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
exptechAuthor Commented:
Mahmoud,

Thank you for your help.
0
Mahmoud_Al_HattabCommented:
Thanks for you on the first place exptech :)
0
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
C#

From novice to tech pro — start learning today.

Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.