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

#c# #sockets #tcp #uwp

#c# #сокеты #tcp #uwp

Вопрос:

Итак, я хочу установить TCP-соединение между 2 приложениями UWP, используя streamsockets. Я нашел этот пример на веб-странице Microsoft, и он работает. Проблема в том, что он закрывает свои сокеты после каждого установленного соединения. Я хочу понять, когда он закрывается (не могу найти его в коде, и это меня немного смущает), и я также хочу знать, как я мог бы поддерживать соединение между сервером и клиентом открытым, чтобы мне не приходилось переподключаться каждый раз, когда я хочу что-то отправить.

Пример: https://learn.microsoft.com/en-us/windows/uwp/networking/sockets#build-a-basic-tcp-socket-client-and-server

Я просмотрел документацию StreamSocket в Windows и не могу найти ничего о закрытии сокета.

Я предполагаю, что это происходит где-то в этом методе. Это серверная часть программы, которая выполняется при получении соединения.

 private async void StreamSocketListener_ConnectionReceived
(Windows.Networking.Sockets.StreamSocketListener sender,   
Windows.Networking.Sockets.
StreamSocketListenerConnectionReceivedEventArgs args)
{
    string request;
    using (var streamReader = new   
    StreamReader(args.Socket.InputStream.AsStreamForRead()))
    {
        request = await streamReader.ReadLineAsync();
    }

    await this.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>    this.serverListBox.Items.Add(string.Format("server received the request: "{0}"", request)));

// Echo the request back as the response.
using (Stream outputStream = args.Socket.OutputStream.AsStreamForWrite())
{
    using (var streamWriter = new StreamWriter(outputStream))
    {
        await streamWriter.WriteLineAsync(request);
        await streamWriter.FlushAsync();
    }
}

string request;
using (var streamReader = new
StreamReader(args.Socket.InputStream.AsStreamForRead()))
{
    request = await streamReader.ReadLineAsync();
}

await this.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
this.serverListBox.Items.Add(string.Format("server received the 
request: "{0}"", request)));

// Echo the request back as the response.
using (Stream outputStream =   
args.Socket.OutputStream.AsStreamForWrite())
{
    using (var streamWriter = new StreamWriter(outputStream))
    {
        await streamWriter.WriteLineAsync(request);
        await streamWriter.FlushAsync();
    }
}

await this.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => 
this.serverListBox.Items.Add(string.Format("server sent back the 
response: "{0}"", request)));

sender.Dispose();

await this.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => 
this.serverListBox.Items.Add("server closed its socket"));
  

}

Любая помощь будет очень признательна!

Ответ №1:

Я нашел этот пример на веб-странице Microsoft, и он работает.

К сожалению, все примеры сокетов Microsoft не являются хорошими примерами того, как писать приложения для сокетов. Это всего лишь примеры того, как вызывать эти API. Создание приложения для сокетов производственного качества нетривиально, и примеры сокетов Microsoft введут вас в заблуждение.

Например, этот сервер сокетов:

  • Используется Dispatcher для обновления пользовательского интерфейса, а не для современных решений, таких как IProgress<T> .
  • Считывает данные из своего входного потока до тех пор, пока не будет найдена новая строка. Это проблема, потому что:
    • Для получения запроса нет времени ожидания.
    • Входной буфер растет без ограничений.
    • Сценарий полуоткрытия не обрабатывается.

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

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

Но чтобы ответить на ваш фактический вопрос:

Я хочу понять, когда он закрывается

С сокетами фактически существует два потока: входной поток и выходной поток. Оба закрываются при sender.Dispose(); вызове. Однако входной поток также закрывается при StreamReader удалении, а выходной поток также закрывается при StreamWriter удалении. Это происходит в конце их using блоков. Вот почему вы не можете прочитать второе сообщение после закрытия StreamReader .

Комментарии:

1. Спасибо за ответ! Теперь я немного лучше понимаю код.

2. Я собираюсь изучить SignalR, потому что мне действительно нравится изучать сокеты