Winsock receiving information from the server

0

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

See 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

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

And this is the winsock server.

Does anyone have any tips or solutions?

    
asked by anonymous 08.02.2015 / 16:43

2 answers

0

Listen.Update

The Read function reading in streams does not guarantee that all data sent at once is read. This happens to improve performance in streams. As the data packets get to the client, they are made available for reading.

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

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

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

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, even if 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();
}
    
12.05.2015 / 20:16
0

Jon Skeet solution: link

The problem with your solution is Read, a stream is a stream and you can never (can 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 is finished.

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

Looking at Jon's snippets, you may notice that we should look at Read's output to make sure it's done. And when finished, check if the size is right. That means if Read read 10K, it does not mean he read everything, how you're going to do it depends on you, I'd follow Skeet's line of thinking.

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

    
08.02.2015 / 21:18