NetworkStream.ReadByte function randomly stutters, pausing all inputs for the duration

The Problem
I am using a hokuyo uam-05lp-t301 sensor as input for a Unity application, every frame it sends data via ethernet to computer. Normally it only takes around 10-20ms to get this data into Unity using NetworkStream.ReadByte, according to the profiler. Sometimes the application freezes and the profiler tells me the ReadByte function takes 2000ms or even 6000 in some cases. The application is completely unusable during this time.
It seems to happen quite randomly but more often when I actually interact with the sensor.

Screen cap of unity profiler at the frozen frame:

Here is the function that performs the byte reading:

static string ReadLine(NetworkStream stream)
{
    if (stream.CanRead)
    {
        StringBuilder sb = new();
        bool is_NL2 = false;
        bool is_NL = false;
        do
        {
            profMarker.Begin();
            char buf = (char)stream.ReadByte();
            profMarker.End();
            if (buf == '\n')
            {
                if (is_NL)
                {
                    is_NL2 = true;
                }
                else
                {
                    is_NL = true;
                }
            }
            else
            {
                is_NL = false;
            }
            sb.Append(buf);
        } while (!is_NL2);
        return sb.ToString();
    }
    else
    {
        return null;
    }
}

I don't understand networking enough to see why this is happening or how I can fix it.

What I have Tried
When I put the ReadLine function in a thread the application no longer freezes and the framerate is much higher, but the input from the sensor still stops for the 2-6 seconds. Using a thread is definitely preferable but not a solution.
I have tried adding ReceiveTimeout to the TCP Client thinking it might just skip the data for the frame if it takes too long. This mostly stops the application from freezing but the input still doesn't work for a few seconds. It also generates an error:
"
SocketException: A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond.
Rethrow as IOException: Unable to read data from the transport connection: A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond.
"

Any advice would be helpful as this issue severely affects the usability of the application. Let me know if more info is required.

Even thought it is transported over a packet based protocol (IP), the TCP protocol is an endless streaming protocol. So data isn't nicely grouped into packets. You seem to read "lines" from the stream, so you're reading character by character until you hit a new line character. However if the input buffer of the stream is currently empty, when calling ReadByte, the ReadByte method would block until new data has arrived. That's because you run the socket in normal "blocking mode". It's possible to read from a socket asynchronously by using BeginRead. However that means the actual reading is done in a callback.

I prefer to work in blocking mode but do the actual receiving inside a receiving thread that I start myself. Of course you would need some sort of synchronisation between the receiving thread and Unity's main thread. Though a ConcurrentQueue is usually enough. The queue takes care about proper locking / syncing and your receiving thread can simply enqueue newly assembled strings and your Unity code can simply TryDequeue a string from the queue.

So a thread is almost always required for any kind of network communication. I can't say why it may freeze. I don't know what kind of protocol you're dealing with and if the server is actually flushing the buffer after each line to make sure the latest info is actually send out. It seems that you always read multiple lines and only return when you receive an empty line (two \n in a row). Is that really the specification of the protocol?

Thank you for the response. I don't really understand what you wrote in your first two paragraphs, I am new to networking and find it difficult to understand.

I can confirm that I am using a thread for reading as that gives 100+ fps instead of barely 30 when not using a thread, but the problem is the freezing.

All the sensor data gets in to unity as it should so I would assume it is correct to return at an empty line like it does.