mr_smit 0 12 мая, 2015 Опубликовано 12 мая, 2015 · Жалоба Прикупил у китайцев плату с чипом W5500. Это обновленная версия Ethernet чипа W5100. Подключил к STM32 Discovery. За основу взял эту статью: http://we.easyelectronics.ru/GYUR22/w5500s...web-server.html. Убрал функции работы с SD картой и скомпилировал всё это в CooCox. Сервер заработал. Т.е. отдает в браузер статичный текст. Хочу добавить на html страницу картинку. Для теста сделал PNG размером 5х5 пикселей. Добавил её в html код: <html> <head> <title>Умный дом</title> <meta http-equiv='content-type' content='text/html; charset=windows-1251'> </head> <body> <br><br><br><center><b>Тестовая страница W5500</b></center><br> <center><img src='test.png'></center> </body> </html> Открыл картинку в hex редакторе и сделал массив сырых данных: const char Img [205] = { 0x89,0x50,0x4E,0x47,0x0D,0x0A,0x1A,0x0A,0x00,0x00,0x00,0x0D,0x49,0x48,0x44,0x52, 0x00,0x00,0x00,0x05, 0x00,0x00,0x00,0x05,0x08,0x02,0x00,0x00,0x00,0x02,0x0D,0xB1,0xB2,0x00,0x00,0x00, 0x01,0x73,0x52,0x47, 0x42,0x00,0xAE,0xCE,0x1C,0xE9,0x00,0x00,0x00,0x06,0x62,0x4B,0x47,0x44,0x00,0xFF, 0x00,0xFF,0x00,0xFF, 0xA0,0xBD,0xA7,0x93,0x00,0x00,0x00,0x09,0x70,0x48,0x59,0x73,0x00,0x00,0x0B,0x13, 0x00,0x00,0x0B,0x13, 0x01,0x00,0x9A,0x9C,0x18,0x00,0x00,0x00,0x07,0x74,0x49,0x4D,0x45,0x07,0xDF,0x05, 0x0A,0x0E,0x01,0x20, 0xA0,0x0A,0x57,0xCD,0x00,0x00,0x00,0x19,0x74,0x45,0x58,0x74,0x43,0x6F,0x6D,0x6D, 0x65,0x6E,0x74,0x00, 0x43,0x72,0x65,0x61,0x74,0x65,0x64,0x20,0x77,0x69,0x74,0x68,0x20,0x47,0x49,0x4D, 0x50,0x57,0x81,0x0E, 0x17,0x00,0x00,0x00,0x28,0x49,0x44,0x41,0x54,0x08,0xD7,0x4D,0x8B,0x41,0x0E,0x00, 0x30,0x08,0xC2,0xA8, 0xFF,0xFF,0x73,0x3D,0x18,0xDD,0xB8,0x34,0x94,0x40,0x12,0x35,0x9B,0x52,0x81,0xD7, 0x67,0x3F,0x55,0x83, 0x53,0xFC,0x67,0xA0,0x01,0x8E,0xCE,0x0F,0x06,0x6E,0x28,0x44,0x78,0x00,0x00,0x00, 0x00,0x49,0x45,0x4E, 0x44,0xAE,0x42,0x60,0x82 }; А в функции tcp_http_mt добавил реакцию на запрос картинки. Т.е. при ответе к HTTP заголовку приклеиваем сырые данные картинки. int32_t tcp_http_mt(uint8_t sn, uint8_t* buf, uint16_t port) { int32_t ret; uint32_t size = 0; char *url,*p; switch(getSn_SR(sn)) { case SOCK_ESTABLISHED : if(getSn_IR(sn) & Sn_IR_CON) { setSn_IR(sn,Sn_IR_CON); } if((size = getSn_RX_RSR(sn)) > 0) { if(size > DATA_BUF_SIZE) { size = DATA_BUF_SIZE; } ret = recv(sn,buf,size); HTTP_reset(sn); if(ret <= 0) { return ret; } url =(char*) buf + 4; if((http_state[sn]==HTTP_IDLE)&&(memcmp(buf, "GET ", 4)==0)&&((p = strchr(url, ' ')))) // extract URL from request header { *(p++) = 0; //making zeroed url string sentsize[sn]=0; if(strcmp(url,"/")==0) { strcpy((char*)buf,http_200); strcat((char*)buf, http_server); strcat((char*)buf,"Content-Type: text/html; charset=windows-1251\r\n"); strcat((char*)buf,"Cache-Control: no-cache\r\n"); strcat((char*)buf,http_connection_close); strcat((char*)buf, http_header_end); strcat((char*)buf,site); size=strlen((char*)buf); http_state[sn]=HTTP_SENDING; } if(strcmp(url,"/test.png")==0) { strcpy((char*)buf,http_200); strcat((char*)buf, http_server); strcat((char*)buf,"Content-Type: image/png\r\n"); // strcat((char*)buf,"Content-Lenght: 205\r\n"); strcat((char*)buf,"Connection: keep-alive\r\n"); strcat((char*)buf,"Keep-Alive: timeout=20\r\n"); strcat((char*)buf,"Accept-Ranges: bytes\r\n"); strcat((char*)buf,"Cache-Control: no-cache\r\n"); strcat((char*)buf, http_header_end); strcat((char*)buf,Img); size=strlen((char*)buf); http_state[sn]=HTTP_SENDING; } } /* Отправляем данные пока размер отправленных данных sentsize[sn] не сравняется с размером данных size которые мы подготовили для отправки ... вроде бы... */ if(http_state[sn]==HTTP_SENDING) { while (sentsize[sn] < size) { //DATA_BUF_SIZE ret = send(sn,buf,size); if(ret < 0) { close(sn); return ret; } sentsize[sn] += ret; // Don't care SOCKERR_BUSY, because it is zero. } HTTP_reset(sn); disconnect(sn); } } break; case SOCK_CLOSE_WAIT : HTTP_reset(sn); if((ret=disconnect(sn)) != SOCK_OK) return ret; break; case SOCK_INIT : HTTP_reset(sn); if( (ret = listen(sn)) != SOCK_OK) return ret; break; case SOCK_CLOSED: HTTP_reset(sn); if((ret=socket(sn,Sn_MR_TCP,port,0x00)) != sn) return ret; break; default: HTTP_reset(sn); break; } return 1; } В итоге запрос на картинку в браузере идет, но сервер её не отдает. При этом браузер получает заголовки ответа. Но самого изображения нет. От безысходности установил HTTP снифер Wireshark. Удалось выяснить, что при передаче картинки сервер отправляет только её первые 8 байт. И на этом всё. 3-й день бьюсь. Не понимаю почему так происходит. Или где то функцию отправки надо изменить или с размером передаваемых данных что то не то. Гуглил и по W5100 и по W5200 и по W5500, но именно как передать изображение не нашел. web_discovery_w5500_sd.zip Может есть у кого какие идеи? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
AlanDrakes 1 12 мая, 2015 Опубликовано 12 мая, 2015 (изменено) · Жалоба Вы пытаетесь поместить бинарный файл в строку. img[] {0x89,0x50,0x4E,0x47,0x0D,0x0A,0x1A,0x0A,0x00,0x00,0x00,0x0D ... }; strcat((char*)buf, http_header_end); strcat((char*)buf,Img); size=strlen((char*)buf); Но на символе конца строки функция и останавливается. Но функция копирования строки останавливается на символе 0x00 - нуль-терминатор. Вам нужен бинарный буфер вместо строкового. И попробуйте сделать через memcpy: strcat((char*)buf, http_header_end); // Заголовок... и всё, что до него. size=strlen((char*)buf); // Текущий размер строк в буфере memcpy((char*)buf,Img[0]*, sizeof(Img)); // Копируем картинку в буфер. size += sizeof(Img); // Увеличиваем на размер картинки. Где-то мог случайно ошибиться. Но на вид всё должно заработать. Исправления касаются только main.c Изменено 12 мая, 2015 пользователем AlanDrakes Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
mr_smit 0 12 мая, 2015 Опубликовано 12 мая, 2015 (изменено) · Жалоба Но функция копирования строки останавливается на символе 0x00 - нуль-терминатор. Вам нужен бинарный буфер вместо строкового. И попробуйте сделать через memcpy: Точно. Похоже на правду. Спасибо! Проверить, к сожалению, смогу только вечером. Изменено 12 мая, 2015 пользователем mr_smit Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
mr_smit 0 12 мая, 2015 Опубликовано 12 мая, 2015 (изменено) · Жалоба strcat((char*)buf, http_header_end); // Заголовок... и всё, что до него. size=strlen((char*)buf); // Текущий размер строк в буфере memcpy((char*)buf,Img[0]*, sizeof(Img)); // Копируем картинку в буфер. size += sizeof(Img); // Увеличиваем на размер картинки. А в таком случае разве memcpy не затрет buf? И в buf будет только сама картинка без заголовков? Может отправить сначала заголовок, а затем картинку? ret = send(sn,buf, strlen(buf)); ret = send(sn, Img, sizeof(Img)); Изменено 12 мая, 2015 пользователем mr_smit Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
kolobok0 0 12 мая, 2015 Опубликовано 12 мая, 2015 · Жалоба ....А в таком случае разве memcpy не затрет buf? И в buf будет только сама картинка без заголовков?... конечно же затрёт. И ещё пять копеек. Картинку Вы точно в бинарном виде собрались передавать, а не в base64? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
mr_smit 0 12 мая, 2015 Опубликовано 12 мая, 2015 (изменено) · Жалоба конечно же затрёт. И ещё пять копеек. Картинку Вы точно в бинарном виде собрались передавать, а не в base64? В бинарном. Я посмотрел, браузер получает картинки в чистом виде. Для этого указывается тип (image/png, image/jpg и т.д.) и количество байт. Хотя черт его знает: Надо попробовать в base64 конвертнуть и strcat-ом приклеить. Изменено 12 мая, 2015 пользователем mr_smit Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
mr_smit 0 12 мая, 2015 Опубликовано 12 мая, 2015 · Жалоба В общем работают оба способа. Если при запросе картинки отдать сначала заголовок, а потом сырые данные картинки, то она отображается. ret = send(sn,buf,size); ret = send(sn,(char*)Img, sizeof(Img)); Так же можно вставить картинку в кодировке base64 непосредственно в html код страницы. const char site[] = "<html>" "<head>" "<title>Умный дом</title>" "<meta http-equiv='content-type' content='text/html; charset=windows-1251'>" "</head>" "<body>" "<br><br><br><center><b>Тестовая страница W5500</b></center><br>" "<center><img src='data:image/png;base64," "iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAIAAAACDbGyAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A" "/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB98FCg4BIKAKV80AAAAZdEVYdENv" "bW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAKElEQVQI102LQQ4AMAjCqP//cz0Y3bg0lEAS" "NZtSgddnP1WDU/xnoAGOzg8GbihEeAAAAABJRU5ErkJggg==" "'></center>" "</body>" "</html>"; В этом случае при запросе страницы она открывается корректно с картинкой. Но когда я взял картинку размером 5 КБ, то страница не открывается. Постоянно висит ожидание. Размер передающего буфера 2 КБ. Т.е. надо разбить данные на блоки. Вот это место: ... sentsize[sn]=0; ... if(http_state[sn]==HTTP_SENDING) { while (sentsize[sn] < size) { ret = send(sn,buf+sentsize[sn],size-sentsize[sn]); if(ret < 0) { close(sn); return ret; } sentsize[sn] += ret; // Don't care SOCKERR_BUSY, because it is zero. } HTTP_reset(sn); disconnect(sn); } ret длина реально отправленных данных. Опять же не уловлю в чем проблема. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
AlanDrakes 1 13 мая, 2015 Опубликовано 13 мая, 2015 · Жалоба strcat((char*)buf, http_header_end); // Заголовок... и всё, что до него. size=strlen((char*)buf); // Текущий размер строк в буфере memcpy((char*)buf,Img[0]*, sizeof(Img)); // Копируем картинку в буфер. size += sizeof(Img); // Увеличиваем на размер картинки. А в таком случае разве memcpy не затрет buf? И в buf будет только сама картинка без заголовков? Может отправить сначала заголовок, а затем картинку? ret = send(sn,buf, strlen(buf)); ret = send(sn, Img, sizeof(Img)); А, да, видимо, как раз там и ошибся. Тогда, передавая картинку бинарными данными, нужно делать так: strcat((char*)buf, http_header_end); // Заголовок... и всё, что до него. size=strlen((char*)buf); // Текущий размер строк в буфере memcpy((char*)buf+size,Img[0]*, sizeof(Img)); // Копируем картинку в буфер (со смещением на _размер_ заголовков). size += sizeof(Img); // Увеличиваем на размер картинки. Но здесь есть вероятность получить переполнение буфера... со всеми вытекающими. Так что, нужно делать очень осторожно. А вообще, для передачи бинарных данных, нужно использовать строку-разделитель. Она же передаётся в заголовке. Но тогда у Вас будет очень большой буфер отправки и он может не поместиться в RAM контроллера. Опять же, грабли. По отправке: if(http_state[sn]==HTTP_SENDING) { while (sentsize[sn] < size) { ret = send(sn,buf+sentsize[sn],size-sentsize[sn]); send() принимает ссылку на буфер? С библиотекой я не знаком, но кажется, Вы получаете на второй итерации выход за границы массива. Попробуйте так: ret = send(sn,buf,size-sentsize[sn]); Опять же, возможно, ошибаюсь.. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
mr_smit 0 13 мая, 2015 Опубликовано 13 мая, 2015 (изменено) · Жалоба int32_t send(uint8_t sn, uint8_t * buf, uint16_t len) Я пока никак не пойму можно ли большой массив передать порциями по 2 КБ. В смысле поддерживает само железо такой финт или нет. Но судя по всему поддерживает, ведь человек из вышеприведенной статьи как то загружает сайт с SD карточки. Изменено 13 мая, 2015 пользователем mr_smit Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
mr_smit 0 13 мая, 2015 Опубликовано 13 мая, 2015 (изменено) · Жалоба Сделал так: ret = send(sn,buf,size); // отправляем заголовок ret = send(sn,(char*)Img1,sizeof(Img1)); // картинка по частям ret = send(sn,(char*)Img2,sizeof(Img2)); // ret = send(sn,(char*)Img3,sizeof(Img3)); // disconnect(sn); в итоге загружается только первая треть картинки или даже меньше: Изменено 13 мая, 2015 пользователем mr_smit Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
AlanDrakes 1 13 мая, 2015 Опубликовано 13 мая, 2015 · Жалоба Передавать нужно, убедившись, что сокет готов отправлять следующую порцию данных. Я бы не рекомендовал передавать за раз более 1400 байт (MTU пакета как правило, ~1500, но лучше ограничить чуть меньшим объёмом) и, если библиотека только записывает данные в ETH, но не проверяет их отправку - то проверять это самостоятельно. Если мне не слишком изменяет память, то W5500 должен быть близок к W5100 в плане памяти - 8кB на 4 сокета, то есть, 2кБ/сокет (приём и передача отдельные), куда записываются сформированные пакеты. А человек, предположительно, загружает меньшими порциями. Кстати, его код пробовали разобрать для понимания? То есть, его последовательность действий: Чтение файла, обработка, передача... и т.п.? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
mr_smit 0 13 мая, 2015 Опубликовано 13 мая, 2015 (изменено) · Жалоба Кстати, его код пробовали разобрать для понимания? То есть, его последовательность действий: Чтение файла, обработка, передача... и т.п.? Так я его исходник и правлю. Функция Send, как я понимаю, сама следит за передающим буфером: int32_t send(uint8_t sn, uint8_t * buf, uint16_t len) { uint8_t tmp=0; uint16_t freesize=0; CHECK_SOCKNUM(); CHECK_SOCKMODE(Sn_MR_TCP); CHECK_SOCKDATA(); tmp = getSn_SR(sn); if(tmp != SOCK_ESTABLISHED && tmp != SOCK_CLOSE_WAIT) return SOCKERR_SOCKSTATUS; if( sock_is_sending & (1<<sn) ) { tmp = getSn_IR(sn); if(tmp & Sn_IR_SENDOK) { setSn_IR(sn, Sn_IR_SENDOK); #if _WZICHIP_ == 5200 if(getSn_TX_RD(sn) != sock_next_rd[sn]) { setSn_CR(sn,Sn_CR_SEND); while(getSn_CR(sn)); return SOCKERR_BUSY; } #endif sock_is_sending &= ~(1<<sn); } else if(tmp & Sn_IR_TIMEOUT) { close(sn); return SOCKERR_TIMEOUT; } else return SOCK_BUSY; } freesize = getSn_TxMAX(sn); if (len > freesize) len = freesize; // check size not to exceed MAX size. while(1) { freesize = getSn_TX_FSR(sn); tmp = getSn_SR(sn); if ((tmp != SOCK_ESTABLISHED) && (tmp != SOCK_CLOSE_WAIT)) { close(sn); return SOCKERR_SOCKSTATUS; } if( (sock_io_mode & (1<<sn)) && (len > freesize) ) return SOCK_BUSY; if(len <= freesize) break; } wiz_send_data(sn, buf, len); #if _WIZCHIP_ == 5200 sock_next_rd[sn] = getSn_TX_RD(sn) + len; #endif setSn_CR(sn,Sn_CR_SEND); /* wait to process the command... */ while(getSn_CR(sn)); sock_is_sending |= (1 << sn); return len; } А отправляет он так: if(fs[sn].fsize != sentsize[sn]) { f_read(&fs[sn], &buf[header_sz], DATA_BUF_SIZE-header_sz, &blocklen); ret = send(sn,buf,blocklen+header_sz); if(ret < 0) { f_close(&fs[sn]); close(sn); return ret; } sentsize[sn] += ret; // Don't care SOCKERR_BUSY, because it is zero. } Читает с карты 2048 байт минус размер HTTP заголовка (DATA_BUF_SIZE-header_sz). А отправляет по факту каждый раз 2048 байт (blocklen+header_sz) кроме последней итерации. Сейчас только заметил blocklen+header_sz. Может надо HTTP заголовок отправлять с каждым куском данных??? Я то сейчас отправляю заголовок, а потом просто куски картинки. А у него в буфере каждый раз оказывается заголовок + новая порция считанных данных &buf[header_sz]. Правильно? Изменено 14 мая, 2015 пользователем IgorKossak [codebox] для длинного кода, [code] - для короткого! Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
kolobok0 0 13 мая, 2015 Опубликовано 13 мая, 2015 (изменено) · Жалоба ... f_read(&fs[sn], &buf[header_sz], DATA_BUF_SIZE-header_sz, &blocklen); ret = send(sn,buf,blocklen+header_sz); ... Читает с карты 2048 байт минус размер HTTP заголовка... ышо 5 копеек. Вы попали в типичную ошибку для протокола TCP. Это протокол ПОТОКОВЫЙ(!) т.е. при вычитывании из буффера может прийти ЛЮБОЕ кол-во байт в пределах максимально переданного кол-ва. Т.е. Передатчик передаёт 500+500+24 Приёмник МОЖЕТ принять 24+100+100+300+400+50+50 почувствуйте разницу... PS Особенно эти ослинные уши начинают лезть, когда система начинает работать в боевых сетях - с различным временным лагом прихода пакетов на IP уровне, с доп. разборкой-сборкой на IP уровне, и т.д.. Изменено 13 мая, 2015 пользователем kolobok0 Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
mr_smit 0 14 мая, 2015 Опубликовано 14 мая, 2015 · Жалоба Передатчик передаёт 500+500+24 Приёмник МОЖЕТ принять 24+100+100+300+400+50+50 почувствуйте разницу... Выходит надо учитывать переменную ret. Это число реально переданных байт. И в зависимости от неё двигать указатель в буфере отправки buf при посылке следующей части. Но вопрос о прикреплении заголовка к каждой части посылки остается открытым. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
AlanDrakes 1 14 мая, 2015 Опубликовано 14 мая, 2015 · Жалоба Нет, по идее, не нужно к каждой посылке прикреплять заголовок, т.к. в этом случае будет нарушен поток. А вот TCP заголовок - да. Но, думаю, это делает сама функция, так что не фатально. Процедура, похоже, действительно обрабатывает количество байт, но там идёт что-то, связанное с допустимыми значениями... я в задумчивости.. Эх, формировал сам пакеты - не было проблем... они уходили такими, какими должны. А вот W5100 и выше умеют сокеты и там уже хитрее - писать нужно в сокет, а чип позаботится об отправке пакета(-ов). Кстати, теоретически, писать можно непрерывно (главное, чтобы указатель записи не пытался затреть данные, которые чип ещё не успел отправить). И, в таком случае, kolobok0 будет прав - пакеты определяются передатчиком. Помнится, когда писал сетевые приложения в Win* - получил странность: при отправке 2-4 мелких пакетов, приходит один, но большой. Долго ломал голову. Оказалось, дело в тех же сокетах и времени. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться