大家好。
我有一个重要的用例、用来证明 TCP/IP 连接上的一种奇怪行为。
方案是:
- CPU#1板、带 TIVAC129XNCZAD (客户端)、IP 192.168.0.100
- CPU#2板、带 TIVAC129XNCZAD (客户端)、带 IP 192.168.0.103
- 具有 IP 192.168.0.2的 PC Dell W10 (服务器)
- 专用协议通信,例如使用“SEND”和“recv”(来自 NDK/socket.h)进行命令响应以传输文件
- 编译器 TI v.18.12.7.LTS
- TI RTOS 2.16.1.14
- NDK v.2.25.00.09
- XDC 3.32.00.06
- TivaWare C_Series 2.1.4.178
- CCS 9.3.0.00012
- Wireshark 3.6.7 (安装在 PC Dell W10上)
在 TCP/IP 会话的正常运行期间,客户端发送的每个数据包都由服务器通过响应进行验证,有时两个 CPU 板中的一个会收到错误代码35 (EWOULDBLOCK)。
专用应用程序尝试使用一个 DO-while 循环恢复此情况,在此循环中,客户端尝试进行另一个接收,但在应用程序超时周期后,套接字将关闭,因为未接收到有效数据。
以下分析是使用 Wireshark 完成的。
您看到的奇怪现象是,有时,在客户端发送数据包(#1006)后,客户端(#1008)不会接收到从服务器发送的响应,因此在超时周期之后(由“setsockopt”指定) “recv”函数返回-1并使用“fdError”函数分析原因您可以看到代码 EWOULDBLOCK。
通过在串行调试接口中使用时间戳参考、我们可以将此行为发生的时间关联到另一个奇怪的事情:最后一个未验证的数据包(#1009)的 TCP 虚假重传。
TI 的 TCP /IP 协议栈似乎会自动重新发送最后一个未经验证的数据包(#1009)、并且服务器正在使用重复的 ACK 进行响应(请参阅#1010)。 我非常确信这一点,因为在我的专用应用程序中,发送计数器会停止到发送的最后一个数据包。
问题是:是否有特殊原因、因为应用程序将获得错误 EWOULDBLOCK?
在撰写本论坛之前,我已经阅读了几篇文章,并尝试了几个建议的修复程序。
我尝试过的修复程序包括:
- 配置具有阻塞和 NO_BLOCKING 的套接字
- 提高任务的堆栈和优先级
- 将错误管理为我的应用程序中无故障(灾难…)
- 增加应用程序超时和“setsockopt”超时
我已阅读的论坛包括:
static Bool CommNetRemote_Receive( void ) { Bool ret = TRUE; Bool bFinish = TRUE; uint16_t pckt_size = 0; int16_t chunk_received = 0; int rc = 0; commNetRemoteHeaderS header; CommNetRemotePktDataS *payload = (CommNetRemotePktDataS*)&commNetRemoteBuffer[SESSION_HEADER_COUNT]; commNetRemoteCtrl.recvErr = COMM_NET_REMOTE_REQ_ACK_GENERIC_OK; commNetRemoteCtrl.flags.recvErr = FALSE; commNetRemoteCtrl.flags.timeout = FALSE; // uncomment if want blocking mode //setsockopt( commNetRemoteCtrl.clientfd, SOL_SOCKET, SO_BLOCKING, &rc, sizeof(rc)); do { rc = recv( commNetRemoteCtrl.clientfd, (BYTE*)commNetRemoteBuffer, REMOTE_BUFFER_LEN, 0); if (rc > 0) { ret = TRUE; // fill-up the header structure, due to aligned with 3 bytes header.IC = commNetRemoteBuffer[0]; header.PS_low = commNetRemoteBuffer[1]; header.PS_high = commNetRemoteBuffer[2]; pckt_size = (header.PS_high << 8) + header.PS_low; // First check, if size of packet is the same of the one declared into header if( rc != (pckt_size + SESSION_HEADER_COUNT) ) { commNetRemoteCtrl.recvErr = COMM_NET_REMOTE_SEND_ERR_PACKET_SIZE; OS_Error("Receive: error packet size"); ret = FALSE; } else { switch( header.IC ) { case SESSION_ACK_IC: if( CommNetRemote_CompareData( payload->sessionAck.serial, (BYTE*)appMain.config.serialNumber, 10 ) == FALSE ) { commNetRemoteCtrl.recvErr = COMM_NET_REMOTE_SEND_ERR_SERIAL_WRONG; OS_Error("Receive: serial number wrong"); ret = FALSE; } else { // Header ok } break; case SESSION_REQACK_IC: commNetRemoteCtrl.chunk_resp++; commNetRemoteCtrl.recvErr = payload->requestAck.retCode; break; case SESSION_ABORT_IC: commNetRemoteCtrl.recvErr = payload->abortReturn.retCode; break; case SESSION_SEND_CHUNK_IC: if( CommNetRemote_CompareData( (BYTE*)payload->sendChunk.GUID, (BYTE*)commNetRemoteCtrl.GUID, REMOTE_GUID_LEN) == TRUE ) { chunk_received = payload->sendChunk.chunk_index[1]; chunk_received *= 256; // equivale *** di 8 bit chunk_received += payload->sendChunk.chunk_index[0]; if( chunk_received == commNetRemoteCtrl.chunk_index ) { // Packet OK commNetRemoteCtrl.iBytesToSend = pckt_size - (SESSION_SEND_CHUNK_PAYLOAD_COUNT - 1); } else { // Packet KO commNetRemoteCtrl.iBytesToSend = 0; commNetRemoteCtrl.recvErr = COMM_NET_REMOTE_SEND_ERR_WRONG_CHUNK; } } else { commNetRemoteCtrl.iBytesToSend = 0; commNetRemoteCtrl.recvErr = COMM_NET_REMOTE_SEND_ERR_WRONG_GUID; } break; default: commNetRemoteCtrl.iBytesToSend = 0; commNetRemoteCtrl.recvErr = COMM_NET_REMOTE_SEND_ERR_UNDEFINED; break; } } } else { bFinish = TRUE; ret = FALSE; rc = fdError(); if( rc == 0 ) { // close communication: error managed by upper layer // commNetRemoteCtrl.recvErr = COMM_NET_REMOTE_SEND_ERR_RECV_ZERO_BYTE; } else { commNetRemoteCtrl.recvErr = rc; if( (rc == EWOULDBLOCK) || (rc == EAGAIN) ) { // The timeout is started by upper layer. when the time is reached the flag commNetRemoteCtrl.flags.timeout is updated // if( commNetRemoteCtrl.flags.timeout == FALSE ) { bFinish = FALSE; OS_Sleep( OS_HUNDRED_MILLISECONDS ); } else { // Timeout reached : error managed by upper layer } } else { // Other error codes: error managed by upper layer } } OS_Error( "Receive: err socket (%ld) @ %ld.", rc, OS_GetTimerTick() ); } } while ( bFinish != TRUE ); return (ret); } void CommNetRemote_ClientProcess(TASK_ARG_T arg0, TASK_ARG_T arg1) { int status = 0; int32_t err = 0; int16_t iWaitCount = 0; struct timeval to; CommNetRemotePktDataS *payload = (CommNetRemotePktDataS*)&commNetRemoteBuffer[SESSION_HEADER_COUNT]; commNetRemoteCtrl.enabled = TRUE; commNetRemoteCtrl.state = COMM_NET_REMOTE_STS_INIT_WAIT; commNetRemoteCtrl.flags.all = 0; commNetRemoteCtrl.recvErr = COMM_NET_REMOTE_REQ_ACK_GENERIC_OK; commNetRemoteCtrl.sendErr = COMM_NET_REMOTE_REQ_ACK_GENERIC_OK; // Create a TCP stream socket. commNetRemoteCtrl.clientfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (commNetRemoteCtrl.clientfd < 0) { commNetRemoteCtrl.flags.quit = TRUE; OS_Error("CommNet_RemoteClientProcess: socket failed."); } else { // Setup the Server IP address and port memset((char *) &commNetRemoteCtrl.servAddr, 0, sizeof(commNetRemoteCtrl.servAddr)); commNetRemoteCtrl.servAddr.sin_family = AF_INET; commNetRemoteCtrl.servAddr.sin_port = htons(REMOTE_SERVER_PORT); commNetRemoteCtrl.servAddr.sin_addr.s_addr = inet_addr(REMOTE_SERVER_IP ); to.tv_sec = REMOTE_SOCKET_TIMEOUT; to.tv_usec = 0; setsockopt( commNetRemoteCtrl.clientfd, SOL_SOCKET, SO_RCVTIMEO, &to, sizeof( to ) ); setsockopt( commNetRemoteCtrl.clientfd, SOL_SOCKET, SO_SNDTIMEO, &to, sizeof( to ) ); // Connect to the server status = connect(commNetRemoteCtrl.clientfd, (struct sockaddr *) &commNetRemoteCtrl.servAddr, sizeof(commNetRemoteCtrl.servAddr)); if (status < 0) { fdClose( commNetRemoteCtrl.clientfd ); commNetRemoteCtrl.flags.quit = TRUE; OS_Error("CommNet_RemoteClientProcess: client connect failed. (%d).",fdError() ); } else { // Init the Error_Block Error_init(&commNetRemoteCtrl.eb); commNetRemoteCtrl.state = COMM_NET_REMOTE_STS_OPEN_SESSION; commNetRemoteCtrl.flags.quit = FALSE; OS_SysDbg( "RemoteControlClientProcess: start clientfd = 0x%x.", commNetRemoteCtrl.clientfd ); } } // Loop while we receive data while( commNetRemoteCtrl.flags.quit == FALSE ) { switch( commNetRemoteCtrl.state ) { case COMM_NET_REMOTE_STS_IDLE: // // controlla le condizioni di uscita if( appMain.diagnostic.general.flags.cableDisconnected == TRUE ) { commNetRemoteCtrl.flags.quit = TRUE; } else { if( Network_IsLANConfigured() == FALSE) { commNetRemoteCtrl.flags.quit = TRUE; } } if( commNetRemoteCtrl.flags.close == TRUE ) { commNetRemoteCtrl.flags.quit = TRUE; } // Check if request flag is active , then close session if( commNetRemoteCtrl.flags.quit == FALSE ) { if( commNetRemoteCtrl.flags.request == TRUE ) { commNetRemoteCtrl.state = COMM_NET_REMOTE_STS_CLOSE_SESSION; } else { OS_Sleep(OS_TEN_MILLISECONDS); } } break; case COMM_NET_REMOTE_STS_WAIT_RESP: if( CommNetRemote_Receive() == FALSE ) { commNetRemoteCtrl.state = COMM_NET_REMOTE_STS_RECV_ERROR; } else { commNetRemoteCtrl.state = COMM_NET_REMOTE_STS_IDLE; } break; case COMM_NET_REMOTE_STS_OPEN_SESSION: // Compose header // Compose payload OS_SysDbg("Open session: %ld ", OS_GetTimerTick() ); if( CommNetRemote_Send( commNetRemoteBuffer, SESSION_OPEN_HEADER_PAYLOAD) == TRUE ) { // decide il prossimo stato commNetRemoteCtrl.state = COMM_NET_REMOTE_STS_WAIT_RESP; } else { commNetRemoteCtrl.recvErr = COMM_NET_REMOTE_SEND_ERR_NONE; commNetRemoteCtrl.state = COMM_NET_REMOTE_STS_SEND_ERROR; iWaitCount = 0; } break; case COMM_NET_REMOTE_STS_CLOSE_SESSION: // Compose the header // Compose payload // Get return code CommNetRemote_GetLastErr(&err); payload->sessionClose.retcode = err; OS_SysDbg("Close session: %ld ", OS_GetTimerTick() ); CommNetRemote_Send( commNetRemoteBuffer, SESSION_CLOSE_HEADER_PAYLOAD); // decide il prossimo stato OS_SetPeriodicTask(commNetRemoteCtrl.timer, OS_ONE_SECOND); OS_StartPeriodicTask(commNetRemoteCtrl.timer); commNetRemoteCtrl.flags.request = FALSE; commNetRemoteCtrl.state = COMM_NET_REMOTE_STS_IDLE; break; case COMM_NET_REMOTE_STS_PUBLISH_FILE: // Compose the header // Compose payload if( CommNetRemote_Send( commNetRemoteBuffer, SESSION_PUBLISH_HEADER_PAYLOAD) == TRUE ) { commNetRemoteCtrl.state = COMM_NET_REMOTE_STS_WAIT_RESP; } else { commNetRemoteCtrl.recvErr = COMM_NET_REMOTE_SEND_ERR_NONE; commNetRemoteCtrl.state = COMM_NET_REMOTE_STS_SEND_ERROR; iWaitCount = 0; } break; case COMM_NET_REMOTE_STS_PUBLISH_CHUNK: // Compose the header // Compose payload if( CommNetRemote_Send( commNetRemoteBuffer, (commNetRemoteCtrl.iBytesToSend + SESSION_HEADER_COUNT) ) == TRUE ) { commNetRemoteCtrl.state = COMM_NET_REMOTE_STS_WAIT_RESP; } else { commNetRemoteCtrl.recvErr = COMM_NET_REMOTE_SEND_ERR_NONE; commNetRemoteCtrl.state = COMM_NET_REMOTE_STS_SEND_ERROR; iWaitCount = 0; } break; case COMM_NET_REMOTE_STS_GET_FILE: // Compose the header // Compose payload if( CommNetRemote_Send( commNetRemoteBuffer, SESSION_GET_HEADER_PAYLOAD) == TRUE ) { commNetRemoteCtrl.state = COMM_NET_REMOTE_STS_WAIT_RESP; } else { commNetRemoteCtrl.recvErr = COMM_NET_REMOTE_SEND_ERR_NONE; commNetRemoteCtrl.state = COMM_NET_REMOTE_STS_SEND_ERROR; iWaitCount = 0; } break; case COMM_NET_REMOTE_STS_GET_CHUNK: // Compose the header if( CommNetRemote_Send( commNetRemoteBuffer, SESSION_GET_CHUNK_HEADER_PAYLOAD ) == TRUE ) { commNetRemoteCtrl.state = COMM_NET_REMOTE_STS_WAIT_RESP; } else { commNetRemoteCtrl.recvErr = COMM_NET_REMOTE_SEND_ERR_NONE; commNetRemoteCtrl.state = COMM_NET_REMOTE_STS_SEND_ERROR; iWaitCount = 0; } break; case COMM_NET_REMOTE_STS_SEND_ERROR: // Wait for error management with flag commNetRemoteCtrl.flags.sendErr in order to be sncronized with upper layer // // Starvation avoid: after 200 times return idle. // iWaitCount++; if( iWaitCount > 200 ) { iWaitCount = 0; commNetRemoteCtrl.state = COMM_NET_REMOTE_STS_IDLE; } else { if( TRUE == commNetRemoteCtrl.flags.sendErr ) { commNetRemoteCtrl.state = COMM_NET_REMOTE_STS_IDLE; } else { OS_Sleep(OS_HUNDRED_MILLISECONDS); } } break; case COMM_NET_REMOTE_STS_RECV_ERROR: commNetRemoteCtrl.flags.recvErr = TRUE; commNetRemoteCtrl.state = COMM_NET_REMOTE_STS_IDLE; break; default: commNetRemoteCtrl.state = COMM_NET_REMOTE_STS_IDLE; break; } } // while( commNetRemoteCtrl.flags.quit == FALSE ) // The process is closed // if( commNetRemoteCtrl.clientfd ) { fdClose( commNetRemoteCtrl.clientfd ); OS_SysDbg("Close socket."); } // Clean all variables OS_SysDbg("CommNetRemote_ClientProcess: quit."); commNetRemoteCtrl.enabled = FALSE; commNetRemoteCtrl.flags.all = 0; OS_DeleteTask(commNetRemoteCtrl.taskHandle); commNetRemoteCtrl.taskHandle = OS_TASK_ERROR; return; }