freertos_lwip stm32F2系列 非线程安全需要加锁保护

来源:互联网 发布:java md5解密密码 编辑:程序博客网 时间:2024/06/07 13:30

我在工作中需要在STM32F2系列下,用freertos和LWIP开发一个服务器端的TCP/IP服务。

由于内存有限并且freertos不便于利用fork函数新建线程,因此采用了select模式编写代码。

开始通讯没有问题但是在多次连接或者长时间通讯时,会出现用于TCP/IP通讯的任务被挂起,导致无法再次接受数据,代码如下:

void NetZeroEntryFun(void const * argument)
{
    int ts_sock_m = -1;
    int ts_sock_s = -1;
    int iSelectRet = -1;
    struct sockaddr_in sin; 
    int sin_len = 0;
    int i = 0;
    int maxi = 0;
    int maxfd;
    fd_set rset;
    fd_set allset; 
    int n_received = -1;
    NET_MSG stNetMsg;
    unsigned short usLen = 0;
    static struct timeval timeout = {30, 0};
    uint16_t nMode = 1;
    

//ether_init
    Net_Initialize(); 

// allocate a socket
    ts_sock_m = socket(AF_INET, SOCK_STREAM, 0);
    if (ts_sock_m < 0)
    {
        return; // Couldn’t Allocate a TCP Socket
    }
// Bind the socket to the local port number and IP address
    memset(&sin, 0, sizeof(sin));
    sin.sin_family = AF_INET;
    sin.sin_port = htons(net_astIp[0].port_no);      

    memcpy((int8_t *)&sin.sin_addr, (int8_t *)net_astIp[0].ip_server_address, 4);

// port number in network byte order
    if (bind(ts_sock_m, (const struct sockaddr *)&sin, sizeof(sin)) < 0)
    {
        closesocket(ts_sock_m);
        return; // Bind Failed
    }

    if (listen(ts_sock_m, NET_MAX_LINK) != 0) // 2 slave ports
    {
        closesocket(ts_sock_m);
        return; // Listen Failed
    }
    
//设置非阻塞模式 
    ioctlsocket(ts_sock_m,FIONBIO,&nMode);

    FD_ZERO(&rset);
    FD_ZERO(&allset);   
    FD_SET(ts_sock_m, &rset);  
    maxfd = ts_sock_m; 

while (1)
    {
      FD_ZERO(&rset);
      FD_SET(ts_sock_m, &rset);
      
      for (i = 0; i < NET_MAX_LINK; i++)  
      {  
        if (net_astRecv0[i].ts_sock_s != -1)  
        {  
          FD_SET(net_astRecv0[i].ts_sock_s, &rset);  
        }  
      }
     
      iSelectRet = select(maxfd + 1, &rset, NULL, NULL, &timeout);
      
      if(iSelectRet < 0)
      {/*错误*/
        continue;
      }
      else if(iSelectRet == 0)
      {/*超时*/
        for (i = 0; i <= maxi; i++) 
        {  
          ts_sock_s = net_astRecv0[i].ts_sock_s; 
          if(ts_sock_s >= 0)
          {/*文件描述符不大于0*/
            closesocket(ts_sock_s);
            FD_CLR(net_astRecv0[i].ts_sock_s, &rset);
            net_astRecv0[i].ts_sock_s = -1;
            net_astRecv0[i].ulTaskId = 0;
          }
        }
        maxi = 0;
        continue;
      }
      else
      {
        for (i = 0; i <= maxi; i++) 
        {  
          ts_sock_s = net_astRecv0[i].ts_sock_s;  
          
          if ((FD_ISSET(ts_sock_s, &rset)) && (ts_sock_s != -1)) 
          {/*有链接则接收数据*/          
              n_received = recv(ts_sock_s, (int8_t *)stNetMsg.aucData, sizeof(stNetMsg.aucData), MSG_DONTWAIT);     
         
              if (n_received <= 0)
              {
                closesocket(ts_sock_s);
                FD_CLR(net_astRecv0[i].ts_sock_s, &rset);
                net_astRecv0[i].ts_sock_s = -1;
                net_astRecv0[i].ulTaskId = 0;
                
              }
              else
              {
                stNetMsg.usLength = n_received;          
                stNetMsg.ulTaskId = net_astRecv0[i].ulTaskId;
                stNetMsg.ucRtNum = i;
                stNetMsg.ucLtNum = NET_LISTEN_TASK0;
                
                if ((stNetMsg.usLength > 7) && 
                    (stNetMsg.aucData[2] == 0x00) && 
                      (stNetMsg.aucData[3] == 0x00))
                {
                  memcpy(&usLen, stNetMsg.aucData + 4, 2);
                  if (usLen == (unsigned short)(stNetMsg.usLength - 6))
                  {
                    xQueueSend(xCtrlQueueHandle, &stNetMsg, 0);
                  }
                }
              }                   
          }
        }
        
        if (FD_ISSET(ts_sock_m, &rset))
        {/*创建新的链接*/
          sin_len = sizeof(struct sockaddr_in);
          ts_sock_s = accept(ts_sock_m, (struct sockaddr *)&sin, (socklen_t *)&sin_len);
          if (ts_sock_s == -1)  
          {
            continue;
          }
          
          /*设置非阻塞模式*/
          nMode = 1;
          ioctlsocket(ts_sock_s,FIONBIO,&nMode);
          for (i = 0; i < NET_MAX_LINK; i++) 
          {  
            if (net_astRecv0[i].ts_sock_s < 0) 
            {  
              net_astRecv0[i].ts_sock_s= ts_sock_s;  
              if (i > maxi)  
                maxi = i;  
              break;  
            }   
          }  
          if (ts_sock_s > maxfd)  
            maxfd = ts_sock_s;  
        }
      }
      osDelay(1);
    }
}

