|
VS2010 прием по TCP, доступ к главной форме из потока |
|
|
|
Nov 23 2015, 20:35
|
Гуру
     
Группа: Свой
Сообщений: 2 246
Регистрация: 17-03-05
Из: Украина, Киев
Пользователь №: 3 446

|
Цитата Недопустимая операция в нескольких потоках: попытка доступа к элементу управления 'chart1' не из того потока, в котором он был создан. В приложении, которое мне необходимо для работы нужно принимать пакет данных по ТСП и "распихивать" их по элементам главной формы. Прием ведется в отдельном потоке: Код Thread th = new Thread(ReceiveRun); th.Start();
// Цикл извлечения сообщений, // запускается в отдельном потоке. void ReceiveRun() { while (true) { try { string s = null; while (ns.DataAvailable == true) { //// Определение необходимого размера буфера приема. //byte[] buffer = new byte[_tcpСlient.Available];
ns.Read(inBuffer, 0, inBuffer.Length); s += Encoding.Default.GetString(inBuffer); }
if (s != null) { ShowReceiveMessage(s); s = String.Empty; }
// Вынужденная строчка для экономия ресурсов процессора. // Неизящный способ. Thread.Sleep(100); } catch { ErrorSound(); }
if (_stopNetwork == true) break;
} } При попытке получить пакет появляется ошибка, которую я привел в начале этого сообщения. Ошибка появляется при обработке функции отображения данных: Код // Код доступа к свойствам объектов главной формы из других потоков delegate void UpdateReceiveDisplayDelegate(string message); void ShowReceiveMessage(string message) { if (chart1.InvokeRequired == true) { UpdateReceiveDisplayDelegate rdd = new UpdateReceiveDisplayDelegate(ShowReceiveMessage);
// Данный метод вызывается в дочернем потоке, // ищет основной поток и выполняет делегат указанный в качестве параметра // в главном потоке, безопасно обновляя интерфейс формы. Invoke(rdd, new object[] { message }); chart1.Series[0].Points.Clear(); chart1.Series[1].Points.Clear(); chart1.Series[2].Points.Clear(); chart1.Series[3].Points.Clear(); chart1.Series["1,3 мкм"].Points.AddY(inBuffer[1] << 8 + inBuffer[0]); chart1.Series["2,11 мкм"].Points.AddY(inBuffer[3] << 8 + inBuffer[2]); chart1.Series["1,8 мкм"].Points.AddY(inBuffer[5] << 8 + inBuffer[4]); chart1.Series["1,93 мкм"].Points.AddY(inBuffer[7] << 8 + inBuffer[6]); } else { // Если не требуется вызывать метод Invoke, обратимся напрямую к элементу формы. chart1.Series[0].Points.Clear(); chart1.Series[1].Points.Clear(); chart1.Series[2].Points.Clear(); chart1.Series[3].Points.Clear(); chart1.Series["1,3 мкм"].Points.AddY(inBuffer[1] << 8 + inBuffer[0]); chart1.Series["2,11 мкм"].Points.AddY(inBuffer[3] << 8 + inBuffer[2]); chart1.Series["1,8 мкм"].Points.AddY(inBuffer[5] << 8 + inBuffer[4]); chart1.Series["1,93 мкм"].Points.AddY(inBuffer[7] << 8 + inBuffer[6]); }
} Как правильно поступить с выводом информации? Прошу помочь. Спасибо.
--------------------
Живи днем так, чтобы ночью ты спал спокойно.
|
|
|
|
2 страниц
1 2 >
|
 |
Ответов
(1 - 27)
|
Nov 25 2015, 08:34
|
Гуру
     
Группа: Свой
Сообщений: 2 246
Регистрация: 17-03-05
Из: Украина, Киев
Пользователь №: 3 446

|
Цитата(XVR @ Nov 25 2015, 08:37)  У вас в ShowReceiveMessage жуткая мешанина из того, что нужно вызывать в потоке приема и того, что нужно вызывать в GUI потоке.
В потоке приема нужно вызывать ТОЛЬКО Invoke от делегата (от ShowReceiveMessage), а в самой ShowReceiveMessage уже обновлять всю GUI часть Спасибо, что откликнулись. Можно попросить уточнить (на пальцах) что куда перенести? Прошу простить, не силен в этой стороне программирования и прибегаю только когда действительно не обойтись. В идеале если есть у Вас какая-то "коза" с правильной организацией вопроса, то был бы очень признателен. А вообще хотелось бы так чтобы в одном потоке происходил прием, а вдругом обработка. Пришло что-то - выставили флаг. В обработчике увидели флаг - обработали что пришло. Как правильно сделать тсп-клиент под это я не знаю. Потому нужна помощь. С остальным, думаю разберусь. Спасибо.
--------------------
Живи днем так, чтобы ночью ты спал спокойно.
|
|
|
|
|
Nov 30 2015, 11:47
|
Гуру
     
