不死鸟P2P

关于不死鸟 UT库 Phoenix库 ETUdp库 UTS库 库使用示范 最新进展 开发历史 下    载 授    权 联系我们
ETUdp使用示范

这个库的接口与Phoenix库比较类似,包括库的引入和回调函数的处理.

#include "ETUdp.h"

#include "winsock2.h"

#define OBJ_SENDPATHFILE_T L"d:\\WindowsXP SP2.iso" //我们用来执行文件传输的原始文件名

#pragma comment(lib,"ws2_32.lib") //连接SOCKET库

#ifdef _WIN64

#pragma comment(lib,".\\lib64\\ETUdp.lib")

#else

#pragma comment(lib,".\\lib32\\ETUdp.lib")

#endif

/*

关于本代码

作者:danscort www.phoenixp2p.com

ETUdp UDP可靠传输库

内码: UTF8 [Unicode]

线程: 2

库类型: DLL

支持平台: winxp/2003/vista/7/8 32位和64位

本代码示范了如何使用ETUdp库 执行文件传输 以及进行极限输出测试

本代码可以任意使用,不带任何条件,无论修改还是再发布

 

ETUdp 是我们为了验证一个UDP 1对1情况下高效传输技术,而重新编写的一个库,是中间附带产物,它的接口风格和我们多对多的phoenix库和文件传输ut库基本相同,

这个库是免费的,但是我们不附带任何保证或者承诺,使用者要自己承担使用这个库可能带来的任何后果,无论好和坏。

ETUdp 只提供了我们认为需要的基本接口,很多复杂的功能,我们认为完全可以通过调用者上层的组合来实现,因此没有放到库接口中,但是不排除以后逐渐增加接口。

如果您认为这个库必须要增加新的接口函数,您可以发邮件告诉我们希望增加的接口函数功能,我们会考虑增加。

UDP 1对1 可靠传输是个很简单的东西,没那么多的技术或者技巧可言,如果您单纯用来实现可靠流传输,其实TCP远比UDP好,具体原因已经另外写文章给出了,这里不再重复。那么对实时性要求很高的应用是否一定要使用UDP呢?

我觉得这完全看应用而定,TCP的慢启动并不象各位想象中的那么慢,它这个慢是针对的延迟恢复,而不是发送,很多朋友其实是被网络上的某些文章给蒙蔽了。作为我个人,实现可靠UDP,并不是用来代替数据流的可靠传输,而是

应用在那种需要同时支持可靠传输和不可靠传输的场合,单纯使用可靠UDP传输代替TCP传输,我个人的理解是完全没必要。

 

 

本代码完整包括2部分,分别是 ClientPort1 + ClientPasv1

ClientPasv1, 启动一个监听 等待另一端执行连接 , 主动发送文件 , 主动发送流量进行极限测试

ClientPort1, 启动一个主动连接 , 去连接指定的远端地址和端口 , 接收文件 , 接收流量进行极限测试

这两个代码为了简化并说明问题,没有执行后台数据与前台界面分离的原则,这是不安全的,但是作为示范,您可以看清楚整个流程

在实际应用中,请您务必遵守界面与后台数据脱离的原则,也就是,在回调函数中,不进行任何界面操作,而改用PostMessage代替,然后在主界面中响应Message

另外,这个代码只是示范了如何实现文件的发和送, 效率是不行的,因为 读盘-》[投递]发送-> [等待发送完成]-》接收-》写盘

高效的应该是 读盘 -》发送 , 然后立即预读下一块 ,我们为了简明,没有进行优化,如果您要实现高效率文件传输,建议对次序进行优化

关于容错,这里我们基本没有进行容错设计,毕竟只是个示范代码

示范代码中所有的文件以及保存路径,请针对您自己的电脑进行修改 , 也就是文件头部分的定义

#define OBJ_SENDPATHFILE_T L"d:\\recvfile.iso" //我们用来接收的文件

#define OBJ_SENDFILESIZE 621346816i64 //必须提前指定文件长度

 

另外如何在长时间传输中避免电脑进入休眠之类的处理,具体请参考windows api里的说明吧。

 

 

 

*/

 

下面的代码是我们在主程序中需要使用到的一些关键变量,其中,pcore1用来指向核心.

public:

