您尚未登录。

楼主 # 2021-06-16 11:22:11

Gentlepig
会员
注册时间: 2018-10-24
已发帖子: 1,378
积分: 1343.5

请教,lwip如何在不同线程里收发?

stm32f429+rtthread,想实现的功能是,can收到的消息通过网口发出去,tcp client收到的信息通过can发出去。

rtt的msh里运行的程序是运行在msh线程里吧?我是在msh线程里初始化了can、tcp client。在msh线程的的主循环里,阻塞读取tcp信息,读取到以后通过can发送出去。到这里能正常执行,pc端通过tcp servier发送信息后,can监视端口能看到信息。

msh线程初始化时,同时新建了个can读线程。can读线程里等待can中断发送的信号量,等到后通过lwip_send()发送出去。
lwip用到的sock,我是在该.c文件里进行了全局定义。结果pc端的tcp servier程序收不到东西。
之前测试时,在msh线程里,tcp client收到信息后立刻返回一段信息,pc端是能正常收到的。

离线

#1 2021-06-16 11:56:30

aozima
会员
所在地: 深圳
注册时间: 2019-05-25
已发帖子: 463
积分: 330.5
个人网站

Re: 请教,lwip如何在不同线程里收发?

>通过lwip_send()发送出去

你在msh线程中创建的socket,可以在其它线程中去收发。
只是建议不要同时在多个不同的线程中对同一个socket进行收发。

另外,你用的rt-thread哪个版本?
因为rt-thread的dfs和sal,对文件系统 和 lwip的fd(scoket)做了统一的管理,
所以你不要再直接调用lwip_send,而应该用SAL这边的send/recv,不然就会乱掉。

离线

楼主 #2 2021-06-22 11:49:14

Gentlepig
会员
注册时间: 2018-10-24
已发帖子: 1,378
积分: 1343.5

Re: 请教,lwip如何在不同线程里收发?

    if((sock=socket(AF_INET,SOCK_STREAM,0))==-1)
    {
        rt_kprintf("Socket  error\n");
        rt_free(tcp_recv_data);
        return;
    }

在线程里调用socket()函数创建socket,过程中创建的都是在线程里,返回的整形sock,即时定义为全局变量,在其它线程调用这个sock也不能直接用来tcp发送。

现在改变想法,can接收线程里不在直接tcp发送了,而是通过消息队列发送给tcp线程。
现在tcp线程的主循环里需要做的是,阻塞等待tcp接收,收到后,通过can发送出去。等待消息队列,等到后读取然后通过tcp发送出去。

又遇到了问题,电脑端打开了tcp server和can监控,tcp server发送消息后,can软件能收到。
can软件发送后,tcp server没有立刻收到消息。而是下次tcp server发送消息时,同时收到了之前的消息。

    while (1)
    {
        bytes_received=recv(sock,tcp_recv_data,BUFSZ-1,0);
        if (bytes_received<0)
        {
            closesocket(sock);
            rt_kprintf("\nreceived  error-1,  close   the socket.\r\n");
            rt_free(tcp_recv_data);
            break;
        }
        else    if(bytes_received==0)
        {
            closesocket(sock);
            rt_kprintf("\nreceived  error0,  close   the socket.\r\n");
            rt_free(tcp_recv_data);
            break;
        }
        tcp_recv_data[bytes_received]='\0';
        {
            rt_kprintf("thread1: recv msg from msg queue, the content:%c\n", tcp_recv_data[0]);
            can_msg.data[0]=tcp_recv_data[0];
            can_msg.data[1]=tcp_recv_data[1];
            can_msg.data[2]=tcp_recv_data[2];
            can_msg.data[3]=tcp_recv_data[3];
            can_msg.data[4]=tcp_recv_data[4];
            can_msg.data[5]=tcp_recv_data[5];
            can_msg.data[6]=tcp_recv_data[6];
            can_msg.data[7]=tcp_recv_data[7];
            can_size=rt_device_write(can_dev,0,&can_msg,sizeof(can_msg));
        }

        /* 从消息队列中接收消息 */
        if (rt_mq_recv(&can_rx_mq, &can_recv_data, 8, 1000) == RT_EOK)
        {
            rt_kprintf("thread1: recv msg from msg queue, the content:%c\n", can_recv_data[0]);
            send(sock,&can_recv_data,8,0);
            // send(sock,"hahahaha",8,0);
        }

        /* 延时 50ms */
        rt_thread_mdelay(50);
    }
    rt_kprintf("thread1: detach mq \n");
    rt_mq_detach(&can_rx_mq);

离线

楼主 #3 2021-06-22 14:52:36

Gentlepig
会员
注册时间: 2018-10-24
已发帖子: 1,378
积分: 1343.5

Re: 请教,lwip如何在不同线程里收发?

想了想,tcp线程的主循环里,有两个等待。
一个是lwip的recv()函数,这个是阻塞读取。
另一个是消息队列的等待。

现在应该是lwip的阻塞读取一直卡住了,消息队列接收只能等Lwip读取到消息后才能执行。

那么lwip的读取改成不阻塞呢?

