最佳实践-p2p传文件客户端源码

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

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

     通过例子可以看到,kkp2p sdk向外提供的接口非常简单易用。下面看具体的代码,该代码在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 <sys/time.h>
#include <time.h>

// 得包含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;
    }
    
    // windows平台加上网络库
    // WSADATA wsadata;
	// WSAStartup(MAKEWORD(2, 2), &wsadata);
        
    // 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秒超时
    kkp2p_connect_ctx_t ctx;
    memset(&ctx, 0, sizeof(ctx));
    strncpy(ctx.peer_id, peerId, 32);
        
    // 创建tcp类型传输通道
    ctx.channel_type = KKP2P_TCP_CHANNEL;
    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) {
        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;
}