Наши проекты:
Журнал · Discuz!ML · Wiki · DRKB · Помощь проекту |
||
ПРАВИЛА | FAQ | Помощь | Поиск | Участники | Календарь | Избранное | RSS |
[18.225.31.159] |
|
Сообщ.
#1
,
|
|
|
Здравствуйте!
Пытаюсь использовать пример из MSDN (ничем от оригинала не отличается, кроме того что шлю свои данные). оригинал в MSDN 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 вижу что ответ пришел. Где вообще можно доступно почитать про сетевое программирование. Очень нужно эту тему освоить. Спасибо. |
Сообщ.
#2
,
|
|
|
Reset() перед WaitOne() не повредит делать. К тому же практика показывает, что потоку необходимо некоторое время, чтобы вернуться в строй, так сказать, так что после Set() рекомендуется делать Thread.Sleep().
|
Сообщ.
#3
,
|
|
|
Spawn.NET поставил Reset() перед каждым WaitOne(). Также после каждого Set() усыпил поток. Не помогло. Данные приходят, но приложение их не ловит.
|
Сообщ.
#4
,
|
|
|
Duncan MacLeod, убедитесь, что данные не приходят до того, как основной поток встает на receiveDone.WaitOne();. Так же поставьте бряку на receiveDone.Set(); и посмотрите в какой момент она отработает и отрабатывает ли она вообще.
И что значит не получает данных? Висит на receiveDone.WaitOne()? или фактически данных нет? PS. Если не хотите после каждого WaitOne() сбрасывать хендлеры, то используйте AutoResetEvent. |
Сообщ.
#5
,
|
|
|
Да, с данными - это я слажал. Они действительно приходят, я был не прав.
Цитата 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 |
Сообщ.
#6
,
|
|
|
Duncan MacLeod
1) Цитата Это потому что ты закрываешь сокет в главном потоке и дергаешь EndReceive в другом.Если я это не прошу делать, то получаю вот такую ошибку 2) Если сервер на закрывает соединение, нужно вешать установку event'а на какое то другое событие, например парсить пришедшею строку и определять когда пора завершать операцию. Цитата Еще не повредит в цикле десять раз вызвать ToString(). Ровно как и описанный совет это и не повредит, и не поможет.Reset() перед WaitOne() не повредит делать Цитата Spawn.NET @ А это просто ерунда... Извините, но я не знаю как вежливо сказать такое. после Set() рекомендуется делать Thread.Sleep(). |
Сообщ.
#7
,
|
|
|
Цитата ANDLL @ Это потому что ты закрываешь сокет в главном потоке и дергаешь EndReceive в другом. Честно говоря, я не очень понимаю почему у меня получается несколько потоков? И как сделать чтобы это все было в одном? Использовать System.Thread и в нем запускать процедуру StartClient? Добавлено Цитата ANDLL @ 2) Если сервер на закрывает соединение, нужно вешать установку event'а на какое то другое событие, например парсить пришедшею строку и определять когда пора завершать операцию. Ну вот в моем конкретно случае событие - это когда от сервера больше нет данных. Т.е. я считал все данные и могу разорвать соединение. Т.е. я ожидаю что вот тут: 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 никогда не выполнится, а процесс будет подвисать? |
Сообщ.
#8
,
|
|
|
Цитата Duncan MacLeod @ Асинхронные callback-и выполняются в отдельных потоках. Что бы все было в одном нужно вырезать 90% кода и использовать блокирующий Receive вместо не-блокирующего BeginReceive. Это будет куда проще в реализации и дешевле по ресурсам.Честно говоря, я не очень понимаю почему у меня получается несколько потоков? И как сделать чтобы это все было в одном? Использовать System.Thread и в нем запускать процедуру StartClient? Цитата Duncan MacLeod @ Ну вот, если сервер как-то уведомляет что у него больше нет данных, то надо это уведомление читать и больше не вызывать BeginReceiveНу вот в моем конкретно случае событие - это когда от сервера больше нет данных Цитата Duncan MacLeod @ Потому что так работает реализация сокета, он вызывает callback и передает в него 0 только если соединение разорвано. если я не прошу сервер закрыть соединение, то команда recieveDone.set никогда не выполнится, а процесс будет подвисать? |
Сообщ.
#9
,
|
|
|
ANDLL хотел плюсануть, а некуда. Спасибо. Все понятно!!! Теперь врубился полностью!!!
|