void * pcore1;//核心指针

bool mb_listen;

volatile bool mb_transfile;//是否已经开始传输文件

CStdioFile mfileread;//文件读

__int64 ifilesize;

__int64 ifileread;

__int64 ifilewrite;

DWORD dw_start;

DWORD dw_end;

unsigned char u_laststep;

//时间记录使用上面的2个dw

volatile bool mb_transnet;//是否进行网络输出压力测试

__int64 i_send;//已经发送的字节数量

__int64 i_recv;//已经接收的字节数量

char * p_raw;//用来执行传输的内存块

下面这些是回调函数,ETudp采用了事件驱动方式,使用函数指针直接回调.

//通知上层 有新的数据包到达 ibuffsize 给出目前已经成功缓冲的数据大小

const void _stdcall P_ETUDataReady12(const __int32 ibuffsize, const unsigned __int64 uspecial)

{

#ifdef _DEBUG

char buffs[512];

sprintf(buffs,"\n===>Core 1 , read buff ready , buffsize=%u \n\n",ibuffsize);

::OutputDebugStringA(buffs);

#endif

}

//通知上层 投递的数据包完成 注意可能是失败 ibuffsize为实际投递成功的长度 当时由于多线程的原因 这个ibuffsize只作为参考

const void _stdcall P_ETUDataSendDone12(const __int32 ibuffsize, const unsigned __int64 uspecial)

{

 

if(pself->mb_transfile)

{

__int64 i641;

unsigned __int32 i321=0,i322=0;

i641=pself->ifilesize-pself->ifileread;

if(i641>=200*1024)

i321=200*1024;

else

i321=(unsigned __int32)i641;

if(i321>0)

{

//执行投递

void * pd1=::ETUdp_GetSendDataPtr(pself->pcore1);

if(pd1!=NULL)

{

//执行投递吧

i322=pself->mfileread.Read(pd1, i321);

CString str; //这里是不安全的写法 注意 界面和后台数据应该分离

str.Format(L"Read file: %u , real read:%u, Tick:%u , lost packet:%I64d , delay=%u",i321,i322,::GetTickCount( ),::ETUdp_GetCurrentLostPackets(pself->pcore1),::ETUdp_GetCurrentDelayTick(pself->pcore1));

pself->m_list.AddString(str);

::ETUdp_SetSendingNewBuffSize(pself->pcore1,i322,NULL);

pself->ifileread+=i322;//实际读到的长度

}

else

{

CString str; //这里是不安全的写法 注意 界面和后台数据应该分离

str.Format(L"Error: failed to get ptr for write new data");

pself->m_list.AddString(str);

}

}

else

{

//传输完成了

pself->dw_end=::GetTickCount( );

pself->m_list.AddString(L"======== Transfer file done==========");

pself->mb_transfile=false;

pself->mfileread.Close( );

pself->m_buttonsend.EnableWindow(TRUE);

//统计并显示吧

CString str;

str.Format(L"File size:%I64d , read:%I64d all done",pself->ifilesize,pself->ifileread);

pself->m_list.AddString(str);

str.Format(L"Transfer seconds:%.2f",(float)((float)(pself->dw_end-pself->dw_start)/1000));

pself->m_list.AddString(str);

//实际上呢 因该采用消息 发送一个消息给主界面 由主界面根据消息来执行界面输出操作

//但是因为是测试代码 为了避免过分复杂 这里直接操作了界面

//再次说明 正规编写 必须实现后台数据与前台界面分离 本代码这里直接操作界面的写法是不安全的

}

}

else if(pself->mb_transnet)

{

//#ifdef _DEBUG

// CString str1;

// str1.Format(L"Success send data , tick=%u\n",::GetTickCount( ));

// ::OutputDebugStringW(str1);

//#endif

//上一个传输结束 由于是压力测试 因此这里采用的是无限制发送

pself->i_send+=500*1024;

//继续下一个传输 注意 由于我们是采用512KB缓冲 因此这里单个投递 500KB 不可以超过缓冲长度

//由于etudp采用了内外缓冲同一指针 这里 进行压力测试 可以跳过重复的内存拷贝 而采用直接修改新缓冲块长度来进行

::ETUdp_SetSendingNewBuffSize(pself->pcore1,500*1024,NULL);

}

 

 

 

}

