На главную Наши проекты:
Журнал   ·   Discuz!ML   ·   Wiki   ·   DRKB   ·   Помощь проекту
ПРАВИЛА FAQ Помощь Участники Календарь Избранное RSS
msm.ru
  
> Асинхронные сокеты: не получает данных , зависает на чтении из сокета, хотя данные приходят
    Здравствуйте!

    Пытаюсь использовать пример из MSDN (ничем от оригинала не отличается, кроме того что шлю свои данные).
    оригинал в MSDN

    ExpandedWrap disabled
      using System;
      using System.Text;
      using System.Net;
      using System.Net.Sockets;
      using System.Threading;
       
      namespace AsyncSocket
      {
          public class StateObject
          {
              // Client socket.
              public Socket workSocket = null;
              // Size of receive buffer.
              public const int BufferSize = 256;
              // Receive buffer.
              public byte[] buffer = new byte[BufferSize];
              // Received data string.
              public StringBuilder sb = new StringBuilder();
          }
       
          public class AsynchronousClient
          {
              // The port number for the remote device.
              private const int port = 1344;
       
              // ManualResetEvent instances signal completion.
              private static ManualResetEvent connectDone =
                  new ManualResetEvent(false);
              private static ManualResetEvent sendDone =
                  new ManualResetEvent(false);
              private static ManualResetEvent receiveDone =
                  new ManualResetEvent(false);
       
              // The response from the remote device.
              private static String response = String.Empty;
       
              public static void StartClient()
              {
                  // Connect to a remote device.
                  try
                  {
                      // Establish the remote endpoint for the socket.
                      // The name of the
                      // remote device is "host.contoso.com".
                      IPHostEntry ipHostInfo = Dns.GetHostEntry("10.60.3.203");
                      IPAddress ipAddress = ipHostInfo.AddressList[0];
                      IPEndPoint remoteEP = new IPEndPoint(ipAddress, port);
       
                      // Create a TCP/IP socket.
                      Socket client = new Socket(AddressFamily.InterNetwork,
                          SocketType.Stream, ProtocolType.Tcp);
       
                      // Connect to the remote endpoint.
                      client.BeginConnect(remoteEP,
                          new AsyncCallback(ConnectCallback), client);
                      connectDone.WaitOne();
       
                      string req = "REQMOD icap://10.60.3.203:1344/reqmod ICAP/1.0\r\nHost: 10.60.3.203:1344\r\nDate: Fri, 24 Jun 2011 08:04:45 GMT\r\nEncapsulated: req-hdr=0, null-body=333\r\nAllow: 204\r\nX-Client-IP: 10.60.3.200\r\n\r\nGET http://ya.ru/ HTTP/1.1\r\nHost: ya.ru\r\nUser-Agent: Mozilla/5.0 (Windows NT 5.1; rv:2.0.1) Gecko/20100101 Firefox/4.0.1\r\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\nAccept-Language: ru-ru,ru;q=0.8,en-us;q=0.5,en;q=0.3\r\nAccept-Encoding: gzip, deflate\r\nAccept-Charset: windows-1251,utf-8;q=0.7,*;q=0.7\r\n\r\n";
       
                      // Send test data to the remote device.
                      Send(client, req);              
                      sendDone.WaitOne();
       
                      // Receive the response from the remote device.
                      Receive(client);
                      receiveDone.WaitOne();
       
                      // Write the response to the console.
                      Console.WriteLine("Response received : {0}", response);
       
                      // Release the socket.
                      client.Shutdown(SocketShutdown.Both);
                      client.Close();
       
                  }
                  catch (Exception e)
                  {
                      Console.WriteLine(e.ToString());
                  }
              }
       
              private static void ConnectCallback(IAsyncResult ar)
              {
                  try
                  {
                      // Retrieve the socket from the state object.
                      Socket client = (Socket)ar.AsyncState;
       
                      // Complete the connection.
                      client.EndConnect(ar);
       
                      Console.WriteLine("Socket connected to {0}",
                          client.RemoteEndPoint.ToString());
       
                      // Signal that the connection has been made.
                      connectDone.Set();
                  }
                  catch (Exception e)
                  {
                      Console.WriteLine(e.ToString());
                  }
              }
       
              private static void Receive(Socket client)
              {
                  try
                  {
                      // Create the state object.
                      StateObject state = new StateObject();
                      state.workSocket = client;
       
                      // Begin receiving the data from the remote device.
                      client.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
                          new AsyncCallback(ReceiveCallback), state);
                  }
                  catch (Exception e)
                  {
                      Console.WriteLine(e.ToString());
                  }
              }
       
              private static void ReceiveCallback(IAsyncResult ar)
              {
                  try
                  {
                      // Retrieve the state object and the client socket
                      // from the asynchronous state object.
                      StateObject state = (StateObject)ar.AsyncState;
                      Socket client = state.workSocket;
       
                      // Read data from the remote device.
                      int bytesRead = client.EndReceive(ar);
       
                      if (bytesRead > 0)
                      {
                          // There might be more data, so store the data received so far.
                          state.sb.Append(Encoding.ASCII.GetString(state.buffer, 0, bytesRead));
       
                          // Get the rest of the data.
                          client.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
                              new AsyncCallback(ReceiveCallback), state);
                      }
                      else
                      {
                          // All the data has arrived; put it in response.
                          if (state.sb.Length > 1)
                          {
                              response = state.sb.ToString();
                          }
                          // Signal that all bytes have been received.
                          receiveDone.Set();
                      }
                  }
                  catch (Exception e)
                  {
                      Console.WriteLine(e.ToString());
                  }
              }
       
              private static void Send(Socket client, String data)
              {
                  // Convert the string data to byte data using ASCII encoding.
                  byte[] byteData = Encoding.ASCII.GetBytes(data);
       
                  // Begin sending the data to the remote device.
                  client.BeginSend(byteData, 0, byteData.Length, 0,
                      new AsyncCallback(SendCallback), client);
              }
       
              private static void SendCallback(IAsyncResult ar)
              {
                  try
                  {
                      // Retrieve the socket from the state object.
                      Socket client = (Socket)ar.AsyncState;
       
                      // Complete sending the data to the remote device.
                      int bytesSent = client.EndSend(ar);
                      Console.WriteLine("Sent {0} bytes to server.", bytesSent);
       
                      // Signal that all bytes have been sent.
                      sendDone.Set();
                  }
                  catch (Exception e)
                  {
                      Console.WriteLine(e.ToString());
                  }
              }
          }
      }


    Зависает на строчке receiveDone.WaitOne();, а вот почему я не очень понимаю. Я через WireShark вижу что ответ пришел.

    Где вообще можно доступно почитать про сетевое программирование. Очень нужно эту тему освоить. :)

    Спасибо.
      Reset() перед WaitOne() не повредит делать. К тому же практика показывает, что потоку необходимо некоторое время, чтобы вернуться в строй, так сказать, так что после Set() рекомендуется делать Thread.Sleep().
        Spawn.NET поставил Reset() перед каждым WaitOne(). Также после каждого Set() усыпил поток. Не помогло. :( Данные приходят, но приложение их не ловит. :(
          Duncan MacLeod, убедитесь, что данные не приходят до того, как основной поток встает на receiveDone.WaitOne();. Так же поставьте бряку на receiveDone.Set(); и посмотрите в какой момент она отработает и отрабатывает ли она вообще.

          И что значит не получает данных? Висит на receiveDone.WaitOne()? или фактически данных нет?

          PS. Если не хотите после каждого WaitOne() сбрасывать хендлеры, то используйте AutoResetEvent.
          Сообщение отредактировано: maxim84_ -
            Да, с данными - это я слажал. Они действительно приходят, я был не прав.

            Цитата

            Call: ConnectCallback
            Socket connected to 10.60.3.203:1344
            connectDone.Set()
            Call: Send
            Call: SendCallback
            Sent 522 bytes to server.
            sendDone.Set()
            Call: Receive
            Call: ReceiveCallback
            bytesRead: 166
            Call: ReceiveCallback
            bytesRead: 256
            Call: ReceiveCallback
            bytesRead: 118

            Поставил везде отладочные сообщения. Call: <что-то> при входе в процедуру, <Event>.Set() - соответственно перед каждым вызовом .Set у события. bytesRead - это после считывания (EndReceive). Посмотрел какие данные получает. Выяснил что получается все что должен был получить до последнего байтика.

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

            В чем может быть проблема? Куда копать?

            Добавлено
            Заменил receiveDone.WaitOne() на receiveDone.WaitOne(5000). Все заработало, но!

            Ответ я получаю только в том случае, если посылаю сообщение где прошу сервер закрыть соединение. Если я это не прошу делать, то получаю вот такую ошибку

            Цитата
            System.ObjectDisposedException: Cannot access a disposed object.
            Object name: 'System.Net.Sockets.Socket'.
            at System.Net.Sockets.Socket.EndReceive(IAsyncResult asyncResult, SocketError
            & errorCode)
            at System.Net.Sockets.Socket.EndReceive(IAsyncResult asyncResult)
            at AsyncTCPClient.AsynchronousClient.ReceiveCallback(IAsyncResult ar) in C:\_
            My projects\AsyncTest\AsyncTCPClient\AsyncTCP.cs:line 157


            Это возникает на строчке bytesRead = client.EndReceive(ar). Ответ приходит, но в какой-то момент теряется, т.к. программа ничего не выводит:

            Цитата
            Call: ConnectCallback
            Socket connected to 10.60.3.203:1344
            connectDone.Set()
            Call: Send
            Call: SendCallback
            Sent 522 bytes to server.
            sendDone.Set()
            Call: Receive
            Call: ReceiveCallback
            bytesRead: 166
            Call: ReceiveCallback
            bytesRead: 256
            Call: ReceiveCallback
            bytesRead: 118
            Response received :
            Call: ReceiveCallback
            Exception: Cannot access a disposed object.
            Object name: 'System.Net.Sockets.Socket'.
            bytesRead: 0
            receiveDone.Set()


            Добавлено
            Если послать сигнал, чтобы соединение было закрыто, то выглядит это так:
            Цитата
            Call: ConnectCallback
            Socket connected to 10.60.3.203:1344
            connectDone.Set()
            Call: Send
            Call: SendCallback
            Sent 541 bytes to server.
            sendDone.Set()
            Call: Receive
            Call: ReceiveCallback
            bytesRead: 161
            Call: ReceiveCallback
            bytesRead: 256
            Call: ReceiveCallback
            bytesRead: 118
            Call: ReceiveCallback
            bytesRead: 0
            receiveDone.Set()
            Response received : ICAP/1.0 200 OK
            Connection: close
            Encapsulated: req-hdr=0, null-body=370
            ISTag: "f727e9a506d44e01bd5780c9"
            Service: ICAP server 1.0

            GET http://ya.ru/ HTTP/1.1
            Host: ya.ru
            User-Agent: Mozilla/5.0 (Windows NT 5.1; rv:2.0.1) Gecko/20100101 Firefox/4.0.1
            Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
            Accept-Language: ru-ru,ru;q=0.8,en-us;q=0.5,en;q=0.3
            Accept-Encoding: gzip, deflate
            Accept-Charset: windows-1251,utf-8;q=0.7,*;q=0.7
            X-Session-ID: 0000000000000011

              Duncan MacLeod
              1)
              Цитата
              Если я это не прошу делать, то получаю вот такую ошибку
              Это потому что ты закрываешь сокет в главном потоке и дергаешь EndReceive в другом.
              2) Если сервер на закрывает соединение, нужно вешать установку event'а на какое то другое событие, например парсить пришедшею строку и определять когда пора завершать операцию.
              Цитата
              Reset() перед WaitOne() не повредит делать
              Еще не повредит в цикле десять раз вызвать ToString(). Ровно как и описанный совет это и не повредит, и не поможет.
              Цитата Spawn.NET @
              после Set() рекомендуется делать Thread.Sleep().
              А это просто ерунда... Извините, но я не знаю как вежливо сказать такое.
              Сообщение отредактировано: ANDLL -
                Цитата ANDLL @
                Это потому что ты закрываешь сокет в главном потоке и дергаешь EndReceive в другом.

                Честно говоря, я не очень понимаю почему у меня получается несколько потоков? И как сделать чтобы это все было в одном? Использовать System.Thread и в нем запускать процедуру StartClient?

                Добавлено
                Цитата ANDLL @
                2) Если сервер на закрывает соединение, нужно вешать установку event'а на какое то другое событие, например парсить пришедшею строку и определять когда пора завершать операцию.

                Ну вот в моем конкретно случае событие - это когда от сервера больше нет данных. Т.е. я считал все данные и могу разорвать соединение. Т.е. я ожидаю что вот тут:

                ExpandedWrap disabled
                          private static void ReceiveCallback(IAsyncResult ar)
                          {
                              try
                              {
                                  Console.WriteLine("Call: ReceiveCallback");
                   
                                  // Retrieve the state object and the client socket
                                  // from the asynchronous state object.
                                  StateObject state = (StateObject)ar.AsyncState;
                                  Socket client = state.workSocket;
                   
                                  int bytesRead;
                   
                                  // Read data from the remote device.
                                  bytesRead = client.EndReceive(ar);
                   
                                  Console.WriteLine("bytesRead: {0}", bytesRead);
                   
                                  if (bytesRead > 0)
                                  {                  
                                      // There might be more data, so store the data received so far.
                                      state.sb.Append(Encoding.ASCII.GetString(state.buffer, 0, bytesRead));                  
                   
                                      // Get the rest of the data.
                                      client.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
                                          new AsyncCallback(ReceiveCallback), state);
                                  }
                                  else
                                  {
                                      // All the data has arrived; put it in response.
                                      if (state.sb.Length > 1)
                                      {
                                          response = state.sb.ToString();
                                      }
                                      // Signal that all bytes have been received.
                                      Console.WriteLine("receiveDone.Set()");
                                      receiveDone.Set();
                                      
                                  }
                              }
                              catch (Exception e)
                              {
                                  Console.WriteLine(e.ToString());
                              }
                          }


                в какой-то момент не выполнится условие (bytesRead > 0), т.е. перейдем в ветку где установим receiveDone.set(), а оно выполняется всегда, а потом все зависает.

                PS: ANDLL по вашему совету добавил проверку state.sb.ToString().EndsWith("\r\n\r\n"). Теперь все работает даже если не посылать серверу команду закрытия соединения, но пока до сих пор не могу понять, почему если я не прошу сервер закрыть соединение, то команда recieveDone.set никогда не выполнится, а процесс будет подвисать?
                Сообщение отредактировано: Duncan MacLeod -
                  Цитата Duncan MacLeod @
                  Честно говоря, я не очень понимаю почему у меня получается несколько потоков? И как сделать чтобы это все было в одном? Использовать System.Thread и в нем запускать процедуру StartClient?
                  Асинхронные callback-и выполняются в отдельных потоках. Что бы все было в одном нужно вырезать 90% кода и использовать блокирующий Receive вместо не-блокирующего BeginReceive. Это будет куда проще в реализации и дешевле по ресурсам.
                  Цитата Duncan MacLeod @
                  Ну вот в моем конкретно случае событие - это когда от сервера больше нет данных
                  Ну вот, если сервер как-то уведомляет что у него больше нет данных, то надо это уведомление читать и больше не вызывать BeginReceive
                  Цитата Duncan MacLeod @
                  если я не прошу сервер закрыть соединение, то команда recieveDone.set никогда не выполнится, а процесс будет подвисать?
                  Потому что так работает реализация сокета, он вызывает callback и передает в него 0 только если соединение разорвано.
                  Сообщение отредактировано: ANDLL -
                    ANDLL хотел плюсануть, а некуда. :( Спасибо. Все понятно!!! Теперь врубился полностью!!!
                    0 пользователей читают эту тему (0 гостей и 0 скрытых пользователей)
                    0 пользователей:


                    Рейтинг@Mail.ru
                    [ Script execution time: 0,0345 ]   [ 17 queries used ]   [ Generated: 26.04.24, 17:03 GMT ]