香橙派增加网络控制功能

香橙派增加网络控制功能

1.TCP心跳机制解决Socket异常断开问题

1.客户端能够发送状态给服务器;正常断开,强制关闭客户端等,客户端能够做出反应。

2.客户端不能发送状态给服务器;突然断网,断电,客户端卡死等,客户端根本没机会做出反应,

服务器更不了解客户端状态,导致服务器异常等待。

为了解决上述问题,引入TCP心跳包机制

心跳包的实现,心跳包就是服务器定时向客户端发送查询信息,如果客户端有回应就代表连接正常,

类似于linux系统的看门狗机制。心跳包的机制有一种方法就是采用TCP_KEEPALIVE机制,它是一种用于

检测TCP连接是否存活的机制,它的原理是在一定时间内没有数据往来时,发送探测包给对方,如果对方

没有响应,就认为连接已经断开。TCP_KEEPALIVE机制可以通过设置一些参数来调整,如探测时间间

隔、探测次数等。

Linux内核提供了通过sysctl命令查看和配置TCP KeepAlive参数的方法。

  • 查看当前系统的TCP KeepAlive参数
sysctl net.ipv4.tcp_keepalive_time
sysctl net.ipv4.tcp_keepalive_probes
sysctl net.ipv4.tcp_keepalive_intvl
  • 修改TCP KeepAlive参数
sysctl net.ipv4.tcp_keepalive_time=3600

2.c语言实现TCP KeepAlive功能

对于Socket而言,可以在程序中通过socket选项开启TCP KeepAlive功能,并配置参数。对应的Socket选

项分别为 SO_KEEPALIVE 、 TCP_KEEPIDLE 、 TCP_KEEPCNT 、 TCP_KEEPINTVL 。

int keepalive = 1; // 开启TCP KeepAlive功能
int keepidle = 5; // tcp_keepalive_time 3s内没收到数据开始发送心跳包
int keepcnt = 3; // tcp_keepalive_probes 每次发送心跳包的时间间隔,单位秒
int keepintvl = 3; // tcp_keepalive_intvl 每3s发送一次心跳包
setsockopt(client_socketfd, SOL_SOCKET, SO_KEEPALIVE, (void *)&keepalive,
sizeof(keepalive));
setsockopt(client_socketfd, SOL_TCP, TCP_KEEPIDLE, (void *) &keepidle, sizeof
(keepidle));
setsockopt(client_socketfd, SOL_TCP, TCP_KEEPCNT, (void *)&keepcnt, sizeof
(keepcnt));
setsockopt(client_socketfd, SOL_TCP, TCP_KEEPINTVL, (void *)&keepintvl, sizeof
(keepintvl));

3.代码实现

socket.c

#include "socket.h"

int socket_init(const char *ipaddr,const char *ipport)
{
    int s_fd=-1;
    int ret=-1;
    struct sockaddr_in s_addr;
    memset(&s_addr,0,sizeof(struct sockaddr_in));
    //1. socket
	s_fd = socket(AF_INET, SOCK_STREAM, 0);
	if(-1==s_fd)
    {
		perror("socket");
		return -1;
	}
	s_addr.sin_family = AF_INET;
	s_addr.sin_port = htons(atoi(ipport));
	inet_aton(ipaddr,&s_addr.sin_addr);
	//2. bind
	ret=bind(s_fd,(struct sockaddr *)&s_addr,sizeof(struct sockaddr_in));
    if(-1==ret)
    {
        perror("bind");
        return -1;
    }
	//3. listen
	ret=listen(s_fd,1); //允许一个监听,只能允许一个时刻一个垃圾桶打开
    if(-1==ret)
    {
       perror("listen");
       return -1;
    }

    return s_fd;
}

socket.h

#ifndef _SOCKET_H_
#define _SOCKET_H_
#include <stdio.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <string.h>

#define IPADDR "192.168.1.103"
#define IPPORT "8192" //不要用8080,摄像头已经用了8080#define IPADDR "192.168.1.103"

int socket_init(const char* ipaddr,const char* ipport);


#endif

