LWIP ECHO SERVER: Как увеличить размер буфера в функции itoa?

Я работаю с дизайном Xilinx Ethernetlite (LWIP). Я могу передавать данные с платы KC на ПК (Hercules) через Ethernet, только если buf =32. Но мой фактический размер буфера 1024. Как увеличить размер буфера с 32 до 1024

Я не могу убедиться, ошибка в коде или в геркулесе. Чтобы прочитать значения (целые числа) в Геркулесе, я выполняю эту функцию.

сначала от Hercules я отправлю команду Hello на Board, а затем на Board приму этот запрос. После этого плата выдаст данные (целочисленные значения) в Hercules.

C-код для itoa

char* itoa(int val, int base)
{
static char buf[32] = {0};          //buf size 
int i = 30;

for(; val && i ; --i, val /= base)
buf[i] = "0123456789abcdef"[val % base];
return &buf[i+1];
}

Модифицированный код

    #define  DAQ_FIFO_DEPTH  128

  int transfer_data() 
  {
  return 0;
  }

  err_t tcp_write_u32_string(struct tcp_pcb *pcb, unsigned char   prefix, u32_t value)
   {
    unsigned char  buf[11]; /* enough room for prefix and value. */
    err_t          result;
    u16_t          len;
    unsigned char *p = buf + sizeof buf;
  do {
    /* ASCII encoding: '0' = 48, '1' = 49, ..., '9' = 57. */
    *(--p) = 48 + (value % 10u);
    value /= 10;
     } while (value);
     if (prefix)
    *(--p) = prefix;
 len = buf + sizeof buf - p;
  if (tcp_sndbuf(pcb) < len) 
     {
    result = tcp_output(pcb);
    if (result != ERR_OK)
        return result;
    }
 return tcp_write(pcb, p, len, TCP_WRITE_FLAG_COPY | TCP_WRITE_FLAG_MORE);
  }

   err_t send_list(struct tcp_pcb *pcb, const u32_t data[], u16_t len)
  {
  static const char  newline[2] = { 13, 10 }; /* ASCII \r\n */
  err_t              result;

    if (len > 0) {
     u16_t  i;


      result = tcp_write_u32_string(pcb, 0, data[0]);
      if (result != ERR_OK)
        return result;
    for (i = 1; i < len; i++) 
   {
        /* ASCII comma is code 44. (Use 32 for space, or 9 for tab.) */
        result = tcp_write_u32_string(pcb, 44, data[i]);
        if (result != ERR_OK)
            return result;
     }
   }
   result = tcp_write(pcb, newline, 2, 0);
    if (result)
    return result; 
   return tcp_output(pcb);
  }
 int application_connection(void *arg, struct tcp_pcb *conn, err_t err)
 {
  struct netif *netif = arg; /* Because of tcp_arg(, netif). */
  u32_t         data[DAQ_FIFO_DEPTH];
  u32_t         i, n;
 if (err != ERR_OK) {
    tcp_abort(conn);
    return ERR_ABRT;
   }
  err = daq_setup();
  if (err != ERR_OK) 
  {
    tcp_abort(conn);
    return ERR_ABRT;
 }
 while (1) 
    {
    xemacif_input(netif);
    tcp_tmr();
    tcp_output(conn);
    n = daq_acquire(data, DAQ_FIFO_DEPTH);
    if (n > DAQ_FIFO_DEPTH)
        break;
    if (tcp_write(conn, data, n * sizeof data[0], TCP_WRITE_FLAG_COPY) != ERR_OK)
        break;
     }
// daq_close();

/* Close the TCP connection. */
    if (tcp_close(conn) == ERR_OK)
     return ERR_OK;

/* Close failed. Abort it, then. */
    tcp_abort(conn);
    return ERR_ABRT;
    }
  int application_main(struct netif *netif, unsigned int port)
  {
   struct tcp_pcb *pcb;
   err_t           err;
   pcb = tcp_new();
   if (!pcb) {
    /* Out of memory error */
    return -1;
      }
    err = tcp_bind(pcb, IP_ADDR_ANY, port);
    if (err != ERR_OK) {
    /* TCP error */
    return -1;
     }
   pcb = tcp_listen_with_backlog(pcb, 1);
  if (!pcb) {
    /* Out of memory. */
    return -1;
    }
  tcp_arg(pcb, netif); 
  tcp_accept(pcb, application_connection);
  while (1)
  xemacif_input(netif);
  }

Вывод Hercules введите здесь описание изображения


