最佳实践-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.