C# TcpSockets Можно ли отключить чистый/правильный способ?

Итак, я знаю, что есть много статей по этой теме, и я прочитал много из них, я бы сказал, но по какой-то причине я уверен, что мой код не делает то, что должен делать.

Я хочу закрыть соединение между моим сервером и моим клиентом. Теперь на стороне сервера я запускаю отключение с помощью этого кода

public void shutdown()
    {
        _socket.Shutdown(SocketShutdown.Both);
        _socket.Close();
    }

И на стороне клиента у меня есть некоторые проблемы с пониманием того, как я получаю отключение, что, как мне кажется, происходит следующим образом: в моем асинхронном обратном вызове получения я должен получить ошибку, так как сервер начал последовательность выключения, и я должен справиться с этим (правильно ???) поэтому мой код для клиента выглядит так: ReceiveCallback:

private void ReceiveCallback(IAsyncResult result)
    {
        int bytesRecieved = 0;
        byte[] tempBuff;

        //Try to receive But if a Socket error occures disconnect otherwise start Receiving again
        try
        {
            bytesRecieved = _socket.EndReceive(result);
        }
        catch (SocketException sockEx)
        {
            Disconnect(sockEx);
            return;
        }
        catch (ObjectDisposedException disposeEx)
        {
            Disconnect(disposeEx);
            return;
        }
        catch (Exception ex)
        {
            StartReceive();
            return;
        }

        if (bytesRecieved == 0)
        {
            StartReceive();
            return;
        }

        tempBuff = new byte[bytesRecieved];
        Buffer.BlockCopy(_buffer, 0, tempBuff, 0, bytesRecieved);
        StartReceive();
        _packHandler.handlePacket(tempBuff);

    }

Отключить:

public void Disconnect()
    {
        if (!_socket.Connected)
        {
            return;
        }
        _socket.BeginDisconnect(false, DisconnectCallback, null);
    }

ОтключитьОбратный звонок

private void DisconnectCallback(IAsyncResult result)
    {
        _socket.EndDisconnect(result);
        _socket.Close();
    }

(Метод Disconnect перегружен, поэтому, если я получаю исключение, он выводит messageBox, а затем также вызывает Disconnect. Просто чтобы я знал, что произошло.)

Где я ошибаюсь и что я могу улучшить???

Я попробовал код, и он, похоже, сработал, но затем я посмотрел с помощью netstat, закрыты ли все сокеты, а клиентский сокет - нет. Это было в FIN_WAIT_2, что означает, что он (или сервер???) еще не отправил пакет FIN, верно? О, а потом я попробовал еще раз с этой измененной строкой:

if (bytesRecieved == 0)
        {
            StartReceive();
            return;
        }

TO

if (bytesRecieved == 0)
        {
            Disconnect;
            return;
        }

который затем выдал исключение на стороне сервера, а на стороне клиента клиент сказал, что соединение было закрыто сервером ???

РЕДАКТИРОВАТЬ: Даже когда я закрыл обе программы, Netstat по-прежнему показывает порт в состоянии ОЖИДАНИЯ. что это мне говорит?


person D4rth B4n3    schedule 19.09.2014    source источник


Ответы (2)


У вашего обычного отключения есть отключение, которое очистит сокет, чтобы он мог правильно отключиться, но ваш асинхронный стиль никогда не вызывает отключение. Я добавил его в удобном месте ниже.

public void Disconnect()
{
    if (!_socket.Connected)
    {
        return;
    }
    _socket.Shutdown(SocketShutdown.Both); // Make sure to do this
    _socket.BeginDisconnect(false, DisconnectCallback, null);
}

ИЗМЕНИТЬ:

Судя по всему, у вас нет причин использовать асинхронный метод? Асинхронные методы предназначены для того, чтобы вы могли отправлять данные в отдельный поток выполнения, освобождая ваш поток, например, для выполнения некоторой обработки данных, когда это происходит.

Я не вижу какой-либо обработки, поэтому я предлагаю вам изменить отключение таким образом и посмотреть, решит ли это проблему. Потому что я не думаю, что вы ждете асинхронных методов, которые не сработают.

public void Disconnect()
{
    if (!_socket.Connected)
    {
        return;
    }
    shutdown(); //Your standard disconnect that you showed up top.  Scoping might be required.
}

Немного данных об Async можно найти здесь: http://msdn.microsoft.com/en-us/library/38dxf7kt(v=vs.110).aspx

