Як правильно і повністю закрити / скинути підключення TcpClient?


78

Який правильний спосіб закрити або скинути підключення TcpClient? У нас є програмне забезпечення, яке взаємодіє з апаратним забезпеченням, але іноді щось йде не так, і ми більше не маємо з ним спілкуватися, поки не перезапустимо програмне забезпечення.

Я спробував примусити TcpClient.Close () і навіть встановити для нього значення null, але це не працює. Працює лише повний перезапуск програмного забезпечення.

Пропозиції?


Я не можу використовувати ключове слово using, оскільки TpcClient визначається лише в одному місці, але використовується у всій бібліотеці. (І в даний момент часу існує лише одне з’єднання)

Це бібліотека, яка займається спілкуванням. Само програмне забезпечення може викликати метод ResetConnection () класу Controller (який представляє апаратне забезпечення).

На даний момент це виглядає так

if (tcpClient != null)
{
    tcpClient.Close();
    tcpClient = null;
}

Тепер із прочитаного тут слід використовувати tcpClient.Dispose () замість "= null"

Я спробую це і подивлюсь, чи це щось змінить.

Відповіді:


93

Вам потрібно закрити потік перед тим, як закрити з'єднання:

tcpClient.GetStream().Close();
tcpClient.Close();

Закриття клієнта не закриває потік.


11
Якщо закриття TcpClient не перериває з'єднання, що, біса, воно закриває? Чи є причина закрити TcpClient?
Кверті

Насправді я не знаю, вам слід перевірити за допомогою Reflector, але він, ймовірно, закриває інші використовувані об'єкти, а не потік (можливо, потік можна розподіляти між різними об'єктами, і тому його не закривають автоматично, просто здогадуючись).
Ігнасіо Солер Гарсія

36
Зверніть увагу, що помилка ( support.microsoft.com/kb/821625 ), яка вимагала закриття потоку, існує лише у .NET 1.1 та раніше. Я протестував зразок коду в .NET 4.5, і він чудово працює, не викликаючи закриття в потоці.
Anssssss 02

4
Я все ще отримую непостійні проблеми в .NET 4.6.1, якщо використовую tcpClient.Close () замість tcpClient.Client.Close () або tcpClient.GetStream (). Закрити (). Днями я ледь не зійшов з глузду, поки не знайшов цього.
Mike Marynowski

За допомогою .NET Standard System.Net.Sockets.TcpClientзателефонуйте Dispose(). Close()Методу немає .
NathanAldenSr 04

27

Враховуючи те, що прийнята відповідь застаріла, і я не бачу нічого в інших відповідях щодо цього, я створюю нову. У .Net 2 і раніше вам довелося вручну закрити потік перед тим, як закрити з'єднання. Ця помилка виправлена ​​у всіх пізніших версіях TcpClientC #, і, як зазначено в документі методу Close, виклик методу Closeзакриває як з'єднання, так і потік

EDIT відповідно до Microsoft Docs

Метод Закрити позначає екземпляр як утилізований і вимагає, щоб пов'язаний Socket закрив TCP-з'єднання. На основі властивості LingerState TCP-з'єднання може залишатися відкритим деякий час після виклику методу Close, коли дані залишаються надсилати. Повідомлення не надається, коли базове з'єднання завершилось.

Виклик цього методу врешті-решт призведе до закриття пов'язаного сокета, а також закриє пов'язаний NetworkStream, який використовується для надсилання та отримання даних, якщо такий був створений.


@Twometer, на яку версію .Net ви націлюєтесь?
Джон Деметріу

це .NET 4.6.1
Твометр

Я телефоную до TcpClient.Close (), tcpClient.Client.Close (), .Disconnect (), що завгодно, і він не закривається. Тільки коли я вбиваю процес, сервер каже "З’єднання закрито"
Твометр

Приклад у статті, яку ви зв’язали, все ще викликає networkStream.Close();перед викликом tcpClient.Close();, тому або приклад застарів, або вам все одно доведеться закрити базовий потік вручну.
Deantwo

@Deantwo Оновив свою відповідь. Він позначає його як готовий до утилізації. Немає жодних гарантій щодо того, коли він буде утилізований.
Джон Деметріу

22

Використовуйте слово: using. Хороша звичка програмувати.

using (TcpClient tcpClient = new TcpClient())
{
     //operations
     tcpClient.Close();
}

42
Не застосовується у всіх моделях. Це чудово, якщо ви тимчасово використовуєте підключення в межах одного блоку коду, але якщо у вас клієнтська реалізація, яка хоче підтримувати зв’язок як член, цей метод не вдасться. Ви також виявите, що, згідно з документацією класу, виклик Dispose () на TcpClient не закриває базовий сокет. Використання лише утилізує.
Гусдор,

1
Це не буде працювати в асинхронних та неблокуючих сокетах, і насправді цей код не можна використовувати, якщо ви хочете десь зберігати з'єднання та керувати ним.
Кас

7
@Gusdor Дивлячись на відображене джерело, здається, Dispose (true) викликає Close () на клієнті. (.NET 4.0)
dtroy

Мабуть, це нове доповнення. Я розмістив посилання на .net 3.5. Добре помічений!
Gusdor

1
@Dermot, @SteveJobs добре, тепер, коли майбутнє тут із asyncключовим словом, воно справді ;-). Це навіть було б сумісним із BeginConnect()та EndConnect(), за умови, що ви телефонуєте End…всередині usingблоку ...
binki

8

Незважаючи на наявність усіх відповідних usingвисловлювань, дзвінки Close, використання деякої експоненціальної логіки відключення та відтворення, TcpClientя все ще бачив проблеми, коли програма не може відновити з'єднання TCP без перезапуску програми. Це продовжує невдало з System.IO.IOException: Unable to read data from the transport connection: An existing connection was forcibly closed by the remote host.

Але є варіант , LingerStateна TcpClientякий з'являється він , можливо, вирішити це питання (може не знати , в протягом декількох місяців , як моя власна установка обладнання не може тільки про те , що часто!). Див. MSDN .

// This discards any pending data and Winsock resets the connection.
LingerOption lingerOption = new LingerOption(true, 0);

using (var tcpClient = new TcpClient 
       {SendTimeout = 2000, ReceiveTimeout = 2000, LingerState = lingerOption })
   ...

Привіт! У мене точно така ж проблема, незважаючи на наявність усього відповідного коду, іноді цей виняток з’являється випадково, LingerOption вирішив вашу проблему? Я серйозно думаю перенести весь свій код на UDP лише для того, щоб позбутися цієї непостійної проблеми
Рафаель,

@rafael Я думаю, що це вирішило мою проблему: я взагалі не бачив цієї проблеми з моменту її публікації.
Ян Мерсер,

6

Замикає з'єднання сокета і дозволяє повторно використовувати сокет:

tcpClient.Client.Disconnect(false);

1
Мене бентежить, навіщо мені це робити, якщо я хочу пізніше знову відкрити з’єднання. Просто закриття потоків і закриття TcpClientтоді викидає виняток на пізніший .Connect.
пеп

6
Ви не маєте на увазі Відключити (істинно)?
WDUK

6

Правильний спосіб закрити сокет, щоб ви могли знову відкрити, це:

tcpClient.Client.Disconnect(true);

Логічний параметр вказує, чи потрібно повторно використовувати сокет:

Відключити використання методу


5

За винятком деяких внутрішніх журналів, Закрити == Утилізувати.

Утилізуйте виклики tcpClient.Client.Shutdown (SocketShutdown.Both), але він виявляє помилки. Можливо, якщо ви зателефонуєте безпосередньо, ви зможете отримати корисну інформацію про винятки.


1

Ви пробували викликати TcpClient.Dispose () ?

І ви впевнені, що у вас є TcpClient.Close () та TcpClient.Dispose () - ed ВСІ з’єднання?


Існує лише один зв’язок, і я закрив його, але не розпорядився ним ... чи це має значення? (Припускаю, що так, просто запитую :-)
TimothyP

TcpClient.Dispose захищений і не може бути викликаний безпосередньо.
theycallmemorty

@theycallmemorty doc говорить інакше. msdn.microsoft.com/en-us/library/vstudio/…
chakrit

2
Це явна реалізація інтерфейсу. Отже, його не видно, поки він не вводить TcpClient, доки його не буде переведено на ID Disposable, тобто((IDisposable)client).Dispose()
Джозеф Леннокс,
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.