C # HttpClient Существующее соединение было принудительно закрыто удаленным узлом

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

Я протестировал объект json, который отправляю на PostMan и cURL, и оба работают, а также заголовок аутентификации, поэтому я думаю, что это не проблема. Вот конструктор моего класса:

public AlternativePaymentsCli(string apiSecretKey)
{
    this._apiSecretKey = apiSecretKey;

    _httpClient = new HttpClient();
    _httpClient.DefaultRequestHeaders.Accept
        .Add(new MediaTypeWithQualityHeaderValue("application/json"));

    var authInfo = _apiSecretKey;
    authInfo = Convert.ToBase64String(System.Text.ASCIIEncoding.ASCII.GetBytes(string.Format("{0}:", _apiSecretKey)));

    // The two line below because I saw in an answer on stackoverflow.
    _httpClient.DefaultRequestHeaders.Add("Connection", "Keep-Alive"); 
    _httpClient.DefaultRequestHeaders.Add("Keep-Alive", "3600");

    _httpClient.DefaultRequestHeaders.UserAgent.ParseAdd("Anything.com custom client v1.0");
    _httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", authInfo);

}

И метод, по которому я публикую данные:

public string CreateHostedPageTransaction(HostedPageRequest req) 
{
    var settings = new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore };

    // I send this same json content on PostMan and it works. The json is not the problem
    var content = new StringContent(JsonConvert.SerializeObject(req, settings), Encoding.UTF8, "application/json");
    var response = _httpClient.PostAsync(this._baseUrl + "/transactions/hosted", content).Result;
    var responseText = response.Content.ReadAsStringAsync().GetAwaiter().GetResult();

    if (response.IsSuccessStatusCode)
        return responseText;

    return "";
}

Затем я получаю эту ошибку: An existing connection was forcibly closed by the remote host в строке PostAsync. Это подробности ошибки:

[SocketException (0x2746): An existing connection was forcibly closed by the remote host]
   System.Net.Sockets.Socket.EndReceive(IAsyncResult asyncResult) +8192811
   System.Net.Sockets.NetworkStream.EndRead(IAsyncResult asyncResult) +47

[IOException: Unable to read data from the transport connection: An existing connection was forcibly closed by the remote host.]
   System.Net.TlsStream.EndWrite(IAsyncResult asyncResult) +294
   System.Net.ConnectStream.WriteHeadersCallback(IAsyncResult ar) +149

[WebException: The underlying connection was closed: An unexpected error occurred on a send.]
   System.Net.HttpWebRequest.EndGetRequestStream(IAsyncResult asyncResult, TransportContext& context) +324
   System.Net.Http.HttpClientHandler.GetRequestStreamCallback(IAsyncResult ar) +137

[HttpRequestException: An error occurred while sending the request.]

Я использую C # 4.5, Asp.Net MVC. Я читал ответы на ту же ошибку, и пока никто из них не решил мою проблему. Что мне не хватает в этом коде?

Спасибо за любую помощь


person André Luiz    schedule 14.09.2017    source источник
comment
Что, если что-нибудь вы видите в ответе в Fiddler?   -  person DiskJunky    schedule 14.09.2017
comment
Просто хороший совет: использование var во всем заставит ваших коллег ненавидеть вас. Используйте var только тогда, когда тип очевиден (т.е. var date = new DateTime();) его очень явно DateTime. Однако var response = _httpClient.PostAsync(this._baseUrl + "/transactions/hosted", content).Result; неясно, потому что .Result - это свойство, а не подразумевается, какой это тип.   -  person maccettura    schedule 14.09.2017
comment
@maccettura, напротив, использование var делает код намного чище. Нет никакой двусмысленности в том, что это за тип, если вы не пишете действительно длинные методы, и в этом случае вам действительно нужно разбивать их на части. Коллеги возненавидят вас, если вы напишете такие длинные методы, что они не смогут понять, что это за типы.   -  person Panagiotis Kanavos    schedule 14.09.2017
comment
@PanagiotisKanavos var делает код чище только тогда, когда тип подразумевается, использование var везде (даже в тех случаях, когда тип не подразумевается) просто плохое программирование.   -  person maccettura    schedule 14.09.2017
comment
@maccettura, что касается .Result, коллеги будут ненавидеть любого, кто блокирует асинхронный вызов с помощью .Wait() или .Result. Тип возврата все равно известен. Это HttpResponseMessage. Увидеть имя вам не поможет, так как вам все равно придется использовать intellisense, чтобы найти, какие методы использовать.   -  person Panagiotis Kanavos    schedule 14.09.2017
comment
@maccettura, что, например, является причиной того, почему функциональные языки используют вывод типа вместо явного указания типа. Возможно, вы не знакомы с выводом типа. Это не делает его плохим программированием. С другой стороны, звонок в .Result должен вызвать раздражение у кого угодно.   -  person Panagiotis Kanavos    schedule 14.09.2017
comment
@PanagiotisKanavos это правда известно? Игнорируя предыдущий опыт работы с какой-либо библиотекой .NET, как мне легче всего узнать тип? (Его объявляют в тех случаях, когда это не подразумевается избыточно.   -  person maccettura    schedule 14.09.2017
comment
@PanagiotisKanavos и да, я согласен с тем, что OP не должен блокировать асинхронные вызовы (если он еще не знал, код, который я опубликовал, взят прямо из вопроса OP, а не моего собственного).   -  person maccettura    schedule 14.09.2017
comment
Полностью выключено. Я не знаю, какую IDe вы используете, я использую Visual Studio, и когда вы наводите курсор на переменную, она сообщает вам тип. Спасибо   -  person André Luiz    schedule 14.09.2017
comment
Возможный дубликат Существующее соединение было принудительно закрыто удаленным хост   -  person Liam    schedule 15.02.2019


Ответы (5)


Я не вижу в вашем примере кода, где вы устанавливаете значение _baseUrl, но я предполагаю, что это где-то делается. Я также предполагаю, что, поскольку это связано с платежами, URL-адрес - HTTPS. Если удаленный хост отключил TLS 1.0, и ваше соединение входит как TLS 1.0, это может вызвать такое поведение. Я знаю, что в C # 4.6 по умолчанию включена поддержка TLS 1.0 / 1.1 / 1.2, но я думаю, что C # 4.6 по-прежнему использует по умолчанию только SSL3 / TLS 1.0, хотя TLS 1.1 и 1.2 поддерживаются. Если это является причиной проблемы, вы можете вручную добавить TLS 1.1 и 1.2 к включенным значениям, используя следующий код.

System.Net.ServicePointManager.SecurityProtocol |= SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12;
person Paul Pearce    schedule 14.09.2017
comment
Большое спасибо, сэр, вы решили мою проблему. Локально я использую поддельный HTTPS, Chrome предупреждает меня, что сайт небезопасен и т. Д. Это должно быть так. - person André Luiz; 14.09.2017
comment
Вам не нужно изменять код, чтобы включить это для приложений, которые в настоящее время работают в продукте: docs.microsoft.com/en-us/officeonlineserver/ - person Sean Anderson; 25.01.2018
comment
да, это необходимо только в домене приложения (с использованием рабочей роли) или мне нужно установить это для запроса HttpClient? - person Nathan Tregillus; 17.06.2018
comment
@Ruchira и NathanTregillus Вы можете установить его для каждого запроса (добавляя код перед отправкой запроса), или вы можете установить его один раз для всего приложения, установив его Application_Start () в Global.asax.cs - person Paul Pearce; 18.06.2018
comment
Спасибо @PaulPearce. В итоге я добавил это в Global.asax. Используется с использованием System.Net; добавить верхнюю часть для поддержки ServicePointManager и SecurityProtocolType. - person Ruchira; 20.06.2018
comment
Спасибо. Моя проблема заключалась в Server 2012. Я переместил старый API без SSL в функции Azure с поддержкой SSL, и это начало происходить. Ваше решение исправило это. Спасибо большое! - person Piotr Kula; 05.12.2018
comment
Бился 3 дня с этим исключением, и это исправило! - person Rush Frisby; 24.06.2019
comment
Я добавил эту строку кода. Но у меня это не работает. Я использую .NET Framework 4.6.1. Я могу сделать тот же вызов api в Postman или в сценарии Powershell без этой ошибки. Но когда я вызываю api с помощью HttpClient, я получаю ту же ошибку. - person user3293338; 08.08.2019
comment
@ user3293338 - вы добавляли код в вызывающий клиент или в принимающую службу? Его следует добавить к вызывающему клиенту, чтобы он мог подключаться с использованием более безопасных версий TLS. Если бы вы могли включить образец своего кода, это было бы полезно для определения проблемы. - person Paul Pearce; 08.08.2019
comment
Я добавил код перед вызовом API. Код выглядит следующим образом: var stream = new FileStream (filePath, FileMode.Open); var content = new StreamContent (поток); client.DefaultRequestHeaders.Add (X-Atlassian-Token, nocheck); System.Net.ServicePointManager.SecurityProtocol | = SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12; var response = client.PostAsync (apiUrl, content) .Result; - person user3293338; 08.08.2019
comment
Это очень хороший ответ, также стоит обновить использование Ssl3: ... | SecurityProtocolType.Ssl3 - person Sxntk; 19.03.2020
comment
Спасибо, ваш ответ решит мою проблему. Это происходит, когда моя служба, использующая Azure, и клиент не включают ее по умолчанию - person Pham Lai; 23.06.2020

Если вы используете .Net 4.0, тогда SecurityProtocolType.Tls11 и SecurityProtocolType.Tls2 не определены, поэтому вместо этого вы можете использовать жестко заданное значение ниже.

ServicePointManager.SecurityProtocol = (SecurityProtocolType)3072;

person Tim Kempster    schedule 08.01.2018

Можно решить проблему без каких-либо изменений в коде, как описано в отличный ответ на аналогичный вопрос:

Перенаправьте веб-проект на .Net 4.6+, затем обновите web.config следующим образом:

<system.web>
  <compilation targetFramework="4.6" /> 
  <httpRuntime targetFramework="4.6" /> 
</system.web>
person d_f    schedule 03.09.2019

Это сработало для меня, первая строка обеспечивает протоколы ssl3 и TLS1.2, а вторая строка игнорирует любые потенциальные ошибки сертификата (игнорируйте и продолжайте - как сертификаты с истекшим сроком действия):

ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3 | SecurityProtocolType.Tls12;
ServicePointManager.ServerCertificateValidationCallback +=  (sender, certificate, chain, sslPolicyErrors) => true;
person sao    schedule 24.02.2020
comment
Здравствуйте и добро пожаловать в сообщество! Попробуйте использовать инструменты форматирования в текстовом редакторе, чтобы ваш ответ выглядел лучше и легче читался. Программа форматирования кода особенно полезна и творит чудеса. - person Fabio Lolli; 24.02.2020

Для меня эта ошибка была вызвана тем, что я забыл настроить прокси-сервер. Используя следующий код для создания HttpClient, я решил эту проблему для моего приложения .NET 4.7.2:

var httpClientHandler = new HttpClientHandler { Proxy = WebRequest.GetSystemWebProxy() };
var httpClient = new HttpClient(httpClientHandler);

Надеюсь, это кому-то поможет.

person Martijn Vink    schedule 09.04.2020