Группа: Свой
Сообщений: 3 123
Регистрация: 7-04-07
Из: Химки
Пользователь №: 26 847

|
Цитата(smk @ Nov 28 2015, 12:42)  Пока получается. Остается вопрос по кнопке "отсоединиться". Отсоединиться получается, но потом не соединяется т.к. элемент _tcpСlient ликвидирован. Что можно сделать? Я не в курсе, можно ли вызвать Connect у TCPClient после Close, но если нельзя, то можно заново создать TCPClient: Код private void button2_Click(object sender, EventArgs e) { tcpСlient.Close(); tcpStream.Close(); tcpСlient = new TcpClient(); } Цитата нужно как-то сделать так чтоб void Display() выводила в отдельном потоке. Для этого нужен отдельный поток и вызывать оттуда сам Display через делегат Вызывать экранные элементы из GUI можно ТОЛЬКО из того потока, где они создавались Цитата И еще такой момент. тспклиент принимает ровно 16 посылок. не понимаю что его ограничивает. Может сервер закрывает соединение?
|
|
|
|
|
Dec 2 2015, 14:39
|
Гуру
     
Группа: Свой
Сообщений: 2 246
Регистрация: 17-03-05
Из: Украина, Киев
Пользователь №: 3 446

|
В прикрепленном проекте все хорошо кроме приема данных. Соединяется, передает, разъединяется, повторно соединяется. Принимает сколько угодно пакетов. Но при приеме никак не выходит отобразить данные, хотя доподлинно известно, что они есть и валидны. Как только пытаюсь что-то вывести примет первый пакет и все. Счетчик показывает 1 и не меняется. Если часть вывода закоментировать, то выводит нули, хотя знаю что не должно быть нулей. Может как-то неправильно в функцию вывода передается массив, но при этом счетчик выводит правильно. Помогите сделать прием пожалуйста. Я уже не знаю что можно еще предпринять.
myTCP.zip ( 86.93 килобайт )
Кол-во скачиваний: 46Как-то трудно на словах описать внятно. Если кто сможет, просто попробуйте.
--------------------
Живи днем так, чтобы ночью ты спал спокойно.
|
|
|
|
|
Dec 2 2015, 15:05
|
Гуру
     
Группа: Свой
Сообщений: 3 123
Регистрация: 7-04-07
Из: Химки
Пользователь №: 26 847

|
Чтение у вас сделано мягко говоря необычно  Ваш код с комментариями Код byte[] bytes = new byte[tcpСlient.ReceiveBufferSize]; // Заказываете буфер для чтения неизвестно какого размера - // то, что вернет tcpСlient.ReceiveBufferSize тут и то, что он вернет 3мя строками позже не обязательно совпадают
while (tcpStream.DataAvailable == true) // Если данные есть - читаем их, если нет - то не читаем, а выдаем пустой buffer // (returndata во 2й раз будет не null, а String.Empty) { tcpStream.Read(bytes, 0, (int)tcpСlient.ReceiveBufferSize); returndata = Encoding.UTF8.GetString(bytes); // Это зачем ??? count++; } if (returndata != null) // Это просто жесть :) { Invoke(rdd, new object[] { bytes }); str = returndata; returndata = String.Empty; } Код жуткий, кроме того, он выдаст нечто нарезанное далеко не по границам пакетов Пакет нужно собирать в приемной нити, и отдавать целиком в Display
|
|
|
|
|
Dec 3 2015, 07:50
|
Гуру
     
Группа: Свой
Сообщений: 2 246
Регистрация: 17-03-05
Из: Украина, Киев
Пользователь №: 3 446

