В настоящее время я работаю над простой чат-программой клиентского сервера (хотел получить информацию о взаимодействии с клиентским сервером на C#). Он работает до сих пор, кроме как правильно отключиться от сервера.Проблемы с закрытием TcpClient и NetworkStream
На клиенте я использую этот код здесь, чтобы закрыть соединение:
client.Client.Disconnect(false); // client is the TcpClient
client.Close();
На сервере имеется резьбовая петля ждет сообщений от клиента:
private void StartChat()
{
int requestCount = 0;
byte[] bytesFrom = new byte[10025];
string dataFromClient = null;
string rCount = null;
while (true)
{
try
{
requestCount++;
NetworkStream stream = tcpClient.GetStream();
int bufferSize = (int)tcpClient.ReceiveBufferSize;
if (bufferSize > bytesFrom.Length)
{
bufferSize = bytesFrom.Length;
}
stream.Read(bytesFrom, 0, bufferSize);
dataFromClient = System.Text.Encoding.UTF8.GetString(bytesFrom);
dataFromClient = dataFromClient.Substring(0, dataFromClient.IndexOf("$"));
rCount = Convert.ToString(requestCount);
string message = client.Name + " says: " + dataFromClient;
program.Broadcast(message);
}
catch(Exception ex) when (ex is ObjectDisposedException || ex is InvalidOperationException || ex is System.IO.IOException)
{
program.UserDisconnected(client);
break;
}
catch(ArgumentOutOfRangeException ex)
{
Debug.WriteLine(ex.ToString());
break;
}
catch(Exception ex)
{
Debug.WriteLine(ex.ToString());
break;
}
}
Если клиент отключается с кодом, показанным выше, функция все время извлекает поток и производит такой выход:
\0\0\0\0\0\0\0 [and so on]
В этом случае ArgumentOutOfRangeException
будет выброшен, потому что нет индекса $
. Я добавил break
, чтобы избежать и бесконечного выполнения цикла.
Удивительно, но ObjectDisposedException
не будет выбрасываться. Также не будет выбрано значение System.IO.IOException
, но он должен быть закрыт, поэтому соединение было отклонено.
Если я просто закрою клиентское приложение, которое подключено к серверу, сервер не останавливает цикл, он просто ждет потока, который никогда не появится, потому что клиент отключен.
Итак, как я могу определить, отключен ли клиент или недоступен? И как я закрываю соединение с клиентом, чтобы закрыть соединение?
Благодарим за помощь!
Update:
private void StartChat()
{
int requestCount = 0;
byte[] bytesFrom = new byte[10025];
string dataFromClient = null;
string rCount = null;
while (true)
{
try
{
requestCount++;
NetworkStream stream = tcpClient.GetStream();
stream.ReadTimeout = 4000;
int bufferSize = (int)tcpClient.ReceiveBufferSize;
if (bufferSize > bytesFrom.Length)
{
bufferSize = bytesFrom.Length;
}
// Wait for a client message. If no message is recieved within the ReadTimeout a IOException will be thrown
try
{
int bytesRead = stream.Read(bytesFrom, 0, bufferSize);
stream.Flush();
if (bytesRead == 0)
{
throw new System.IO.IOException("Connection seems to be refused or closed.");
}
}
catch (System.IO.IOException)
{
byte[] ping = System.Text.Encoding.UTF8.GetBytes("%");
stream.WriteTimeout = 1;
stream.Write(ping, 0, ping.Length);
continue;
}
dataFromClient = System.Text.Encoding.ASCII.GetString(bytesFrom);
dataFromClient = dataFromClient.Substring(0, dataFromClient.IndexOf("$"));
rCount = Convert.ToString(requestCount);
string message = client.Name + " says: " + dataFromClient;
program.Broadcast(message);
}
catch(Exception ex) when (ex is ObjectDisposedException || ex is InvalidOperationException || ex is System.IO.IOException)
{
Debug.WriteLine(ex.ToString());
program.UserDisconnected(client);
break;
}
catch(ArgumentOutOfRangeException ex)
{
Debug.WriteLine(ex.ToString());
}
catch(Exception ex)
{
Debug.WriteLine(ex.ToString());
break;
}
}
}
Означает ли это, что вы не можете обнаружить, что клиент ушел? Глядя на документ, он должен вызывать исключение ObjectDispoedException или, по крайней мере, исключение IOEx, потому что он больше не может читать от него. – chris579
Отключение _graceful_ не является исключительным случаем и обнаруживается 'stream.Read()' return 0. Вы действительно должны есть обработчик исключений, но я сомневаюсь, что «ObjectDisposedException» будет выброшен, если вы не разместите его самостоятельно, а затем попытайтесь его использовать. –
Вы правы, DisposedException не будет выбрасываться, если этот объект все еще отсутствует и не удаляется сборщиком мусора или вручную удаляется. Однако огромное спасибо, это сработало! Я отвечу на ваш ответ как можно скорее. Редактировать: Если я закрою клиент, цикл не закончится и останется, пока приложение сервера не будет закрыто. Как я могу это исправить? – chris579