HttpClient.SendAsync зависает при вызове GET-запроса на обновление протокола при работе в ASP.NET Core 2.0, но тот же код успешно работает в .NET 4.7
.
Я создал класс, который имеет всю логику рукопожатия Socket.IO (я реверсировал ее, используя комбинацию инструментов разработчика Chrome и Wireshark). Этот класс успешно завершает рукопожатие при запуске в тестовом приложении с графическим интерфейсом, созданным с использованием .NET 4.7 и GTK # (и может успешно emit()
сообщения), но зависает при запросе protocol upgrade
GET при работе в ASP.NET Core 2.
Обратите внимание, что первые два шага в рукопожатии работают, и они также используют HttpClient
(получить идентификатор сеанса, выберите room
(пространство имен). 3-й вызов не выполняется, только в ASP.NET Core 2 (см. код ниже). Наконец, я использую один и тот же HttpClient
экземпляр для всех вызовов, как в приложении .NET 4.7, так и в ASP.NET Core 2 (я создал класс под названием SocketMediator
и я использую этот точный класс в обоих местах).
URL-адрес, который я использую для запроса на обновление протокола: "http://127.0.0.1:7200/socket.io/?EIO=3&transport=polling&t=1546875159&sid=cD5KhP_8FKPV-ojhAAAA"
sid
предоставляется при первом вызове, а t является сгенерированной меткой времени.
using (var protocolUpgradeRequest = new HttpRequestMessage(HttpMethod.Get, probeTURl))
{
protocolUpgradeRequest.Headers.Add("DNT", "1");
protocolUpgradeRequest.Headers.Add("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3642.0 Safari/537.36");
protocolUpgradeRequest.Headers.Add("Cookie", $"io={sessionId}");
protocolUpgradeRequest.Headers.Add("Connection", "Upgrade");
protocolUpgradeRequest.Headers.Add("Pragma", "no-cache");
protocolUpgradeRequest.Headers.Add("Cache-control", "no-cache");
protocolUpgradeRequest.Headers.Add("Upgrade", "websocket");
protocolUpgradeRequest.Headers.Add("Sec-Websocket-Version", "13");
protocolUpgradeRequest.Headers.Add("Accept-Encoding", "gzip, deflate, br");
protocolUpgradeRequest.Headers.Add("Sec-Websocket-Key", websocketKey);
protocolUpgradeRequest.Headers.Add("Sec-Websocket-Extensions", "permessage-deflate; client_max_window_bits");
try
{
using (var response = await httpClient.SendAsync(protocolUpgradeRequest, CancellationToken.None))
{
if (response.StatusCode != HttpStatusCode.SwitchingProtocols)
{
throw new HttpRequestException(
"Did not correctly receive protocol switch from server!");
}
var accept = response.Headers.GetValues("Sec-Websocket-Accept");
if (!accept.Any())
{
throw new HttpRequestException("Did not get Sec-Websocket-Accept header!");
}
}
}
catch (Exception ex)
{
Console.Error.WriteLine(ex);
throw ex;
}
}
Я прошёл JS-код рукопожатия в Socket.IO и вижу, что он принимает обновление протокола. Он возвращает успешное подтверждение (возвращается заголовок Sec-Websocket-Accept
), но HttpClient.SendAsync сидит там зависшим (я вижу, что этот заголовок также возвращается с Wireshark. Socket.IO в конечном итоге закрывает сокет, потому что это зависание вызывает ping timeout
Когда гнездо закрыто принудительно, именно тогда SendAsync()
«развязывается» и продолжается.
Опять же, ключ к этой проблеме в том, что она прекрасно работает при работе в .NET 4.7 (даже если созданный мной класс живет в проекте стандарта dotnet 2.0!)
Я подтвердил, что я "полностью асинхронен". У меня на самом деле BackgroundService
работает в ASP.NET Core 2, который инициирует это соединение асинхронно.