I have a simple TCP client application running on the nRF9160. After connection to the server, recv is called and writes the data over Segger RTT channel 1, where on the host JLinkRTTLogger.exe logs the data to a file.
The server is the unix netcat utility that is sending a binary file of random data (256kB) from stdin.
cat rand.bin | nc -l PORT
If revc is called with no latency, the file is received in its entirety and verified with the original file using the diff utility. However, if a sleep statement is added in the recv loop, simulating processing the chunk of data (like erasing and writing to flash) the stream drops data to the application in 708 byte chunks and the received file is corrupted. The connection is monitored with wireshark on the server and shows the entire file is sent, indicated by the acknowledgment byte count.
My expectation with the TCP would be the flow-control/backpressure mechanism to throttle the connection and the sender would send data when the receiver has availability in its Receive Window. It does seem to be doing this in the modem firmware as I can see the Receive Window for the client shrink and grow dynamically in wireshark. The current behavior through the entire stack to the application violates the TCP protocol of reliable transmission. I can hack around this limitation by adding complexity to the application layer ... but this problem has been solved with the TCP standard. I would also rather the connection be closed than random data in the TCP stream be dropped and not sent to the application.
Is there an option/configuration I am missing that would fix this issue?
Modem firmware: 0.7.0-2.9alpha
SW version: see west.yml
#include <zephyr.h> #include <net/socket.h> #include <lte_lc.h> #include <SEGGER_RTT.h> static uint8_t rtt_buf[1024]; static uint8_t recv_buf[1024]; #ifdef CONFIG_BSD_LIBRARY void bsd_recoverable_error_handler(uint32_t err) { printk("bsdlib recoverable error: %u\n", err); } void bsd_irrecoverable_error_handler(uint32_t err) { printk("bsdlib irrecoverable error: %u\n", err); __ASSERT_NO_MSG(false); } #endif void main(void) { int err; printk("tcp_client sample\n"); printk("Configuring RTT up buffer 1 ... "); SEGGER_RTT_ConfigUpBuffer(1, "File", rtt_buf, sizeof(rtt_buf), SEGGER_RTT_MODE_BLOCK_IF_FIFO_FULL); printk("done.\n"); printk("LTE Link Connecting ...\n"); err = lte_lc_init_and_connect(); __ASSERT(err == 0, "LTE link could not be established."); printk("LTE Link Connected!\n"); struct addrinfo *res; struct addrinfo hints = { .ai_family = AF_INET, .ai_socktype = SOCK_STREAM}; err = getaddrinfo(CONFIG_TCP_SERVER_HOST, NULL, &hints, &res); __ASSERT(err == 0, "Server hostname could not be resolved, err=%d, errno=%d", err, errno); ((struct sockaddr_in *)res->ai_addr)->sin_port = htons(CONFIG_TCP_SERVER_PORT); int client_fd; for(int i=0; i<16; i++) { client_fd = socket(AF_INET, SOCK_STREAM, 0); printk("client_fd: %d\n\r", client_fd); err = connect(client_fd, (struct sockaddr *)res->ai_addr, sizeof(struct sockaddr_in)); if (err==0) { break; } err = close(client_fd); printk("Attempt %d: could not connect to server, err=%d, errno=%d\n", i, err, errno); k_sleep(1000*i*i); //simple exponential back-off } __ASSERT(err == 0, "Error, could not connect to server, err=%d, errno=%d", err, errno); printk("Connected to server.\n"); int ret; int total_rx_bytes = 0; while(1) { ret = recv(client_fd, recv_buf, sizeof(recv_buf), 0); if (ret > 0) { total_rx_bytes += ret; printk("Rx %d bytes, total=%d\n", ret, total_rx_bytes); SEGGER_RTT_Write(1, recv_buf, ret); } else if (ret < 0 && errno == EAGAIN) { printk("EAGAIN\n"); } else { printk("Unhandled error, ret=%d, errno=%d\n", ret, errno); break; } k_sleep(4000); //<-- seems to be the culprit for lost data :( } printk("Closing socket ... "); err = close(client_fd); __ASSERT(err == 0, "Could not close socket, err=%d.", err); printk("goodbye.\n"); }
# General config CONFIG_TRUSTED_EXECUTION_NONSECURE=y CONFIG_TEST_RANDOM_GENERATOR=y CONFIG_ASSERT=y CONFIG_REBOOT=y CONFIG_NO_OPTIMIZATIONS=y CONFIG_HEAP_MEM_POOL_SIZE=1024 # Logging CONFIG_LOG=y CONFIG_PRINTK=y CONFIG_CONSOLE=y CONFIG_STDOUT_CONSOLE=y CONFIG_LOG_BUFFER_SIZE=2048 # Segger RTT CONFIG_UART_CONSOLE=n CONFIG_HAS_SEGGER_RTT=y CONFIG_USE_SEGGER_RTT=y CONFIG_RTT_CONSOLE=y CONFIG_LOG_BACKEND_RTT=y # Main thread CONFIG_MAIN_THREAD_PRIORITY=7 CONFIG_MAIN_STACK_SIZE=4096 # BSD library (nrf) CONFIG_BSD_LIBRARY=y CONFIG_BSD_LIBRARY_TRACE_ENABLED=n # Networking CONFIG_NETWORKING=y CONFIG_NET_SOCKETS=y CONFIG_NET_SOCKETS_OFFLOAD=y CONFIG_NET_SOCKETS_POSIX_NAMES=y CONFIG_NET_LOG=y # LTE link control CONFIG_LTE_LINK_CONTROL=y CONFIG_LTE_AUTO_INIT_AND_CONNECT=n CONFIG_LTE_LINK_CONTROL_LOG_LEVEL=4
# The west manifest file for RED firmware west: url: https://github.com/zephyrproject-rtos/west revision: v0.5.8 manifest: remotes: - name: red url-base: [email protected]:bbr/red - name: ncs url-base: https://github.com/NordicPlayground - name: zephyrproject url-base: https://github.com/zephyrproject-rtos - name: throwtheswitch url-base: https://github.com/ThrowTheSwitch - name: armmbed url-base: https://github.com/ARMmbed projects: - name: fw-nrfconnect-nrf path: nrf revision: 6ebf0385ddc560d32168ec175d19738135bfb6f3 remote: ncs - name: fw-nrfconnect-zephyr path: zephyr west-commands: scripts/west-commands.yml revision: 3c4f27282002e634d60216f23fb341317a4b199f remote: ncs - name: fw-nrfconnect-mcuboot path: mcuboot revision: 4b919890cc848316304b81d9e160fe5a10ce3764 remote: ncs - name: fw-nrfconnect-tinycbor path: modules/lib/tinycbor revision: ef1f9c3d87474ec3570b1f46e91fd4b54a4fb421 remote: ncs - name: nrfxlib path: nrfxlib revision: v0.4.0 remote: ncs - name: cmock path: test/cmock revision: c243b9a7a7b3c471023193992b46cf1bd1910450 remote: throwtheswitch - name: unity path: test/cmock/vendor/unity revision: 031f3bbe45f8adf504ca3d13e6f093869920b091 remote: throwtheswitch - name: mbedtls path: mbedtls revision: mbedtls-2.13.1 remote: armmbed self: path: fw