4
votes

I'm incredibly confused as to what is going on here. I've been putting in break points and I just can't seem to understand. Basically, I have a client and a server. I want the client to send two separate strings of data. From putting in break points, I noticed that my client does in fact fill both strings with the appropriate data. However, the server never ever sees the second string. Why is this happening and how do I fix it? Any help at all would be greatly appreciate! Below is my code:

Server:

    private static void HandleClientComm(object client)
    {
        /** creating a list which contains DatabaseFile objects **/
        List<DatabaseFile> theDatabase = new List<DatabaseFile>();

        TcpClient tcpClient = (TcpClient)client;
        NetworkStream clientStream = tcpClient.GetStream();

        StringBuilder response = new StringBuilder();
        byte[] message = new byte[4096];
        int bytesRead;

        do
        {
            bytesRead = 0;

            try
            {
                /*do
                {
                    bytesRead = clientStream.Read(message, 0, message.Length);
                    response.Append(Encoding.ASCII.GetString(message, 0, bytesRead));
                } while (clientStream.DataAvailable);*/

when i change this commented code to bytesRead = clientStream.Read(message, 0, 4096); i get an IOException Error that reads as follows: Unable to write data to the transport connection: An existing connection was forcibly closed by the remote host. Hence, i changed it to a do while loop. How do i get around this IOException and accept the second string?

                ASCIIEncoding encoder = new ASCIIEncoding();
                String file = encoder.GetString(message, 0, bytesRead);
                Menu.Insert(theDatabase, file); 
            }
            catch (Exception)
            {
                // A socket error has occured
                break;
            }

            if (bytesRead == 0)
            {
                // The client has disconnected from the server
                break;
            }
        } while (clientStream.DataAvailable);

        // Release connections
        clientStream.Close();
        tcpClient.Close();
    }

Client:

    static void Main(string[] args)
    {
        TcpClient client = new TcpClient();

        IPEndPoint serverEndPoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 8888);

        client.Connect(serverEndPoint);

        NetworkStream clientStream = client.GetStream();
        NetworkStream clientStream2 = client.GetStream();

        ASCIIEncoding encoder = new ASCIIEncoding();
        ASCIIEncoding encoder2 = new ASCIIEncoding();
        String text = System.IO.File.ReadAllText("FirstNames.txt");
        String text2 = System.IO.File.ReadAllText("LastNames.txt");

        byte[] buffer = encoder.GetBytes(text);

        Console.ReadLine(); 
        clientStream.Write(buffer, 0, buffer.Length);
        clientStream.Flush();
        Console.ReadLine();

        byte[] buffer2 = encoder2.GetBytes(text2);

        clientStream2.Write(buffer2, 0, buffer2.Length);
        clientStream2.Flush();
        Console.ReadLine(); 
    }
}
3

3 Answers

2
votes

The communication between client and server happens like this (note that the order of steps is just for illustration purposes, the actual order at runtime may be different):

  1. Client: client.Connect(serverEndPoint)
    Server: HandleClientComm(newClient)

  2. Client: clientStream.Write(buffer, 0, buffer.Length)
    Server: bytesRead = clientStream.Read(message, 0, message.Length)
    Note that Read is not guaranteed to read entire message. It is perfectly ok to return just the portion that has been received so far

  3. Client: Console.ReadLine()
    Server: while (clientStream.DataAvailable)
    There is no data on the stream - the client has not sent any. This would likely happen even without ReadLine - there is a window of time before the client sends data again

  4. Server: tcpClient.Close()

  5. Client: clientStream2.Write(buffer2, 0, buffer2.Length)
    You can get an exception here, or not - depending on whether the server has already closed the connection, in any case the server is not reading anymore.

You need to define your own message protocol that both server and client will honor. For example, you can have the client close the connection when it is done sending:

Client:

using (var client = new TcpClient("localhost", 8888))
using (var clientStream = client.GetStream())
{
    var buffer = Encoding.ASCII.GetBytes( File.ReadAllText("FirstNames.txt") );
    clientStream.Write(buffer, 0, buffer.Length);

    buffer = Encoding.ASCII.GetBytes( File.ReadAllText("LastNames.txt") );
    clientStream.Write(buffer, 0, buffer.Length);
}

Server:

using (var tcpClient = (TcpClient)client)
using (var clientStream = tcpClient.GetStream())
{
    // Store everything that the client sends.
    // This will work if the client closes the connection gracefully
    // after sending everything

    var receivedData = new MemoryStream();
    clientStream.CopyTo(receivedData);

    var rawBytes = receivedData.ToArray();
    var file = Encoding.ASCII.GetString(rawBytes, 0, rawBytes.Length);

    Menu.Insert(theDatabase, file);
}

The protocol is simple, and may be enough for your case. However, there are issues with it which should be addressed in production code (e.g. what if the client sends too much data, exhausting server memory, what if the client stops sending without closing the connection, etc.)

2
votes

because while (clientStream.DataAvailable) is no longer true after your first client call.

1
votes

You are exiting your client-recv loop in your server just because DatAvailable is false. This means if the client were to send a frame of data (which you consume) and pause then your server would see no data available at that moment and disconnect, even if a split second later another frame of data from the client was about to come in. Almost always, the end of a dialog is based on the content of the data being passed. You can certainly never try to rely on the fact that DataAvailable happens to be false one time.

As a follow-up, if you provide more info on the protocol that is used we can give more assitance. For example, if the prototcol is that two strings are sent with CR/LF at the end then the server loop should be inspecting the buffer for that to know when to disconnect.