简体   繁体   English

LWIP ECHO SERVER:如何增加itoa函数中的缓冲区大小?

[英]LWIP ECHO SERVER: How to increase the buffer size in itoa function?

I am working with Xilinx Ethernetlite (LWIP) design. 我正在使用Xilinx Ethernetlite(LWIP)设计。 I am able to transfer the data from KC board to PC (Hercules) through Ethernet only if buf =32. 仅当buf = 32时,我才能通过以太网将数据从KC板传输到PC(Hercules)。 But my actual buffer size is 1024. How to increase the buffer size from 32 to 1024 但是我的实际缓冲区大小是1024。如何将缓冲区大小从32增加到1024

I am unable to make sure whether the mistake is in code or in hercules. 我无法确定错误是在代码中还是在大力士中。 To read the values (integer) in hercules i am doing this function. 要读取大力神中的值(整数),我正在执行此功能。

initially, From hercules i will send the Hello command to Board,then board with accept that request. 最初,我将从Hercules将Hello命令发送到Board,然后Board接受该请求。 After that, board will output the data (integer values) to Hercules. 此后,开发板将数据(整数值)输出到Hercules。

C code for itoa itoa的C代码

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];
}

Modified code 修改后的代码

    #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 output enter image description here 大力神输出在此处输入图像描述

So, this is a continuation from the discussion at Xilinx forums ? 那么,这是Xilinx论坛上讨论的延续?

The itoa() function converts an unsigned integer (stored in an int ) into the first 30 or so characters in the buffer buf . itoa()函数将无符号整数(存储在int )转换为缓冲区buf的前30个左右字符。

The recv_callback() function makes little to no sense. recv_callback()函数几乎没有意义。

The call to aurora_rx_main() is documented as a "FUNCTION CALL", which is rather less than helpful (because we have no idea what it does), and even its return value is completely ignored. aurora_rx_main()的调用记录为“功能调用”,这没有帮助(因为我们不知道它的作用),甚至其返回值也被完全忽略。

The first for loop dumps the contents of the first 100 u32 's in DestinationBuffer[] for debugging purposes, so that code is unrelated to the task at hand. 第一个for循环转储DestinationBuffer[]前100个u32的内容以进行调试,因此代码与手头的任务无关。 However, we don't know who or what filled DestinationBuffer . 但是,我们不知道谁或什么填充了DestinationBuffer It might or might not have been filled by the aurora_rx_main() call; aurora_rx_main()调用可能已填充也可能未填充; we're not told either way. 我们都没有被告知。

(The tcp_*() functions seem to follow the API described in lwIP Wiki at Wikia.) tcp_*()函数似乎遵循Wikia上lwIP Wiki中描述的API。)

If the p parameter is NULL, then tcp_close(tcpb) is called, followed by a tcp_recv(tcpb, NULL) call. 如果p参数为NULL,则tcp_close(tcpb) ,然后调用tcp_recv(tcpb, NULL) This makes the least sense of all: why try to receive anything (and why the NULL parameter) after a close? 这使所有事情变得毫无意义:为什么在关闭后尝试接收任何内容(以及为什么使用NULL参数)?

The next part is similarly baffling. 下一部分同样令人困惑。 It looks like the if test checks if the TCP send buffer is over 1024 bytes in size. 好像if测试检查TCP发送缓冲区的大小是否超过1024字节。 If not, the p buffer is freed. 如果不是,则释放p缓冲区。 Otherwise, the for loop tries to convert each u32 in DestinationBuffer to a string, write that string to the TCP buffer; 否则, for循环尝试将DestinationBuffer中的每个u32转换为一个字符串,然后将该字符串写入TCP缓冲区; however, rather than the proper api flags, it uses the constant 1 , and does not even check if appending to the TCP send buffer works. 但是,它使用常数1而不是适当的api标志,甚至不检查附加到TCP发送缓冲区是否有效。