person Abi    schedule 26.10.2018    source источник
comment
Как увеличить размер буфера с 32 до 1024 зачем вы хотите это сделать? Диапазон int ограничен, а вместе с ним и размер, необходимый для его текстового представления. Чтобы иметь число до apx. 1000 символов в ширину, вам нужно дооооолгое целое.   -  person alk    schedule 26.10.2018


Ответы (1)


Итак, это продолжение обсуждения на Форумы Xilinx?

Функция itoa() преобразует целое число без знака (хранящееся в int) в первые 30 или около того символов в буфере buf.

Функция recv_callback() практически не имеет смысла.

Вызов aurora_rx_main() задокументирован как «FUNCTION CALL», что мало чем полезно (потому что мы понятия не имеем, что он делает), и даже его возвращаемое значение полностью игнорируется.

Первый цикл for выгружает содержимое первых 100 u32 в DestinationBuffer[] в целях отладки, поэтому этот код не имеет отношения к выполняемой задаче. Однако мы не знаем, кто или что заполнило DestinationBuffer. Он мог быть заполнен или не заполнен вызовом aurora_rx_main(); нам не говорят в любом случае.

(Похоже, что функции tcp_*() следуют API, описанному в lwIP Wiki на Wikia.)

Если параметр p равен NULL, то вызывается tcp_close(tcpb), за которым следует вызов tcp_recv(tcpb, NULL). Это имеет наименьший смысл: зачем пытаться что-то получить (и почему параметр NULL) после закрытия?

Следующая часть так же озадачивает. Похоже, что тест if проверяет, превышает ли размер буфера отправки TCP 1024 байта. Если нет, буфер p освобождается. В противном случае цикл for пытается преобразовать каждое u32 в DestinationBuffer в строку, записать эту строку в буфер TCP; однако вместо правильных флагов API он использует константу 1 и даже не проверяет, работает ли добавление в буфер отправки TCP.

В общем, это выглядит как куча скопированного кода, который ничего толкового не делает. Увеличение размера буфера в функции itoa не только не нужно (u32, даже при преобразовании в int, всегда будет умещаться в пределах 12 символов (за исключением знака минус или нулевого байта в конце, поэтому сделайте эти 13 символов всего), но совершенно не связан с проблемой, которую он должен решить.

Основная проблема в том, что код ужасен. Его изменение похоже на нанесение наполнителя на кусок старой жевательной резинки в попытке «починить» его. Правильное решение — полностью удалить этот ненужный код и использовать вместо него что-то получше.

Изменить: ОП заявляет, что они новые программисты, поэтому приведенные выше комментарии следует воспринимать как прямое и честное мнение о показанном коде, а не о самих ОП. Давайте посмотрим, сможем ли мы помочь OP создать лучший код.


Во-первых, показанная функция itoa() глупа. Предполагая, что намерение действительно состоит в том, чтобы отправить обратно u32_ts в DestinationBuffer в виде десятичных строк, гораздо лучше реализовать вспомогательную функцию для выполнения преобразования. Поскольку перед значением должна стоять запятая (или какой-либо другой разделитель), мы также можем добавить это тривиально. Так как он будет отправлен с использованием tcp_write(), объединим функционал:

err_t tcp_write_u32_string(struct tcp_pcb *pcb,
                           unsigned char   prefix, /* 0 for none */
                           u32_t           value)
{
    /* Because 0 <= u32_t <= 4294967295, the value itself is at most 10 digits long. */
    unsigned char  buf[11]; /* enough room for prefix and value. */
    err_t          result;
    u16_t          len;
    unsigned char *p = buf + sizeof buf;

    /* Construct the value first, from right to left. */
    do {
        /* ASCII encoding: '0' = 48, '1' = 49, ..., '9' = 57. */
        *(--p) = 48 + (value % 10u);
        value /= 10;
    } while (value);

    /* Prepend the prefix, if any. */
    if (prefix)
        *(--p) = prefix;

    /* Calculate the length of this part. */
    len = buf + sizeof buf - p;

    /* If the TCP buffer does not have enough free space, flush it. */
    if (tcp_sendbuf(pcb) < len) {
        result = tcp_output(pcb);
        if (result != ERR_OK)
            return result;
    }

    /* Append the buffer to the TCP send buffer.
       We also assume the packet is not done yet. */
    return tcp_write(pcb, p, len, TCP_WRITE_FLAG_COPY | TCP_WRITE_FLAG_MORE);
}

так что для отправки num u32_ts из указанного массива в виде десятичных строк с новой строкой в ​​конце вы можете использовать

err_t send_list(struct tcp_pcb *pcb,
                const u32_t data[],
                u16_t len)
{
    static const char  newline[2] = { 13, 10 }; /* ASCII \r\n */
    err_t              result;

    if (len > 0) {
        u16_t  i;

        /* The first number has no prefix. */
        result = tcp_write_u32_string(pcb, 0, data[0]);
        if (result != ERR_OK)
            return result;

        /* The following numbers have a comma prefix. */
        for (i = 1; i < len; i++) {
            /* ASCII comma is code 44. (Use 32 for space, or 9 for tab.) */
            result = tcp_write_u32_string(pcb, 44, data[i]);
            if (result != ERR_OK)
                return result;
        }
    }

    /* We add a final newline.
       Note that this one can be referenced,
       and it does complete what we wanted to send thus far. */
    result = tcp_write(pcb, newline, 2, 0);
    if (result)
        return result;

    /* and flush the buffer, so the packet gets sent right now. */
    return tcp_output(pcb);
}

Я не писал C для Xilinx и вообще не использовал стек lwIP, поэтому приведенный выше код написан вслепую. Тем не менее, я почти уверен, что это работает (за исключением опечаток или мыслей; если вы найдете их, сообщите о них в комментарии, и я проверю и исправлю).

Два буфера (buf и newline) объявлены как static, поэтому, хотя они видны только внутри соответствующих функций, их значение допустимо в глобальной области видимости.

Поскольку TCP является потоковым протоколом, нет необходимости подгонять каждый ответ к одному пакету. Помимо 11-символьного (для каждого числа и символа префикса) и 2-символьного (новая строка) буферов, единственный большой буфер, который вам нужен, — это буфер отправки TCP (максимальная единица передачи или максимальный сегмент size, поскольку я не уверен, как lwIP использует внутренний буфер), обычно между 536 и 1518 байтами.

Две вышеупомянутые функции пытаются разделить пакеты между номерами, но это просто потому, что это проще, чем пытаться заполнить каждый пакет точно. Если следующее (запятая и) значение помещается в буфер, то оно добавляется в буфер; в противном случае сначала очищается буфер, а затем в буфер добавляется следующее значение (запятая и).

Со стороны получателя вы должны получить хороший, читаемый поток, используя, например. netcat. (Я понятия не имею, является ли Hercules приложением или просто именем вашей локальной машины.) Поскольку TCP является потоковым протоколом, получатель не может (надежно) определить, где были границы пакета (в отличие, скажем, от дейтаграмм UDP). На практике TCP-соединение — это всего лишь два потока данных, каждый из которых идет в одну сторону, а разделение на пакеты — это всего лишь детали протокола, о которых программистам приложений не нужно беспокоиться. Для lwIP, поскольку это такая низкоуровневая библиотека, нужно немного позаботиться, но, как видно из приведенного выше кода, на самом деле это не так уж много.


В комментарии OP объяснил, что они не очень опытны и что общая цель состоит в том, чтобы устройство принимало TCP-соединение и передавали данные (образцы, полученные отдельной платой сбора данных) в виде 32-битных целых чисел без знака через соединение.

Поскольку я хотел бы иметь одну из этих плат FPGA (у меня есть несколько задач, которые я мог бы решить, если бы я мог перенести их на FPGA), но нет ресурсов для ее получения, я попытаюсь описать здесь все приложение. Обратите внимание, что единственная информация, которую я могу предоставить, — это версия Xilinx OS and Libraries Document Collection (UG643) 2018 года (PDF). Похоже, OP хочет использовать необработанный API для повышения производительности.

Преобразование образцов в текст глупо, особенно если требуется высокая производительность. Мы должны просто использовать необработанный двоичный код и любой порядок следования байтов, который использует KC705. (Я не увидел этого при беглом просмотре документации, но подозреваю, что это прямой порядок байтов).

Согласно документации, необработанный API main() выглядит примерно так:

int main(void)
{
    /* MAC address. Use an unique one. */
    unsigned char  mac[6] = { 0x00, 0x0A, 0x35, 0x00, 0x01, 0x02 };

    struct netif  *netif = NULL;
    ip_addr_t      ipaddr, netmask, gateway;

    /* Define IP address, netmask, and gateway. */
    IP4_ADDR(&ipaddr,  192, 168,   1,  1);
    IP4_ADDR(&netmask, 255, 255, 255,  0);
    IP4_ADDR(&gateway,   0,   0,   0,  0);

    /* Initialize lwIP networking stack. */
    lwip_init();

    /* Add this networking interface, and make it the default one */
    if (!xemac_add(netif, &ipaddr, &netmask, &gateway, mac, EMAC_BASEADDR)) {
        printf("Error adding network interface\n\r");
        return -1;
    }
    netif_set_default(netif);

    platform_enable_interrupts();

    /* Bring the network interface up (activate it) */
    netif_set_up(netif);

    /* Our application listens on port 7. */
    return application_main(netif, 7);
}

В примерах документации вместо return application_main(netif); вы увидите вызов start_application(), а затем бесконечный цикл, который вместо этого регулярно вызывает xemacif_input(netif). Это просто означает, что out application_main() должен регулярно вызывать xemacif_input(netif), чтобы иметь возможность получать данные. (В документации lwIP сказано, что мы также должны вызывать sys_check_timeouts() или tcp_tmr() через равные промежутки времени.)

Обратите внимание, что я пропустил отчеты об ошибках printfs, и вместо того, чтобы корректно восстанавливать ошибки, это просто вернет (из main()); Я не уверен, вызывает ли это перезапуск KC705 или что-то еще.

int application_main(struct netif *netif, unsigned int port)
{
    struct tcp_pcb *pcb;
    err_t           err;

    pcb = tcp_new();
    if (!pcb) {
        /* Out of memory error */
        return -1;
    }

    /* Listen for incoming connections on the specified port. */
    err = tcp_bind(pcb, IP_ADDR_ANY, port);
    if (err != ERR_OK) {
        /* TCP error */
        return -1;
    }
    pcb = tcp_listen_with_backlog(pcb, 1);
    if (!pcb) {
        /* Out of memory. */
        return -1;
    }

    /* The accept callback function gets the network interface
       structure as the extra parameter. */
    tcp_arg(pcb, netif);

    /* For each incoming connection, call application_connection(). */
    tcp_accept(pcb, application_connection);

    /* In the mean time, process incoming data. */
    while (1)
        xemacif_input(netif);
}

Для каждого TCP-подключения к порту мы получаем вызов application_connection(). Это функция, которая настраивает плату сбора данных и передает данные до тех пор, пока этого хочет получатель.

/* How many DAQ samples to process in each batch.
 * Should be around the DAQ FIFO depth or so, I think. */
#define  DAQ_FIFO_DEPTH  128

err_t application_connection(void *arg, struct tcp_pcb *conn, err_t err)
{
    struct netif *netif = arg; /* Because of tcp_arg(, netif). */
    u32_t         data[DAQ_FIFO_DEPTH];
    u32_t         i, n;

    /* Drop the connection if there was an error. */
    if (err != ERR_OK) {
        tcp_abort(conn);
        return ERR_ABRT;
    }

    /* Setup the data aquisition. */
    err = daq_setup();
    if (err != ERR_OK) {
        tcp_abort(conn);
        return ERR_ABRT;
    }

    /* Data acquisition to TCP loop. */
    while (1) {

        /* Keep the networking stack running. */
        xemacif_input(netif);
        tcp_tmr();

        /* Tell the networking stack to output what it can. */
        tcp_output(conn);

        /* Acquire up to DAQ_FIFO_DEPTH samples. */
        n = daq_acquire(data, DAQ_FIFO_DEPTH);
        if (n > DAQ_FIFO_DEPTH)
            break;

        /* Write data as-is to the tcp buffer. */
        if (tcp_write(conn, data, n * sizeof data[0], TCP_WRITE_FLAG_COPY) != ERR_OK)
            break;
    }

    /* Stop data acquisition. */
    daq_close();

    /* Close the TCP connection. */
    if (tcp_close(conn) == ERR_OK)
        return ERR_OK;

    /* Close failed. Abort it, then. */
    tcp_abort(conn);
    return ERR_ABRT;
}

Осталось реализовать еще три функции: daq_setup(), которая должна настроить сбор данных и FIFO; daq_acquire(u32_t *data, u32_t count), который сохраняет до count отсчетов в data[] и возвращает фактическое количество сохраненных отсчетов — было бы лучше, если бы он просто очищал FIFO, а не ждал поступления новых отсчетов — и, наконец, daq_close(), который останавливает данные получение.

Я считаю, что они должны быть примерно такими:

XLlFifo         daq_fifo;

err_t daq_setup(void)
{
    XLlFifo_Config *config = NULL;

    config = XLlFifo_LookupConfig(DAQ_FIFO_ID);
    if (!config)
        return ERR_RTE;

    if (XLlFifo_CfgInitialize(&daq_fifo, config, config->BaseAddress) != XST_SUCCESS)
        return ERR_RTE;
}

u32_t daq_acquire(u32_t *data, u32_t max)
{
    u32_t len, have;

    have = XLlFifo_iRxGetLen(&daq_fifo);
    if (have < 1)
        return 0;
    else
    if (have < max)
        max = have;

    for (len = 0; len < max; len++)
        data[len] = XLlFifo_RxGetWork(&daq_fifo);

    return len;
}

err_t daq_close(void)
{
    /* How to stop the FIFO? Do we need to? */
}

Вот об этом.

person Nominal Animal    schedule 26.10.2018
comment
Я только что разместил эту тему на форуме XILINX. Но я не получил ответа от них. Итак, нет другого пути, я разместил свой запрос в переполнении стека. Я не очень разбираюсь в программировании. Ошибка в кодах прошу извинить. - person Abi; 27.10.2018
comment
Извините, пожалуйста, перейдите по ссылке XILINX. В этом, пожалуйста, просмотрите часть кода RX, то есть функцию aurora_rx_main(). На самом деле данные генерируются с платы сбора данных и передаются на плату KC705 по оптическому кабелю. В KC705 полученные данные сохраняются в буфере назначения. Теперь я пытаюсь прочитать эти данные, хранящиеся в буфере назначения на ПК, через Ethernetlite (шаблон LWIP Echo). Где мне нужно включить функции recv_callback и accept_callback в код. Пожалуйста, проясните меня. Спасибо - person Abi; 27.10.2018
comment
@Abi: Помните, что мы можем видеть только то, что вы публикуете. Это выглядело очень похоже на уловку в виде домашнего задания, а не на настоящую попытку найти решение. Так что извиняться совершенно не надо: только уточнить. - person Nominal Animal; 27.10.2018
comment
@Abi: Итак, на самом деле это не эхо-сервер, а сервер сбора данных, который отправляет данные любому клиенту, подключенному через TCP, в виде потока текста (выборки в виде десятичного числа). Это меняет дело, но не сильно. Я могу попытаться написать набросок, но поскольку у меня нет KC705 (очень жаль, что у меня его нет! Я мог бы с ним столько всего попробовать!), вам придется многое дополнить самостоятельно. - person Nominal Animal; 27.10.2018
comment
Я использовал ваш полный код в своем первом сообщении (отредактировано). Но я получаю сообщение об ошибке, что application_connection не объявлен, и конфликтуют типы для tcp_write_u32_string. Пожалуйста, предложите мне - person Abi; 29.10.2018
comment
@Abi: Приведенные выше функции нуждаются либо в объявлениях, либо в исходном файле в обратном порядке, потому что в показанном порядке функция выше использует функции ниже. Я предлагаю вам поставить функции в обратном порядке и повторить попытку. - person Nominal Animal; 29.10.2018
comment
Я запустил ваш полный код. Но в Hercules я получаю значение мусора. Я приложил скриншот. Где мне нужно вызвать эту функцию tcp_write_u32_string. Как только я нажимаю «Подключиться» от Геркулеса, он печатает некоторые значения и автоматически отключается. Мне нужно отправить сообщение от Геркулеса. Предложение пожалуйста - person Abi; 29.10.2018
comment
@Abi: Нет, вы ищете двоичные u32_t данные (четыре байта/символа на число), как если бы это был текст. Вы неправильно смотрите на данные. Вы должны интерпретировать каждые четыре байта как u32_t (32-битное целое число без знака). Я не знаю, как это сделать в Геркулесе. Причина, по которой соединение разрывается, является одним из операторов break в сборе данных для срабатывания цикла TCP. Отправлять сообщения из Hercules бесполезно, потому что код как есть только отправляет образцы и не пытается ничего получить. - person Nominal Animal; 29.10.2018
comment
ссылка. Перейдите по этой ссылкессылка. Мне нужно отправить некоторые данные с ПК, тогда только он отправит данные daq на ваш ПК. Чтобы прочитать десятичные данные, я использовал функцию itoa. - person Abi; 29.10.2018
comment
На самом деле я могу читать данные в Hercules без каких-либо ненужных значений, только если len=32. ошибка = tcp_write(tpcb,результат,32,1). то же самое. Если я увеличу len=64 (некоторые данные отсутствуют и печатаются с нежелательными значениями), я смогу увидеть только 45 значений данных с нежелательными значениями. - person Abi; 29.10.2018
comment
@Abi: Возможно, вам следует немного больше изучить C и сказать, что сеть сокетов POSIX / Berkeley, прежде чем приступать к этой задаче? Похоже, у вас еще нет навыков, необходимых для построения системы, а у меня нет ни времени, ни желания делать это за вас. Возможно, начните с Beej's Guide to Networking или с одного из других C руководства по сетевому программированию. - person Nominal Animal; 29.10.2018