最佳实践-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; 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]; 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; }