P2P穿透(ENet丢包重传)

来源:互联网 发布:mac优酷弹幕怎么设置 编辑:程序博客网 时间:2024/06/06 07:23

资源链接:http://download.csdn.net/download/yuanchunsi/10159049


P2P穿透成功后,获得对端IP和端口通过ENet进行连接传输数据,解决了UDP丢包视频卡顿的问题!



服务端:

/* * =========================================================================== * *       Filename:  enetc.c *    Description:   *        Version:  1.0 *        Created:  2017年12月11日 14时10分03秒 *       Revision:  none *       Compiler:  gcc *         Author:   (ycs),  *        Company:   * * =========================================================================== */#include<string.h>#include<pthread.h>#include<stdio.h>#include<stdlib.h>#include"enet/enet.h"//全局变量pthread_t pthread_S;pthread_t pthread_R;ENetPeer *peer = NULL;ENetHost *host = NULL;ENetEvent event; //模拟媒体数据结构typedef struct code_{  char data[128];  int size ;}tmp_t;//发送数据线程,模拟项目框架void *run_s(void*data){  pthread_detach(pthread_self());   static int count = 0;   while(1) {    sleep(1);    if(NULL != peer) {    ENetPacket *packet = enet_packet_create(NULL,78,ENET_PACKET_FLAG_RELIABLE);//ENET_PACKET_FLAG_RELIABLE:这个flag表示支持丢包重发    char tmp[78];    snprintf(tmp,78,"%s count: %d","I am server",count++);//发送带有seq的数据模拟RTP的序列号,确保ENet发送无异常    //strcpy((char*)packet->data,tmp);memcpy((char*)packet->data,tmp,78);//此处很重要,媒体数据不能用strcpy!!!    enet_peer_send(peer,2,packet);    enet_host_flush(host);    }  }}//接收数据线程,模拟项目框架void *run_R(void*data){  pthread_detach(pthread_self());   static int count = 0;   while (enet_host_service (host, &event, 5000)>=0)    {    printf("event type == %d\n",event.type);    if(event.type == ENET_EVENT_TYPE_CONNECT){      peer = event.peer;      char ip[256];      ENetAddress remote = event.peer->address;      enet_address_get_host_ip(&remote,ip,256);      printf("ip %s port %d\n",ip,remote.port);    } else if(event.type == ENET_EVENT_TYPE_RECEIVE){      printf("datalenth %d  %s channel %d\n",event.packet->dataLength,event.packet->data,event.channelID);    //接收媒体数据      tmp_t tmp;      tmp.size = event.packet->dataLength;      memcpy(tmp.data,event.packet->data,tmp.size);      printf("data: %s size: %d\n",tmp.data,tmp.size);      enet_packet_destroy(event.packet);    } else if(event.type == ENET_EVENT_TYPE_DISCONNECT){      printf("peer is disconnect \n");     }  }  }int main(){  int ret = -1;  if(enet_initialize())    {    printf("init failed\n");      return -1;    }  ENetAddress haddr;    haddr.host = ENET_HOST_ANY;  haddr.port=1234;  //创建本地Host,绑定地址和端口   host = enet_host_create(&haddr,        15,   //允许15个客户端连接       3,    //有3个channel      0,        0);    if(host == NULL)    {    printf("create failed\n");    return -1;    }      //模拟项目框架,发送和接收为两个线程  ret = pthread_create(&pthread_S,NULL,run_s,NULL);    ret = pthread_create(&pthread_R,NULL,run_R,NULL);    getchar();  enet_deinitialize();    return 0;}



客户端:

