大家好。
我有一个重要的用例、用来证明 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;
}
