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