Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: IP/UDP/TFTP uIP v1.0
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > ARM
Цырен
Недавно поднял uIP 1.0 для своей платы с LPC2364. Реализовал на ней web сервер, потом - telnet. Документация потрясающая, все понятно. Но это все были стеки IP/TCP/, моя же техническая задача состоит в реализации IP/UDP/TFTP(modbus). Если кто имеет исходнички на UDP и TFTP, плиз, ПОДЕЛИТЕСЬ. А если кто-то, вообще, уже делал такую связку, то я буду несказанно рад взять и воспользоваться вашими плодами работы безо всяких угрызений совести blush.gif.
Попутно спрошу.
1. Я так понимаю, что в uIP есть возможность подключения нескольких соединений равно как и несколько портов, но последнее не означает, что в uIP можно врубить два сервака сразу, например, web сервер и telnet, поскольку существует только одно определение app_call, которое вызывается обработчиком событий TCP. Правильно я понимаю?
2. Могут ли одновременно работать TCP и UDP в uIP?

Спасибо за внимание.
e-yes
1. Не правильно понимаешь. Есть еще uip_app_registration.[c|h].
2. Why not?

P.S. Интересует скорость... у нас с uIP - полная #опа. Пинги быстрые, но TCP тормозит страшно (скорость ~10-15 Мбит).
etoja
Исходники прилагаю.
Каким компилятором пользовались?
Цырен
2 etoja
Спасибо, впервые вижу этот код, хотя всю сетку облазил. Буду разбираться. Я пользуюсь IAR-ом 3.40.

2 e-yes
"1. Не правильно понимаешь. Есть еще uip_app_registration.[c|h]."
Странно, я не нашел файлы, которые вы указали. Если не сложно, можно пояснить теорию по их использованию и ссылочку на них дать?
ig_z
Цитата(Цырен @ Aug 10 2007, 14:31) *
1. Я так понимаю, что в uIP есть возможность подключения нескольких соединений равно как и несколько портов, но последнее не означает, что в uIP можно врубить два сервака сразу, например, web сервер и telnet, поскольку существует только одно определение app_call, которое вызывается обработчиком событий TCP. Правильно я понимаю?


Недокурили доку smile.gif
Где то в доке есть пример как запускать два сервака вместе. У меня именно web сервер и telnet работали вместе. По памяти проверяешь порт и запускаешь нужное апп
Цырен
Цитата(ig_z @ Aug 13 2007, 12:05) *
Недокурили доку smile.gif
Где то в доке есть пример как запускать два сервака вместе. У меня именно web сервер и telnet работали вместе. По памяти проверяешь порт и запускаешь нужное апп


Я полазил по зарубежным форумам и нашел следующую подсказку от автора uIP:
You can use a union, something like this:

typedef union {
struct app1_state a1;
struct app2_state a2;
struct app3_state a3;
} uip_tcp_appstate_t;


and access the state structures through appstate->a1.var1; and so forth. This way no extra memory is required.

Hope this helps,

/adam

Marc Lehmann wrote:

Hello everyone out there

I'm trying to build an application that has several features using uIP.
Each of that features has its own structure to store the application
state. And that's where the problems start. Of course it's not possible to
typedef more than one structure to be uip_tcp_appstate_t.

I tried the add-on module uip_app_registration, but it doesn't take care
of uip_tcp_state.

Any ideas? Is there any workaround? Of couse I could put all the
application states together into one huge structure, but I'd rather prefer
a "smart" solution. wink.gif

Thanks
Marc


--
Adam Dunkels, Swedish Institute of Computer Science


Буду следовать совету автора.

2 e-yes
Я нашел указанные файлы на старой версии сайта uIP

2 ig_z
Вы делали также? Если нет, то можно подробнее?
e-yes
>Я нашел указанные файлы на старой версии сайта uIP
Ну у меня они на локальном диске, уже не помню где бралsmile.gif
Но и без них подобную функциональность реализовать - не вопросsmile.gif

Так что насчет скорости у uIP?
Цырен
Цитата(e-yes @ Aug 13 2007, 19:07) *
>
Так что насчет скорости у uIP?