//调用上层函数 处理不可靠传输数据包 以包的形式进行处理

const void _stdcall P_ETUUdpPacketFunc12(const void * pdata , const unsigned __int16 isize, const unsigned __int64 uspecial)

{

//#ifdef _DEBUG

// char buffs[512];

// sprintf(buffs,"\n===>Core 1 udp packet function , udp packet size=%u \n\n",isize);

// ::OutputDebugStringA(buffs);

//#endif

}

//通知上层 连接完成 注意检测结果 可能是成功或者失败

const void _stdcall P_ETUConnectDone12(const unsigned __int64 uspecial )

{

//#ifdef _DEBUG

// ::OutputDebugStringW(L"->Core 1 connect done is called .....\n");

//#endif

}

//通知上层 连接中断 由于多线程运行的原因 这个函数可能会被多次调用 但是只需要处理一次就足够了

const void _stdcall P_ETUDisconnect12(const unsigned __int64 uspecial )

{

//#ifdef _DEBUG

// ::OutputDebugStringW(L"->Core 1 disconnect done is called .....\n");

//#endif

}

 

////上面的回调函数处理完成,下面看功能的实现

CClientPasv1Dlg::CClientPasv1Dlg(CWnd* pParent /*=NULL*/)

: CDialog(CClientPasv1Dlg::IDD, pParent)

{

m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);

pcore1=NULL;//第一个核心指针

mb_listen=false;

u_laststep=0;

::ETUdp_InitAtStart( );

pself=&(*this);

 

//时间记录使用上面的2个dw

mb_transnet=false;//是否进行网络输出压力测试

i_send=0i64;//已经发送的字节数量

i_recv=0i64;//已经接收的字节数量

p_raw=NULL;//用来执行传输的内存块

this->ifileread=0i64;

this->ifilesize=0i64;

this->dw_end=0;

this->dw_start=0;

 

this->mb_transfile=false;

}

void CClientPasv1Dlg::OnBnClickedButton1()

{

// TODO: Add your control notification handler code here

//start listen

if(this->mb_listen) //already listen

{

this->KillTimer(1234);

this->m_list.AddString(L"Now will try to stop core 1");

::ETUdp_StopCore(pcore1);

::ETUdp_FreeCore(pcore1);

pcore1=NULL;

this->mb_listen=false;

this->u_laststep=0;

this->m_buttonfile.EnableWindow(FALSE);

this->m_buttonsend.EnableWindow(FALSE);

if(this->mb_transfile)

{

this->mfileread.Close( );

}

this->i_recv=0i64;

this->i_send=0i64;

this->mb_transfile=false;

this->mb_transnet=false;

this->m_list.AddString(L"Success stop core 1");

}

else

{

this->pcore1=::ETUdp_CreateCore( );

if(pcore1==NULL)

{

this->m_list.AddString(L"Failed to create pcore1");

return;

}

else

{

this->m_list.AddString(L"Success create pcore1");

}

//设置参数吧

union

{

unsigned char b[4];

unsigned __int32 u32;

}uip;

uip.b[3]=127;

uip.b[2]=0;

uip.b[1]=0;

uip.b[0]=1;

CString str;

this->m_editremoteip.GetWindowTextW(str);

str.TrimLeft( ).TrimRight( );

if(str.GetLength( )>=7)

{

unsigned int uu[4];

if(::swscanf(str,L"%u.%u.%u.%u",&uu[3],&uu[2],&uu[1],&uu[0])==4)

{

uip.b[3]=(unsigned char)uu[3];

uip.b[2]=(unsigned char)uu[2];

uip.b[1]=(unsigned char)uu[1];

uip.b[0]=(unsigned char)uu[0];

}

}

unsigned __int32 uiport=3500;

this->m_editlocalport.GetWindowTextW(str);

str.TrimLeft( ).TrimRight( );

if(str.GetLength( )>0)

{

if(::swscanf(str,L"%u",&uiport)==1)

{

//success

}

else

{

uiport=3500;

}

}

//注意,我们开启的端口是3500 , 当然你可以更换成其他端口 ,但是另一端接收部分也需要更换成相同端口

::ETUdp_SetPasvConnectIpv4Mode(pcore1,uip.u32,0,uiport/*3500*/);

::ETUdp_SetDataBuffSize(pcore1,512*1024,512*1024);//默认采用 512KB缓冲 因此单次最多投递或者接收不能超这个数字 那么500KB是比较理想的投递数字

unsigned __int64 u64arr[4];

u64arr[0]=1i64;

u64arr[1]=2i64;

u64arr[2]=3i64;

u64arr[3]=4i64; //设置握手的保密字 只要双方的保密字相同 就可以连接 否则会被拒绝

::ETUdp_SetConnectKey(pcore1,u64arr);

::ETUdp_SetUpnpMode(pcore1,false);

//下面设置回调函数

::ETUdp_SetCallbackOnBreak(pcore1,P_ETUDisconnect12);

::ETUdp_SetCallbackOnConnectDone(pcore1,P_ETUConnectDone12);

::ETUdp_SetCallbackOnDataReady(pcore1,P_ETUDataReady12);

::ETUdp_SetCallbackOnSendDone(pcore1,P_ETUDataSendDone12);

::ETUdp_SetCallbackOnUdpPacketArrive(pcore1,P_ETUUdpPacketFunc12);

//回调函数设置完成

__int32 iresult;

iresult=::ETUdp_StartCore(pcore1);

str.Format(L"pcore1 , start return value=%d",iresult);

this->m_list.AddString(str);

if(iresult==0)

{

this->mb_listen=true;

//等待完成 开启定时器

this->SetTimer(1234,1000,NULL);

}

else

{

::ETUdp_StopCore(pcore1);

::ETUdp_FreeCore(pcore1);

pcore1=NULL;

str.Format(L"Error , last step code=%d",::ETUdp_GetStepNum(pcore1));

this->m_list.AddString(str);

}

 

 

 

 

 

 

 

 

}

 

 

}

