openwrt中使用udt通讯

在项目当中一般使用tcp作为网络通信连接,但需要经历多次握手和分手才能建立可靠连接,

并且当物联网设备突然断电后连接会在内核中挂起,出现超长等待, 使用udp虽然不会让通讯

出现挂起现象但是数据不是按照队列方式发送,并且也不能确保数据收发的完整性,

所以在面向物联网的产品使用开源项目udt能比较好解决上述二者的缺陷.

udt交叉编译修改udt4/src/Makefile

#C++ = g++
C++ = /home/openwrt-master/staging_dir/toolchain-mips_74kc_gcc-5.3.0_musl-1.1.16/bin/mips-openwrt-linux-g++

ifndef os
   os = LINUX
endif

ifndef arch
   arch = IA32
endif

CCFLAGS = -fPIC -Wall -Wextra -D$(os) -finline-functions -O3 -fno-strict-aliasing -fvisibility=hidden

ifeq ($(arch), IA32)
   CCFLAGS += -DIA32
endif

ifeq ($(arch), POWERPC)
   CCFLAGS += -mcpu=powerpc
endif

ifeq ($(arch), SPARC)
   CCFLAGS += -DSPARC
endif

ifeq ($(arch), IA64)
   CCFLAGS += -DIA64
endif

ifeq ($(arch), AMD64)
   CCFLAGS += -DAMD64
endif

OBJS = api.o buffer.o cache.o ccc.o channel.o common.o core.o epoll.o list.o md5.o packet.o queue.o window.o
DIR = $(shell pwd)

all: libudt.so libudt.a udt

%.o: %.cpp %.h udt.h
	$(C++) $(CCFLAGS) $< -c

libudt.so: $(OBJS)
ifneq ($(os), OSX)
	$(C++) -shared -o $@ $^
else
	$(C++) -dynamiclib -o libudt.dylib -lstdc++ -lpthread -lm $^
endif

libudt.a: $(OBJS)
	ar -rcs $@ $^

udt:
	cp udt.h udt

clean:
	rm -f *.o *.so *.dylib *.a udt

install:
	export LD_LIBRARY_PATH=$(DIR):$$LD_LIBRARY_PATH

执行交叉编译 make -e os=LINUX arch=-DMIPS

在openwrt中创建udt的封装库libudt_trans.so

#ifndef __UDT_TRANS_H__
#define __UDT_TRANS_H__

#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <pthread.h>
#include <unistd.h>
#include <time.h>
#include <errno.h>
#include <signal.h>
#include <sys/time.h>
#include <netdb.h>

#include <stdlib.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <ctype.h>
#include <fcntl.h>

#include <udt.h>


typedef char                      I8;
typedef unsigned char             UI8;

#define int8_t                    I8
#define uint8_t                   UI8

typedef short                     I16;
typedef unsigned short            UI16;

typedef int                       I32;
typedef unsigned int              UI32;

typedef enum{FALSE,TRUE=!FALSE}   BOOLEAN;


#define  DELAY_1MS                (1000)
#define  DELAY_5MS                (5*1000)
#define  DELAY_10MS               (10*1000)
#define  DELAY_30MS               (30*1000)
#define  DELAY_100MS              (100*1000)
#define  DELAY_300MS              (300*1000)
#define  DELAY_500MS              (500*1000)
#define  DELAY_1S                 (1000*1000)
#define  DELAY_2S                 (2000*1000)
#define  DELAY_3S                 (3000*1000)


//server info
#define UDT_SERVER_DEF_IP         "192.168.59.254"
#define UDT_SERVER_PORT           (59342)
#define UDT_SERVER_PORT_STR       "59342"


//-1为无穷,非负数即有效
#define SEND_TIMEOUT              (0)
#define BUFSIZE                   (128)

#define UDT_IPADDR_FILE_PATH      "/etc/udt_ipaddr"


#define DEBUG