Знаешь, в моей задаче скорость - не критичный параметр, поэтому я не интересовался, но скажу, что скорость в 10-15Мбит - это уже не плохо.
ig_z
Цитата(Цырен @ Aug 13 2007, 11:15) *
2 ig_z
Вы делали также? Если нет, то можно подробнее?


Я делал по другому, как советовал автор.
Я не помню названия пдфки (я несколько пдф склеил для печати книжкой), но в ней есть раздел
"6.3 Di®erentiating between applications". Все идеи оттуда.

Ключевые моменты привожу ниже.

Из Main
Код
int main(void)
{
  uip_init();
  app_init();          

  uip_kos_input.RunDly(0);
  uip_kos_output.RunDly(0);
  uip_kos_arp.RunDly(0);
  OS.Start();
}


Из App
Код
void app_init()
{
...
  telnetd_init();
  http_uart_bridge_init();
}

void application()
{
    switch (uip_conn->lport)
    {
    case HTONS(IP_UART_PORT):
        http_uart_bridge_appcall();
        break;

    case HTONS(TELNET_PORT):
        telnetd_appcall();
        break;
        
    default:       /* Should never happen. */
        uip_abort();
        break;
    }
}


http_uart_bridge_appcall() и telnetd_appcall() - приложения верхнего уровня. В данном случае 2 сервера: мост уарт-тцпип и телнет. А начиналось с серверов нттп и телнет.
Цырен
2 ig_z
Спасибо. Когда встанет вопрос о мультизадачности, воспользуюсь вашим советом и вспомню добрым словом.

Сижу и и прикручиваю свои нужны к uIP. Пока ищу место, откуда бы вставить свой tftp к UDP.
Пока я думаю как определить uip_udp_appstate_t и каким должен быть состав структуры tftp соединения...
Цырен
Я сделал так, чтобы на uIP работали сразу два сервака!!! В моем случае - telnet и web
А сделал я вот что:
1. Создал файлики
multiapplication.h:
#ifndef __MULTIAPPLICATION_H__
#define __MULTIAPPLICATION_H__

#include "webserver.h"
#include "telnetd.h"

extern void application(void);


typedef union {
struct telnetd_state uip_tcp_telnet;
struct httpd_state uip_tcp_http;
} uip_tcp_appstate_t;

#endif /*__MULTIAPPLICATION_H__*/

, multiapplication.c:
#include "uip.h"
#include "webserver.h"
#include "telnetd.h"
#include "multiapplication.h"

void application(void)
{
switch (uip_conn->lport)
{
case HTONS(80):
httpd_appcall();
break;

case HTONS(23):
telnetd_appcall();
break;

default: /* Should never happen. */
uip_abort();
break;
}
}
2. Закоментил typedef-нги в telnetd.h и webserver.h соответственно
//typedef struct telnetd_state uip_tcp_appstate_t;
//typedef struct httpd_state uip_tcp_appstate_t;

3. Определил UIP_APPCALL как (старые определения удалил)

#ifndef UIP_APPCALL
#define UIP_APPCALL application
#endif

4. Инклудил multiapplication.h в uIP.c

