RT-Thread-packages / wiznet

WIZnet TCP/IP chips (such as W5500/W5100..) SAL framework implement.
Apache License 2.0
49 stars 35 forks source link

read收数据时会导致线程一直执行 #64

Open zt449569708 opened 2 years ago

zt449569708 commented 2 years ago

问题: TCP通信,当使用单独的线程去收数据时,并且线程里只使用read函数,不使用select函数配合,代码如下:

 while (1)
     {
         ret = read(sock, buf, 64);
         if (ret <= 0)
         {
             break;
         }

         rt_ringbuffer_put(rx_ring_buf, buf, ret);
     }

如果此时对方只发了一次数据,那么线程会一直执行,不会阻塞,导致低优先级线程无法调度。

原因是wiz_recvfrom函数里面有这样一段代码,

        uint16_t recvsize = getSn_RX_RSR(socket);
        /* receive last transmission of remaining data */
        if (recvsize > 0)
        {
            rt_mutex_take(sock->recv_lock, RT_WAITING_FOREVER);
            recv_len = wizchip_recv(socket, mem, len);
            if (recv_len > 0)
            {
                rt_mutex_release(sock->recv_lock);
                goto __exit;
            }
            rt_mutex_release(sock->recv_lock);
        }

        if (socket_state == SOCK_CLOSED)
        {
            return 0;
        }
        else if (socket_state != SOCK_ESTABLISHED)
        {
            LOG_E("WIZnet receive failed, get socket(%d) register state(%d) error.", socket, socket_state);
            result = -1;
            goto __exit;
        }

        while (1)
        {
            /* wait the receive semaphore */
            if (rt_sem_take(sock->recv_notice, timeout) < 0)
            {
                result = -1;
                /* blocking mode will prints an error and non-blocking mode exits directly */
                if ((flags & MSG_DONTWAIT) == 0)
                {
                    LOG_E("WIZnet socket (%d) receive timeout (%d)!", socket, timeout);
                    errno = EAGAIN;
                }
                goto __exit;
            }
            else
            {
                if (sock->state == SOCK_ESTABLISHED)
                {
                    /* get receive buffer to receiver ring buffer */
                    rt_mutex_take(sock->recv_lock, RT_WAITING_FOREVER);
                    recv_len = wizchip_recv(socket, mem, len);
                    if (recv_len < 0)
                    {
                        LOG_E("WIZnet socket(%d) receive data failed(%d).", socket, recv_len);
                        rt_mutex_release(sock->recv_lock);
                        result = -1;
                        goto __exit;
                    }
                    rt_mutex_release(sock->recv_lock);
                }
                else if (sock->state == SOCK_CLOSED)
                {
                    result = 0;
                    goto __exit;
                }
                break;
            }
        }
        break;
    }

这段代码先通过getSn_RX_RSR判断是否有数据,有的话就返回,这就导致第二次read时,getSn_RX_RSR判断到无数据,就到下面的 if (rt_sem_take(sock->recv_notice, timeout) < 0)这,因为这里信号量已经在中断里面释放过了,所以这里是不会阻塞的,从而进入到wizchip_recv函数,而这个函数里面就一直在判断getSn_RX_RSR是否有数,因为对端只发了一次,第一次已经读走了,因此getSn_RX_RSR一直返回0,从而形成无阻赛的死循环。

所以wiz_recvfrom函数应该改进,通过注释(/ receive last transmission of remaining data /)可以看出,是否防止数据读不干净,而这样做的。那么为啥不在wizchip_recv执行完后,再去判断getSn_RX_RSR是否有数,如果有,再释放一次信号量即可

环境: RT-Thread Studio 2.1.2 wiznet latest

xiangxistu commented 2 years ago

这个思路挺好的(^_^),可以修改看看效果;好使的话,可以提交一份 PR 。

BernardXiong commented 2 years ago

这个在底层是lwIP时应该不存在吧?

zt449569708 commented 2 years ago

这个在底层是lwIP时应该不存在吧?

lwip就不清楚了,因为我现在只用了wiznet