|
Цитата(XVR @ Dec 2 2015, 22:38)  returndata и все манипуляции вокруг него не нужны вообще Вызывать Invoke(rdd,...) нужно сразу после tcpStream.Read и передавать в него разиер реально прочтенных данных, но лучше все же собирать пакет после tcpStream.Read, и только готовый отдавать на показ
Какой формат принимаемых пакетов? Спасибо. Формат сейчас 256 байт вне зависимости от того сколько реально значащих. К-во значащих зависит от содержания запроса, остальные при формировании буфера передачи просто не модифицируются. В целом размер передаваемого буфера всегда будет не более 256 байт и может меняться только в сторону уменьшения. Оптимольный размер покажет практика. Хочу спросить, что значит "собирать пакет"? Как это делается? Вот так сейчас сделал. Счетчик тикает, данные идут, отображения всеравно нет. Код private void ReceiveRun() { UpdateReceiveDisplayDelegate rdd = new UpdateReceiveDisplayDelegate(Display); try { while (true) { if (tcpStream.CanRead) { byte[] bytes = new byte[256]; while (tcpStream.DataAvailable == true) { tcpStream.Read(bytes, 0, (int)256); count++; } Invoke(rdd, new object[] { bytes }); } else { label1.Text = "Прием невозможен"; th.Abort(); tcpСlient.Close(); tcpStream.Close(); return; } } } catch { label1.Text = "ошибка"; } }
delegate void UpdateReceiveDisplayDelegate(Byte [] mydata); private void Display(Byte[] mydata) //вывод на форму { Int32 temp; temp = ((mydata[3] << 24) + (mydata[2] << 16) + (mydata[1] << 8) + mydata[0]); label1.Text = temp.ToString("D"); label3.Text = Convert.ToString(count); //if (mydata[0] > 0) label2.Text = "OK"; //else label2.Text = "0"; }
--------------------
Живи днем так, чтобы ночью ты спал спокойно.
|
|
|
|
|
Dec 3 2015, 12:36
|
Гуру
     
Группа: Свой
Сообщений: 3 123
Регистрация: 7-04-07
Из: Химки
Пользователь №: 26 847

|
Цитата Хочу спросить, что значит "собирать пакет"? Это значит, что TCP соединение не является datagram ориентированным. Т.е. это просто поток байтов. И никто не гарантирует, что приемная сторона будет получать эти байты кусками такого же размера, как передавала передающая сторона. Т.е. вы свои 256 байт можете получить в несколько чтений (и мелкой нарезкой). Цитата Как это делается? Принимаются данные и накапливаются в буфере, пока не наберется полный пакет. Цитата while (tcpStream.DataAvailable == true) { tcpStream.Read(bytes, 0, (int)256); count++; } Invoke(rdd, new object[] { bytes }); Это неправильно. У вас в буфере будет мешанина из кусков принятых данных. Надо как то так Код byte[] bytes = new byte[256]; ...
int size = 0; while(size<256) { while (tcpStream.DataAvailable == true) { size+=tcpStream.Read(bytes, size, 256-size); } if (size<256) Thread::Sleep(100); } count++;
Invoke(rdd, new object[] { bytes });
|
|
|
|
|
Dec 3 2015, 14:03
|
Гуру
     
Группа: Свой
Сообщений: 2 246
Регистрация: 17-03-05
Из: Украина, Киев
Пользователь №: 3 446

|
Судя по режиму отладки функция Display выполняется очень часто. И действительно в буфере нули. Если и приходит что-то, то оно сразуже затирается нулями при следующем вызове Display. Как красиво сделать так чтоб Display вызывалась только если приняты свежие данные? Пока сделал вот так: Код if(count_old != count)Invoke(rdd, new object[] { bytes }); Но всеравно в Display передаются нули.
--------------------
Живи днем так, чтобы ночью ты спал спокойно.
|
|
|
|
|
Dec 3 2015, 20:45
|
Гуру
     
Группа: Свой
Сообщений: 2 246
Регистрация: 17-03-05
Из: Украина, Киев
Пользователь №: 3 446

|
Вот что получается. Если я кнопкой отправляю 16 запросов то после 16-го все последующие не обрабатываются. Ну или еще один с большой задержкой. Если я командую прибору отвечать без запроса - молотит без сбоев. Если геркулесом отправляю запросы - тоже никаких проблем, отвечает хоть на 50-й. Даже не представляю каким способом вычислить этот затык. Можно что-то придумать? Спасибо. Содержание пакета не имеет значения. Просто перестает отправлять или вообще подвисает. На мой непрофессиональный взгляд все вполне должно работать. Код private void button3_Click(object sender, EventArgs e)//Отправить {
if (tcpStream.CanWrite) { outBuffer[0] = 1; if (checkBox1.Checked) { outBuffer[1] = 2; outBuffer[2] = 1; } else { outBuffer[1] = 2; outBuffer[2] = 0; } tcpStream.Write(outBuffer, 0, outBuffer.Length); } else { label1.Text = "Передача невозможна"; th.Abort(); tcpСlient.Close(); tcpStream.Close(); return; } } Как я успел понять - программа ничего не отсылает. Почему - непонятно.
--------------------
Живи днем так, чтобы ночью ты спал спокойно.
|
|
|
|
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|