Таким образом, можно вставлять в uIP серваки по различным портам скока хочешь(в пределах имеющихся на свете с учетом размера памяти в МК smile.gif

Но, вот такая байда, что-то я отошел от прямой задачи - портировать TFTP поверх IP/UDP.
Если кто что может полезное кинуть, сказать - прошу, я буду рад.
etoja
TFTP гораздо проще, чем FTP.
TFPT использует для доставки информации пакеты UDP и открыват при этом один порт. В TFTP нет авторизации через логин и пароль.
FTP использует для доставки информации протоколы TCP/IP, открывает два порта - один для команд, второй для данных, использует авторизацию через логин и пароль.
Цырен
Я почти близок к своей заветной цели. Я вот думаю как организовать тайм-аут. Ведь у каждого нового подключения есть своя история (был ли подтвержден последний блок, сколько было попыток ретрансляции и еще что-нибудь). Помоему одной глобальной переменной не обойтись, как вы считаете, те, кто разбирался в TFTP?
Кстати пришлось много где поискать инфу по протоколу. RFC1350 (ftp://ftp.rfc-editor.org/in-notes/rfc1350.txt) - похоже единственный источник, давший полное представление о протоколе. Но по началу очень помогла ссылка http://andr.ru/lib/comp/net/tcpip/tcpip_il...ated/tcp15.html - очень доступным языком. Да и вообще на этом сайте есть все про протоколы.

Мысли вслух:
1. Интересно получается, для того, чтобы TFTP заработал надо инициализировать его, создав в таблице UDP-соединений соединение с параметрами ripaddr=0.0.0.0, rport=0 и lport=69 (ну там еще ttl есть, не понятно для чего он там для TFTP). TFTP постоянно прослушивает 69-й порт и когда появляется UDP-сообщение на наш IP и порт 69 (это запрос на чтение или запись: opcode=1или2), сервер должен создать в таблице UDP-соединений новое соединение по случайному номеру локального порта lport. В этой новой записи сохраняются даные об IP-адресе и номере порта удаленного хоста(ripaddr=x, rport=y), вытащенные из UDP-пакета. И сразуже происходит обработка пришедших данных, с последующим ответом (подтверждение на запись или посылка первого блока запрошенных данных). Т.о. в таблице UDP-соединений появляются две записи, одна - для ублажения нужд подключившегося клиента, вторая - для рецепшена возможных новых соединений. А о состоянии соединения будут отражать какие-нибудь глобальные переменные. Если так, то получается для каждого соединения нет индивидуальных данных о том, было ли подтверждение на прошлый блок или, например, сколько времени прошло с момента посылки последнего блока. А если соединений три, допустим, и в одном из них произошел разрыв. Что же, теперь все остальные соединения канут в лету? Как же быть? Создать структуру(struct) в которой будут все параметры состояния соединения и построить массив такого типа размером равным размеру массива UDP-соединений, чтобы потом ассоциировать каждый элемент такого массива состояний с соответствующим соединением?

2. TFTP должно само позаботиться о том как поправить UDP-заголовок. А точнее задать IP-адрес получателя, наш IP-адрес, порт получателя, наш порт в пакете сообщения и длину UDP-пакета с заголовком. А надо ли длину?
zltigo
Цитата(Цырен @ Aug 20 2007, 00:37) *
RFC1350 (ftp://ftp.rfc-editor.org/in-notes/rfc1350.txt) - похоже единственный источник, давший полное представление о протоколе.

Полное? А эти забыли еще почитать RFC 1785, ,RFC 2090, RFC 2347, RFC 2348, RFC 2349, если будете работать с продвинутыми клиентами.
Цырен
Цитата(zltigo @ Aug 20 2007, 01:54) *
Полное? А эти забыли еще почитать RFC 1785, ,RFC 2090, RFC 2347, RFC 2348, RFC 2349, если будете работать с продвинутыми клиентами.


Это уже бег с препятствиями. А мне бы пока первые шаги освоить.
Цырен
Кажется я ответил себе как отследить состояние каждого соединения для того, чтобы реализовать множественное подключение к моему серверу.
Ответ был как всегда под руками и никаких дополнительных глобальных переменных, отслеживающих состояние сервера не надо. Ответ вот где:

struct uip_udp_conn {
uip_ipaddr_t ripaddr; /**< The IP address of the remote peer. */
u16_t lport; /**< The local port number in network byte order. */
u16_t rport; /**< The remote port number in network byte order. */
u8_t ttl; /**< Default time-to-live. */

/** The application state. */
uip_udp_appstate_t appstate;
};

typedef struct tftp_state uip_udp_appstate_t;

struct tftp_state {
u16_t opcode; /* Êîä îïåðàöèè */
u16_t block_id; /* Íîìåð áëîêà */
u8_t *data; /* Óêàçàòåëü íà äàííûå */
};

Делая так:
struct uip_udp_conn *tftp_conn; // некое UDP-соединение
tftp_conn = (struct uip_udp_conn*)uip_udp_conn;
имеем доступ к состоянию текущего UDP-соединения (из таблицы UDP-соединений):

tftp_conn->appstate.opcode -код операции, которую данное соед. совершает (дает читать или записывать)
tftp_conn->appstate.block_id -какой блок данных он дает читать/записывать
tftp_conn->appstate.data -указатель на данные с которыми сервер должен сделать что-либо(записать/прочитать блок в/из файл).

Кому интересно:

Причем текущее соединение устанавливается только если:
1. в таблице UDP-соединений есть хоть одна запись с локальным номером порта TFTP(69 или иной не равный 0)
2. UDP-датаграмма предназначена именно порту текущего соединения
3. Текущее соединение "узнает" номер порта удаленного хоста или видит его впервые, но готов пообщаться, т.к. rport=0(типа сервер свободен и готов к новой связи)
4. Текущее соединение "узнает" IP-адрес удаленного хоста или видит его впервые, но готов пообщаться, т.к. tipaddr=0.0.0.0.
(IP-адрес проверяет IP)

При входе в наш обработчик APP_CALL() (у меня это void tftp_appcall(void)) надо:
1. определить состояние флага uip_flags( либо UIP_POLL либо UIP_NEWDATA)
1.1. Если UIP_POLL, то проверить условие истечения таймаута
1.2. Если UIP_NEWDATA, то проверить для какого порта именно предназначен пакет:
1.2.1 Если для порта 69, то создать новое соединение tftp_conn = uip_udp_new(&ripaddr, rport) и
обработать запрос (ничего другого быть и не может).
1.2.2 Если для нашего(а это проверялось еше в uip_process()), тогда обработать данные или
подтверждение.

З.Ы. При закрытии соединения (удаления ее из таблицы UDP-соединений) обязательно обнулять поля ripaddr и rport.
З.Ы. При инициализации надо создавать UDP-соединение в таблице под номером порта 69.
void tftp_init(void)
{
uip_ipaddr_t addr;
struct uip_udp_conn *c;

uip_ipaddr(&addr, 0,0,0,0);
c = uip_udp_new(&addr, HTONS(0)); // Адрес и номер порта удаленного хоста нулевые.
if(c != NULL) {
uip_udp_bind(c, HTONS(69)); // Стандартный порт TFTP
}
}
Usatyj
уважаемый Цырен!
поможите исходничком, а? не понимаю, как обрабатывать tftp_appcall (сущность его какая должна быть).
заранее благодарен.
Aprox
Цитата(Цырен @ Aug 20 2007, 16:51) *
Кажется я ответил себе как отследить состояние каждого соединения для того, чтобы реализовать множественное подключение к моему серверу.


Как я понял, вам нужен UDP сервер? Только что закончил такую разработку и прекрасно обошелся без готовых uIP, в которых сам черт ногу сломит. Суть проста. -Принимаем пакет, адресованный серверу по МАС, по IP, и по номеру порта. Выделяем запрос, обрабатываем его и выдаем пакет ответа в хидере которого, стоят МАС клиента, IP-клиента, и порт запроса клиента. Все эти данные выделяются из хидера запроса. Таким образом, UDP-сервер откликается всякому клиенту, кто грамотно к нему обратился. И никаких таблиц соединений и прочей лабуды не требуется.

Кстати, скорость передачи потока данных в пакетах UDP размером 1.5К у меня на STR912FAW44 получилась 96 Мbit/s Получилась только потому, что никакого готового софта uIP не использовал.
PoReX
Подскажите с UDP на uIP v1.0. Никак не получается отправить пакет, хотя принимать получается.
Код
void uip_udp_appcall(void)
{
    if(uip_udp_conn->lport == HTONS(BOOTETH_LISTENUDPPORT))
    {
    //-----------------
        if(uip_newdata()) {udp_newdata(uip_udp_conn);}
    //-----------------
    }
}

обработчик принятых данных
Код
void udp_newdata(struct uip_udp_conn *udp_conn)
{
    struct uip_udp_state *s = &(udp_conn->appstate);
    struct uip_udp_conn *udp_connection;
    uint8_t *pBuffer,i;
    uint32_t posnum=0;
    uint32_t BytesLeft=bMIN(UIP_CONF_BUFFER_SIZE,uip_datalen());
    udp_connection=uip_udp_new(&udp_conn->ripaddr, udp_conn->rport);
        if(udp_connection==NULL)
            return;
        //-------------------
        //тут обработка полученных байт
        //-------------------
    uip_udp_remove(udp_connection);

}

отправка буфера по UDP
Код
void ETH_EthUDPFindResponse(void)
{
    //----------------------------------
    //формирование буфера
    //----------------------------------
    //memcpy(uip_appdata,ETH_TransmitBuffer,Len);
    //uip_udp_send(Len);    
    //uip_send(ETH_TransmitBuffer,(uint32_t)pBuffer-(uint32_t)ETH_TransmitBuffer);    
    uip_slen = Len;
    memcpy(&uip_buf[UIP_LLH_LEN + UIP_IPUDPH_LEN], ETH_TransmitBuffer, Len > UIP_BUFSIZE? UIP_BUFSIZE: Len);
  uip_process(UIP_UDP_SEND_CONN);
    uip_slen = 0;
}


Может есть у кого рабочий пример с UDP?
podelkin
а lwip слишком тяжел? По мне так продвинутее на порядок
PoReX
Цитата(podelkin @ Feb 5 2013, 16:17) *
а lwip слишком тяжел? По мне так продвинутее на порядок

Да, это проект bootloader'a, чем меньше размер тем лучше. lwIP используется в основной прошивке.
scifi
Цитата(PoReX @ Feb 5 2013, 16:17) *
Может есть у кого рабочий пример с UDP?

Есть рабочий пример. Как раз загрузчик.
PoReX
Цитата(scifi @ Feb 5 2013, 19:08) *
Есть рабочий пример. Как раз загрузчик.

Спасибо за пример, но что-то не получается. Вроде все так же делаю, а в чем причина не пойму. Может какую-нибудь мелочь упустил?
Во вложении проект в Keil и приложение для отправки UDP пакетов.

Только увидел, что у меня более старая версия uIP. Попробую завтра с вашей версией.
PoReX
Одна проблема решилась. Оказывается uip шлет udp сообщения, но адрес получателя и порт нулевые, поэтому ближайший роутер отбрасывает это сообщение и я в Wireshark'е на компе его не вижу. После подключения STM к компу на прямую, сообщения стали видны, но проблема с неправильным адресом получателя и портом осталась. Насколько я понял, происходит это из-за того что при отправке сообщения uip использует первую структуру(соединение)
Код
struct uip_udp_conn
, в которой как раз и есть все нули в поле адреса и порта. Пробовал присвоить указателю
Код
uip_udp_conn
адрес структуры, полученной при создании нового соединения, но тогда сообщения вообще не отправляются.
CODE
void udp_newdata(struct uip_udp_state *s)
{

struct uip_udp_conn *udp_connection;
struct uip_udpip_hdr *hdr;
uint8_t *pBuffer;
uint32_t BytesLeft=bMIN(UIP_CONF_BUFFER_SIZE,uip_datalen());

hdr = (struct uip_udpip_hdr *)&uip_buf[UIP_LLH_LEN];
udp_connection= uip_udp_new(&hdr->srcipaddr, hdr->srcport);
if(udp_connection==NULL)
return;
memcpy(s->inputbuffer,uip_appdata,BytesLeft);
pBuffer=(uint8_t*)&s->inputbuffer;
if(memcmp(pBuffer,tcp_startnewmessage,sizeof(tcp_startnewmessage))==0)
{
uip_udp_conn = udp_connection; //<<<<<<< присваиваем новый указатель
ETH_SendUDPResponse();
}

uip_udp_remove(udp_connection);

}


P.S. uIP из моего проекта.
PoReX
Наконец я победил uip:) Как и думал проблема заключалось в одном маленьком недочете:
При начальной инициализации udp вместо нулевого указателя на адрес удаленного хоста
Код
udp_conn = uip_udp_new(0, 0);
я передавал указатель на структуру
Код
uip_ipaddr_t
с забитыми нулями
Код
uip_ipaddr(ipaddr, 0,0,0,0);
udp_conn = uip_udp_new(&ipaddr, 0);
ElectronicsLab
Добрый день. Что-то не получается у меня отправить данных с МК на комп. Отвечать на сообщения с компа я умею, колбэки вызываются, там обрабатываю пакеты, отправляю ответы в виде uip_send().
Но вся беда в том, что uip_send() нужно вызывать только в качестве ОТВЕТА на запрос с компа.

А как сделать так, чтобы независимо слать данные с МК на комп? Чтобы через WireShark видеть что что-то шлется..?
kolobok0
Цитата(ElectronicsLab @ Aug 20 2013, 14:31) *
...независимо слать данные с МК на комп? Чтобы через WireShark видеть что что-то шлется..?


Вы бы посмотрели тем же ваирШарком как это делает обычная рабочая станция. в теории - зная IP шлётся бродкаст ARP запрос. По ответу(если отозвался) формируется пакет, со всеми уже известными полями.
Как реализовано в конкретной библиотеке - надо вкуривать.
kramener
Цитата(PoReX @ Feb 7 2013, 14:55) *
Наконец я победил uip:) Как и думал проблема заключалось в одном маленьком недочете:
При начальной инициализации udp вместо нулевого указателя на адрес удаленного хоста
Код
udp_conn = uip_udp_new(0, 0);
я передавал указатель на структуру
Код
uip_ipaddr_t
с забитыми нулями
Код
uip_ipaddr(ipaddr, 0,0,0,0);
udp_conn = uip_udp_new(&ipaddr, 0);


Столкнулся с такой же проблемой: адрес и порт получателя нулевые, хотя нулевой указатель передаю и адрес структуры при получении сообщения.
Вы не могли бы выложить свою программу для контроллера, отвечающую за отправку UDP?
kramener
Только что сам разобрался с uIP sm.gif) Выложу код на всякий случай. Вдруг кому пригодится.
Разрабатываю на Stellaris DK-LM3S9D96. uIP 1.0