person Carter    schedule 19.09.2014
comment
Хорошо, спасибо за ответ. Не могли бы вы объяснить, что происходит, когда я вызываю завершение работы, а затем что происходит с beginDisconnect и в чем разница между синхронными и асинхронными сокетами для этого отключения? - person D4rth B4n3; 19.09.2014
comment
ОБНОВЛЕНИЕ: я пробовал, но сокет все еще открыт. РЕДАКТИРОВАТЬ: теперь мой сервер также падает с исключением, говорящим, что клиент закрыл соединение уже в тот момент, когда я останавливаю свой сервер - person D4rth B4n3; 19.09.2014
comment
Я думаю, что вы плохо используете смешанные асинхронные методы и не ждете их. Во всяком случае, я отредактировал и объяснил некоторые выше. - person Carter; 19.09.2014
comment
Хорошо, я думаю, вы правы, я больше не хочу выполнять какую-либо обработку, поэтому я возвращаю ее к синхронизации. У меня все еще есть один открытый сокет в FIN_WAITING_2, хотя он переходит в ОЖИДАНИЕ, если я закрываю клиентское приложение. РЕДАКТИРОВАТЬ: но я также получаю исключение, если я добавляю строку _socket.Close() после завершения работы в клиенте, и в этот момент мой сервер падает, но если вы посмотрите с помощью netstat, он говорит, что статус подключения ПОДКЛЮЧЕН ???? - person D4rth B4n3; 19.09.2014
comment
У вас есть _socket.Close() внутри вашей функции завершения работы, поэтому ее повторный вызов приведет к исключению. - person Carter; 19.09.2014
comment
На стороне сервера да, а на стороне клиента нет? разве это не должно быть похоже на то, что сервер отключается, затем клиент отключается, а затем оба делают Close ??? - person D4rth B4n3; 19.09.2014
comment
Однако вы можете добавить _socket.Dispose() после завершения работы _socket.Close. Посмотрим, поможет ли это, в любом случае всегда полезно утилизировать. - person Carter; 19.09.2014
comment
Сокет все еще в FIN_WAITNG_2 - person D4rth B4n3; 19.09.2014
comment
Похоже, что ваш клиент полностью асинхронный выше, я перепутал эту часть. Используете ли вы EventWaitHandles или что-то еще, чтобы знать, когда эти части были обработаны? msdn.microsoft.com/en -нас/библиотека/ - person Carter; 19.09.2014
comment
Я думаю, что проблема заключается в попытке сделать асинхронный вызов внутри асинхронного вызова. Вы должны обрабатывать их из основного потока. - person Carter; 19.09.2014
comment
Хорошо вместо того, чтобы переделывать мою версию, как это обычно делается??? какие функции нужно вызывать в каком порядке на какой стороне, чтобы он делал то, что я хочу???? - person D4rth B4n3; 22.09.2014

Важно:

  • Если сервер запускает последовательность выключения, вы ДОЛЖНЫ справиться с этим.
  • Обе стороны должны вызвать отключение на своем сокете
  • Вам нужен способ заметить разъединение (это не выдаст вам ошибку, по крайней мере, не для меня)

Для этого я создал свой собственный класс customSocket, который наследуется от Socket

public class customSocket : Socket
{
    #region Properties
    private readonly Timer _timer;
    private const int _interval = 1000;

    private bool Connected
    {
        get
        {
            bool part1 = Poll(1000, SelectMode.SelectRead);
            bool part2 = (Available == 0);
            if (part1 && part2)
                return false;
            else 
                return true;
        }
    }

    public bool EventsEnabled
    {
        set
        {
            if (value)
            {
                _timer.Start();
            }
            else
                _timer.Stop();
        }
    }

    #endregion

    #region Constructors

    public customSocket(AddressFamily addressFamily, SocketType sockType, ProtocolType protocolType)
        : base(addressFamily, sockType, protocolType)
    {
        _timer = new Timer { Interval = _interval };
        _timer.Elapsed += TimerTick;
    }

    public customSocket(SocketInformation sockInfo)
        : base(sockInfo)
    {
        _timer = new Timer { Interval = _interval };
        _timer.Elapsed += TimerTick;
    }

    #endregion

    #region Events

    public event EventHandler<EventArgs> Socket_disconected;
    public void Raise_Socket_disconnected()
    {
        EventHandler<EventArgs> handler = Socket_disconected;
        if (handler != null)
        {
            handler(this,new EventArgs());
        }
    }

    private void TimerTick(object sender, EventArgs e)
    {
        if (!Connected)
        {
            Raise_Socket_disconnected();
        }
    }

    #endregion
}

Эта версия сокета имеет событие для отключения. Теперь, если вы создаете экземпляр своего класса сокета, вам нужно подключить обработчик и установить для EventsEnabled значение true.

Затем этот обработчик вызывает отключение, и ваш сокет не остается в состоянии FIN_WAIT_2.

person D4rth B4n3    schedule 22.09.2014