In your while loop, you are mixing calls to a TCP layer API with calls to TLS layer API that runs over it. This won't work properly.
The ssl.Socket property is actually an instance of .NET socket (System.Net.Sockets.Socket class), which represents the TCP socket that underlies the TlsServerSocket instance. That underlying socket does not "know" anything about TLS/SSL or about the TlsServerSocket, and in almost all scenarios involving TlsServerSocket or TlsClientSocket, it's best to leave it alone.
The ssl.Socket.Poll call at the TCP layer implies that you would miss any data that has already been received by the TlsServerSocket and is waiting in its internal buffer, and the ssl.Socket.Available property returns the number of bytes available at the TCP level, which also includes the length of TLS packets that don't represent actual data, but are instead part of the TLS protocol itself.
For example, the value of 31 you get when the socket is closed at the client side represents the length of TLS Alert packet that informs the server that the TLS channel has been closed.
A typical server-side while loop using asynchronous ReceiveAsync might look like this:
byte[] data = new byte[4096]; // allocate a buffer for incoming data
while (true)
{
// receive data over the TLS socket
int receivedBytes = await ssl.ReceiveAsync(data);
if (receivedBytes == 0)
{
// when ReceiveAsync returns zero, it indicates
// that the connection has been closed and that
// it is time to close it from the server-side
// and exit the 'while' loop
await ssl.DisposeAsync();
break;
}
// otherwise, do something with the data
DoSomething(new ArraySegment<byte>(data, 0, receivedBytes));
}
There is actually no need to call ssl.Socket.Poll or ssl.Socket.Available at all.