p2p sdk发送文件客户端源码

发布于:2022-12-22 ⋅ 阅读:(132) ⋅ 点赞:(0)

#  KKP2P SDK介绍
kkp2p sdk是库快科技(kkuai.com)研发的支持p2p通信的中间件,是一套与业务无关的通用的p2p sdk库, 可以免费下载试用版本使用。
一句话概括其特点:支持面向登录账号进行网络编程。
即只要传入对端的账号id,应用层就能得到一个socket句柄fd,应用层通过读写该socket fd句柄和对端进行通信。

优秀特性 说明
跨平台 kkp2p的sdk库是由c语言开发,在linux、windows、android、ios等平台编译出了静态库,以及其他一些嵌入式平台也编译出了静态库,大家可以直接下载进行使用。云端服务是由golang语言开发,也支持在各种平台下编译出直接可以运行的程序,配置也比较简单,大家下载之后就可以按照官网文档说明自行进行部署。
体积小 kkp2p不依赖于任何第三方库,编译出来的库只有500KB左右大小。
性能强 在服务器上测试,P2P方式通信的速度可以超过10MB每秒;中转方式通信的速度取决于您云端服务器的带宽
易使用 提供了类似于socket编程接口的kkp2p_connect、kkp2p_listen、kkp2p_accept、kkp2p_read、kkp2p_write几个核心函数,使用起来非常简单方便。您只要指定对端的登录账号通过kkp2p_connect函数就能和对端创建一个虚拟的传输管道,然后通过kkp2p_read和kkp2p_write函数来读写数据和对端进行通信。您还可以通过参数指定是使用P2P方式通信还是使用中转(relay)方式通信,完全不用关心底层传输通道的创建和管理细节,一切由kkp2p的sdk库帮您解决。
高安全 支持加密通道传输,您只需要在kkp2p_connect的参数中指定需要加密数据即可。sdk会自动创建一个加密的虚拟通信管道出来;您写入明文,sdk会自动加密成密文传输;sdk收到密文,会自动解密成明文返回给您。通信双方的共同密钥是双方的sdk通过DH算法自动协商而成,外界无法获取;并且每次sdk的启动都会自动协商生成一个新的动态密钥,严格保障您的通信数据的安全。如果您为了提升数据传输的性能,不想对数据进行加解密,只需要在创建连接的函数kkp2p_connect参数中指定不需要加密数据即可。sdk的数据加解密功能只有在商业版本中才有,在个人试用版本中没有该功能。
通用性 kkp2p是一套适用于各种场景的通用的通信中间件,会完全透传用户的数据,您可以灵活的自定义通信双方的协议,kkp2p不会解析您的业务数据。kkp2p的P2P通信是基于udp实现的,kkp2p会自动帮您解决丢包、乱序、重传问题,也会根据您的实际带宽做自适应的带宽流控,您使用起来具有tcp传输的效果,相当于是用udp模拟实现了tcp。中转(relay)通信是基于tcp原生实现的。

#  发送文件客户端

本例子用于举例说明,使用kkp2p sdk如何向对端传输一个文件,本例子是传文件客户端,接收文件服务端请看其他章节。

传文件过程:首先向对端发送4个字节(网络序,例子仅支持4G左右文件大小,如果想支持更大文件,得传输8个字节文件大小,您可以自行修改),然后再传输文件内容;传输完成后,等待对端应答。收到对端应答后,就关闭连接的fd句柄以及关闭连接,最后退出。

 通过例子可以看到,p2p sdk向外提供的接口非常简单易用。下面看具体的代码,该代码在linux平台验证测试通过

#  源码讲解

#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 <sys/time.h>
#include <time.h>

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

// 简单封装一下发送指定长度数据接口
int SendData(int fd, char* buff, int len) {
    int sended = 0 ;
    while (sended < len) {
        // 1秒超时时间
        int wl = kkp2p_write(fd, buff + sended, len - sended, 1000);
        if (wl < 0) {
            printf("SendData error,fd:%d,ret:%d,len:%d,errno:%d,desc:%s.\n",fd,wl, len, errno, strerror(errno));
            return -1;
        }
        sended += wl;
    }
    return len;
}

