Как вы корректно закрываете TCP соединения без исключений? - PullRequest
1 голос
/ 26 августа 2009

Что такое шаблон и / или как бы вы изменили следующий код, чтобы соединение можно было корректно закрыть с сервера или с клиента без обработки исключений.

  1. TcpClient против Socket: Я не привязан к использованию TcpClient клиентский класс. Я написал следующее код, чтобы попытаться продемонстрировать самый простой случай я был используя Socket & SocketAsyncEventArgs но дела становились слишком сложно разобраться с этим вопросом.

  2. Блокирование по сравнению с асинхронным: Из-за блокировка звонков это может быть сложно или невозможно Если это так, это нормально, но как вы решаете это в асинхронный случай?

  3. Quit Token?: Я экспериментировал с отправкой какого-то токена "Quit" на другую сторону, чтобы он знал о завершении работы, но не получил его работу, и я хотел представить минимальный пример здесь.

  4. Обработка исключений необходима в любом случае: Я знаю, что обработка исключений будет необходима в приложении real , поскольку сетевые подключения и т. Д. Не будут работать. Но не может ли 1029 *

Редактировать: Я переместил оригинал и код ответа на Суть .

Оригинал Не удалось Пример : Переместился сюда: https://gist.github.com/958955#file_original.cs

Текущий рабочий ответ : https://gist.github.com/958955#file_answer.cs

class Channel
{
    private readonly TcpClient client;
    private readonly NetworkStream stream;
    private readonly string name;
    private readonly ManualResetEvent quit = new ManualResetEvent(false);

    public Channel(string name, TcpClient client)
    {
        this.name = name;
        this.client = client;
        stream = client.GetStream();
    }

    public void Run()
    {
        Console.WriteLine(name + ": connected");
        byte[] buffer = new byte[client.Client.ReceiveBufferSize];
        stream.BeginRead(buffer, 0, buffer.Length, this.Read, buffer);

        var writer = new StreamWriter(stream, Encoding.ASCII);

        while (true)
        {
            var line = Console.ReadLine();
            if (String.IsNullOrEmpty(line) || !this.client.Connected) break;

            writer.WriteLine(line);
            writer.Flush();
        }

        if (client.Connected)
        {
            Console.WriteLine(name + " shutting down send.");
            client.Client.Shutdown(SocketShutdown.Send);
            Console.WriteLine(name + " waiting for read to quit.");
            quit.WaitOne();
        }
        else
        {
            Console.WriteLine(name + " socket already closed");
        }

        Console.WriteLine(name + " quit, press key to exit.");
        Console.ReadKey();
    }

    private void Read(IAsyncResult result)
    {
        var bytesRead = this.stream.EndRead(result);
        if (bytesRead == 0)
        {
            Console.WriteLine(name + " read stopped, closing socket.");
            this.client.Close();
            this.quit.Set();
            return;
        }

        var buffer = (byte[])result.AsyncState;
        Console.WriteLine(name + " recieved:" + Encoding.ASCII.GetString((byte[])result.AsyncState, 0, bytesRead));

        stream.BeginRead(buffer, 0, buffer.Length, this.Read, buffer);
    }
}

Ответы [ 3 ]

2 голосов
/ 26 августа 2009

Используйте Socket.Shutdown () перед вызовом Socket.Close (). Дождитесь завершения обработки выключения (например, ReceiveAsync () вернет 0 байтов).

  1. Выбор абстракции (в основном) не является проблемой.
  2. Асинхронный ввод / вывод означает, что вам никогда не придется видеть ваше сожаление.
  3. Из документации MS для метода Socket.ReceiveAsync (): для байтовых потоков нулевые прочитанные байты означают плавное закрытие и то, что больше не будет считываться никаких байтов.
0 голосов
/ 26 августа 2009

Я давно не делал сокеты, но я помню, что вам пришлось вызывать shutdown () для них.

.NET эквивалент, я думаю, будет socket.Shutdown(SocketShutdown.Both)

0 голосов
/ 26 августа 2009

TcpClient реализует IDisposable, так что должен иметь возможность использовать его таким образом, а затем просто не беспокоиться о закрытии клиента - оператор using должен сделать это для вас независимо от того, выдано ли исключение:

class Client
{
    static void Main(string[] args)
    {
        using (TcpClient client = new TcpClient(AddressFamily.InterNetwork))
        {

            Console.WriteLine("client: created, press key to connect");
            Console.ReadKey();

            client.Connect(IPAddress.Loopback, 5000);

            var channel = new Channel("client", client);
            channel.Run();
        }
    }
}

Тем не менее, обычно типы CLR, которые реализуют IDisposable, в явном виде говорят, что они закрывают свой определенный базовый ресурс в документации (например: SqlConnection ), но TcpClient документы 1011 * по этому вопросу странно тихо & mdash; вы можете сначала проверить это.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...