#ifdef __cplusplus
extern "C"{
#endif

//udt库初始化
int udt_trans_init(void);

//udt通道发送数据
int udt_trans_send(UI8*data,UI8 datalen);

//udt通道接收数据
int udt_trans_recv(UI8**data,UI8*datalen);

//data为udt服务端ip地址
int udt_trans_conf(I8*data);


#ifdef __cplusplus
}
#endif

#endif

/**
create by iversondeng168
*/
#include "udt_trans.h"


static UI8 recvbuf[BUFSIZE];
static int is_recv_stream_runing        = 0;


static UDTSOCKET client;
static struct addrinfo hints,*local,*peer;
static char serverip[32]      = {0};
static int is_udt_connect     = 0;
static int is_ip_need_refresh = 0;


void* udt_trans_loop(void*param);

void* udt_trans_recv_thread_func(void*param);


static char cmdbuf[BUFSIZE];

static char* cmd_system(const char* command)
{     
     char* result = NULL; 
     FILE *fpRead;    
	 fpRead = popen(command, "r");    
	    
	 memset(cmdbuf,'\0',BUFSIZE);  
	 
	 while(fgets(cmdbuf,BUFSIZE - 1,fpRead) != NULL)
	 {        
	    result = cmdbuf;    
	 }
	 
	 if(fpRead!=NULL)        
	 	pclose(fpRead);  

	 return result;
}




//查找字符串中指定字符最后出现的索引
static int get_last_char_index(char*str,char c)
{
    int i;
	int slen   = 0;
	int tarind = -1;

	if(str == NULL || strlen(str) <= 0) return tarind;

	slen = strlen(str);
	for(i = 0;i < slen;i++)
	{
		if(str[i] == c){
            tarind = i;
		}
	}

	return tarind;
}



/**
wlan0网段匹配
*/
static BOOLEAN wlan0_netseg(char*serverip)
{
	#define  WLAN0_GET_IP   "ifconfig wlan0 | grep 'inet addr' | awk '{ print $2}' | awk -F: '{print $2}'"

    I8*wlan0ip;
	int trycnt;
	int tmp;

	//不同网段对socket连接有影响 
	wlan0ip      = cmd_system(WLAN0_GET_IP);
	trycnt       = 0;
	while((wlan0ip == NULL || strlen(wlan0ip) == 0) && trycnt++ < 5)
	{
		 usleep(DELAY_100MS); 
		 wlan0ip   = cmd_system(WLAN0_GET_IP);
	}
	if(trycnt >= 5) {
		 if(wlan0ip == NULL) wlan0ip = (I8*)"";
	}
	 

	#ifdef DEBUG
	printf("wlan0ip=%s\n",wlan0ip);
	#endif


    tmp = get_last_char_index(wlan0ip,'.');
	if(tmp <= -1) return FALSE;

	if(strncmp(serverip,wlan0ip,tmp)){
	    #ifdef DEBUG
		printf("local ip SERVER_IP are not in lan\n");
		#endif
		
		return FALSE;
	}

	return TRUE;
}






//文件读
static int read_file(const char* _fileName, void* _buf, int _bufLen)
{
    FILE* fp = NULL;
    if( NULL == _buf || _bufLen <= 0 ) return (-1);

    fp = fopen(_fileName, "rb"); 

    if( NULL == fp )
    {
        return (-1);
    }

    fread(_buf, _bufLen, 1, fp);

    fclose(fp);
    return 0;        
}




//文件写
static int write_file(const char* _fileName, void* _buf, int _bufLen)
{
    FILE * fp = NULL;
    if( NULL == _buf || _bufLen <= 0 ) return (-1);

    fp = fopen(_fileName, "wb");

    if( NULL == fp )
    {
        return (-1);
    }

    fwrite(_buf, _bufLen, 1, fp); 

    fclose(fp);
    fp = NULL;

    return 0;    
}




