1

I am trying to write a program using socket programming. This program just sends a message from client to server.

the client code is something like this :

  class Program
    {
        static void Main(string[] args)
        {

            TcpClient client = new TcpClient();
            IPEndPoint serverEndPoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 3000);
            client.Connect(serverEndPoint);
            NetworkStream clientStream = client.GetStream();
            ASCIIEncoding encoder = new ASCIIEncoding();
            byte[] buffer = encoder.GetBytes("Hello Server!");
            clientStream.Write(buffer, 0, buffer.Length);
            clientStream.Flush();
        }
    }

The client as you can see just send a Hello server to the server. The server code is like this :

 class Server
    {
        private TcpListener tcpListener;
        private Thread listenThread;

    public Server()
    {
        this.tcpListener = new TcpListener(IPAddress.Any, 3000);
        this.listenThread = new Thread(new ThreadStart(ListenForClients));
        this.listenThread.Start();
    }
    private void ListenForClients()
    {
        this.tcpListener.Start();

        while (true)
        {
            //blocks until a client has connected to the server
            TcpClient client = this.tcpListener.AcceptTcpClient();

            //create a thread to handle communication 
            //with connected client
            Thread clientThread = new Thread(new ParameterizedThreadStart(HandleClientComm));
            clientThread.Start(client);
        }
    }
    private void HandleClientComm(object client)
    {
        TcpClient tcpClient = (TcpClient)client;
        NetworkStream clientStream = tcpClient.GetStream();

        byte[] message = new byte[4096];
        int bytesRead;

        while (true)
        {
            bytesRead = 0;

            try
            {
                //blocks until a client sends a message
                bytesRead = clientStream.Read(message, 0, 4096);
            }
            catch
            {
                //a socket error has occured
                break;
            }

            if (bytesRead == 0)
            {
                //the client has disconnected from the server
                break;
            }

            //message has successfully been received
            ASCIIEncoding encoder = new ASCIIEncoding();
            System.Diagnostics.Debug.WriteLine(encoder.GetString(message, 0, bytesRead));
            Console.ReadLine();
        }

        tcpClient.Close();
    }

}

And in main class i call the SERVER class like this :

 class Program
    {

         Server obj=new Server();
        while (true)
        {

        }
    }

As you can see in server code I try to show the message but it doesn't work. Why doesn't it work?

Best regards

Benjamin Trent
  • 7,378
  • 3
  • 31
  • 41