In summary, this looks like a pile of copy-pasted code that does nothing sensible. 总而言之,这看起来像一堆复制粘贴的代码,什么都不做。 Increasing the buffer size in itoa function is not only unnecessary (an u32 , even when converted to an int , will always fit within 12 characters (excluding either the minus sign, or the nul byte at end, so make that 13 characters total), but completely unrelated to the problem it is supposed to fix. 不仅没有必要增加itoa函数中的缓冲区大小( u32 ,即使转换为int ,也总是可以容纳12个字符(不包括负号或末尾的nul字节,因此使总共13个字符),但与应该解决的问题完全无关。

The root problem is that the code is horrible. 根本问题是代码太可怕了。 Modifying it is like putting car body filler over a piece of old chewing gum, in an effort to "fix" it. 修改它就像将车身填充物放在一块旧的口香糖上,以试图“固定”它。 The proper fix is to rip out that junk code altogether, and use something better instead. 正确的解决方法是完全删除那些垃圾代码,并使用更好的东西。

Edit: The OP states that they're a new programmer, so the comments above should be taken as a direct, honest opinion of the code shown, and not about OP themselves. 编辑:OP声明他们是新程序员,因此上面的注释应作为对所显示代码的直接,诚实的见解,而不是OP本身。 Let's see if we can help OP produce better code. 让我们看看是否可以帮助OP生成更好的代码。


First, the itoa() function shown is silly. 首先,所示的itoa()函数很愚蠢。 Assuming the intent is really to send back the u32_t s in the DestinationBuffer as decimal strings, it is much better to implement a helper function for doing the conversion. 假设实际上是要以十进制字符串形式发回DestinationBufferu32_t ,那么最好实现一个辅助函数来执行转换。 Since the value is to be preceded with a comma (or some other separator), we can add that trivially as well. 由于该值前面带有逗号(或其他分隔符),因此我们也可以对其进行琐碎的添加。 Since it will be sent using tcp_write() , we'll combine the functionality: 由于它将使用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);
}

so that to send num u32_t s from a specified array as decimal strings, with a newline at end, you could use 这样就可以将指定数组中的num u32_t发送为十进制字符串,并以换行符结尾,您可以使用

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);
}