main.c

    #include<stdio.h>
    #include<stdlib.h>
    #include<string.h>
    #include<unistd.h>
    #include<errno.h>
    #include<wiringPi.h>
    #include<pthread.h>
    #include "pwm.h"
    #include "uartTool.h"
    #include "garbage.h"
    #include "myoled.h"
    #include "socket.h"

    static int detect_process(const char * process_name) //判断进程是否在运行
    {
        int n = -1;
        FILE *strm;
        char buf[128]={0};
        sprintf(buf,"ps -ax | grep %s|grep -v grep", process_name);  //指令
        if((strm = popen(buf, "r")) != NULL) //读取数据
        {
            if(fgets(buf, sizeof(buf), strm) != NULL) //获取pid
            {
                n = atoi(buf);  //转换为数字
            }
        }
        else
        {
            return -1;
        }
        pclose(strm);
        return n;
    }

    #if  0
    int main(int argc,char* argv[])
    {
        int serial_fd=-1;
        int len=0;
        int ret=-1;
        char* category=NULL;
        unsigned char buffer[6]={0xAA,0x55,0x00,0x00,0x55,0xAA};

        wiringPiSetup(); //初始化wiringPi库
        garbage_init(); //初始化阿里云接口

        ret=detect_process("mjpg_streamer"); //简称mjpg_streamer是否打开
        if(-1==ret)
        {
            printf("detect process failed\n");
            goto END;
        }

        serial_fd=myserialOpen(SERIAL_DEV,BAUD); //初始化串口
        if(-1==serial_fd) //串口打开失败
        {
            printf("open serial failed\n");
            goto END;
        }

        //进行垃圾分类识别
        while(1)
        {
            len=serialGetstring(serial_fd,buffer); //获取串口的数据
            if(len>0&&buffer[2]==0x46) //识别垃圾类型
            {
                buffer[2]=0x00; //对buffer[2]进行清空
                system(WGET_CMD); //进行拍照
                if(0==access(GARBAGE_FILE,F_OK)) //文件存在
                {
                    category=garbage_category(category); //调用阿里云识别接口
                    if(strstr(category,"干垃圾")) //识别出干垃圾
                    {
                        buffer[2]=0x41;
                    }
                    else if(strstr(category,"湿垃圾")) //识别出湿垃圾
                    {
                        buffer[2]=0x42;
                    }
                    else if(strstr(category,"可回收垃圾")) //识别出可回收垃圾
                    {
                        buffer[2]=0x43;
                    }
                    else if(strstr(category,"有害垃圾")) //识别出有害垃圾
                    {
                        buffer[2]=0x44;
                    }
                    else               //无法识别
                    {
                        buffer[2]=0x45; 
                    }
                }
                else  //无法识别
                {
                    buffer[2]=0x45;
                }

                serialSendstring(serial_fd,buffer,6); //将识别的垃圾类型回传给语音模块
                if(buffer[2]==0x43)
                {
                    pwm_write(PWM_RECOVERALE_GARBAGE); //开盖
                    delay(5000);//开盖5s
                    pwm_stop(PWM_RECOVERALE_GARBAGE);//关盖
                }
                else if(buffer[2]==0x41)
                {
                    pwm_write(PWM_GARBAGE); //开盖
                    delay(5000);//开盖5s
                    pwm_stop(PWM_GARBAGE);//关盖
                }
                else if(buffer[2]==0x42)
                {
                    pwm_write(PWM_WET_GARBAGE); //开盖
                    delay(5000);//开盖5s
                    pwm_stop(PWM_WET_GARBAGE);//关盖
                }
                else if(buffer[2]==0x44)
                {
                    pwm_write(PWM_HARMFUL_GARBAGE); //开盖
                    delay(5000);//开盖5s
                    pwm_stop(PWM_HARMFUL_GARBAGE);//关盖
                }
                buffer[2]=0x00;//清空是为了下一次识别
                remove(GARBAGE_FILE); //清理缓存
            }
        }

        close(serial_fd);
        END:
        garbage_final(); //释放python解析器   
        return 0;
    }
    #endif


    int serial_fd=-1;

    pthread_cond_t cond;//条件变量
    pthread_mutex_t mutex; //互斥锁

    //获取语音数据线程
    void* get_voice(void *arg)
    {
        unsigned char buffer[6]={0xAA,0x55,0x00,0x00,0x55,0xAA};
        int len=0;
        printf("%s|%s|%d\n",__FILE__,__func__,__LINE__);
        if(-1==serial_fd)
        {
            printf("%s|%s|%d:open serial failed\n",__FILE__,__func__,__LINE__);   
            pthread_exit(0);
        }
        printf("%s|%s|%d\n",__FILE__,__func__,__LINE__);
        while(1)
        {
            len=serialGetstring(serial_fd,buffer);
            printf("%s|%s|%d,len=%d\n",__FILE__,__func__,__LINE__,len);
            if(len>0&&buffer[2]==0x46)
            {
                pthread_mutex_lock(&mutex);
                buffer[2]=0x00;
                pthread_cond_signal(&cond);
                pthread_mutex_unlock(&mutex);
            }

        }

        pthread_exit(0);
    }

    //发送语音线程
    void* psend_voice(void *arg)
    {
        pthread_detach(pthread_self()); //父子线程分离,防止等待太久 
        unsigned char *buffer=(unsigned char*)arg; 

        if(-1==serial_fd)
        {
            printf("%s|%s|%d:open serial failed\n",__FILE__,__func__,__LINE__);   
            pthread_exit(0);
        } 
        if(NULL!=buffer)
        {
            serialSendstring(serial_fd,buffer,6); //将识别的垃圾类型回传给语音模块
        }
        pthread_exit(0);
    }

    //垃圾桶开盖线程
    void* popen_trash_can(void *arg)
    {
        pthread_detach(pthread_self()); //父子线程分离,防止等待太久
        unsigned char *buffer=(unsigned char*)arg; 
        if(buffer[2]==0x43)
        {
            printf("%s|%s|%d,buffer[2]=0x%x\n",__FILE__,__func__,__LINE__,buffer[2]);
            pwm_write(PWM_RECOVERALE_GARBAGE); //开盖
            delay(2000);//开盖5s
            pwm_stop(PWM_RECOVERALE_GARBAGE);//关盖
        }
        else if(buffer[2]!=0x45)
        {
            printf("%s|%s|%d,buffer[2]=0x%x\n",__FILE__,__func__,__LINE__,buffer[2]);
            pwm_write(PWM_GARBAGE); //开盖
            delay(2000);//开盖5s
            pwm_stop(PWM_GARBAGE);//关盖
        }

        pthread_exit(0);
    }

    //oled显示线程
    void* poled_show(void* arg)
    {
        pthread_detach(pthread_self());

        myoled_init();
        oled_show(arg);

        pthread_exit(0);
    }

    //垃圾分类识别线程
    void* pcategory(void *arg)
    {
        unsigned char buffer[6]={0xAA,0x55,0x00,0x00,0x55,0xAA};
        char* category=NULL;
        pthread_t send_voice_tid;
        pthread_t trash_tid;
        pthread_t oled_tid;

        printf("%s|%s|%d\n",__FILE__,__func__,__LINE__);
        while(1)
        {   
            printf("%s|%s|%d\n",__FILE__,__func__,__LINE__);
            pthread_mutex_lock(&mutex);
            pthread_cond_wait(&cond,&mutex);
            pthread_mutex_unlock(&mutex);
            printf("%s|%s|%d\n",__FILE__,__func__,__LINE__);
            buffer[2]=0x00;//清空是为了下一次识别
            system(WGET_CMD); //进行拍照
            if(0==access(GARBAGE_FILE,F_OK)) //文件存在
            {
                category=garbage_category(category); //调用阿里云识别接口
                if(strstr(category,"干垃圾")) //识别出干垃圾
                {
                    buffer[2]=0x41;
                }
                else if(strstr(category,"湿垃圾")) //识别出湿垃圾
                {
                    buffer[2]=0x42;
                }
                else if(strstr(category,"可回收垃圾")) //识别出可回收垃圾
                {
                    buffer[2]=0x43;
                }
                else if(strstr(category,"有害垃圾")) //识别出有害垃圾
                {
                    buffer[2]=0x44;
                }
                else               //无法识别
                {
                    buffer[2]=0x45; 
                }
            }
            else  //文件不存在无法识别
            {
                buffer[2]=0x45;
            }

            //垃圾桶开盖线程
            pthread_create(&trash_tid,NULL,popen_trash_can,(void*)buffer);
            //发送语音播报线程
            pthread_create(&send_voice_tid,NULL,psend_voice,(void*)buffer);
            //oled显示线程
            pthread_create(&oled_tid,NULL,poled_show,(void*)buffer);


            remove(GARBAGE_FILE); //清理缓存
        }

        pthread_exit(0);
    }

    //网络线程
    void* pget_socket(void *arg)
    {
        int s_fd=-1;
        int c_fd=-1;
        int n_read=-1;
        char buffer[6];
        struct sockaddr_in c_addr;
        memset(&c_addr,0,sizeof(struct sockaddr_in));

        s_fd=socket_init(IPADDR,IPPORT);
        if(-1==s_fd)
        {
            printf("%s|%s|%d:s_fd=%d\n",__FILE__,__func__,__LINE__,s_fd);
            pthread_exit(0);
        }
        int clen = sizeof(struct sockaddr_in);

        while(1)
        {
            c_fd = accept(s_fd,(struct sockaddr *)&c_addr,&clen);
            int keepalive = 1; // 开启TCP KeepAlive功能
            int keepidle = 5; // tcp_keepalive_time 3s内没收到数据开始发送心跳包
            int keepcnt = 3; // tcp_keepalive_probes 每次发送心跳包的时间间隔,单位秒
            int keepintvl = 3; // tcp_keepalive_intvl 每3s发送一次心跳包
            setsockopt(c_fd, SOL_SOCKET, SO_KEEPALIVE, (void *)&keepalive,
                       sizeof(keepalive));
            setsockopt(c_fd, SOL_TCP, TCP_KEEPIDLE, (void *) &keepidle, sizeof
                       (keepidle));
            setsockopt(c_fd, SOL_TCP, TCP_KEEPCNT, (void *)&keepcnt, sizeof
                       (keepcnt));
            setsockopt(c_fd, SOL_TCP, TCP_KEEPINTVL, (void *)&keepintvl, sizeof
                       (keepintvl));
            printf("%s|%s|%d:Accept a connection from %s:%d\n",__FILE__,__func__,__LINE__, inet_ntoa(c_addr.sin_addr), ntohs(c_addr.sin_port));
            if(-1==c_fd)
            {
                perror("accept");
                continue;
            }
            while(1)
            {
                memset(buffer,0,sizeof(buffer));
                n_read=recv(c_fd,buffer,sizeof(buffer),0);
                printf("%s|%s|%d:n_read=%d,buffer=%s\n",__FILE__,__func__,__LINE__,n_read,buffer);
                if(n_read>0)
                {
                    if(strstr(buffer,"open")) //给阿里云交互线程发信号
                    {
                        pthread_mutex_lock(&mutex);
                        pthread_cond_signal(&cond);
                        pthread_mutex_unlock(&mutex);
                    }
                }
                else if(0==n_read||-1==n_read)
                {
                    break;
                } 
            }
            close(c_fd);
        }

        pthread_exit(0);
    }

    int main(int argc,char* argv[])
    {
        int len=0;
        int ret=-1;
        char* category=NULL;
        pthread_t get_voice_tid;
        pthread_t category_tid;
        pthread_t get_socket_tid;

        wiringPiSetup(); //初始化wiringPi库
        garbage_init(); //初始化阿里云接口

        ret=detect_process("mjpg_streamer"); //简称mjpg_streamer是否打开
        if(-1==ret)
        {
            printf("detect process failed\n");
            goto END;
        }

        serial_fd=myserialOpen(SERIAL_DEV,BAUD); //初始化串口
        if(-1==serial_fd) //串口打开失败
        {
            printf("open serial failed\n");
            goto END;
        }

        //开语音线程
        printf("%s|%s|%d\n",__FILE__,__func__,__LINE__);
        pthread_create(&get_voice_tid,NULL,get_voice,NULL);
        //开阿里云交互线程
        printf("%s|%s|%d\n",__FILE__,__func__,__LINE__);
        pthread_create(&category_tid,NULL,pcategory,NULL);
        //开网络线程
        printf("%s|%s|%d\n",__FILE__,__func__,__LINE__);
        pthread_create(&get_socket_tid,NULL,pget_socket,NULL);

        pthread_join(get_voice_tid,NULL); //阻塞语音线程
        pthread_join(category_tid,NULL); //阻塞阿里云交互线程
        pthread_join(get_socket_tid,NULL); //阻塞网络线程

        pthread_mutex_destroy(&mutex);
        pthread_cond_destroy(&cond);

        close(serial_fd);
        END:
        garbage_final(); //释放python解析器   

        return  0;
    }
  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值