It's hard to tell what's going on in your custom line reader. There are a variety of different reasons this could be occurring and it's hard to tell what the root cause is, especially given that it's happening inside your custom line reader which we can't see.
First, I'd recommend removing the `token' (cancellation token) from the ReadLineAsync() to see if that's causing the error.
Second, in your client code make sure you're closing the stream and the client in succession. You should not need the line marked #1, but depending on what you're doing on the client side it may solve the problem
CLIENT
using var client = new TcpClient(server, port);
using var stream = client.GetStream();
// YOUR APP CODE
// 1. try flushing the stream [UPDATE]
await stream.FlushAsync();
// 2. give this a try if you get error w/ #3 without it
client.Client.Shutdown(SocketShutdown.Both);
// 3. make close both in order
stream.Close(10000); //allow for timeout
client.Close();
SERVER
I would recommend switching to StreamReader if you're line endings are terminated with supported some combination of CR and LF. It's incredibly fast and battle tested. Again, you can use pass the cancellation token to the ReadLineAsync() command if you want, but it doesn't seem meaningful given your use case and may throw an unwanted exception (based upon your question).
public static async Task ServeStream(Stream stream, CancellationToken token)
{
string command = default;
using var stream = new NetworkStream(socket);
using var reader = new StreamReader(stream, Encoding.ASCII);
while ((command = await reader.ReadLineAsync()) != null)
{
byte[] response = await ProcessCommand(command);
stream.Write(response, 0, response.Length);
}
stream.Close(1000);
socket.Shutdown(SocketShutdown.Both);
socket.Close();
}
Alternatively, I'd suggest using StreamWriter as well. It handles all of the buffering and edge cases for your...
public static async Task ServeStream(Stream stream, CancellationToken token)
{
string command = default;
using var stream = new NetworkStream(socket);
using var writer = new StreamWriter(stream, Encoding.ASCII);
using var reader = new StreamReader(stream, Encoding.ASCII);
while ((command = await reader.ReadLineAsync()) != null)
{
if (command == "Hello")
{
await writer.WriteLineAsync($"Hi from Client{clientId} - {_receiveCnt}");
await writer.FlushAsync();
}
}
stream.Close(1000);
socket.Shutdown(SocketShutdown.Both);
socket.Close();
}
FULL WORKING EXAMPLE W/2 ASYNC CLIENTS
Copy and paste into Linqpad or a console app, add references (System.Net, System.Net.Sockets, System.Threading.Tasks) and hit F5...
async Task Main()
{
using var server = new Server();
_ = server.Start();
Task.WaitAll(new Client().Connect(),new Client().Connect());
server.Stop();
}
public class Client
{
Socket _client;
int _sendCount = 0;
IPEndPoint _endPoint;
public Client()
{
// Set the endpoint to connect on port 13000.
Int32 port = 13000;
_endPoint = new(IPAddress.Loopback, port);
_client = new(
_endPoint.AddressFamily,
SocketType.Stream,
ProtocolType.Tcp);
}
public async Task Connect()
{
await _client.ConnectAsync(_endPoint);
if (!_client.Connected) throw new Exception("Connection error.");
using var stream = new NetworkStream(_client);
using var streamWriter = new StreamWriter(stream);
using var streamReader = new StreamReader(stream);
while (_sendCount < 10)
{
await streamWriter.WriteLineAsync("Hello");
await streamWriter.FlushAsync();
var response = streamReader.ReadLine();
_sendCount++;
Console.WriteLine(response);
}
stream.Close(1000);
_client.Close();
}
}
public class Server : IDisposable
{
Socket _server = null;
IPEndPoint _endpoint;
int _clientCnt = 0;
int _receiveCnt = 0;
CancellationTokenSource _cancellation;
List<Task> _clients = new List<Task>();
public Server()
{
// Set the TcpListener on port 13000.
Int32 port = 13000;
_endpoint = new(IPAddress.Loopback, port);
_server = new(_endpoint.AddressFamily,
SocketType.Stream,
ProtocolType.Tcp);
_cancellation = new CancellationTokenSource();
}
public async Task Start()
{
try
{
// Start listening for client requests.
_server.Bind(_endpoint);
_server.Listen();
do
{
// Perform a blocking call to accept requests.
var socket = await _server.AcceptAsync(_cancellation.Token);
// Handle client async on background thread and continue
_ = HandleClientAsync(socket, _cancellation.Token);
} while (_server.IsBound);
}
catch (OperationCanceledException e)
{
Console.WriteLine("Async socket cancallation requested.");
}
catch (SocketException e)
{
Console.WriteLine("SocketException: {0}", e);
}
catch (Exception ex)
{
Console.WriteLine("Exception: {0}", ex);
}
finally
{
// Stop listening for new clients.
_server.Close();
}
}
public void Stop()
{
if (_server.IsBound)
{
_cancellation.Cancel();
_server.Close(10000);
}
}
public async Task HandleClientAsync(Socket socket, CancellationToken token)
{
var clientId = Interlocked.Increment(ref _clientCnt);
Console.WriteLine($"Client {_clientCnt} Connected!");
string command = default;
using var stream = new NetworkStream(socket);
using var writer = new StreamWriter(stream, Encoding.ASCII);
using var reader = new StreamReader(stream, Encoding.ASCII);
while ((command = await reader.ReadLineAsync()) != null)
{
Interlocked.Increment(ref _receiveCnt);
if (command == "Hello")
{
await writer.WriteLineAsync($"Hi from Client{clientId} - {_receiveCnt}");
await writer.FlushAsync();
}
}
stream.Close(1000);
socket.Shutdown(SocketShutdown.Both);
socket.Close();
Interlocked.Decrement(ref _clientCnt);
Console.WriteLine($"Client {clientId} disconnected.");
}
public void Dispose()
{
if (_server.IsBound)
{
Stop();
_server.Dispose();
}
}
}