// 简单封装一下读取指定长度数据接口
// 例子主要用该函数接收服务端的返回
int RecvData(int fd, char* buff, int len) {
    int recved = 0 ;
    while (recved < len) {
        // 1秒超时时间
        int wl = kkp2p_read(fd, buff + recved, len - recved, 1000);
        if (wl < 0) {
            printf("RecvData error,fd:%d,ret:%d,len:%d,errno:%d,desc:%s.\n",fd,wl, len,errno, strerror(errno));
            return -1;
        }
        recved += wl;
    }
    return len;
}

// 例子需要输入两个参数,一个是对端的登录peerId,一个是需要发送文件的路径
int main(int argc, char** argv)
{
    if (argc < 3) {
        printf("usage:%s peerId filename.\n",argv[0]);
        return -1;
    }

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

    // 日志级别切换为debug模式
    kkp2p_switch_log_level(p2pEngine, 4);

    char* peerId = argv[1];
    char* fileName = argv[2];
    FILE* pRead = fopen(fileName, "rb");
    if (pRead == NULL) {
        printf("fopen %s error.\n", fileName);
        kkp2p_engine_destroy(p2pEngine);
        return -1;
    }
    
    // 获取文件大小
    struct stat statInfo;
    stat(fileName, &statInfo);
    uint32_t fileSize = statInfo.st_size;

    // 创建连接,使用自动模式,非加密连接,2秒超时
    // 自动模式由engince层自动判断是p2p传输还是中转传输
    kkp2p_connect_ctx_t ctx;
    memset(&ctx, 0, sizeof(ctx));
    strncpy(ctx.peer_id, peerId, 32);
    ctx.connect_mode = 0;
    ctx.encrypt_data = 0;
    ctx.timeout = 2000;

    kkp2p_channel_t channel;
    int connRet = -1 ;
    
    // 一直等待创建连接成功
    while (connRet != 0) {
        time_t starT = time(NULL);
        // 同步建连
        connRet = kkp2p_connect(p2pEngine, &ctx, &channel);
        time_t endT = time(NULL);
        if (connRet != 0 ) {
            printf("pid:%d connect timeout,cost:%d,try again.\n",getpid(),endT-starT);
        }
    }
     // 建连成功
     printf("pid %d create new connection success,fd:%d, mode is %d,channel id:%u.\n",getpid(),channel.fd, channel.transmit_mode,channel.channel_id);

    // 发送四个字节文件大小,网络序,仅支持4G左右大小,如果希望支持更大,得发送8字节大小
    uint32_t netSize = htonl(fileSize);
    int ret = SendData(channel.fd, (char*)&netSize, sizeof(netSize));
    if (ret < 0) {
        printf("send error,channel id:%u.\n",channel.channel_id);
        kkp2p_close_fd(channel.fd);
        kkp2p_close_channel(p2pEngine, channel.channel_id);
        kkp2p_engine_destroy(p2pEngine);
        return -1;
    }

    // 循环发送文件内容,直至全都发送结束
    char szBuff[1024];
    int readLen = fread(szBuff, 1, sizeof(szBuff), pRead);
    uint32_t totalSend = 0;
    while (readLen > 0) {
        // 通过返回的fd句柄向对端发送数据
        ret = SendData(channel.fd, szBuff, readLen);
        if (ret < 0) {
            printf("send error,file size:%u,total send:%u,channel id:%u.\n",fileSize, totalSend, channel.channel_id);
            kkp2p_close_fd(channel.fd);
            kkp2p_close_channel(p2pEngine, channel.channel_id);
            kkp2p_engine_destroy(p2pEngine);
            return -1;
        }
        totalSend += ret;
        readLen = fread(szBuff, 1, sizeof(szBuff), pRead);
    }
    fclose(pRead);

    // 发送完成后等待对端应答,文件接收端需要接收完毕后需要发送响应
    char ack;
    ret = RecvData(channel.fd, &ack, 1);
    if (ack == '1') {
        printf("send success,total send len:%u,channel id:%u.\n",totalSend, channel.channel_id);
    } else {
        printf("send error,total send len:%u,channel id:%u.\n",totalSend, channel.channel_id);
    }
    // 最后关闭连接的代理fd句柄(否则会有句柄泄漏),关闭连接
    kkp2p_close_fd(channel.fd);
    kkp2p_close_channel(p2pEngine, channel.channel_id);
    
    //释放kkp2p sdk的engine
    kkp2p_engine_destroy(p2pEngine);
    return 0;
}

本文含有隐藏内容,请 开通VIP 后查看