Ehsan Akbar
  • 6,977
  • 19
  • 96
  • 180
  • 1
    You start server program. Main method is called. A Server object is created. The Main method exits right after and thus shuts down your enitre program (including the Server thread). Solution: Prevent your Main method from exiting while the Server thread runs. –  May 26 '14 at 15:10
  • So what should i change? – Ehsan Akbar May 26 '14 at 15:13
  • What do you mean with this question exactly? For starters, you can just add an infinite loop `for(;;){}` in the main method right after you created the Server object... –  May 26 '14 at 15:15
  • You could also add a `Console.ReadKey();`, so your program will close when you type something in the console window... – Bun May 26 '14 at 15:17
  • @Bun, no he can't without much pain. Look at his HandleClientComm method in the server, there he reads from the console, too... –  May 26 '14 at 15:18
  • I added a loop after the server ,but again doesn't work – Ehsan Akbar May 26 '14 at 15:18
  • @elgonzo Oops, didn`t see that. You are totally right, although I don`t really get why it`s there... – Bun May 26 '14 at 15:19
  • Guys i just follow this link :http://tech.pro/tutorial/704/csharp-tutorial-simple-threaded-tcp-server – Ehsan Akbar May 26 '14 at 15:20
  • You know i did that ,after calling the constructor the programming is coming back to main !!!!and it remains without exit!!! – Ehsan Akbar May 26 '14 at 15:24
  • Hold on. I do not understand your last comment (sorry, i deleted my previous comment since it was worded very badly and confusing) –  May 26 '14 at 15:25
  • Do you mean even after adding the infinite loop to the Main method, it still exits? –  May 26 '14 at 15:26
  • No it doesn't exits,it just remains in **}** . – Ehsan Akbar May 26 '14 at 15:28
  • [Let's join chat...](http://chat.stackoverflow.com/rooms/54459/ea-vs-elgonzo) –  May 26 '14 at 15:42
  • @elgonzo Please post your answer . – Ehsan Akbar May 27 '14 at 04:37
  • I'll do. Just give me some time :) –  May 27 '14 at 16:00

2 Answers2

13

The code presented in the question has a number of issues, which i decided to address in several sections of my answer. It is worth to note that the code is from a blog post (a sort of brief tutorial) published about six years ago and which already exhibits some of the issues addressed in this answer.


1. Why is the server program exiting immediately?

The reason for the server program exiting immediately is rather simple: When the Main method exits, the whole process of the program is shutdown, including any thread belonging to this process (such as the thread started in the constructor of the Server class). To fix this problem, the Main method needs to be prevented from exiting.

One common approach is to add Console.ReadKey() or Console.ReadLine() as last line in the Main method, which will not let the server program exit until the user provides some keyboard input. However, for the particular code given here this approach would not be such an easy fix because the client connection handler method (HandleClientComm) does also read console keyboard input (which itself might become a problem, see section 5 below).

As i do not want to start my answer with elaborating on keyboard input, i suggest a different and admittedly more primitive solution: adding an infinite loop at the end of the Main method (which has also been included in the edited question):

static void Main(string[] args)
{
    Server srv = new Server();
    for (;;) {}
}

Since the tutorial code of the server is essentially a console application, it can still be terminated by pressing CTRL+C.


2. The server still doesn't seem to do anything. Why?

Most (if not all) errors or problems occurring in classes and methods of the .NET frameworks are reported via .NET's exception mechanism.

And here the tutorial code makes a grave mistake in the server method for handling client connections:

try
{
    //blocks until a client sends a message
    bytesRead = clientStream.Read(message, 0, 4096);
}
catch
{
    //a socket error has occured
    break;
}

What is that try-catch block doing? Well, the only thing which it does is catching any exception -- which could answer the question Why? -- and then silently and unceremoniously discards this useful information. Ugh...!

Of course it makes sense to catch exceptions in the client connection handler threads. Otherwise, uncaught exceptions would cause the shutdown not only of the failed client connection handler thread but of the whole server console application. But these exceptions need to be handled in a meaningful way, so that no information is lost which can help in troubleshooting problems.

To provide the valuable information of exceptions thrown in our simple tutorial code, the catch block will output the exception information to the console (and also taking care of outputting any possible "inner exceptions").

Additionally, using statements are being utilized to ensure that both the NetworkStream as well as the TcpClient objects are being properly closed/disposed, even in situations where exceptions cause the client connection thread to exit.

private void HandleClientComm(object client)
{
    using ( TcpClient tcpClient = (TcpClient) client )
    {
        EndPoint remoteEndPoint = tcpClient.Client.RemoteEndPoint;

        try
        {
            using (NetworkStream clientStream = tcpClient.GetStream() )
            {
                byte[] message = new byte[4096];

                for (;;)
                {
                    //blocks until a client sends a message
                    int bytesRead = clientStream.Read(message, 0, 4096);

                    if (bytesRead == 0)
                    {
                        //the client has disconnected from the server
                        Console.WriteLine("Client at IP address {0} closed connection.", remoteEndPoint);
                        break;
                    }

                    //message has successfully been received
                    Console.WriteLine(Encoding.ASCII.GetString(message, 0, bytesRead));

                    // Console.ReadLine() has been removed.
                    // See last section of the answer about why
                    // Console.ReadLine() was of little use here...
                }
            }
        }
        catch (Exception ex)
        {
            // Output exception information
            string formatString = "Client IP address {2}, {0}: {1}";
            do
            {
                Console.WriteLine(formatString, ex.GetType(), ex.Message, remoteEndPoint);
                ex = ex.InnerException;
                formatString = "\tInner {0}: {1}";
            }
            while (ex != null);
        }
    }
}

(You might notice the code is remembering the client's (public) IP address in remoteEndPoint. The reason is that the Socket object in the property tcpClient.Client will be disposed when the NetworkStream is being closed - which happens when the scope of the respective using statement is being left, which in turn will make it impossible to access tcpClient.Client.RemoteEndPoint in the catch block afterwards.)

By letting the server output information from exceptions on the console, we will be able to see the following information from the server when the client attempts to send a message:

Unable to read data from the transport connection: An existing connection was forcibly closed by the remote host.

This is a pretty strong indication that either something is wrong with the client or that some network device has some strange problem. As it happened, the O/P runs the client and the server software on the same computer using the IP address "127.0.0.1", which makes worrying about malfunctioning network devices a moot argument and rather points at a problem with the client software.


3. What's wrong with the client?

Running the client does not throw any exception. Looking at the source code, one might falsely believe that the code seems to be mostly okay: The connection with the server is established, the byte buffer filled with the message and written to the NetworkStream, then the NetworkStream is flushed. The NetworkStream is not closed, though -- which certainly is related to the problem. But even if the NetworkStream has not been closed explicitly, flushing the stream should have sent the message to the server anyway, or...?

The previous paragraph contains two wrong assumptions. The first wrong assumption is related to the issue of the NetworkStream not being closed properly. What happens in the client code is that directly after writing the message to the NetworkStream, the client program exits.

When the client program exits, a "Connection terminated" signal is sent to the server (simplified speaking). If at that point in time the message still lingers in some TCP/IP-related buffer on the sender side, the buffer would be simply discarded and the message not being send anymore. But even if the message has been received by the server-side TCP/IP stack and kept in a TCP/IP-related receive buffer, the more or less immediately following "Connection terminated" signal still invalidates this receive buffer before the TCP/IP stack could acknowledge receiving this message and thus lets NetworkStream.Read() fail with the aforementioned error.

The other false assumption (and which is easily overlooked by someone not doing regular network-related programming in .NET) is in how the code tries to utilize NetworkStream.Flush() in an attempt to force transmission of the message. Let's take a look at the "Remarks" section of the MSDN documentation of NetworkStream.Flush():

The Flush method implements the Stream.Flush method; however, because NetworkStream is not buffered, it has no affect on network streams.

Yes, you did read that correctly: NetworkStream.Flush() does exactly... Nothing!
(...obviously, since NetworkStream does not buffer any data)

So, to make the client program work properly, all that needs to be done is to close the client connection properly (which also ensures that the message is being send and received by the server before the connection is severed). As already demonstrated in the server code above, we will make use of the using statement, which will take care of properly closing and disposing of the NetworkStream and TcpClient objects under any circumstances. Also, the misleading and useless call of NetworkStream.Flush() is being removed:

static void Main(string[] args)
{
    IPEndPoint serverEndPoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 3000);
    using ( TcpClient client = new TcpClient() )
    {
        client.Connect(serverEndPoint);

        using ( NetworkStream clientStream = client.GetStream() )
        {
            byte[] buffer = Encoding.ASCII.GetBytes("Hello Server!");
            clientStream.Write(buffer, 0, buffer.Length);
        }
    }
}


4. Advices regarding reading and writing messages from/to the NetworkStream

What always should be kept in mind is that NetworkStream.Read(...) does not guarantee to read the complete message at once. Depending on the size of the message, NetworkStream.Read(...) might read just fragments of a message. The reasons for this are related to how the client software sends data and how TCP/IP manages the transport of data through the network. (Granted, with such a short message like "Hello world!" it is unlikely that you will not encounter such a situation. But, depending on your server and client OS and the used NICs + drivers, you might start observing messages being fragmented if they become larger than 500-something bytes.)

Thus, to make the server more robust and less error-prone, the server code should be changed to cater for fragmented messages.

If you stick with ASCII encoding you might choose an approach as illustrated in Alexander Brevig's answer -- simply write the received message fragments with ASCII bytes into a StringBuilder. However, this approach only works reliably because any ASCII character is represented by a single byte.

As soon as you use another encoding which could encode a single character in multiple bytes (any UTF encoding, for example) this approach won't work reliably anymore. A possible fragmentation of the byte data could split the byte sequence of a multi-byte character in such a way that one NetworkStream.Read invocation just reads the first byte of such a character, and only a subsequent invocation of NetworkStream.Read would fetch the remaining bytes of this multi-byte character. This would upset the character decoding if each message fragment would be decoded individually. Thus, it generally is safer for such encodings to store the complete message in a single byte buffer (array) before doing any text decoding.

One problem is still present with reading messages of different lengths. How does the server know when the complete message has been sent? Now, in the tutorial given here, the client only sends one message and then disconnects. Thus, the server knows that the complete message has been received simply via the fact of the client disconnecting.

But what if your client wants to send multiple messages, or what if the server needs to send a response to the client? In both scenarios, the client cannot simply disconnect after sending the first message. So, how will the server know when a message has been completely sent?

A very simple and reliable method is to let the client prepend the message with a short integer value (2 bytes) or integer value (4 bytes) which specifies the message length in bytes. Thus, after receiving those 2 bytes (or 4 bytes) the server will know how many more bytes have to be read to obtain the complete message.

Now, you do not need to implement such a mechanism all by yourself. The good thing is that .NET already provides classes with methods which do all this work for you: BinaryWriter and BinaryReader, which make even sending of messages composed of different data types almost as easy and enjoyable as the proverbial walk in the park.

The client using BinaryWriter.Write(string):

static void Main(string[] args)
{
    IPEndPoint serverEndPoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 3000);
    using ( TcpClient client = new TcpClient() )
    {
        client.Connect(serverEndPoint);

        using ( BinaryWriter writer = new BinaryWriter(client.GetStream(), Encoding.ASCII) )
        {
            writer.Write("Hello Server!");
        }
    }
}

The server's client connection handler using BinaryReader.ReadString():

private void HandleClientComm(object client)
{
    using ( TcpClient tcpClient = (TcpClient) client )
    {
        EndPoint remoteEndPoint = tcpClient.Client.RemoteEndPoint;

        try
        {
            using ( BinaryReader reader = new BinaryReader(tcpClient.GetStream(), Encoding.ASCII) )
            {
                for (;;)
                {
                    string message = reader.ReadString();
                    Console.WriteLine(message);
                }
            }
        }
        catch (EndOfStreamException ex)
        {
            Console.WriteLine("Client at IP address {0} closed the connection.", remoteEndPoint);
        }
        catch (Exception ex)
        {
            string formatString = "Client IP address {2}, {0}: {1}";
            do
            {
                Console.WriteLine(formatString, ex.GetType(), ex.Message, remoteEndPoint);
                ex = ex.InnerException;
                formatString = "\tInner {0}: {1}";
            }
            while (ex != null);
        }
    }
}

You might notice the special handling of the EndOfStreamException exception. This exception is used by BinaryReader to indicate that the end of the stream is reached; i.e., the connection has been closed by the client. Instead of printing it out like any other exception (and which thus could be misunderstood as an error - which it indeed might be in different application scenarios), a specific message is printed out on the console to make the fact of the connection being closed very clear.

(Side note: If you intent to let your client software connect and exchange data with a 3rd-party server software, BinaryWriter.Write(string) might or might not be a feasible option, as it uses ULEB128 for encoding the byte length of a string. In cases where BinaryWriter.Write(string) is not feasible you can very likely still make good use of some combination of BinaryWriter.Write(short)/BinaryWriter.Write(int) in conjunction with BinaryWriter.Write(byte[]) to prepend your message byte data with a messageLength value.)


5. Console keyboard input

The client connection handler method HandleClientComm in the code from the question waits for keyboard input after receiving a message from a client before it will continue waiting and reading for the next message. Which is rather pointless, as HandleClientComm can just continue waiting for the next message without requiring explicit keyboard input.

But perhaps your intention is to use the console keyboard input as a response that will be sent back to the client -- i don't know. As long as you just toy around with one single simple client this approach would do it just fine, i guess.

However, as soon as you have two or more clients which concurrently access the server, even some simple test/toy scenario might require you to take measures to ensure that several client connection handler threads do not interleave their console output and that access to console keyboard input for these threads is managed in an understandable manner. That said, it really depends on what you want to do in detail -- it might very well also just be a non-issue...

  • 1
    "One byte equals exactly one ASCII character": Actually there are only a few character set encodings were that's true for all possible byte values. CP437 is one. ASCII and Windows-1252 are not. – Tom Blodget Jun 02 '14 at 02:32
  • @TomBlodget, agreed - the way i phrased this was kind of backward and wrong. Sorry :) Edited my answer to make the intended argument more clear: Every ASCII character is represented by a single byte. –  Jun 02 '14 at 09:41
  • @elgonzo: Great article! One Question: Why does the using in the client code ensure that the application does not exit (or even the connection not being disposed) before all data is transmitted? Shouldn't `Write` block until all data is sent (See msdn)? Could you add this little missing bit? – eFloh Jun 02 '14 at 10:46
  • 1
    @eFloh, NetworkStream uses *Socket* class for sending data. *Socket* class uses Winsock2's *send* function. Look at its [online MSDN documentation](http://msdn.microsoft.com/en-us/library/windows/desktop/ms740149.aspx). However, the online documentation is somewhat ambiguous. Especially pay attestation to one sentence in its Remarks section: "If no **buffer space is available within the transport system to hold the data to be transmitted**, send will block unless the socket has been placed in nonblocking mode [...]" (contd.) –  Jun 02 '14 at 11:53
  • 1
    @eFloh, that sentence is a strong indication that the documentation understands the phrase "sending data" in the sense of just being a handover of the data to the TCP/IP stack. Additionally, read the comment there from a user called *Jeris*; i quote: *"It is possible that the data wasn't sent. For example, sender has 64k send buffer, while receiver has 32k receive buffer. It is possible that sender receives a zero receive window after it sends 32k data. At this time, send is called successfully, while the data is only in the send buffer, but hasn’t be sent."* –  Jun 02 '14 at 11:57
  • @eFloh, also worth of note is the [TcpClient.NoDelay property](http://msdn.microsoft.com/en-us/library/system.net.sockets.tcpclient.nodelay.aspx). Quote from its MSDN documentation: *"When NoDelay is false, a TcpClient does not send a packet over the network until it has collected a significant amount of outgoing data"* (which is implemented in *TcpClient* by passing certain option flags to the underlying *Socket* object, which in turn will set the respective Winsock2 options) –  Jun 02 '14 at 12:25
  • 1
    @eFloh, ooops, i forgot the 1st part of your question. When the process exits, no objects are GC'd anymore (they just disappear together with the heap of the process). Thus, in the original code of the question, *Socket.Dispose()* would not be called (*Socket.Dispose()* would make sure that the proper TCP connection shutdown would happen for a still open connection). The internal native Winsock2 socket would then just be destroyed when the process exits, without doing the normal TCP connection shutdown handshake but only sending a "RESET" packet (i guess; i am currently not able to verify...) –  Jun 02 '14 at 13:27
  • @elgonzo: Thank you very much for that great background, comment points well deserved :) – eFloh Jun 05 '14 at 07:19
  • @elgonzo Hi dear friend i need some help here :http://stackoverflow.com/questions/24448304/send-a-value-by-socket-and-read-the-value-by-reader-readint32#24448865 .thank you – Ehsan Akbar Jun 27 '14 at 09:59
-1

Try changing this

try
{
    //blocks until a client sends a message
    bytesRead = clientStream.Read(message, 0, 4096);
}

To this

StringBuilder myCompleteMessage = new StringBuilder();
try {
    do{
        bytesRead = clientStream.Read(message, 0, message.Length);
        myCompleteMessage.AppendFormat("{0}", Encoding.ASCII.GetString(message, 0, bytesRead );                             
    } while(clientStream.DataAvailable);
}
///catch and such....
System.Diagnostics.Debug.WriteLine(myCompleteMessage);
AlexanderBrevig
  • 1,967
  • 12
  • 17
  • Yupp, more or less this after ruling out premature Main-exits and possible exceptions being thrown during TcpClient setup. –  May 26 '14 at 15:34
  • @EA, i think it is time for you to start the debugger and look closely what your server is doing -- check if any exceptions happen that might trip your server methods. Also check in the debugger if the methods really work with the data and values as you expect... –  May 26 '14 at 15:35
  • @EA, note a `try {...} catch { break; }` (as in your HandleClientComm method) is very, very bad, as it will hide any exception from you and thus make you unable to diagnose and fix any problems... –  May 26 '14 at 15:36
  • @elgonzo ok i just do that again – Ehsan Akbar May 26 '14 at 15:36
  • @elgonzo i did it again right now ,you know my program calls just server constructor and remains in while loop!!!without calling any other functions in server class – Ehsan Akbar May 26 '14 at 15:38