Инициализация:
Код
void udp_init()
{
  struct uip_udp_conn *udp_conn;
  udp_conn = uip_udp_new(0, 0);
  
  if (udp_conn == 0)
    return;

  udp_conn->appstate = 0;
  uip_udp_bind(udp_conn, HTONS(DEFAULT_UDP_PORT));
}


Callback. process_msg записывает в структуру outputMsg содержимое и выходной размер outputLen
Код
void udp_appcall(void)
{
  if (uip_newdata())
  {
    if (uip_udp_conn->lport == HTONS(DEFAULT_UDP_PORT))
    {
        static struct uip_udpip_hdr *hdr;
        hdr = (struct uip_udpip_hdr *)&uip_buf[UIP_LLH_LEN];
        uip_ipaddr_copy(&uip_udp_conn->ripaddr, hdr->srcipaddr);
        uip_udp_conn->rport = hdr->srcport;
  
        inputLen = bMIN(uip_datalen(), sizeof(tRawUdpMessage));
        memcpy((char *)&inMsg, (char *)uip_appdata, inputLen);
        
        process_msg(&inMsg, &inputLen, &outMsg, &outputLen);
        
        memcpy((char *)uip_appdata(), (char *)&outMsg, outputLen);
        uip_slen = outputLen;
  
        uip_process(UIP_UDP_SEND_CONN);
    }
  }
}


