2023-03-06 09:25 AM
I have been trying to debug my custom Azure RTOS NetXDuo application (which is based on the Nx_Tcp_Echo Server example code). My application gets stuck in a loop that it cannot return from after reading the socket_state of an established socket. The application runs correctly, but at some point (for some as-yet-unknown reason) the socket_state changes from NX_TCP_ESTABLISHED to either NX_NOT_CONNECTED, or NX_NOT_LISTEN_STATE. Based on the code of the example, the application interprets this as IDLE state, which is not correct. At this point a client can no longer connect to my server application.
Here's the code.
while(1)
{
ULONG socket_state;
TX_MEMSET(data_buffer, '\0', sizeof(data_buffer));
/* get the socket state */
nx_tcp_socket_info_get(&TCPSocket, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &socket_state, NULL, NULL, NULL);
/* if the connections is not established then accept new ones, otherwise start receiving data */
if(socket_state != NX_TCP_ESTABLISHED)
{
ret = nx_tcp_server_socket_accept(&TCPSocket, NX_IP_PERIODIC_RATE);
}
// if(ret== NX_NOT_CONNECTED || ret == NX_NOT_LISTEN_STATE)
// {
// nx_tcp_socket_disconnect(&TCPSocket, NX_WAIT_FOREVER);
// nx_tcp_server_socket_unaccept(&TCPSocket);
// nx_tcp_server_socket_relisten(&NetXDuoEthIpInstance, DEFAULT_PORT, &TCPSocket);
// }
// else
if(ret == NX_SUCCESS)
{
// tx_trace_enable(&tracex_buffer,TRACEX_BUFFER_SIZE,30);
/* receive the TCP packet send by the client */
ret = nx_tcp_socket_receive(&TCPSocket, &data_packet, NX_WAIT_FOREVER);
I have tried adding the following code (commented out above) in an attempt to force a disconnect and begin re-listening. But this does not reset the socket and no attempts by the client to connect are successful.
// if(ret== NX_NOT_CONNECTED || ret == NX_NOT_LISTEN_STATE)
// {
// nx_tcp_socket_disconnect(&TCPSocket, NX_WAIT_FOREVER);
// nx_tcp_server_socket_unaccept(&TCPSocket);
// nx_tcp_server_socket_relisten(&NetXDuoEthIpInstance, DEFAULT_PORT, &TCPSocket);
// }
// else
I would expect that I should not be able to confuse the socket_state with any communication attempt from the client. I would also expect the code above to resolve a "stuck" socket_state.
I am new to NetXDuo and would appreciate some sage advice on the best way to handle this "stuck" code.
For completeness, here's the entire app_netxduo.c code.
static VOID App_TCP_Thread_Entry(ULONG thread_input)
{
uint8_t cmdLen = 64;
UINT uret_tx;
UINT uret_rx;
UINT ret;
UCHAR data_buffer[512];
ULONG source_ip_address;
NX_PACKET *data_packet;
UINT source_port;
ULONG bytes_read;
/* create the TCP socket */
ret = nx_tcp_socket_create(&NetXDuoEthIpInstance, &TCPSocket, "TCP Server Socket", NX_IP_NORMAL, NX_FRAGMENT_OKAY,
NX_IP_TIME_TO_LIVE, WINDOW_SIZE, NX_NULL, NX_NULL);
if (ret != NX_SUCCESS)
{
Error_Handler();
}
/*
* listen to new client connections.
* the TCP_listen_callback will release the 'Semaphore' when a new connection is available
*/
ret = nx_tcp_server_socket_listen(&NetXDuoEthIpInstance, DEFAULT_PORT, &TCPSocket, MAX_TCP_CLIENTS, tcp_listen_callback);
if (ret != NX_SUCCESS)
{
Error_Handler();
}
else
{
printf("TCP Server listening on PORT %d ..\n", DEFAULT_PORT);
}
// thx_thread_info_get(NxAppThread, TX_NULL, TX_NULL, &run_count, TX_NULL, TX_NULL, TX_NULL, TX_NULL , TX_NULL);
if(tx_semaphore_get(&DHCPSemaphore, TX_WAIT_FOREVER) != TX_SUCCESS)
{
Error_Handler();
}
else
{
/* accept the new client connection before starting data exchange */
ret = nx_tcp_server_socket_accept(&TCPSocket, TX_WAIT_FOREVER);
if (ret != NX_SUCCESS)
{
Error_Handler();
}
}
//Test delayed TraceX
// tx_trace_enable(&tracex_buffer,TRACEX_BUFFER_SIZE,30);
while(1)
{
ULONG socket_state;
TX_MEMSET(data_buffer, '\0', sizeof(data_buffer));
/* get the socket state */
nx_tcp_socket_info_get(&TCPSocket, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &socket_state, NULL, NULL, NULL);
/* if the connections is not established then accept new ones, otherwise start receiving data */
if(socket_state != NX_TCP_ESTABLISHED)
{
ret = nx_tcp_server_socket_accept(&TCPSocket, NX_IP_PERIODIC_RATE);
}
// if(ret== NX_NOT_CONNECTED || ret == NX_NOT_LISTEN_STATE)
// {
// nx_tcp_socket_disconnect(&TCPSocket, NX_WAIT_FOREVER);
// nx_tcp_server_socket_unaccept(&TCPSocket);
// nx_tcp_server_socket_relisten(&NetXDuoEthIpInstance, DEFAULT_PORT, &TCPSocket);
// }
// else
if(ret == NX_SUCCESS)
{
// tx_trace_enable(&tracex_buffer,TRACEX_BUFFER_SIZE,30);
/* receive the TCP packet send by the client */
ret = nx_tcp_socket_receive(&TCPSocket, &data_packet, NX_WAIT_FOREVER);
if (ret == NX_SUCCESS)
{
// HAL_GPIO_TogglePin(LED_GREEN_GPIO_Port, LED_GREEN_Pin);
/* get the client IP address and port */
nx_udp_source_extract(data_packet, &source_ip_address, &source_port);
/* retrieve the data sent by the client */
nx_packet_data_retrieve(data_packet, data_buffer, &bytes_read);
int i;
printf("Command: ");
for(i=0; i<16; i++)printf("%d,", data_buffer[i] );
printf("\n");
//Use blocking HAL_UART_Transmit and Receive since there are no interrupts in the HAL routines
uret_tx = HAL_UART_Transmit(&huart2, data_buffer, cmdLen, 2000);
if(!uret_tx)
{
TX_MEMSET(data_buffer, '\0', sizeof(data_buffer));
uret_rx = HAL_UART_Receive(&huart2, data_buffer, cmdLen, 2000 );
}
printf("Response: ");
for(i=0; i<16; i++) printf("%d,", data_buffer[i] );
printf("\n");
if(uret_tx) printf("USART TX Error %d.\n", uret_tx);
if(uret_rx)
{
printf("USART RX Error %d.\n", uret_rx);
HAL_UART_MspDeInit(&huart2);
HAL_UART_MspInit(&huart2);
huart2.Instance = USART2;
huart2.Init.BaudRate = 230400;
huart2.Init.WordLength = UART_WORDLENGTH_8B;
huart2.Init.StopBits = UART_STOPBITS_1;
huart2.Init.Parity = UART_PARITY_NONE;
huart2.Init.Mode = UART_MODE_TX_RX;
huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart2.Init.OverSampling = UART_OVERSAMPLING_16;
huart2.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE;
huart2.Init.ClockPrescaler = UART_PRESCALER_DIV1;
huart2.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;
if (HAL_RS485Ex_Init(&huart2, UART_DE_POLARITY_HIGH, 0, 0) != HAL_OK)
{
Error_Handler();
}
if (HAL_UARTEx_DisableFifoMode(&huart2) != HAL_OK)
{
Error_Handler();
}
}
data_packet->nx_packet_append_ptr = data_packet->nx_packet_prepend_ptr;
ret = nx_packet_data_append(data_packet,data_buffer,cmdLen,&NxAppPool,NX_WAIT_FOREVER);
data_packet->nx_packet_length=cmdLen;
ret = nx_tcp_socket_send(&TCPSocket, data_packet, NX_WAIT_FOREVER);
if (ret == NX_SUCCESS) HAL_GPIO_TogglePin(LED_GREEN_GPIO_Port, LED_GREEN_Pin);
else
{
printf("TCP Send Error %d.\n", ret);
nx_tcp_socket_disconnect(&TCPSocket, NX_WAIT_FOREVER);
nx_tcp_server_socket_unaccept(&TCPSocket);
nx_tcp_server_socket_relisten(&NetXDuoEthIpInstance, DEFAULT_PORT, &TCPSocket);
}
}
else
{
HAL_GPIO_TogglePin(LED_RED_GPIO_Port, LED_RED_Pin);
nx_tcp_socket_disconnect(&TCPSocket, NX_WAIT_FOREVER);
nx_tcp_server_socket_unaccept(&TCPSocket);
nx_tcp_server_socket_relisten(&NetXDuoEthIpInstance, DEFAULT_PORT, &TCPSocket);
}
}
else
{
// nx_tcp_socket_disconnect(&TCPSocket, NX_WAIT_FOREVER);
// nx_tcp_server_socket_unaccept(&TCPSocket);
// nx_tcp_server_socket_relisten(&NetXDuoEthIpInstance, DEFAULT_PORT, &TCPSocket);
/*toggle the green led to indicate the idle state */
HAL_GPIO_TogglePin(LED_GREEN_GPIO_Port, LED_GREEN_Pin);
}
}
}
Thanks,
MikeH
Solved! Go to Solution.
2023-04-10 09:21 AM
Hi,
Based on the Nx_TCP_Server application, the socket state follows this process :
At the beginning socket starts listening with NX_TCP_LISTEN_STATE.
Once the connection with client is established, socket state goes to NX_TCP_ESTABLISHED.
When client disconnect, server disconnected too and re-listen again, at this stage, socket goes back to NX_TCP_LISTEN_STATE.;
The NX_NOT_CONNECTED return is related to the TCP socket disconnection; at this stage, a TCP deferred cleanup routine is processing, and the AppTCPThread is considered as suspended with NX_NOT_CONNECTED status.
But this does not block the client to connect again to the server and send data.
However, the NX_NOT_LISTEN_STATE blocks the TCP client to reconnect to the Socket. And it's coming from the fact that the server socket is in a closing process and has no pending packets to proceed.
An update of the application is required to handle the NX_NOT_LISTEN_STATE and do not consider it as an idle state.
And this is done by adding the section below here (between lines 440 - 441) :
else if (re == NX_NOT_LISTEN_STATE)
{
nx_tcp_socket_disconnect(&TCPSocket, NX_WAIT_FOREVER);
nx_tcp_server_socket_unaccept(&TCPSocket);
nx_tcp_server_socket_relisten(&NetXDuoEthIpInstance, DEFAULT_PORT, &TCPSocket);
}
With this update, TCP client will be able to connect to the server even after an NX_NOT_LISTEN_STATE of the server socket.
I hope my answer helps.
Regards
Mahdy
2023-04-10 09:21 AM
Hi,
Based on the Nx_TCP_Server application, the socket state follows this process :
At the beginning socket starts listening with NX_TCP_LISTEN_STATE.
Once the connection with client is established, socket state goes to NX_TCP_ESTABLISHED.
When client disconnect, server disconnected too and re-listen again, at this stage, socket goes back to NX_TCP_LISTEN_STATE.;
The NX_NOT_CONNECTED return is related to the TCP socket disconnection; at this stage, a TCP deferred cleanup routine is processing, and the AppTCPThread is considered as suspended with NX_NOT_CONNECTED status.
But this does not block the client to connect again to the server and send data.
However, the NX_NOT_LISTEN_STATE blocks the TCP client to reconnect to the Socket. And it's coming from the fact that the server socket is in a closing process and has no pending packets to proceed.
An update of the application is required to handle the NX_NOT_LISTEN_STATE and do not consider it as an idle state.
And this is done by adding the section below here (between lines 440 - 441) :
else if (re == NX_NOT_LISTEN_STATE)
{
nx_tcp_socket_disconnect(&TCPSocket, NX_WAIT_FOREVER);
nx_tcp_server_socket_unaccept(&TCPSocket);
nx_tcp_server_socket_relisten(&NetXDuoEthIpInstance, DEFAULT_PORT, &TCPSocket);
}
With this update, TCP client will be able to connect to the server even after an NX_NOT_LISTEN_STATE of the server socket.
I hope my answer helps.
Regards
Mahdy