Toggle navigation
首页
问答
文章
积分商城
专家
专区
更多专区...
文档中心
返回主站
搜索
提问
会员
中心
登录
注册
LWIP
tcp
tcp client如何实现重连?
发布于 2021-06-30 16:44:22 浏览:4659
订阅该版
使用的是rtthread+stm32f429,想实现can和tcp的相互转发。 目前有2个线程,tcp线程,使用select方法非阻塞接收tcp消息,收到后调用can发送函数发送出去。can线程,can接收后调用send函数通过tcp client发送出去。 现在测试能正常工作。但是觉得万一通讯有问题,最好能实现重连。 于是在tcp client线程里写了里外两个while(1)循环。外层循环了,调用的是socket(),connect()函数,内层循环里是调用select()根据结果来决定是否接收,出错后关闭socket,然后跳到外层循环。 想的是这样的,可实际操作后,上电通讯成功后,关闭tcp server然后再打开,发现打印出重新重建了socket和重新连接了。电脑端通过can发送信息后,设备接收后也可以通过tcp发送到tcp server.多次发送也没问题。 但电脑端第一次发送tcp消息后,设备接收后可以通过can发过来。电脑端再次发送,接报错了... ```c 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; } ... } } ``` ``` \ | / - 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: ```
查看更多
小小李sunny
2021-07-04
这家伙很懒,什么也没写!
**对于socket掉线重连的问题,关键在掉线判断:** 1.接收判断,socket接收采用阻塞模式,当服务端主动断开,或者出现异常,那么接收长度=0,或者小于0,可以判断掉线,关闭当前socket连接,然后重连; 2.发送判断,socket发送数据,当socket连接出现异常,发送失败,认为掉线,关闭当前连接,然后重连; 3.心跳包,通信协议实现,socket的客户端和服务端增加心跳包,定时交互一条数据,如果超时未收到心跳数据包,则认为掉线,然后重连;(当服务端未主动断开,而板子的网络出现异常时,我这边遇到过) **了解了socket的掉线判断之后,重连机制就好做了,具体思路如下:** 1.单独做一个socket的连接线程,该线程做socket连接状态判断,如果判断socket处于掉线状态,那么就进行connect,connect成功后,则启动接收线程; 2.socket接收线程,采用阻塞接收模式,当接收错误,判断掉线后,把socket的状态置位掉线状态,同时关闭当前socket连接,释放接收缓存,关闭该线程(跳出while循环,即可自动关闭该线程); 3.调用socket发送后,如果发送失败,判断掉线,也需把socket连接状态置为掉线状态,同时关闭当前socket(socket关闭后,接收会出错,能直接跳出while循环,关闭接收线程); 按照上述思路,两个线程基本上能实现绝大部分情况的掉线重连,不过需要注意最好不要把CAN的发送放在接收线程里,避免CAN发送异常影响socket接收数据的情况。
4
个回答
默认排序
按发布时间排序
cxhxy12345
2021-07-01
这家伙很懒,什么也没写!
1、不建议这种循环嵌套循环的处理方式 2、你主要是在TCP连接状态下出现断开的情况的处理。所以要设置一个sock超时时间,在recv函数接受到-1时说明对方已经断开。
ZCmengyang
2021-07-01
这家伙很懒,什么也没写!
TCP好像有个心跳包,用那个做断开重连
Any
2021-07-03
这家伙很懒,什么也没写!
群里一位好心的朋友帮我改了程序,可以实现tcp重连。但是有个问题,上电后一开始tcp接收不能正常工作,只有tcp重连后,才正常。 我也没明白我的程序和这个程序区别在哪里... 这是我的程序 ```c #include
#include "rtdevice.h" #include
#include
#include
// #include
// #include
// #include
#include
#include
#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); ``` 这是群里朋友的程序 ```c #include
#include "rtdevice.h" #include
#include
#include
// #include
// #include
// #include
#include
#include
#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); ``` 想请大家分析下tcp重连的方法。
撰写答案
登录
注册新账号
关注者
0
被浏览
4.7k
关于作者
Any
这家伙很懒,什么也没写!
提问
4
回答
68
被采纳
0
关注TA
发私信
相关问题
1
RT-THREAD在STM32H747平台上移植lwip
2
{lwip}使能RT_LWIP_DHCP时可以获取到ip
3
stm32f103 LWIP 2.0.2 TCP收发问题
4
lwip2.1不重启修改IP
5
关于网络协议栈的测试
6
可否将LWIP升级到2.1.2 和 2.0.3?
7
socket(AF_INET, SOCK_RAW, IPPROTO_UDP);
8
tcpclient 插拔网线问题?
9
两个tcpclient同时通讯可以吗?
10
SO_BINDTODEVICE 未定义该如何解决
推荐文章
1
RT-Thread应用项目汇总
2
玩转RT-Thread系列教程
3
国产MCU移植系列教程汇总,欢迎查看!
4
机器人操作系统 (ROS2) 和 RT-Thread 通信
5
五分钟玩转RT-Thread新社区
6
【技术三千问】之《玩转ART-Pi》,看这篇就够了!干货汇总
7
关于STM32H7开发板上使用SDIO接口驱动SD卡挂载文件系统的问题总结
8
STM32的“GPU”——DMA2D实例详解
9
RT-Thread隐藏的宝藏之completion
10
【ART-PI】RT-Thread 开启RTC 与 Alarm组件
最新文章
1
rt-thread 小内存算法源码分析
2
env中添加lvgl软件包后,keil编译包--c99错误
3
【NXP-MCXA153】 定时器驱动移植
4
GD32F450 看门狗驱动适配
5
【NXP-MCXA153】看门狗驱动移植
热门标签
RT-Thread Studio
串口
Env
LWIP
SPI
AT
Bootloader
Hardfault
CAN总线
FinSH
ART-Pi
USB
DMA
文件系统
RT-Thread
SCons
RT-Thread Nano
线程
MQTT
STM32
RTC
FAL
rt-smart
ESP8266
I2C_IIC
UART
WIZnet_W5500
ota在线升级
PWM
freemodbus
flash
cubemx
packages_软件包
BSP
潘多拉开发板_Pandora
定时器
ADC
GD32
flashDB
socket
中断
编译报错
Debug
rt_mq_消息队列_msg_queue
SFUD
keil_MDK
msh
ulog
C++_cpp
MicroPython
本月问答贡献
踩姑娘的小蘑菇
7
个答案
2
次被采纳
a1012112796
18
个答案
1
次被采纳
红枫
5
个答案
1
次被采纳
Ryan_CW
5
个答案
1
次被采纳
张世争
4
个答案
1
次被采纳
本月文章贡献
YZRD
3
篇文章
6
次点赞
catcatbing
3
篇文章
6
次点赞
lizimu
2
篇文章
11
次点赞
qq1078249029
2
篇文章
2
次点赞
xnosky
2
篇文章
1
次点赞
回到
顶部
发布
问题
分享
好友
手机
浏览
扫码手机浏览
投诉
建议
回到
底部