开始担心自己select用法不对,因此特意做了一个最简单的没有select的TCP/IP通讯的服务端,但是还是与之前的效果一致。

开始对这个bug非常的困惑,也搜了不少关于LWIP和SELECT的用法都没有什么发现,后来一个同事(我的前辈哦)帮我搜索了一下,发现freertos_lwip是非线程安全的,也就是说我在调用recv和send的时候LWIP并没有做保护,而我调用的recv和send的还不在一个任务中,导致了recv和send相互堵塞的情况。

为了解决上述问题,在recv和send调用的时候添加了一个锁。在应用层保证了lwip的线程安全性,修改代码如下:红色部分。。。是修改的内容哦。。。。

修改后的接受部分:

    while (1)
    {
      FD_ZERO(&rset);
      FD_SET(ts_sock_m, &rset);
      
      for (i = 0; i < NET_MAX_LINK; i++)  
      {  
        if (net_astRecv0[i].ts_sock_s != -1)  
        {  
          FD_SET(net_astRecv0[i].ts_sock_s, &rset);  
        }  
      }
     
      iSelectRet = select(maxfd + 1, &rset, NULL, NULL, &timeout);
      
      if(iSelectRet < 0)
      {/*错误*/
        continue;
      }
      else if(iSelectRet == 0)
      {/*超时*/
        for (i = 0; i <= maxi; i++) 
        {  
          ts_sock_s = net_astRecv0[i].ts_sock_s; 
          if(ts_sock_s >= 0)
          {/*文件描述符不大于0*/
            closesocket(ts_sock_s);
            FD_CLR(net_astRecv0[i].ts_sock_s, &rset);
            net_astRecv0[i].ts_sock_s = -1;
            net_astRecv0[i].ulTaskId = 0;
          }
        }
        maxi = 0;
        continue;
      }
      else
      {
        for (i = 0; i <= maxi; i++) 
        {  
          ts_sock_s = net_astRecv0[i].ts_sock_s;  
          
          if ((FD_ISSET(ts_sock_s, &rset)) && (ts_sock_s != -1)) 
          {/*有链接则接收数据*/          
            if(osOK == osSemaphoreWait(NET_SEMPHORE_ZEROHandle, MAX_DELAY))
            {
              n_received = recv(ts_sock_s, (int8_t *)stNetMsg.aucData, sizeof(stNetMsg.aucData), MSG_DONTWAIT);     
              osSemaphoreRelease(NET_SEMPHORE_ZEROHandle);
         
              if (n_received <= 0)
              {
                closesocket(ts_sock_s);
                FD_CLR(net_astRecv0[i].ts_sock_s, &rset);
                net_astRecv0[i].ts_sock_s = -1;
                net_astRecv0[i].ulTaskId = 0;
                
              }
              else
              {
                stNetMsg.usLength = n_received;          
                stNetMsg.ulTaskId = net_astRecv0[i].ulTaskId;
                stNetMsg.ucRtNum = i;
                stNetMsg.ucLtNum = NET_LISTEN_TASK0;
                
                if ((stNetMsg.usLength > 7) && 
                    (stNetMsg.aucData[2] == 0x00) && 
                      (stNetMsg.aucData[3] == 0x00))
                {
                  memcpy(&usLen, stNetMsg.aucData + 4, 2);
                  if (usLen == (unsigned short)(stNetMsg.usLength - 6))
                  {
                    xQueueSend(xCtrlQueueHandle, &stNetMsg, 0);
                  }
                }
              }                   
            }
          }
        }
        
        if (FD_ISSET(ts_sock_m, &rset))
        {/*创建新的链接*/
          sin_len = sizeof(struct sockaddr_in);
          ts_sock_s = accept(ts_sock_m, (struct sockaddr *)&sin, (socklen_t *)&sin_len);
          if (ts_sock_s == -1)  
          {
            continue;
          }
          
          /*设置非阻塞模式*/
          nMode = 1;
          ioctlsocket(ts_sock_s,FIONBIO,&nMode);
          for (i = 0; i < NET_MAX_LINK; i++) 
          {  
            if (net_astRecv0[i].ts_sock_s < 0) 
            {  
              net_astRecv0[i].ts_sock_s= ts_sock_s;  
              if (i > maxi)  
                maxi = i;  
              break;  
            }   
          }  
          if (ts_sock_s > maxfd)  
            maxfd = ts_sock_s;  
        }
      }
      osDelay(1);
    }
}

修改后的发送部分:

void Net_send_to_PC(NET_MSG* pstNetMsg)
{
    NET_RECV_S stRecvS;
    
    if (pstNetMsg == NULL)
    {
        return;
    }
    
    switch (pstNetMsg->ucLtNum)
    {
    case NET_LISTEN_TASK0:
        if (!_Net_Get_Recv_0(&stRecvS, pstNetMsg->ucRtNum) || 
            (pstNetMsg->ulTaskId != stRecvS.ulTaskId) || 
            (stRecvS.ts_sock_s < 0))
        {
            return;
        }
        break;
    default:
        return;
    }
    
    if(osOK == osSemaphoreWait(NET_SEMPHORE_ZEROHandle, MAX_DELAY))
    {    

      send(stRecvS.ts_sock_s, (int8_t *)pstNetMsg->aucData, pstNetMsg->usLength, 0);
      osSemaphoreRelease(NET_SEMPHORE_ZEROHandle);
    }
    
}

原创粉丝点击