2013-09-17 5 views
3

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

Я ожидал, что сделаю I/O по TCP-соединению, которое было закрыто, чтобы выбрасывать какое-то исключение ввода-вывода, но вместо этого программа, похоже, только висит, ожидая навсегда для другой конечной точки, чтобы ответить , Очевидно, что если соединение закрыто, этот ответ никогда не наступает. (Это даже не кажется тайм-аутом, если вы оставите его, скажем, двадцать минут.)

Есть ли способ я могу force удаленный конец «видеть», что я закрыл сетевое соединение ?

Update: Вот код ...

public sealed class Client 
{ 
    public void Connect(IPAddress target) 
    { 
    var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); 
    socket.Connect(ipAddress, 1177); 
    _stream = new NetworkStream(socket); 
    } 

    public void Disconnect() 
    { 
    _stream.Close(); 
    } 
} 

public sealed class Server 
{ 
    public void Listen() 
    { 
    var listener = new TcpListener(IPAddress.Any, 1177); 
    listener.Start(); 
    var socket = listener.AcceptSocket(); 
    _stream = new NetworkStream(socket); 
    ... 
    } 

    public void Disconnect() 
    { 
    socket.Shutdown(SocketShutdown.Both); 
    socket.Disconnect(false); 
    } 
} 
+0

В TCP процесс, как правило, не может вешать вечно ... TCP отправляет сигналы «alive?», Чтобы проверить, активен ли другой узел. –

+0

... отсюда мой недоумение. – MathematicalOrchid

+1

Вы считали «TcpClient.LingerState»? Как вы используете TCP? Программирование сокетов или какая-то специальная библиотека? –

ответ

1

Когда приложение закрывает сокет правильный путь, он посылает сообщение, содержащее 0 байт. В некоторых случаях вы можете получить SocketException, указав, что что-то пошло не так. В третьей ситуации удаленная сторона больше не подключена (например, отключив сетевой кабель) без какой-либо связи между двумя сторонами.

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

Прочитав код, который вы отправили сейчас: при использовании NetworkStream операция Read будет возвращать значение 0 (байты), чтобы указать, что клиент закрыл соединение.

documentation это упоминается как

«Если данные не доступны для чтения, метод Read возвращает 0.»

и

«Если удаленный узел закрывает соединение, и все имеющиеся данные были получены, метод чтения завершается немедленно и возвращать ноль байт.»

в том же абзаце. В действительности NetworkStream блокирует, если данные не доступны для чтения, пока соединение открыто.

+0

Вы имеете в виду 'Read()' возвращает нулевые байты, а не бросает исключение? О, дорогой ... – MathematicalOrchid

+0

@MathematicsOrchid точно –

+0

Так что предположительно 'ReadByte()' также собирается возвращать '-1', а не бросать фактическое исключение тоже ... – MathematicalOrchid

1

Привет MathematicalOrchid,

Вы можете найти то, что вы ищете здесь:

http://blog.stephencleary.com/2009/05/detection-of-half-open-dropped.html

Существует некоторая большая информация там, когда дело доходит до работы с TCP сокетов и обнаружения половины открытых соединений.

Вы также можете обратиться к этому сообщению, которое, кажется, имеет такое же решение:

TcpClient communication with server to keep alive connection in c#?

-Dave

+0

Это интересно, но, похоже, речь идет о том, когда процесс умирает, или сетевая ссылка снижается. Я пытаюсь _explicitly_ закрыть соединение на одном конце, которое должно уведомить другой конец, вы бы Думаю ... – MathematicalOrchid

1

Открывает сокет, и назначая его в поток. В конце процесса вы закрываете сетевой поток, но не сокет.

Для того чтобы закрыть базовый сокет, он должен иметь параметры собственности, установленные в конструкторе, равными true. См. Документы MSDN по адресу http://msdn.microsoft.com/en-us/library/te7e60bx.aspx.

Это может привести к тому, что соединение будет висящим, поскольку основной разъем не был правильно закрыт.

Изменить

_stream = new NetworkStream(socket); 

Для

_stream = new NetworkStream(socket, true); 

На стороне записки, если вы не требуют максимальной производительности для малого приложения вы должны попробовать использовать TCPClient вместо этого - http://msdn.microsoft.com/en-us/library/system.net.sockets.tcpclient%28v=vs.100%29.aspx. Это оболочка вокруг сокета, и она предоставляет средства проверки состояния соединения.

+0

Является ли подобным шагом, если я обертываю поток в объект StreamReader или StreamWriter? – MathematicalOrchid

+0

@MathematicsOrchid 'StreamReader/StreamWriter' не имеет конструкторов сокетов, поэтому вам придется обернуть их вокруг 'NetworkStream' .Это все равно приведет к описанному выше сценарию. Кроме того,' StreamReader' будет блокироваться до тех пор, пока он не получит данные. Если нет данных (потеря связи или что-то еще), вы можете получить тайм-аут или неопределенный зависание. чтобы использовать/внедрять какой-то вид живого. – Kami

Смежные вопросы