离线

楼主 #4 2021-06-22 16:14:33

Gentlepig
会员
注册时间: 2018-10-24
已发帖子: 1,378
积分: 1343.5

Re: 请教,lwip如何在不同线程里收发?

我将tcp线程的接收改为非阻塞模式,

bytes_received=recv(sock,tcp_recv_data,BUFSZ-1,MSG_DONTWAIT)

结果仍一样。

应该还死卡在tcp接收这里,甚至阻塞了can线程的运行。直到tcp线程接收到tcp信息后,can线程才执行can读取操作及消息队列的发送。

离线

楼主 #5 2021-06-25 15:34:10

Gentlepig
会员
注册时间: 2018-10-24
已发帖子: 1,378
积分: 1343.5

Re: 请教,lwip如何在不同线程里收发?

现在改用select方式,不再阻塞can线程了,但是tcp线程还是阻塞在tcp接收这里。
tcp线程主循环里,现在执行的是,用select方式读取tcp消息,还有等待消息队列。
现在现象是,能看到can线程收到can信息,大概也能通过消息队列把信息发出去,而tcp线程是要等到tcp接收后才能执行消息队列的接收。

离线

楼主 #6 2021-06-25 16:49:11

Gentlepig
会员
注册时间: 2018-10-24
已发帖子: 1,378
积分: 1343.5