/**
udt configure initial
*/
int udt_trans_init(void)
{
   int optval;
   int tmp;
   pthread_t utl;

   /**
   获取用户自定义IP /etc/udt_ipaddr
   */
   if(access(UDT_IPADDR_FILE_PATH,F_OK) < 0)
   {
    
   }
   else
   {
	   read_file(UDT_IPADDR_FILE_PATH,serverip,sizeof(serverip));

	   tmp = strlen(serverip);
	   if(tmp >= 1){

		   tmp -= 1;
		   if(serverip[tmp] == 0x0a){
              serverip[tmp] = 0;
		   }
	   }
   }

   if(strlen(serverip) <= 0)
   {
   	   memcpy(serverip,UDT_SERVER_DEF_IP,strlen(UDT_SERVER_DEF_IP));
   }



   //it starts the garbage collection thread
   UDT::startup();

   memset(&hints, 0, sizeof(struct addrinfo));

   hints.ai_flags     = AI_PASSIVE;
   hints.ai_family    = AF_INET;
   hints.ai_socktype  = SOCK_STREAM;

   if(0 != getaddrinfo(NULL,UDT_SERVER_PORT_STR, &hints, &local))
   {
	  #ifdef DEBUG
      printf("incorrect network address.\n");
	  #endif

      return -1;
   }


   /**
    create socket
   */
   client = UDT::socket(local->ai_family, local->ai_socktype, local->ai_protocol);


   optval = SEND_TIMEOUT;
   /*
     send timeout Default -1 (infinite).
     send:Non-blocking call failure.
   */
   UDT::setsockopt(client, 0, UDT_SNDTIMEO,&optval,sizeof(int));	   

 

   /**
    configure server ip&port
   */
   if(0 != getaddrinfo(serverip,UDT_SERVER_PORT_STR,&hints,&peer))
   {
	  #ifdef DEBUG
      printf("incorrect server/peer address\n");
	  #endif

      return -1;
   }

   

   if(pthread_create(&utl,NULL,udt_trans_loop,NULL) != 0)
   {	
	   #ifdef DEBUG
	   printf("The thread of utl is not create!\n");
	   #endif

       return -1;
   }
 
   pthread_detach(utl);

   return 0;
}



/**
data send
*/
int udt_trans_send(UI8*data,UI8 datalen)
{

    if(is_udt_connect)
	{
        //加入发送队列
		return 0;
	}

	return -1;
}



