I'm using LWIP Stable 2.0.3 in uVision Keil 5.23 with armcc compiler,
Cortex-M0 MCU, raw API.
My code is waiting for incoming TCP connection and then starts to send
packets to the 'client'. Basically, I'm doing multiple calls to
tcp_write with more or less equal intervals. I couldn't find any info
that suggested this would be wrong or that I shall wait for previous
tcp_write to be completed, so I went with it.
I'm sending rather small packets, about 50 bytes each.
It works well until time interval between consequent calls to tcp_write
is around 1 second. But when I try to call tcp_write more frequently,
something breaks and I get a lot of zeros glued to the end of my packet.
I see those zeros in the input array of the function that does actual
ethernet frame sending.
After adding a lot of debug printing I saw that everything works if
tcp_write calls are infrequent enough for every packet to get its
own ethternet frame and is be sent separately.
But I need to send them at least 10 times per second.
I thought that consequent packets would be somehow queued and maybe
even sent in the same ethernet frame. But for some reason this doesn't
Please, tell me, what am I doing wrong and how to fix this!
Хазанский Роман wrote
> I couldn't find any info
> that suggested this would be wrong or that I shall wait for previous
> tcp_write to be completed, so I went with it.
According to this document
https://www.eremex.ru/upload/iblock/d11/lwip_rawapi.pdf the top of page 4 says:
"TCP data is sent by enqueueing the data with a call to tcp_write(). When
the data is successfully transmitted to the remote host, the application
will be notified with a call to a specified callback function."
So I think you should do the same instead of successive tcp_writes without
being sure that the data was sent.
>According to this document
>https://www.eremex.ru/upload/iblock/d11/lwip_rawapi.pdf >the top of page 4 says:
>"TCP data is sent by enqueueing the data with a call to tcp_write(). When
>the data is successfully transmitted to the remote host, the application
>will be notified with a call to a specified callback function."
>So I think you should do the same instead of successive tcp_writes without
>being sure that the data was sent.
And a little lower on the same page I read: "The proper way to use this
function is to call the function with at most tcp_sndbuf() bytes of data.
If the function returns ERR_MEM, the application should wait until some
of the currently enqueued data has been successfully received by the
other host and try again."
I interpet this as "the application should wait only if tcp_write returns
My calls of tcp_write didn't return ERR_MEM so I presumed that's how
I'm supposed to do it.
Also if I'm going to wait for the end of transmission, sending frequency
would be too low.
Each time you send the 50 bytes, a pbuf will be reserved.
The pbuf will be freed up only after the successful reception by the host.
So if you don't wait for ACK, your pbuf memory will be consumed by many
consecutive transmission packets waiting for ACK.
You may as well check your low level driver if there is data sent which
should not be.
I am not an expert in LwIP, I am just guessing.
>Each time you send the 50 bytes, a pbuf will be reserved.
>The pbuf will be freed up only after the successful reception by the host.
>So if you don't wait for ACK, your pbuf memory will be consumed by many
>consecutive transmission packets waiting for ACK.
>You may as well check your low level driver if there is data sent which
>should not be.
>I am not an expert in LwIP, I am just guessing.
I've enabled every LwIP assert possible and I don't get any memory errors
when allocating new pbufs, so I don't think that's the reason.
Low level driver (and LwIP for that matter) was able to sustain very
high frequency ping, and ping responses are about the same size (44 bytes?)
I finally figured it out. As *stevestrong* suggested problem was in my
The problem actually looked like this:
- any amount of ping is fine, ping -f is fine for any given time
- low-frequency and small tcp packets are fine
- however, if I tried to send the same small (about 50-60 bytes) packets
frequently (more than a 10 times per second) - everything broke.
How did it broke? Well, in wireshark I saw a packet with a bunch of zeros at
the end. And that packet was not ACKed by the server. It baffled me because
server was just a netcat, it should ACK anything.
It turned out that wireshark doesn't show (or check) ethernet frame checksum
by default! It needs to be enabled. So this last packet had incorrect
ethernet CRC and that's why it wasn't ACKed.
The actual problem was in my low-level driver. I pulled it together myself
using an example; in example there was one special array for eth sending
buffer. That array should be aligned by 4 and it is only accessed by 4
bytes. But for some reason it was declared as uint8_t (with
__attribute_align__). Naturally I made it uint32_t.
And forgot to modify one function that copied pbuf's into it :(
So when no pbuf chaining occured, packet was copied correctly and everything
worked. But if two pbuf were chained, counter variable went mad and copied
memory from beyond pbuf (which just happened to always have zeros in it).