Почему не вызывается обратный вызов BeginGetResponse?

#c# #windows-phone-7 #httpwebrequest #multithreading #manualresetevent

#c# #windows-phone-7 #httpwebrequest #многопоточность #manualresetevent

Вопрос:

Вот мой код:

 namespace RequestApi
{
    public partial class MainPage : PhoneApplicationPage
    {
        private BackgroundWorker bw;
        private string ans;
        private JObject ansJson;
        private static ManualResetEvent allDone = new ManualResetEvent(false);
        // Constructor
        public MainPage()
        {
            InitializeComponent();

        }

        private void button1_Click(object sender, RoutedEventArgs e)
        {
            string url = "http://192.168.0.43:8182/Account/SignIn";
            CreateRequest(url);

            userId.Text = ansJson.Value<int>("user_id").ToString();

        }


        private void CreateRequest(string url)
        {
            Debug.WriteLine("CreateRequest");
            HttpWebRequest req = (HttpWebRequest)HttpWebRequest.Create(url);
            req.ContentType = "application/json";
            req.Method = "POST";
            req.BeginGetRequestStream(new AsyncCallback(RequestCallback), req);
            allDone.WaitOne();
        }

        private  void RequestCallback(IAsyncResult aresult)
        {
            Debug.WriteLine("RequestCallback");
            HttpWebRequest req = (HttpWebRequest)aresult.AsyncState;
            Stream postStream = req.EndGetRequestStream(aresult);

            string obj = "{ 'username': 'test_2@aragast.com', 'password': 'a123456' }";
            JObject json = JObject.Parse(obj);
            string s = JsonConvert.SerializeObject(json);
            byte[] postdata = System.Text.Encoding.Unicode.GetBytes(s);

            postStream.Write(postdata, 0, postdata.Length);
            postStream.Close();

            req.BeginGetResponse(new AsyncCallback(ResponseCallback), req);


        }

        private  void ResponseCallback(IAsyncResult aresult)
        {
            Debug.WriteLine("ResponseCallback");
            HttpWebRequest req = (HttpWebRequest)aresult.AsyncState;
            HttpWebResponse resp = (HttpWebResponse)req.EndGetResponse(aresult);
            StreamReader reader = new StreamReader(resp.GetResponseStream());
            string response = reader.ReadToEnd();
            Debug.WriteLine(response);
            JObject responseJson = JObject.Parse(response);
            ansJson = responseJson;
            Debug.WriteLine("ansJson from responseCallback {0}", ansJson);
            reader.Close();
            resp.Close();
            allDone.Set();

        }
    }
}
  

Когда я отлаживаю приложение, оно вводит CreateRequest, затем вводит RequestCallback, но затем останавливается и никогда не вводит ResponseCallback, вместо этого он пытается использовать идентификатор пользователя.Текст для присвоения значения asnJson, которое равно null, потому что оно не вводит ResponseCallback . Когда я делаю неправильно, и почему он никогда не вводит ResponseCallback?

Ответ №1:

Ваш ManualResetEvent создается с true помощью аргумента, поэтому он уже сигнализируется для начала. Это означает allDone.Wait() , что вызов будет немедленно продолжен… so CreateRequest завершится, и вы сразу же попытаетесь использовать asnJson переменную, которая, как вы сказали, есть null . Я подозреваю, что это приведет к завершению работы приложения, поэтому у вас никогда не будет шанса получить ответ.

Теперь решение этой проблемы заключается не в изменении ManualResetEvent конструктора — вы все равно не должны так ждать в потоке пользовательского интерфейса! Вы заблокируете его, и вы удалили весь смысл Windows Phone 7, сделав все асинхронным для начала.

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

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

1. Нет, это false, я просто ошибаюсь и пишу здесь true. Но на самом деле это false, но я, хотя и не могу увеличить обратный вызов ResponseCallback, когда я перехожу к debug, я достигаю этой строки req. BeginGetResponse(new AsyncCallback(ResponseCallback), req); и затем он никуда не перемещается

2. @AramGevorgyan: Тогда, возможно, это сбой, потому что вы блокируете поток пользовательского интерфейса. Вы действительно, действительно не должны этого делать. Измените код так, чтобы обратный вызов ответа запускал оставшийся код пользовательского интерфейса, и посмотрите, по-прежнему ли он терпит неудачу.

3. Да, когда я не блокирую поток пользовательского интерфейса, он вводит ResponseCallback, но он вводится поздно, прежде чем он сделает userId. Текст = ansJson. Значение<int>(«user_id»). toString(); который равен нулю, поэтому я должен каким-то образом дождаться ответа, поэтому я блокирую поток пользовательского интерфейса. Как мне это сделать?

4. @AramGevorgyan: Вы не блокируете поток пользовательского интерфейса — вы перемещаете бит кода, используемый ansJson в другой обратный вызов, который выполняется только после завершения ответа — используйте Dispatcher. BeginInvoke` из обратного вызова ответа, чтобы выполнить его в потоке пользовательского интерфейса.

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

Ответ №2:

Не совсем связано с ответом на вопрос, но блок ответов, похоже, является единственным местом, подходящим для рекомендации, которую я собираюсь сделать… У вас должна быть соответствующая защита ресурсов вокруг ваших потоков, используя using блоки как таковые:

Исходный код:

         Stream postStream = req.EndGetRequestStream(aresult);

        string obj = "{ 'username': 'test_2@aragast.com', 'password': 'a123456' }";
        JObject json = JObject.Parse(obj);
        string s = JsonConvert.SerializeObject(json);
        byte[] postdata = System.Text.Encoding.Unicode.GetBytes(s);

        postStream.Write(postdata, 0, postdata.Length);
        postStream.Close();
  

Новый код (также убедитесь, что ваша кодировка верна для запроса: действительно ли ваш веб-сервис ожидает UTF-16? Чаще всего веб-серверы используют UTF-8 (кодировка.UTF8)) :

         using (Stream postStream = req.EndGetRequestStream(aresult))
        {
            string obj = "{ 'username': 'test_2@aragast.com', 'password': 'a123456' }";
            JObject json = JObject.Parse(obj);
            string s = JsonConvert.SerializeObject(json);
            byte[] postdata = System.Text.Encoding.Unicode.GetBytes(s);

            postStream.Write(postdata, 0, postdata.Length);
        }
  

Исходный код:

         HttpWebResponse resp = (HttpWebResponse)req.EndGetResponse(aresult);
        StreamReader reader = new StreamReader(resp.GetResponseStream());
        string response = reader.ReadToEnd();
        Debug.WriteLine(response);
        JObject responseJson = JObject.Parse(response);
        ansJson = responseJson;
        Debug.WriteLine("ansJson from responseCallback {0}", ansJson);
        reader.Close();
        resp.Close();
  

Новый код:

         using (HttpWebResponse resp = (HttpWebResponse)req.EndGetResponse(aresult))
        using (StreamReader reader = new StreamReader(resp.GetResponseStream()))
        {
            string response = reader.ReadToEnd();
            Debug.WriteLine(response);
            JObject responseJson = JObject.Parse(response);
            ansJson = responseJson;
            Debug.WriteLine("ansJson from responseCallback {0}", ansJson);
        }
  

Я бы также рекомендовал окружить ваши req resp операции и try..catch блоками, чтобы что-то могло обрабатывать исключительные условия — в противном случае оно будет всплывать в обработчике исключений AppDomain (также может быть подключено событием UnhandledException).

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

1. Большое вам спасибо за совет. Да, нет проблем с тем, что сервер utf-16 получает этот запрос и отправляет правильный ответ. Насчет try catch вы совершенно правы, и это хорошая идея об использовании, но это тестовое приложение, и я просто пишу его, чтобы протестировать некоторые вещи, прежде чем приступать к написанию более сложного приложения, так что здесь не так важно, что такое try catch.