/**
udt data real loop send/recv
*/
void* udt_trans_loop(void*param)
{
   #define EMPTY_PACKET_SIZE (1)

   int ss,tmp;
   int optval  = SEND_TIMEOUT;

   UI8*sendata; 
   UI8 sendatalen;

   UI8 empty_packet[EMPTY_PACKET_SIZE] = {0};

   pthread_t rec_t;
   void**retcode = NULL;


   while(true)
   {

	  is_udt_connect = 0;

  
      //wlan0网段匹配与否
      if(!wlan0_netseg(serverip))
	  {
         usleep(DELAY_2S);

         continue;  
	  }


      /*
	    动态切换ip,防止强制切换IP产生
	    connect:Operation not supported: Cannot do this operation on a CONNECTED socket
        和close关闭不了
	  */
	  if(is_ip_need_refresh)
	  {
         is_ip_need_refresh = 0;

		 UDT::startup();

		 client = UDT::socket(local->ai_family, local->ai_socktype, local->ai_protocol);
		 UDT::setsockopt(client, 0, UDT_SNDTIMEO,&optval, sizeof(int));

		 if(0 != getaddrinfo(serverip,UDT_SERVER_PORT_STR,&hints,&peer))
		 {
		    #ifdef DEBUG
		    printf("incorrect server/peer address\n");
		    #endif

		    return 0;
		 }
	  }


      //connect to the server, implict bind
      if(UDT::ERROR == UDT::connect(client, peer->ai_addr, peer->ai_addrlen))
      {
		 #ifdef DEBUG
         printf("connect:%s\n",UDT::getlasterror().getErrorMessage());
         #endif
		
		 tmp = UDT::getlasterror().getErrorCode();


         /*server does not exist,
		   这样无休止的连接会耗尽cpu,尽管速度很慢!
		   解决的办法就是释放内存,终止线程,同时多休息
		 */
         if(1001 == tmp)
		 {
            UDT::close(client);
		    UDT::cleanup();
		 
		    usleep(DELAY_100MS);
		    UDT::startup();

		    client = UDT::socket(local->ai_family, local->ai_socktype, local->ai_protocol);
		    UDT::setsockopt(client, 0, UDT_SNDTIMEO,&optval, sizeof(int));
		 }


         //aborted due to security reasons.
		 //连接Android手机会导致这个异常并且内存暴增加
         if(1004 == tmp)
		 {
            UDT::close(client);
		    UDT::cleanup();
		 
		    usleep(DELAY_100MS);
		    UDT::startup();

		    client = UDT::socket(local->ai_family, local->ai_socktype, local->ai_protocol);
		    UDT::setsockopt(client, 0, UDT_SNDTIMEO,&optval, sizeof(int));
			continue;
		 }


         //socket connected,just skip 
         if(5002 == tmp)
		 {
            goto CONNECT_OK;
		 }

		 //u socket is invalid!
	     if(5004 == tmp)
	     {
		    client = UDT::socket(local->ai_family, local->ai_socktype, local->ai_protocol);
		    UDT::setsockopt(client, 0, UDT_SNDTIMEO,&optval, sizeof(int));
	     }
		 
         usleep(DELAY_1S);

         continue;
      }
      else
      {

CONNECT_OK:

		 if(is_recv_stream_runing)
		 {
			pthread_cancel(rec_t);
			pthread_join(rec_t,retcode);

			usleep(DELAY_100MS);

			#ifdef DEBUG
			printf("rec_t thread exit code=%d\n",(int)retcode);	
			#endif
		 }
		

		 if(pthread_create(&rec_t, NULL, udt_trans_recv_thread_func, NULL) != 0){

			#ifdef DEBUG
			printf("The thread of rec is not create!\n");
			#endif

			return 0;
		 }



         //connect ok !
         is_udt_connect = 1;

		 #ifdef DEBUG
		 printf("connect ok\n");
		 #endif

         while(true)
         {
			//网络检查
            if(!is_udt_connect)
			{
			   if(is_recv_stream_runing)
			   {
				  is_recv_stream_runing = 0;

				  pthread_cancel(rec_t);
			   }

			   pthread_join(rec_t,retcode); 

			   break;
			}


			/*
			   没有数据发送时,也要保持发送一个类似心跳包的东西,才能实时检测到服务端断开,
			   比如可以每30ms发送一个字节0.
             */
            if(1)
			{
			   sendata     = empty_packet;
			   sendatalen  = sizeof(empty_packet);

			   usleep(DELAY_30MS);
			}
			else{
            
			   //从发送队列中取数据
            }

            
            //发送数据
            if( sendata != NULL &&
                sendatalen > 0  &&
				UDT::ERROR == (ss = UDT::send(client,(char*)sendata,sendatalen,0)))
            {
			   #ifdef DEBUG
			   printf("send:%s\n",UDT::getlasterror().getErrorMessage());
			   #endif

			   tmp = UDT::getlasterror().getErrorCode();

			   #ifdef DEBUG
			   printf("errorno=%d\n",tmp);
			   #endif
		      
		       /*
		         2001 : server is close,6003 send timeout,
				 5004 : u socket is invalid,
				 7000:server is broken,
			   */
		       if(2001 == tmp || 6003 == tmp || 5004 == tmp || 7000 == tmp)
		       {

				   if(6003 == tmp)
				   {

					  UDT::cleanup();
 
					  usleep(DELAY_100MS);

                      UDT::startup();
				   }
                   

                   //网络主动断开时产生的异常不重新初始化client
				   if(is_udt_connect){
		              client = UDT::socket(local->ai_family, local->ai_socktype, local->ai_protocol);
		              UDT::setsockopt(client, 0, UDT_SNDTIMEO,&optval, sizeof(int));
				   }


				   //确保接收线程关闭,释放部分内存
				   if(is_recv_stream_runing)
				   {
					  is_recv_stream_runing = 0;

					  #ifdef DEBUG
			          printf("pthread_cancel\n");
			          #endif

					  pthread_cancel(rec_t);
				   }


				   #ifdef DEBUG
				   printf("pthread_join\n");
				   #endif

				   pthread_join(rec_t,retcode);

				   #ifdef DEBUG
				   printf("pthread_join ok\n");
				   #endif
               }

		       break;
	        }
			else{

			   //send data ok!
			}
		 }
	  }
   }
	
		
   UDT::close(client);

   UDT::cleanup();

   return 0;
}



/**
data recv
*/
int udt_trans_recv(UI8**data,UI8*datalen)
{
    if(is_udt_connect)
	{
        #ifdef DEBUG
        printf("datalen=%d\tdata=%s\n",*datalen,*data);
        #endif

	    //加入数据接收队列

		return 0;
	}

	return -1;
}




void* udt_trans_recv_thread_func(void*param)
{
	int len;
    UI16 tmp;

    is_recv_stream_runing = 1;

	while(true)
	{
		if(UDT::ERROR == (len = UDT::recv(client,(char*)recvbuf,sizeof(recvbuf),0)))
		{
			 #ifdef DEBUG
			 printf("recv:%s\n",UDT::getlasterror().getErrorMessage());
			 #endif
			
			 break;
		}


        if(len > 0)
		{
			if(len > BUFSIZE){
			   len = BUFSIZE;
			}


			#ifdef DEBUG
			printf("len=%d\trecvbuf=%s\n",len,recvbuf);
			#endif

		}
	}

    is_recv_stream_runing = 0;

	return 0;
}



/**
server ip重定向
*/
int udt_trans_conf(I8*data)
{

   if(data == NULL || strlen(data) <= 0) return -1;
   if(!strcmp(serverip,data)) return -1;
   
   memset(serverip,0,sizeof(serverip));
   memcpy(serverip,data,strlen(data));

  
   //动态切换,必须先置位,以便close后立马起作用
   is_ip_need_refresh = 1;


   /**
    close函数会卡主的原因是因为它需要等待send()和recv()结束,
	然而之前的方式直接先把recv线程强行终止和send不再调用,导致close函数
	一直在等待同步释放的信号,所以永远也关不了,释放不了资源!!!
   */
   UDT::close(client);
   UDT::cleanup();

   //重新连接
   is_udt_connect     = 0;

   #ifdef DEBUG
   printf("new ip=%s\n",serverip);
   #endif

   return 0;
}


执行交叉编译

GPP=/home/openwrt-master/staging_dir/toolchain-mips_74kc_gcc-5.3.0_musl-1.1.16/bin/mips-openwrt-linux-g++ 
$GPP udt_trans.c -fPIC -shared -o libudt_trans.so -O3 -I../src  -L../src  -ludt -lstdc++ -lpthread
 

openwrt本地测试

/**
create by iversondeng168
*/
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <unistd.h>
#include <time.h>
#include <errno.h>
#include <signal.h>
#include <sys/time.h>

#include <unistd.h>
#include "udt_trans.h"


int main(int argc,char*argv[])
{
   unsigned char*recvdata;
   unsigned char recvdatalen;
      
   int cnt= 0;
   I8 ip[32] = {0};

   int ret;
   unsigned char s[]={0xFE,0x01,0x00,0x02,0x00,0x2c,0x01,0xFF,0xFF};

   srand(time(NULL));

   ret = udt_trans_init();
   printf("udt_trans_init ret=%d\n",ret);

   
   while(1){

		usleep(1000 * 1000);
		//1s发送一次数据
		udt_trans_send(s,sizeof(s));
        
		//60s更新一次ip
		if(!(cnt++ % 60))
	    {
		   sprintf((char*)ip,"192.168.59.%d",150 + rand() % 100);
		   printf("target ip=%s\n",ip);

           udt_trans_conf(ip);
	    }
   }

   return 0;
}

执行交叉编译

GPP=/home/openwrt-master/staging_dir/toolchain-mips_74kc_gcc-5.3.0_musl-1.1.16/bin/mips-openwrt-linux-g++ 
$GPP main.c -o udt_main_test -O3 -I../src  -L./ -L../src -ludt_trans -ludt

Android udt测试可以使用

barchart-udt

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值