/* * =========================================================================== * *       Filename:  enetc.c *    Description:   *        Version:  1.0 *        Created:  2017年12月11日 14时10分03秒 *       Revision:  none *       Compiler:  gcc *         Author:   (ycs),  *        Company:   * * =========================================================================== */#include<string.h>#include<pthread.h>#include<stdio.h>#include<stdlib.h>#include"enet/enet.h"//全局变量pthread_t pthread_S;pthread_t pthread_R;ENetPeer *peer = NULL;ENetHost *host = NULL;ENetEvent event;  //发送数据线程,模拟项目框架void *run_s(void*data){  pthread_detach(pthread_self());  static int count = 0;   while(1) {    sleep(1);    if(NULL != peer) {    ENetPacket *packet = enet_packet_create(NULL,78,ENET_PACKET_FLAG_RELIABLE);//ENET_PACKET_FLAG_RELIABLE:这个flag表示支持丢包重发    char tmp[78];    snprintf(tmp,78,"%s count: %d","I am client",count++);//发送带有seq的数据模拟RTP的序列号,确保ENet发送无异常    //strcpy((char*)packet->data,tmp);memcpy((char*)packet->data,tmp,78);//此处很重要,媒体数据不能用strcpy!!!    enet_peer_send(peer,2,packet);    enet_host_flush(host);    }  }}//接收数据线程,模拟项目框架void *run_R(void*data){  pthread_detach(pthread_self());   static int count = 0;   while (enet_host_service (host, &event, 5000)>=0)    {    printf("event type == %d\n",event.type);    if(event.type == ENET_EVENT_TYPE_CONNECT){      peer = event.peer;      char ip[256];      ENetAddress remote = event.peer->address;      enet_address_get_host_ip(&remote,ip,256);      printf("ip %s port %d\n",ip,remote.port);    } else if(event.type == ENET_EVENT_TYPE_RECEIVE){      printf("datalenth %d  %s channel %d\n",event.packet->dataLength,event.packet->data,event.channelID);      enet_packet_destroy(event.packet);    } else if(event.type == ENET_EVENT_TYPE_DISCONNECT){      printf("peer is disconnect \n");     }  }  }int main(){  int ret = 0;    //初始化  if(enet_initialize())    {    printf("init failed\n");      return -1;    }     //创建本地HOST对象,客户端不需要绑定地址    host = enet_host_create(NULL,        1,   //只允许连接一个服务器        0,      0,        0);    if(host == NULL)    {    printf("create failed\n");    return -1;    }    ENetAddress paddr;    enet_address_set_host(&paddr,"192.168.0.110");//设备端IP地址,此地址为StunServer提供(P2P穿透成功)    paddr.port=1234;//设备端ENet端口,测试端口    //连接设备端ENet,3为设备端3个Channel  peer = enet_host_connect(host,&paddr,3,0);    if(peer == NULL)    {      printf("peer connect failed\n");    return -1;    }      //模拟项目框架,发送和接收为两个线程  ret = pthread_create(&pthread_S,NULL,run_s,NULL);    ret = pthread_create(&pthread_R,NULL,run_R,NULL);    getchar();  enet_deinitialize();    return 0;}



总结:

1、enet_peer_send():实质把发送数据insert_list。由enet_host_service接口内部实现send操作


2、ENetPacket *packet=enet_packet_create(NULL,ret,ENET_PACKET_FLAG_RELIABLE); 

选择合适的FLAGS创建packet

  1. ENET_PACKET_FLAG_RELIABLE packet must be received by the target peer and resend attempts should be made until the packet is delivered  
  2. ENET_PACKET_FLAG_UNSEQUENCED packet will not be sequenced with other packets not supported for reliable packets  
  3. ENET_PACKET_FLAG_NO_ALLOCATE packet will not allocate data, and user must supply it instead  
  4. ENET_PACKET_FLAG_UNRELIABLE_FRAGMENT packet will be fragmented using unreliable (instead of reliable) sends if it exceeds the MTU  
  5. ENET_PACKET_FLAG_SENT whether the packet has been sent from all queues it has been entered into  


3、enet_host_service():很重要,实质接收发送数据都在这个函数做的;包括event事件、获取list,ACK重发。

enet_protocol_dispatch_incoming_commands (ENetHost * host, ENetEvent * event);//list

enet_protocol_receive_incoming_commands(host, event));//接收

enet_protocol_send_outgoing_commands (ENetHost * host, ENetEvent * event, int checkForTimeouts);//发送及丢包统计

enet_protocol_send_acknowledgements (ENetHost * host, ENetPeer * peer);这个是判断发送ack,如果ack不正确,会将continueSending置为1,从而让这个while一直循环发送该包。