Now, I haven't written C for Xilinx or used the lwIP stack at all, so the above code is written blind. 现在,我还没有为Xilinx编写C或根本没有使用lwIP堆栈,因此上述代码是盲目的编写。 Yet, I'm pretty confident it works (barring any typos or thinkos; if you find any, report them in a comment, and I'll verify and fix). 但是,我非常有信心它会起作用(除非有任何错别字或想法,否则,请在评论中进行举报,然后我将进行验证和解决)。

The two buffers ( buf and newline ) are declared static , so that although they're only visible within their respective functions, their value is valid in the global scope. 这两个缓冲区( bufnewline )被声明为static ,因此尽管它们仅在各自的函数中可见,但它们的值在全局范围内有效。

Because TCP is a stream protocol, it is not necessary to fit each response to a single packet. 由于TCP是流协议,因此不必将每个响应都适合单个数据包。 Other than the 11-character (for each number and its prefixing character) and the 2-character (newline) buffers, the only large buffer you need is the TCP send buffer (the maximum transmission unit or maximum segment size as I'm not sure how lwIP uses the buffer internally), typically between 536 and 1518 bytes. 除了11个字符(每个数字及其前缀字符)和2个字符(换行符)缓冲区外,您唯一需要的大缓冲区是TCP发送缓冲区( 最大传输单位最大段大小,因为我不是)确定lwIP如何在内部使用缓冲区),通常介于536和1518字节之间。

The two above functions try to split packets between numbers, but that's just because it's easier than to try and fill each packet exactly. 上面的两个函数试图在数字之间分割数据包,但这仅仅是因为比完​​全填充每个数据包要容易得多。 If the next (comma and) value fit in the buffer, then it is added to the buffer; 如果下一个(逗号和)值适合缓冲区,则将其添加到缓冲区。 otherwise the buffer is flushed first, and then the next (comma and) value added to the buffer. 否则,首先刷新缓冲区,然后将下一个(逗号和)值添加到缓冲区。

From the recipient side, you should obtain a nice, readable stream using eg netcat . 在接收方,您应该使用netcat获得一个漂亮的,可读的流。 (I have no idea if Hercules is an application, or just the name of your local machine.) Because TCP is a stream protocol, the recipient cannot (reliably) detect where the packet boundaries were (unlike in, say, UDP datagrams). (我不知道Hercules是应用程序还是本地计算机的名称。)由于TCP是流协议,因此接收方无法(可靠地)检测到数据包边界在哪里(不同于UDP数据报)。 In practice, a TCP connection is just two streams of data, each going one way, and the split into packets is just a protocol detail application programmers don't need to worry about. 实际上,TCP连接只是两个数据流,每个数据流都是一种方式,而拆分为数据包只是应用程序程序员无需担心的协议详细信息。 For lwIP, because it is such a low-level library, a little bit of care need to be taken, but as one can see from the above code, it's really not that much. 对于lwIP,由于它是一个低级别的库,因此需要多加注意,但是从上面的代码可以看出,实际上并没有那么多。


In a comment, OP explained that they are not very experienced, and that the overall intent is to have the device accept a TCP connection, and stream data (samples acquired by a separate acquisition board) as unsigned 32-bit integers via the connection. OP在一条评论中解释说,他们不是非常有经验,并且总体目的是让设备接受TCP连接,并通过该连接将数据流(由单独的采集板采集的样本)作为无符号的32位整数传输。

Because I would love to have one of those FPGA boards (I have several tasks I could see if I could offload to an FPGA), but no resources to get one, I shall try to outline the entire application here. 因为我很想拥有其中的一块FPGA板(我可以完成一些任务,看看是否可以卸载到FPGA上),但是却没有足够的资源来获得一块,所以我将在这里概述整个应用。 Do note that the only information I have to go on, is the 2018 version of Xilinx OS and Libraries Document Collection (UG643) ( PDF ). 请注意,我唯一需要继续的信息是Xilinx OS和库文档集合(UG643)( PDF )的2018年版本。 It looks like OP wants to use the raw API, for high performance. 好像OP想要使用原始API来获得高性能。

Converting the samples to text is silly, especially if high performance is desired. 将样本转换为文本是愚蠢的,尤其是在需要高性能的情况下。 We should just use raw binary, and whatever endianness the KC705 uses. 我们应该只使用原始二进制文件,以及KC705使用的任何字节序。 (I didn't see it at a quick glance from the documentation, but I suspect it is little-endian). (我没有从文档中快速看到它,但是我怀疑它是低端的)。

According to the documentation, the raw API main() is something similar to following: 根据文档,原始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);
}

In the documentation examples, rather than return application_main(netif); 在文档示例中,而不是return application_main(netif); , you'll see a call to start_application() , and then an infinite loop that regularly calls xemacif_input(netif) instead. ,您将看到对start_application()的调用,然后是一个定期调用xemacif_input(netif)的无限循环。 It just means that out application_main() must call xemacif_input(netif) regularly, to be able to receive data. 这只是意味着application_main()必须定期调用xemacif_input(netif)才能接收数据。 (The lwIP documentation says that we should also call sys_check_timeouts() or tcp_tmr() at regular intervals.) (lwIP文档说,我们还应该定期调用sys_check_timeouts()tcp_tmr() 。)

Note that I've omitted error reporting printfs, and that rather than recovering from errors gracefully, this will just return (from main() ); 请注意,我已经省略了错误报告printfs,并且它不会从错误中恢复,而是会返回(从main() ); I'm not certain whether this causes the KC705 to restart or what. 我不确定这是否导致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);
}

For each TCP connection to the port, we get a call to application_connection() . 对于与端口的每个TCP连接,我们都会调用application_connection() This is the function that sets up the data acquisition board, and transfers the data for as long as the recipient wants it. 此功能可设置数据采集板,并在接收者需要的时间内传输数据。

/* 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;
}

There are three more functions to implement: daq_setup() , which should setup the data acquisition and FIFOs; 还有另外三个函数要实现: daq_setup() ,它应该设置数据采集和FIFO; daq_acquire(u32_t *data, u32_t count) that stores up to count samples to data[] , and returns the actual number of samples stored -- it would be best if it just drained the FIFO, rather than waited for new samples to arrive --, and finally daq_close() , that stops the data acquisition. daq_acquire(u32_t *data, u32_t count)可以存储最多样本countdata[] ,并返回实际存储的样本数-最好是耗尽FIFO,而不是等待新样本到达- -,最后是daq_close() ,它将停止数据获取。

I believe they should be something like this: 我相信他们应该是这样的:

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? */
}

That's about it. 就是这样

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM