C# NetworkStream. Считайте причуду

Кто-либо может указать на дефект в этом коде? Я получаю некоторый HTML с TcpClient. NetworkStream. Читайте (), никогда, кажется, не заканчивается когда говорящий с сервером IIS. Если я иду, используют прокси Скрипача вместо этого, он хорошо работает, но при разговоре непосредственно с целевым сервером .read () не выйдет цикл до исключений соединения с ошибкой как "удаленный сервер закрыл соединение".

internal TcpClient Client { get; set; }

/// bunch of other code here...

try
{

NetworkStream ns = Client.GetStream();
StreamWriter sw = new StreamWriter(ns);

sw.Write(request);
sw.Flush();

byte[] buffer = new byte[1024];

int read=0;

try
{
    while ((read = ns.Read(buffer, 0, buffer.Length)) > 0)
    {
        response.AppendFormat("{0}", Encoding.ASCII.GetString(buffer, 0, read));
    }
}
catch //(SocketException se)
{

}
finally
{
    Close();
}

Обновление

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

Заключение, Как был сказан здесь, лучше использовать в своих интересах предложения протокола (в случае HTTP, заголовка Довольной Длины) для определения, когда транзакция является завершенной. Однако я нашел, что не всем страницам установили довольную длину. Так, я теперь использую гибридное решение:

  1. Для ВСЕХ транзакций, набор запрос Connection заголовок для "закрытий", так, чтобы сервер был отговорен сохранить сокет открытым. Это улучшает возможности, что сервер закроет соединение, когда это будет посредством ответа на Ваш запрос.

  2. Если Content-Length установлен, используйте его для определения, когда запрос завершен.

  3. Еще, установите свойство NetworkStream's RequestTimeout на большое, но разумное, значение как 1 секунда. Затем цикл на NetworkStream.Read() до любого a) тайм-аут происходит, или b) Вы читаете меньше байтов, чем Вы попросили.

Благодаря всем для их превосходных и подробных ответов.

8
задан 3Dave 4 May 2010 в 15:30
поделиться

5 ответов

Не уверен, полезно это или нет, но с HTTP 1.1 основное соединение с сервером может не быть закрыто, так что, возможно, поток тоже не будет закрыт? Идея в том, что вы можете повторно использовать соединение для отправки нового запроса. Я думаю, что вы должны использовать длину содержимого. Альтернативно используйте классы WebClient или WebRequest.

2
ответ дан 5 December 2019 в 10:41
поделиться

Читайте ответ, пока не дойдете до двойного CRLF. Теперь у вас есть заголовки ответа. Проанализируйте заголовки, чтобы прочитать заголовок Content-Length, который будет счетчиком байтов, оставшихся в ответе.

Вот регулярное выражение, которое может перехватывать заголовок Content-Length.

Обновленное регулярное выражение Дэвида

Content-Length: (?<1>\d+)\r\n

Content-Length

Примечание

Если сервер неправильно устанавливает этот заголовок, я бы не стал его использовать.

3
ответ дан 5 December 2019 в 10:41
поделиться

Два предложения ...

  1. Пробовали ли вы использовать свойство DataAvailable NetworkStream? Он должен вернуть истину, если есть данные для чтения из потока.

    while (ns.DataAvailable)
    {
     //Do stuff here
    }
  1. Другой вариант - изменить ReadTimeOut на низкое значение, чтобы не блокироваться на долгое время. Это можно сделать так:

    ns.ReadTimeOut=100;
0
ответ дан 5 December 2019 в 10:41
поделиться

Попробуйте добавить.. «/» в начале URI. Например:

../images/outbound-blue.png

.. «/» в начале URI подсказывает браузеру подниматься на один уровень в родительский каталог, а затем искать в каталоге images . В настоящее время набор поиск подкаталога с именем images в каталоге, содержащем таблицы стилей.

-121--2762242-

У меня есть приложение, которое вызывает существующую службу SOAP, написанную на J2EE языке и размещенную в WebSphere.

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

В обоих случаях Visual Studio создает класс прокси и соответствующие записи конфигурации для службы.

В консольном приложении Service Reference я получаю гораздо больше параметров конфигурации, которые я не вижу в приложении Web Service app. В частности, Я могу установить максимальный размер сообщения и т.д.

для правильной работы консольного приложения Service Reference, Мне пришлось увеличить размер сообщения по умолчанию, чтобы вернуть все данные, отправленные в одном из вызовов метода.

Вот как выглядит конфигурация в приложении Service Reference:

<configuration>
    <system.serviceModel>
        <bindings>
            <basicHttpBinding>
                <binding name="ClaimSoapBinding" closeTimeout="00:01:00" openTimeout="00:01:00"
                    receiveTimeout="00:10:00" sendTimeout="00:01:00" allowCookies="false"
                    bypassProxyOnLocal="false" hostNameComparisonMode="StrongWildcard"
                    maxBufferSize="65536000" maxBufferPoolSize="524288" maxReceivedMessageSize="65536000"
                    messageEncoding="Text" textEncoding="utf-8" transferMode="Buffered"
                    useDefaultWebProxy="true">
                    <readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384"
                        maxBytesPerRead="4096" maxNameTableCharCount="16384" />
                    <security mode="None">
                        <transport clientCredentialType="None" proxyCredentialType="None"
                            realm="" />
                        <message clientCredentialType="UserName" algorithmSuite="Default" />
                    </security>
                </binding>
            </basicHttpBinding>
        </bindings>
        <client>
            <endpoint address="http://urlgoeshere/ClaimService"
                binding="basicHttpBinding" bindingConfiguration="ClaimSoapBinding"
                contract="ClaimService.Claim" name="ClaimService" />
        </client>
    </system.serviceModel>
</configuration>

В моем старом школьном приложении для консоли Web Service мне вообще не пришлось изменять конфигурацию, чтобы вернуть гигантский набор данных. Вот как выглядит его конфигурация:

<configuration>
    <configSections>
        <sectionGroup name="applicationSettings" type="System.Configuration.ApplicationSettingsGroup, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" >
            <section name="ServiceTesterOldSchool.Properties.Settings" type="System.Configuration.ClientSettingsSection, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />
        </sectionGroup>
    </configSections>
    <applicationSettings>
        <ServiceTesterOldSchool.Properties.Settings>
            <setting name="ServiceTesterOldSchool_ClaimService_ClaimService"
                serializeAs="String">
                <value>http://urlgoeshere/ClaimService</value>
            </setting>
        </ServiceTesterOldSchool.Properties.Settings>
    </applicationSettings>
</configuration>

Это намного проще, но не хватает многих опций, которые мы получаем с помощью ссылок на услуги.

Фактический код, вызывающий услугу, практически идентичен в обоих случаях.

Однако, чтобы ответить на ваш вопрос, я думаю, что важно придерживаться нынешнего способа действий. Microsoft делает это ясно, заставляя вас пройти через несколько уровней диалогов, прежде чем вы даже можете добавить старый школьный веб-справочник (по крайней мере, в VS2008).

Я думаю, что способ WCF более гибкий, и конфигурация намного более описательна относительно того, что происходит.

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

-121--2321898-

Возможно, я не прав, но похоже, что ваш вызов Write записывается (под колпаком) в поток ns (через StreamWriter ). Позже выполняется чтение из того же потока ( ns ). Я не совсем понимаю, почему вы делаете это?

В любом случае, вам может понадобиться использовать Seek на потоке, чтобы переместиться в место, где вы хотите начать чтение. Я бы предположил, что он стремится к концу после написания. Но, как я уже сказал, я не уверен, что это полезный ответ!

1
ответ дан 5 December 2019 в 10:41
поделиться

В отличие от того, что подразумевает документация на NetworkStream.Read, поток, полученный от TcpClient делает не, а просто возвращает 0 для количества прочитанных байтов при отсутствии данных - он блокирует.

Если вы посмотрите документацию для TcpClient, то увидите эту строку:

Класс TcpClient предоставляет простые методы подключения, отправки и получения потоковых данных по сети в режиме синхронной блокировки .

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

Моим первым предложением было бы устранить StreamWriter как возможную причину (т.е. нюансы буферизации/кодирования), и записать непосредственно в поток, используя метод NetworkStream.Write. Если это работает, убедитесь, что используете правильные параметры для StreamWriter.

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

NetworkStream netStream = client.GetStream();
int read = 0;
byte[] buffer = new byte[1024];
StringBuilder response = new StringBuilder();
do
{
    read = netStream.Read(buffer, 0, buffer.Length);
    response.Append(Encoding.ASCII.GetString(buffer, 0, read));
}
while (netStream.DataAvailable);
10
ответ дан 5 December 2019 в 10:41
поделиться
Другие вопросы по тегам:

Похожие вопросы: