最佳实践-p2p异步建连源码

    本例子用于说明如何使用异步建立连接的用法。创建连接是最核心的接口。

    如sdk说明文档说明,创建连接的参数中的kkp2p_connect_ctx_s结构体里面的func非空,就是使用异步连接。当engine层创建连接结束之后就在engine线程里面回掉该函数,使用时候需要注意该函数是在engine线程中执行,不是在自己的业务线程中执行。

    下面看具体的例子代码,该代码在linux平台编译测试验证通过。

     kkp2p sdk的头文件和库请在个人试用版本页面免费下载,免费使用和体验。

#include <stdio.h>
#include <stdint.h>
#include <errno.h>
#include <sys/stat.h>
#include <string.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include<stdlib.h>
#include <sys/time.h>
#include <time.h>

// 得包含kkp2p sdk的头文件,以及链接libkkp2p.a
#include "kkp2p_sdk.h"

// 传给engine层的自定义参数,方便实现回掉逻辑
// fd是管道描述符
// channel是业务层指针,用户保存回掉函数中的channel信息
struct ConnParam {
    int fd;
    kkp2p_channel_t* channel;
};

void ConnectCallBack(int result, const kkp2p_channel_t* channel, void* param) {
    // 自定义回掉函数,由engine层线程创连结束后回掉
    struct ConnParam* connParam = (struct ConnParam*)param;
    if (result < 0) {
        // result小于0表示创建连接失败,写入管道-1
        char ch = -1;
        write(connParam->fd, &ch, 1);
    } else {
        // 创建连接成功,写入管道0
        char ch = 0;
        write(connParam->fd, &ch, 1);
    }
    // 保存返回的channel信息,里面有peerId,以及连接相关的信息,比如是P2P连接,还是中转连接等
    memcpy(connParam->channel, channel, sizeof(kkp2p_channel_t));
}

int main(int argc, char** argv)
{
    // 需要输入两个参数,一个是对方的登录peerId,另外一个是希望的连接类型
    // 0 自动模式,p2p和中转谁创建的快就用谁
    // 1 仅P2P模式
    // 2 仅relay模式
    if (argc < 3) {
        printf("usage:%s peerId,connMode\n",argv[0]);
        return -1;
    }

    // 初始化kkp2p sdk的配置信息
    // 包括云端的登录信息,局域网侦听端口以及日志信息
    kkp2p_engine_conf_t kkp2p_conf;
    memset(&kkp2p_conf, 0, sizeof(kkp2p_engine_conf_t));
    kkp2p_conf.login_domain = "125.72.218.199";
    kkp2p_conf.login_port = 3080;
    kkp2p_conf.lan_search_port = 3549;
    kkp2p_engine_t* p2pEngine = kkp2p_engine_init(&kkp2p_conf, 5000);
    if (p2pEngine == NULL) {
        printf("init kkp2p engine error.\n");
        return -1;
    }

    char* peerId = argv[1];

    // 创建管道,业务层读管道,创连结束后的回掉函数中写管道
    int pipefd[2] = {0};
    int ret = pipe(pipefd);
    if (ret == -1)
    {
        printf("pipe error.\n");
        return -1;
    }
    // 申请一个channel,用于保存回掉函数返回的channel信息
    kkp2p_channel_t channel;
    
    // 传给回掉函数的自定义参数
    ConnParam connParam;
    connParam.channel = &channel;
    connParam.fd = pipefd[1];

    // 建连参数,包括对端peerId,以及超时时间
    kkp2p_connect_ctx_t ctx;
    memset(&ctx, 0, sizeof(ctx));
    strncpy(ctx.peer_id, peerId, 32);
    ctx.connect_mode = atoi(argv[2]);

    // 创建tcp类型传输通道
    ctx.channel_type = KKP2P_TCP_CHANNEL;
    
    // 不使用加密连接
    ctx.encrypt_data = 0;
    
    // 建连超时时间
    ctx.timeout = 2000;
    
    // 传入回掉函数的指针以及自定义回掉函数参数,如果指针为空则使用的是同步建连模式
    ctx.func = ConnectCallBack;
    ctx.func_param = &connParam;
    
    // 创建连接,该函数返回之后只是向engine层发送建连请求成功
    kkp2p_connect(p2pEngine, &ctx, &channel);
    char ch = -1;
        // 计算建连开始时间,方便统计建连耗时
    struct timeval startT;
    gettimeofday(&startT, NULL);
    uint64_t startM = (uint64_t)startT.tv_sec * 1000 + (uint64_t)startT.tv_usec / 1000;
    
    // 业务层开始读管道,如果管道没有数据,会一直阻塞
    read(pipefd[0],&ch,1);
        
    // 读到管道数据,-1标识建连失败,0标识建连成功,看回掉函数自定义实现,并计算建连结束时间
    struct timeval endT;
    gettimeofday(&endT, NULL);
    uint64_t endM = (uint64_t)endT.tv_sec * 1000 + (uint64_t)endT.tv_usec / 1000;    
    if (ch < 0) {
         // 建连失败
      printf("connect %s error,cost %llu millisecond.\n",channel.peer_id,endM-startM);
    } else {
      // 建连成功,并打印连接相关的信息,最后关闭连接的代理fd句柄和连接
      printf("connect %s success,channel id:%u,channel fd:%u,mode:%d,cost %llu millisecond.\n",channel.peer_id, channel.channel_id, channel.fd, channel.transmit_mode,endM-startM);
      kkp2p_close_fd(channel.fd);
      kkp2p_close_channel(p2pEngine, channel.channel_id);
    }
    
    // 释放kkp2p sdk engine
    kkp2p_engine_destroy(p2pEngine);
    return 0;
}

该函数执行后有如下打印日志输出,mode表示连接类型,1是p2p模式,2是中转模式

connect kkuai-ipc-00001 success,channel id:3451373715,channel fd:11,mode:1,cost 82 millisecond.