P.S: Просьба модераторам объединить 2 сообщения.

Цитата(ElectronicsLab @ Aug 20 2013, 14:31) *
А как сделать так, чтобы независимо слать данные с МК на комп? Чтобы через WireShark видеть что что-то шлется..?

Юзайте uip_poll(). Вот что по этому поводу говорит мануал: http://www.gaisler.com/doc/net/uip-0.9/doc/html/main.html
Цитата
The polling event has two purposes. The first is to let the application periodically know that a connection is idle, which allows the application to close connections that have been idle for too long. The other purpose is to let the application send new data that has been produced. The application can only send data when invoked by uIP, and therefore the poll event is the only way to send data on an otherwise idle connection.
Myrik
Мне бы вернутся к теме о двух серваках на uIP. Делаю всё как в посте Цырена. Но в результате получаю ошибку "Error[Pe070]: incomplete type is not allowed" в строке struct httpd_state uip_tcp_http;.
При этом typedef struct httpd_state uip_tcp_appstate_t; компилируется без проблем.
Что я сделал не так?
Код
#ifndef __MULTIAPPLICATION_H__
#define __MULTIAPPLICATION_H__

#include "webserver.h"
#include "telnetd.h"

extern void application(void);


typedef union{
  struct telnetd_state uip_tcp_telnet;
  struct httpd_state uip_tcp_http;
} uip_tcp_appstate_t;

//typedef struct httpd_state uip_tcp_appstate_t;

#ifndef UIP_APPCALL
#define UIP_APPCALL application
#endif

#endif
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.