void CClientPasv1Dlg::OnBnClickedButton2()

{

// TODO: Add your control notification handler code here

//start send file

if(this->mb_transfile==false) //已经在传输了 那么结束

{

//this->mfileread.Close( );

//this->mfilewrite.Close( );

this->m_list.AddString(L"========Start file transfer==========");

if(this->pcore1==NULL )

{

this->m_list.AddString(L"Found net not ready , please connect first...\n");

return;

}

 

mb_transfile=false;//是否已经开始传输文件

//CStdioFile mfileread,mfilewrite;//分别是读和写

ifilesize=0i64;

ifileread=0i64;

ifilewrite=0i64;

dw_start=::GetTickCount( );

dw_end=0;

if(mfileread.Open(OBJ_SENDPATHFILE_T,CFile::modeRead|CFile::typeBinary,NULL)==FALSE)

{

this->m_list.AddString(L"Failed to open file for read test.");

return ;

}

this->m_list.AddString(L"====Success open file for read and write test ....\n");

ifilesize=(__int64)mfileread.GetLength( );

CString str;

str.Format(L"File size:%I64d , Start tick=%u",ifilesize,dw_start);

this->m_list.AddString(str);

//按 200KB为1单位 执行传输

__int64 i641;

unsigned __int32 i321=0,i322=0;

i641=ifilesize-ifileread;

if(i641>=200*1024)

i321=200*1024;

else

i321=(unsigned __int32)i641;

this->m_buttonsend.EnableWindow(FALSE);//禁止其他按钮

//是的 开始传输了

this->mb_transfile=true;

if(i321>0)

{

//执行投递

void * pd1=::ETUdp_GetSendDataPtr(this->pcore1);

if(pd1!=NULL)

{

//执行投递吧

i322=mfileread.Read(pd1, i321);

str.Format(L"Read file: %u , real read:%u\n",i321,i322);

::OutputDebugStringW(str);

this->m_list.AddString(str);

::ETUdp_SetSendingNewBuffSize(pcore1,i322,NULL);

ifileread+=i322;//实际读到的长度

}

}

 

 

//this->SetTimer(8802,1000,NULL);

 

}

else

{

//否则 开始结束传输

this->mb_transfile=false;

this->mfileread.Close( );

this->m_list.AddString(L"----------File transfer done----------------");

}

 

 

}

具体我们建议您参考我们提供的2个示范工程代码.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 
(c) 2011-2016 phoenixp2p.com