Winsock receiving information from the server

Asked

Viewed 122 times

0

I’m trying to send a really big information to the server (11000), and the problem I have is that it’s not coming full.

Look at the code:

On the server, there is a loop.

    do
    {

        Tick = Environment.TickCount;

        Listen.AcceptClient();
        Listen.Update();
    }

Listen.update

public static void UpdateClient(UserConnection client)
{
    string data = null;
        byte[] buffer = new byte[Convert.ToInt32(client.TCPClient.Available)];
        try
        {
            client.TCPClient.GetStream().Read(buffer, 0, Convert.ToInt32(client.TCPClient.Available));
        }
        catch
        {
            int code = System.Runtime.InteropServices.Marshal.GetExceptionCode();
            Console.WriteLine("Erro Num: " + code);
        }
        data = Encoding.UTF8.GetString(buffer);
        Console.WriteLine("Data is: " + data);
        Console.WriteLine("Size is: " + data.Length);
        Server.Network.ReceiveData.SelectPacket(client.Index, data);
        client.TCPClient.GetStream().Flush();
}

Listen.Acceptclient client

    if (listener.Pending())
    {
        //Adicionamos ele na lista
        Clients.Add(new UserConnection(listener.AcceptTcpClient(), Clients.Count()));

And that’s the Winsock server.

Does anyone have any hint or solution?

  • Would be problem with Toint32? I believe that Tcpclient.Available already returns Int32

2 answers

0

Jon Skeet Solution: http://www.yoda.arachsys.com/csharp/readbinary.html

The problem with your solution is Read, a stream is a stream and you can never (sometimes, but should not) assume that the content will always be read at once, not always the stream will have information to be consumed, but this does not indicate that it has finished.

You need to read until you’re sure you’ve read everything.

Looking at Jon’s snippets, one can see that we should look at the result of Read to make sure it’s over. And when you’re done, make sure the size is right. That means if Read read read 10K, it doesn’t mean he read everything, the way you’re going to do it depends on you, I’d follow Skeet’s line of thought.

When Read returns 0 or negative it is over, if the size is wrong then there was error.

  • I noticed that, the bytes arrive at size 11076, and the date is at size 11000, which makes sense, since the date is only missing a few characters. I think the problem may be related to utf8?

  • Also, the code is written synchronously, so you assume that the packages will always arrive in the order they were sent and continuously, that’s not how the real world works. A congestion on one of the network nodes can cause packet retransmission, you need to check the order and treat the receipt in a separate thread asynchronously.

  • @user3571412, probably the size difference must be occurring due to the character set. Since the UTF-8 standard is exactly equal to ASCII between U+00 characters.. 7F and that from U+80 we have more than one byte to represent a character, and can extend to up to 4 bytes. See the table at: http://en.wikipedia.org/wiki/UTF-8#Description .

0


Listen.Update

It turns out that the function Read that reads in streams does not guarantee the reading of all data sent at once. This happens to improve performance in streams. As data packets reach the customer, they are made available for reading.

When you call the function Read it only returns immediately if some data packet has already arrived to the client through the connection, otherwise it goes into standby mode and waits for the arrival of some package. This routine can take several rounds.

The function Read will read the available data up to a certain length (buffer length, 1024 in this case) and return the amount of bytes read. If no data is available for reading, the function Read will go into standby mode until it reaches the time set in ReadTimout or a package arrives. If ReadTimout if reached, an exception will be thrown. And if the stream or connection is terminated, the function Read return 0 (zero), indicating that no more data is to be read.

Therefore, you will need to close the stream or connection to indicate that the data has been fully uploaded.

public static void UpdateClient(UserConnection client) {
    System.IO.MemoryStream mStream = new System.IO.MemoryStream(client.TCPClient.Available);
    System.Net.Sockets.NetworkStream nStream = client.TCPClient.GetStream();

    byte[] buffer = new byte[1024];
    try {
        while(true) {
            int nBytes = nStream.Read(buffer, 0, 1024);
            if(nBytes <= 0)
                break;

            mStream.Write(buffer, 0, nBytes);
        }

    } catch {
        int code = System.Runtime.InteropServices.Marshal.GetExceptionCode();
        Console.WriteLine("Erro Num: " + code);
        return;
    }

    buffer = mStream.ToArray();

    string data = null;
    data = Encoding.UTF8.GetString(buffer);
    Console.WriteLine("Data is: " + data);
    Console.WriteLine("Size is: " + data.Length);
    Server.Network.ReceiveData.SelectPacket(client.Index, data);
    client.TCPClient.GetStream().Flush();
}

A good practice is to define a protocol for flow control, however simple. As an example, we can agree that before sending any information, the server will send 4 bytes (32 bits) indicating the amount of bytes that will compose the next information.

public static byte[] ReadBytes(System.IO.Stream stream, int size) {
    if (size <= 0)
        throw new ArgumentOutOfRangeException();

    byte[] buffer = new byte[size];

    int i = 0;
    while (size > 0) {
        int nBytes = stream.Read(buffer, i, size);
        if (nBytes <= 0)
            break;
        i += nBytes;
        size -= nBytes;
    }

    if(size > 0) {
        byte[] nBuffer = new byte[buffer.Length - size];
        Array.Copy(buffer, 0, nBuffer, 0, nBuffer.Length);
        buffer = nBuffer;
    }

    return buffer;
}

public static void UpdateClient(UserConnection client) {
    System.Net.Sockets.NetworkStream nStream = client.TCPClient.GetStream();

    byte[] buffer = null;
    try {
        int size = BitConverter.ToInt32(ReadBytes(nStream, 4), 0);
        if (size <= 0)
            throw new InvalidOperationException();

        buffer = ReadBytes(nStream, size);

        if(buffer.Length != size)
            throw new InvalidOperationException();
    } catch {
        int code = System.Runtime.InteropServices.Marshal.GetExceptionCode();
        Console.WriteLine("Erro Num: " + code);
        return;
    }

    string data = null;
    data = Encoding.UTF8.GetString(buffer);
    Console.WriteLine("Data is: " + data);
    Console.WriteLine("Size is: " + data.Length);
    Server.Network.ReceiveData.SelectPacket(client.Index, data);
    client.TCPClient.GetStream().Flush();
}
  • The code passed returns the buffer as a null value, could fix it?

  • Buffer returned null if any error occurred during reading. Fixed.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.