Re: 请教,lwip如何在不同线程里收发?

    maxfdp1 =   sock+1;
    tim.tv_sec=0;
    tim.tv_usec=100000;

    while (1)
    {
        FD_ZERO(&readset);
        FD_SET(sock,&readset);
        i=select(maxfdp1,&readset,0,0,&tim);
        if(i==0)
            continue;
        if(FD_ISSET(sock,&readset))
        {
            bytes_received=recv(sock,tcp_recv_data,BUFSZ-1,MSG_DONTWAIT);
            if(bytes_received==-1)
            {...}
            else if (bytes_received<0)
            {...}
            else    if(bytes_received==0)
            {...}
            tcp_recv_data[bytes_received]='\0';
            {...}
        }

        /* 从消息队列中接收消息 */
        if (rt_mq_recv(&can_rx_mq, &can_recv_data, 8, 100) == RT_EOK)
        {
            rt_kprintf("thread1: recv msg from msg queue, the content:%c\n", can_recv_data[0]);
            send(sock,&can_recv_data,8,0);
        }

改成这样了,接收时间也改了。但还是阻塞在接收这里。

最近编辑记录 Gentlepig (2021-06-25 16:50:30)

离线

楼主 #7 2021-06-25 17:43:52

Gentlepig
会员
注册时间: 2018-10-24
已发帖子: 1,378
积分: 1343.5

Re: 请教,lwip如何在不同线程里收发?

tcp循环里更改了下顺序,先等待消息队列,然后在进行tcp的select接收。这样就可以了。
之前是在select()以后,根据返回结果进行判断,有的直接continue,跳过了后面的等待消息队列。

离线

楼主 #8 2021-06-26 16:15:41

Gentlepig
会员
注册时间: 2018-10-24
已发帖子: 1,378
积分: 1343.5

Re: 请教,lwip如何在不同线程里收发?

send可以在另一个线程里使用。
我之前在其它线程里send不成功,有两个问题,一个是tcp线程没有用select,recv阻塞了其它线程。另一个是send里第三个参数写错了...

现在收发过程正常了,还有2个问题需要考虑。
1,can总线上只有1个设备时,can初始化无法成功,我靠在can初始化这里while一直等待can初始化成功?
2,网络断了的话,如何进行tcp重连?我是在FDISSET()里,进行recv()接收后,判断返回的数,如果是<=0,则执行connect(),然后continu,跳到下次循环。结果目前现象是,一旦断网就卡住了。

离线

#9 2021-06-26 16:41:01

david
会员
注册时间: 2018-03-05
已发帖子: 393
积分: 324.5

Re: 请教,lwip如何在不同线程里收发?

Socket函数没有配置选项吗 感觉应该是创建在heap里 然后返回句柄 感觉这种经典问题应该是早被解决了

离线

楼主 #10 2021-06-28 17:18:02

Gentlepig
会员
注册时间: 2018-10-24
已发帖子: 1,378
积分: 1343.5

Re: 请教,lwip如何在不同线程里收发?

有重试了几次,发现些现像。
我现在在tcp线程里,有两个while循环,外层里有socket(),connect()函数,内层执行select接收。
select接收出错后,关闭soket,跳出内层循环。在外层循环里重新建立socket及连接。

实验时,上电后正常,可以can和tcp相互转发。
我将pc的tcp server断开后再重连,然后板子can接收tcp发送正常,tcp接收转can发送第一次正常,第二次就报错了。

(rt_object_get_type(&mb->parent.parent) == RT_Object_Class_MailBox) assertion failed at function:rt_mb_send_wait, line number:

(rt_object_get_type(&mb->parent.parent) == RT_Object_Class_MailBox) assertion failed at function:rt_mb_send_wait, line number:

最近编辑记录 Gentlepig (2021-06-28 17:18:23)

离线

楼主 #11 2021-07-01 14:46:12

Gentlepig
会员
注册时间: 2018-10-24
已发帖子: 1,378
积分: 1343.5

Re: 请教,lwip如何在不同线程里收发?

目前tcp线程大致是这样的,在tcp client线程里写了里外两个while(1)循环。外层循环了,调用的是socket(),connect()函数,内层循环里是调用select()根据结果来决定是否接收,出错后关闭socket,然后跳到外层循环。

想的是这样的,可实际操作后,上电通讯成功后,关闭tcp server然后再打开,发现打印出重新重建了socket和重新连接了。电脑端通过can发送信息后,设备接收后也可以通过tcp发送到tcp server.多次发送也没问题。
但电脑端第一次发送tcp消息后,设备接收后可以通过can发过来。电脑端再次发送,接报错了...

while(1)
{
    ...
    sock=-1;
    while(sock==-1)
    {
        sock=socket(AF_INET,SOCK_STREAM,0);
    }
    rt_kprintf("socke is created.\r\n");
    ...
    if (connect(sock,(struct sockaddr*)&server_addr,sizeof(struct    sockaddr))==-1)
    {
        rt_kprintf("Connect fail!\n");
        closesocket(sock);
        rt_free(tcp_recv_data);
        return;
    }
    else
    {
        rt_kprintf("Connect successful\n");
    }
    ...
    while (1)
    {
        FD_ZERO(&readset);
        FD_SET(sock,&readset);
        i=select(maxfdp1,&readset,0,0,&tim);
        if(i==0)
            continue;
        if(FD_ISSET(sock,&readset))
        {
            bytes_received=recv(sock,tcp_recv_data,BUFSZ-1,MSG_DONTWAIT);
            if (bytes_received<0)
            {
                closesocket(sock);
                rt_kprintf("\nreceived  error-1,  close   the socket.\r\n");
                rt_free(tcp_recv_data);
                break;
            }
            else    if(bytes_received==0)
            {
                closesocket(sock);
                rt_kprintf("\nreceived  error0,  close   the socket.\r\n");
                rt_free(tcp_recv_data);
                break;
            }
            ...
        }
    }

msh终端是这样提示的

 \ | /
- RT -     Thread Operating System
 / | \     4.0.3 build Jun 26 2021
 2006 - 2020 Copyright by rt-thread team
lwIP-2.0.2 initialized!
[I/sal.skt] Socket Abstraction Layer initialize success.
msh />ifconfig
network interface device: e0 (Default)
MTU: 1500
MAC: 00 80 e1 05 37 2b
FLAGS: UP LINK_UP INTERNET_DOWN DHCP_ENABLE ETHARP BROADCAST IGMP
ip address: 0.0.0.0
gw address: 0.0.0.0
net mask  : 0.0.0.0
dns server #0: 192.168.1.1
dns server #1: 0.0.0.0
msh />can2tcp
socke is created.
Connect successful
tcp_thread: recv data = aabbccdd
tcp_thread: recv data = aabbccdd
tcp_thread: recv data = aabbccdd
ID:1 0 0 0 0 0 0 0 0
can_rx_thread: send message -
ID:1 0 0 0 0 0 0 0 0
can_rx_thread: send message -
ID:1 0 0 0 0 0 0 0 0
can_rx_thread: send message -
ID:1 0 0 0 0 0 0 0 0
can_rx_thread: send message -

received  error0,  close   the socket.
socke is created.
Connect successful
tcp_thread: recv data = aabbccdd
(rt_object_get_type(&mb->parent.parent) == RT_Object_Class_MailBox) assertion failed at function:rt_mb_send_wait, line number:

离线

楼主 #12 2021-07-03 08:54:26

Gentlepig
会员
注册时间: 2018-10-24
已发帖子: 1,378
积分: 1343.5

Re: 请教,lwip如何在不同线程里收发?

我的程序。断开tcpserver后再打开,tcp第一次接收没问题,第二次接收就报错了。

#include <rtthread.h>
#include "rtdevice.h"

#include <lwip/api.h>
#include <lwip/sockets.h>
#include <lwip/netdb.h>

// #include    <sys/select.h>
// #include    <sys/socket.h>
// #include    <netdb.h>
#include    <string.h>
#include    <finsh.h>


#define BUFSZ   1024

#define CAN_DEV_NAME    "can1"

static  int sock;

ALIGN(RT_ALIGN_SIZE)
static char thread1_stack[1024];
static struct rt_thread thread1;

static  struct rt_semaphore rx_sem;
static  rt_device_t can_dev;

static  rt_err_t    can_rx_call(rt_device_t dev,    rt_size_t   size)
{
    rt_sem_release(&rx_sem);
    return  RT_EOK;
}

static  void    can_rx_thread(void  *parameter)
{
    int i;
    rt_err_t    res;
    struct rt_can_msg   rxmsg={0};
    char    tcp_send_data[]="hahaha\r\n";
    
    rt_device_set_rx_indicate(can_dev,  can_rx_call);
    while(1)
    {
        rt_sem_take(&rx_sem,RT_WAITING_FOREVER);
        rt_device_read(can_dev,0,&rxmsg,sizeof(rxmsg));
        rt_kprintf("ID:%x",rxmsg.id);
        for(i=0;i<8;i++)
        {
            rt_kprintf("%2x",rxmsg.data[i]);
        }
        rt_kprintf("\n");

        res = send(sock, rxmsg.data, rxmsg.len, 0);

        rt_kprintf("can_rx_thread: send message - %c\n", rxmsg.data[0]);

        /* 延时 50ms */
        rt_thread_mdelay(50);
    }
}

/* 线程 1 入口函数 */
void can2tcp(int  argc,char *argv[])
{
    int i;
    struct rt_can_msg can_msg = {0};
    rt_err_t    res;
    rt_size_t   can_size;
    rt_thread_t thread_can_rx;
    char    can_name[RT_NAME_MAX];

    char    *tcp_recv_data;
    struct  hostent *host;
    // int sock,   bytes_received;
    int bytes_received;
    struct sockaddr_in  server_addr;
    const   char    *url;
    int port;
    int maxfdp1;
    fd_set readset;
    struct timeval   tim;

    uint8_t can_recv_data[8];
    char sendData[]="hahahaha";

    url =  "192.168.1.143";
    port   =    5000;

/*-------------初始化tcp client---------------------*/

    host    =   gethostbyname(url);
    tcp_recv_data    =   rt_malloc(BUFSZ);
    if(tcp_recv_data==RT_NULL)
    {
        rt_kprintf("No  memory\n");
        return;
    }
    
    // if((sock=socket(AF_INET,SOCK_STREAM,0))==-1)
    // {
    //     rt_kprintf("Socket  error\n");
    //     rt_free(tcp_recv_data);
    //     return;
    // }

    // server_addr.sin_family=AF_INET;
    // server_addr.sin_port=htons(port);
    // server_addr.sin_addr=*((struct  in_addr*)host->h_addr);
    // rt_memset(&(server_addr.sin_zero),0,sizeof(server_addr.sin_zero));

    // if (connect(sock,(struct sockaddr*)&server_addr,sizeof(struct    sockaddr))==-1)
    // {
    //     rt_kprintf("Connect fail!\n");
    //     closesocket(sock);
    //     rt_free(tcp_recv_data);
    //     return;
    // }
    // else
    // {
    //     rt_kprintf("Connect successful\n");
    // }

/*-------------打开can线程----------------*/

    rt_strncpy(can_name,CAN_DEV_NAME,RT_NAME_MAX);

    can_dev =   rt_device_find(can_name);
    if(!can_dev)
    {
        rt_kprintf("find    %s  failed!\n",can_name);
        return;
    }

    rt_sem_init(&rx_sem,"rx_sem",0,RT_IPC_FLAG_FIFO);

    can_msg.id=0x78;
    can_msg.ide=RT_CAN_STDID;
    can_msg.rtr=RT_CAN_DTR;
    can_msg.len=8;

    res=rt_device_open(can_dev,RT_DEVICE_FLAG_INT_TX|RT_DEVICE_FLAG_INT_RX);
    RT_ASSERT(res==RT_EOK);

    thread_can_rx=rt_thread_create("can_rx",can_rx_thread,RT_NULL,1024,25,10);
    if(thread_can_rx!=RT_NULL)
    {
        rt_thread_startup(thread_can_rx);
    }
    else
    {
        rt_kprintf("creat can_rx thread failed!\n");
    }

/*-------------tcp client主循环-------------------*/

    uint8_t buf[8] = {0,0,0,0,0,0,0,0};
    rt_uint8_t cnt = 0;

    maxfdp1 =   sock+1;
    tim.tv_sec=0;
    tim.tv_usec=100;

    while(1)
    {
    sock=-1;
    while(sock==-1)
    {
        sock=socket(AF_INET,SOCK_STREAM,0);
        int mode = 0;  //阻塞模式
		ioctlsocket(sock, FIONBIO, &mode);
    }
    rt_kprintf("socket is created.\r\n");

    server_addr.sin_family=AF_INET;
    server_addr.sin_port=htons(port);
    server_addr.sin_addr=*((struct  in_addr*)host->h_addr);
    rt_memset(&(server_addr.sin_zero),0,sizeof(server_addr.sin_zero));

    // i=-1;
    // while(i==-1)
    // {
    //     i=connect(sock,(struct sockaddr*)&server_addr,sizeof(struct    sockaddr));
    // }
    // rt_kprintf("tcp is connected.\r\n");
    if (connect(sock, (struct sockaddr*)&server_addr, sizeof(struct sockaddr))==-1)
    {
        rt_kprintf("Connect fail!\n");
        closesocket(sock);
        // rt_free(tcp_recv_data);
        // return;
        continue;
    }
    else
    {
        rt_kprintf("Connect successful\n");
    }

    while (1)
    {
        FD_ZERO(&readset);
        FD_SET(sock,&readset);
        i=select(maxfdp1,&readset,0,0,&tim);
        if(i==0)
            continue;
        if(FD_ISSET(sock,&readset))
        {
            bytes_received=recv(sock,tcp_recv_data,BUFSZ-1,MSG_DONTWAIT);
            if (bytes_received<0)
            {
                closesocket(sock);
                rt_kprintf("\nreceived  error-1,  close   the socket.\r\n");
                rt_free(tcp_recv_data);
                break;
            }
            else    if(bytes_received==0)
            {
                closesocket(sock);
                rt_kprintf("\nreceived  error0,  close   the socket.\r\n");
                rt_free(tcp_recv_data);
                break;
            }
            tcp_recv_data[bytes_received]='\0';
            {
                // rt_kprintf("thread1: recv msg from msg queue, the content:%c\n", tcp_recv_data[0]);
                rt_kprintf("tcp_thread: recv data = %s\n", tcp_recv_data);
                can_msg.data[0]=tcp_recv_data[0];
                can_msg.data[1]=tcp_recv_data[1];
                can_msg.data[2]=tcp_recv_data[2];
                can_msg.data[3]=tcp_recv_data[3];
                can_msg.data[4]=tcp_recv_data[4];
                can_msg.data[5]=tcp_recv_data[5];
                can_msg.data[6]=tcp_recv_data[6];
                can_msg.data[7]=tcp_recv_data[7];
                can_size=rt_device_write(can_dev,0,&can_msg,sizeof(can_msg));
            }
        }


        /* 延时 50ms */
        rt_thread_mdelay(50);
    }
    }
}

/*--------------------------------------*/

/* 导出到 msh 命令列表中 */
MSH_CMD_EXPORT(can2tcp, can to tcp client sample);

can2tcp.zip
现在是不是下载任何文件都要积分了?

还好,我的这层回复里,引用的就是整个.c文件了,没必要单独下载附件。

最近编辑记录 Gentlepig (2021-07-03 09:23:18)

离线

楼主 #13 2021-07-03 08:57:00

Gentlepig
会员
注册时间: 2018-10-24
已发帖子: 1,378
积分: 1343.5

Re: 请教,lwip如何在不同线程里收发?

群里好心的朋友帮我修改的程序,可以实现tcp重连。但是每次上电后一开始tcp不能接收,只有tcp断开重连一次后才正常。

#include <rtthread.h>
#include "rtdevice.h"

#include <lwip/api.h>
#include <lwip/sockets.h>
#include <lwip/netdb.h>

// #include    <sys/select.h>
// #include    <sys/socket.h>
// #include    <netdb.h>
#include    <string.h>
#include    <finsh.h>

#define CAN_DEV_NAME    "can1"

/* 邮箱控制块 */
static struct rt_mailbox can_mb;
/* 用于放邮件的内存池 */
static char can_mb_pool[256] = {0};

/* 消息队列控制块 */
static struct rt_messagequeue tcp2can_mq;
/* 消息队列中用到的放置消息的内存池 */
static rt_uint8_t tcp2can_msg_pool[2048];


char    can_name[RT_NAME_MAX];
static  rt_device_t can_dev;


int can_msg_send(uint8_t *data,uint16_t len)
{
	rt_uint32_t mailbox_rx_data = 0x20000000;
	uint8_t send_len = 0;
	rt_err_t res;
	while(len > 0 )
	{
		if(len > 8)
		{
			send_len = 8;
		}else
		{
			send_len = len;
		}
		res = rt_mq_send(&tcp2can_mq, data, send_len);//数据存入 tcp2can_mq
		rt_mb_send(&can_mb,mailbox_rx_data);  //通知线程处理
		
		data += send_len;
		len -= send_len;
	}
}

static  rt_err_t can_rx_call(rt_device_t dev, rt_size_t size)
{
	rt_uint32_t mailbox_rx_data = 0x10000000;
	rt_mb_send(&can_mb,mailbox_rx_data); //通知线程处理
    return  RT_EOK;
}

extern int tcp_msg_send(uint8_t *data,uint16_t len);

static void can_thread(void  *parameter)
{
	rt_err_t result;
    /* 初始化一个 mailbox */
    result = rt_mb_init(&can_mb,
                        "can_mb",                      /* 名称是 can_mb */
                        &can_mb_pool[0],                /* 邮箱用到的内存池是 mb_pool */
                        sizeof(can_mb_pool) / 4,        /* 邮箱中的邮件数目,因为一封邮件占 4 字节 */
                        RT_IPC_FLAG_FIFO);          /* 采用 FIFO 方式进行线程等待 */
						
	/* 初始化消息队列 */
    result = rt_mq_init(&tcp2can_mq,
                        "tcp2can_mq",
                        &tcp2can_msg_pool[0],             /* 内存池指向 msg_pool */
                        8,                          /* 每个消息的大小是 8 字节 */
                        sizeof(tcp2can_msg_pool),        /* 内存池的大小是 msg_pool 的大小 */
                        RT_IPC_FLAG_FIFO);       /* 如果有多个线程等待,按照先来先得到的方法分配消息 */					
	
	/*-------------打开can线程----------------*/
    rt_strncpy(can_name,CAN_DEV_NAME,RT_NAME_MAX);
    can_dev =   rt_device_find(can_name);
    if(!can_dev)
    {
        rt_kprintf("find %s  failed!\n",can_name);
        return;
    }

    rt_thread_mdelay(3000);

    rt_device_open(can_dev, RT_DEVICE_FLAG_INT_TX|RT_DEVICE_FLAG_INT_RX);
    rt_device_set_rx_indicate(can_dev,  can_rx_call);
	
	int rc = 0;
	int size = 0;
    struct rt_can_msg  can_rx_msg = {0};
    struct rt_can_msg  can_tx_msg = {0};
    rt_int8_t i;
	
	rt_uint32_t mailbox_rx_data = 0;
    while(1)
    {
		/* 从邮箱中收取邮件 */
		if (rt_mb_recv(&can_mb, (rt_uint32_t *)&mailbox_rx_data, RT_WAITING_FOREVER) == RT_EOK)
        {
			if(mailbox_rx_data == 0x10000000)//表示CAN接受
			{
				can_rx_msg.hdr = -1; /* hdr 值为 - 1,表示直接从 uselist 链表读取数据 */
				rc = rt_device_read(can_dev,0,&can_rx_msg,sizeof(can_rx_msg));
				if(rc > 0)
				{
					rt_kprintf("ID:%x",can_rx_msg.id);
					for(i=0;i<8;i++)
					{
						rt_kprintf("%2x",can_rx_msg.data[i]);
					}
					rt_kprintf("\n");
					tcp_msg_send(can_rx_msg.data,8);  //发送给tcp
				}
			}
			if(mailbox_rx_data == 0x20000000)//表示CAN发送
			{
				/* 从消息队列中接收消息 */ 
				if (rt_mq_recv(&tcp2can_mq, (void*)can_tx_msg.data, 8, 0) == RT_EOK) //不堵塞接收
				{
					can_tx_msg.id = 0x78;              /* ID 为 0x78 */
					can_tx_msg.ide = RT_CAN_STDID;     /* 标准格式 */
					can_tx_msg.rtr = RT_CAN_DTR;       /* 数据帧 */
					can_tx_msg.len = 8;                /* 数据长度为 8 */
					/* 发送一帧 CAN 数据 */
					size = rt_device_write(can_dev, 0, &can_tx_msg, sizeof(can_tx_msg));
					if (size == 0)
					{
						rt_kprintf("can dev write data failed!\n");
					}
				}
			}
		}
    }
}

#define HOST "192.168.1.143"
#define PORT "5000"
static  int socketfd = -1;

int tcp_msg_send(uint8_t *data,uint16_t len)
{
	if(socketfd > 0)
	{
		send(socketfd, data, len, 0);
	}
}

int tcp_connect1(const char *host, const char *port)
{
	int socket_fd = -1;
	struct addrinfo hints, *addr_list, *cur;
	/* Do name resolution with both IPv6 and IPv4 */
	memset(&hints, 0, sizeof(hints));
	hints.ai_family = AF_INET;
	hints.ai_socktype = SOCK_STREAM;
	hints.ai_protocol = IPPROTO_TCP;
	//hints.ai_protocol = PROTOCOL_TLS;
    rt_err_t ret;

	ret = getaddrinfo(host, port, &hints, &addr_list);
	if (ret != 0)
	{
			rt_kprintf("getaddrinfo fail [%d]\n",ret);
			return ret;
	}
	for (cur = addr_list; cur != NULL; cur = cur->ai_next)
	{
		socket_fd = socket(cur->ai_family,SOCK_STREAM, IPPROTO_TCP);
		if (socket_fd < 0) 
		{
			continue;
		}
		int mode = 0;  //阻塞模式
		ioctlsocket(socket_fd, FIONBIO, &mode);
		ret = connect(socket_fd, cur->ai_addr, cur->ai_addrlen);
		rt_kprintf("socket:%d  ret:%d\n",socket_fd,ret);
		if (ret == 0)
		{
			break;//连接成功
		}
		closesocket(socket_fd);
	}
	 freeaddrinfo(addr_list);
	 
	 return socket_fd;
}


static void tcp_rx_thread(void  *parameter)
{
    int maxfdp1;
    fd_set readset;
	struct timeval timeout;
	int rc = 0;
	
	uint8_t *tcp_recv_data = rt_malloc(1024);
	/*-------------tcp client主循环-------------------*/
    while (1)
    {
		socketfd = tcp_connect1(HOST,PORT);  //tcp 
		while(socketfd  > 0)
		{
			FD_ZERO(&readset);
			FD_SET(socketfd,&readset);
			
			maxfdp1 = socketfd+1;
			
			timeout.tv_sec=1;
			timeout.tv_usec=1000;
			
			rc = select(maxfdp1,&readset,0,0,&timeout);
			if(rc==0)
				continue;
			if(FD_ISSET(socketfd,&readset))
			{
                rt_kprintf("开始接受\r\n");
				rc = recv(socketfd,tcp_recv_data,1024,MSG_DONTWAIT);
				if(rc <= 0)
				{
                    rt_kprintf("接受出错\r\n");
					if(!(errno == EINTR || errno == EWOULDBLOCK || errno == EAGAIN))
					{
						rt_kprintf("socket recv err :%d\n",rc);
						closesocket(socketfd);
						socketfd = -1;
						break;
					}else
					{
						rt_kprintf("socket recv timeout :%d\n",rc);
						
					}
				}else
				{
					rt_kprintf("socket recv :%d\n",rc);
					// can_msg_send(tcp_recv_data,rc);
				}
			}
		}
        rt_thread_mdelay(5000); //断线5s后重连
    }
	rt_free(tcp_recv_data);
}


/* 线程 1 入口函数 */
void can2tcp(int  argc,char *argv[])
{
    // rt_thread_t can_tid;
    // can_tid = rt_thread_create("can_task",can_thread,RT_NULL,1024,25,10);
    // if(can_tid!=RT_NULL)
    // {
    //     rt_thread_startup(can_tid);
    // }
    // else
    // {
    //     rt_kprintf("create can_task thread failed!\n");
    // }
	
    rt_thread_t tcp_tid;
    tcp_tid = rt_thread_create("tcp_task",tcp_rx_thread,RT_NULL,1024,25,10);
    if(tcp_tid!=RT_NULL)
    {
        rt_thread_startup(tcp_tid);
    }
    else
    {
        rt_kprintf("create tcp_task thread failed!\n");
    }
}
/*--------------------------------------*/
/* 导出到 msh 命令列表中 */
MSH_CMD_EXPORT(can2tcp, can to tcp client select sample);

can2tcp_select_1.zip

希望大家帮分析下为什么这位朋友的程序可以实现tcp重连,而我的程序不能?

最近编辑记录 Gentlepig (2021-07-03 09:22:03)

离线

#14 2021-07-03 08:57:51

哇酷小二
管理员
所在地: 你猜
注册时间: 2020-04-22
已发帖子: 3,388
积分: 1902
个人网站

Re: 请教,lwip如何在不同线程里收发?

@Gentlepig
是的,还望理解,不过只要正常发过帖子的朋友基本都不会受到影响。





离线

楼主 #15 2021-07-03 09:26:33

Gentlepig
会员
注册时间: 2018-10-24
已发帖子: 1,378
积分: 1343.5

Re: 请教,lwip如何在不同线程里收发?

哇酷小二 说:

@Gentlepig
是的,还望理解,不过只要正常发过帖子的朋友基本都不会受到影响。

来都来了,帮我分析下啊。

离线

#16 2021-07-03 12:36:30

aozima
会员
所在地: 深圳
注册时间: 2019-05-25
已发帖子: 463
积分: 330.5
个人网站

Re: 请教,lwip如何在不同线程里收发?

Gentlepig 说:

(rt_object_get_type(&mb->parent.parent) == RT_Object_Class_MailBox) assertion failed

这个是比较明确的内存写穿,进入仿真调试,给这个变量下个写断点,应该一下就能把肇事者找出来。

离线

楼主 #17 2021-07-06 09:10:47

Gentlepig
会员
注册时间: 2018-10-24
已发帖子: 1,378
积分: 1343.5

Re: 请教,lwip如何在不同线程里收发?

aozima 说:
Gentlepig 说:

(rt_object_get_type(&mb->parent.parent) == RT_Object_Class_MailBox) assertion failed

这个是比较明确的内存写穿,进入仿真调试,给这个变量下个写断点,应该一下就能把肇事者找出来。

不知道该监控哪个变量,看这个提示mb应该是mailbox,但我没用mailbox,可能lwip接收自动用了mailbox吧。

离线

#18 2021-07-06 09:40:35

aozima
会员
所在地: 深圳
注册时间: 2019-05-25
已发帖子: 463
积分: 330.5
个人网站

Re: 请教,lwip如何在不同线程里收发?

你可以把这条assertion小改一下,改为if,然后在里面下普通断点停下来,就知道是哪个mailbox了

离线

楼主 #19 2021-07-06 09:53:51

Gentlepig
会员
注册时间: 2018-10-24
已发帖子: 1,378
积分: 1343.5

Re: 请教,lwip如何在不同线程里收发?

我的程序中,接收出错,就释放了tcp_recv_data。
rt_free(tcp_recv_data);
不应该这么早释放。屏蔽这句就没那个错误了。

离线

#20 2021-07-06 20:47:20

aozima
会员
所在地: 深圳
注册时间: 2019-05-25
已发帖子: 463
积分: 330.5
个人网站

Re: 请教,lwip如何在不同线程里收发?

好吧,最近正在学习Rust语言,希望能对类似的问题有所帮助。

离线

楼主 #21 2021-07-08 14:33:03

Gentlepig
会员
注册时间: 2018-10-24
已发帖子: 1,378
积分: 1343.5

Re: 请教,lwip如何在不同线程里收发?

大部分问题解决了。

群里朋友帮我改的那个程序,有个while(socketfd>0)判断,实际上socketfd第一次创建往往是0,也是创建成功的。改成while(socketfd>=0)后,基本没问题了。

我的程序修改那个变量释放的位置后,tcp断线重连没问题了。
但是又遇到这么个问题,之前是在msh里手动启动程序的,正常。现在设为启动后自动运行程序,则一直connect失败,然后按我的程序逻辑,就会新建socket然后再次尝试connect,但会一直连接失败。在tcp线程创建前加了个10秒延迟,正常。改成5秒延迟,则还是不行。

离线

楼主 #22 2021-07-09 16:22:52

Gentlepig
会员
注册时间: 2018-10-24
已发帖子: 1,378
积分: 1343.5

Re: 请教,lwip如何在不同线程里收发?

应该找到问题了,host    =   gethostbyname(url)这句之前至少在程序里执行了一次。我改到重连的循环里后,就可以了。
感觉是执行connect()函数时,将host变量指向的内容修改了。

离线

#23 2021-07-09 16:36:21

aozima
会员
所在地: 深圳
注册时间: 2019-05-25
已发帖子: 463
积分: 330.5
个人网站

Re: 请教,lwip如何在不同线程里收发?

gethostbyname得到的host要检查,

另外把 gethostbyname 修改为 getaddrinfo吧,以后都建议用这个了。

离线

楼主 #24 2021-07-10 15:52:45

Gentlepig
会员
注册时间: 2018-10-24
已发帖子: 1,378
积分: 1343.5

Re: 请教,lwip如何在不同线程里收发?

现在测试can和tcp互相转发,在pc端手动点击can软件和tcp server端,能正常收发。
pc端通过can软件向板子自动发送几百条信息,间隔时间设为几微秒,结果tcp server端收到的倒是正确,就是慢...
网上搜了下,将socket设为tcp_nodelay,但还是没效果。

    const char chOpt=1;   
    int   nErr=setsockopt(   m_socket,   IPPROTO_TCP,   TCP_NODELAY,   &chOpt,   sizeof(char));   

离线

楼主 #25 2021-07-13 11:44:10

Gentlepig
会员
注册时间: 2018-10-24
已发帖子: 1,378
积分: 1343.5

Re: 请教,lwip如何在不同线程里收发?

之前测试,tcp发送慢的原因找到了,是can线程里加了个延时,结果can接收慢了。

现在将can线程里的延时去掉,再次尝试,发送数据少时没事,pc端通过can每隔2ms发送100万条指令完成后,tcp server接收要晚3分钟左右才收完。

网上搜,一般说是tcp_write()后调用tcp_output()可以立即发送。我追踪lwip_send,发现lwip_netconn_do_writemore(struct netconn *conn  WRITE_DELAYED_PARAM)
函数里,执行了tcp_write()后,是立即调用了tcp_output()的。

那么,只能有这样的延迟了吗?

最近编辑记录 Gentlepig (2021-07-13 11:46:41)

离线

#26 2021-07-14 11:04:52

liliow
会员
注册时间: 2021-07-14
已发帖子: 13
积分: 1.5

Re: 请教,lwip如何在不同线程里收发?

if (connect(sock,(struct sockaddr*)&server_addr,sizeof(struct    sockaddr))==-1)
    {
        rt_kprintf("Connect fail!\n");
        closesocket(sock);
        rt_free(tcp_recv_data);
        return;
    }

离线

楼主 #27 2021-07-15 09:23:20

Gentlepig
会员
注册时间: 2018-10-24
已发帖子: 1,378
积分: 1343.5

Re: 请教,lwip如何在不同线程里收发?

liliow 说:

if (connect(sock,(struct sockaddr*)&server_addr,sizeof(struct    sockaddr))==-1)
    {
        rt_kprintf("Connect fail!\n");
        closesocket(sock);
        rt_free(tcp_recv_data);
        return;
    }


我一开始是这么写的,后来把rt_free(tcp_recv_data);放到最外层了。

现在的问题是can接收大量信息然后通过tcp发出去时,有延迟。
想了想,延迟可能是can接收造成的。

离线

楼主 #28 2021-07-15 16:39:51

Gentlepig
会员
注册时间: 2018-10-24
已发帖子: 1,378
积分: 1343.5

Re: 请教,lwip如何在不同线程里收发?

延迟问题找到了,can接收线程里有打印语句,将打印语句屏蔽掉,就同步了。
但不明白为什么打印语句会造成延迟。

离线

#29 2021-12-20 12:26:58

lsmcld
会员
所在地: 成都
注册时间: 2021-12-19
已发帖子: 27
积分: 12
个人网站

Re: 请教,lwip如何在不同线程里收发?

既然你都用了socket,链接建立后,任意处直接send就行了啊,成败都有反馈

离线

页脚

工信部备案:粤ICP备20025096号 Powered by FluxBB

感谢为中文互联网持续输出优质内容的各位老铁们。 QQ: 516333132, 微信(wechat): whycan_cn (哇酷网/挖坑网/填坑网) service@whycan.cn