注册 登录
LUPA开源社区 返回首页

yyq224444的个人空间 http://www.lupaworld.com/?439809 [收藏] [复制] [分享] [RSS]

我的博客

飞鸽程序分析(三)

热度 4已有 2719 次阅读2011-5-25 08:54 |个人分类:网络编程|系统分类:IT技术|

从一个SendDlg对话框中选择和发送文件或文件夹到RecvDlg对话框接收一个文件的全过程

点击的发送文件的按钮:

这样会产生一个带有按钮ID号的WM_COMMAND消息,由TApp::Run函数交给TApp::WinProc函数,然后交给产生消息的TSendDlg类的WinProc,由于TSendDlg继承TDlg,而TDlg继承Twin类,WinProc是虚函数,而TSendDlgTDlg都没有定义WinProc函数,所以调用的是从Twin复制来的WinProc,在WinProc中,WM_COMMAND消息的响应函数是EvCommand函数,这里应该调用的是TSendDlg::EvCommand函数,函数中根据消息中的按钮的ID号为ADDFILE,则调用TShareDlg::FileAddDlg函数将创建一个添加文件的对话框:

FileAddDlg(TDlg *dlg, ShareMng *shareMng, ShareInfo *shareInfo, Cfg *cfg)

{

       char buf[MAX_BUF] = "", path[MAX_BUF];

       if (OpenFileDlg(dlg, OpenFileDlg::MULTI_OPEN).Exec(buf, ADDFILE_MSGSTR, OPENFILEALL_MSGSTR, cfg->lastOpenDir) != TRUE)

              return      FALSE;

       int           dirlen = strlen(cfg->lastOpenDir);

       if (buf[dirlen]) //当选中的只有一个文件

              return      shareMng->AddFileShare(shareInfo, buf);

       for (char *fname=buf+dirlen+1; *fname; fname += strlen(fname) +1)//选中多个文件

       {

              if (MakePath(path, buf, fname) >= MAX_PATH)

                     continue;

              shareMng->AddFileShare(shareInfo, path);

       }

       return      TRUE;

}

FileAddDlg函数产生一个OpenFileDlg的对话框,当你选中一个或多个文件的时候,会把每个文件的完整路径记录到缓冲区buf中,然后根据buf里的信息将每个文件的信息添加到ShareMng中,同时将发送文件的信息显示到SendDlg窗口上。

然后当你点击SendDlg对话框中的发送按钮的时候,会触发带有IDOKWM_COMMAND消息,同样的处理路径,最后会落到TSendDlg::EvCommad函数上,然后根据按钮的ID号为IDOK,调用isSending判断是否正在发送,否则调用sendmsg函数发送一个UDP消息。然后设定一个时间,若收不到后会设置一个Timer,定时发送一个含有IPMSG_SEND_TIMERWM_TIMER消息。

 

现在再说接收端,当你收到一个UDP消息时,会发送一个WM_UDPEVENT消息,然后消息传递到TMainWin::WinProc函数中,然后调用UdpEvent函数,然后通过msg中的IPMSG_SENDMSG字段,选择调用TMainWin::MsgSendMsg()函数,然后发送一个带有IPMSG_RECVMSGUDP消息,然后调用RecvDlgOpen()函数创建一个RecvDlg对话框窗口,并连接到recvList链表中。

 

再回到发送端,发送端接收到IPMSG_RECVMSGUDP消息继续触发WM_UDPEVENT消息,然后继续将消息交至TMainWin::UdpEvent函数处理,根据IPMSG_RECVMSG的值,选择调用TMainWin::MsgInfoSub()函数处理,函数只是把接收方的信息和地址和端口号记录。

 

再回到接收端,在RecvDlg对话框中当你点击接收的时候,会触发WM_COMMAND函数,调用RecvDlg::EvCommand函数然后根据按钮的IDIDOK转到不同的部分处理,会创建一个TSaveCommonDlg的保存文件的窗口dlg,然后执行dlg.Exec函数,在Exec函数中,创建一个OpenFileDlg,将选择的文件路径保存到fname中,退出后,在RecvDlg::EvCommand函数中创建一个RecvFileObj对象,然后调用SaveFile()函数;在SaveFile函数中调用了TRecvDlg::ConnectRecvFile()函数,这里调用MsgMng::Connect()函数,MsgMng::Connect函数会调用socket套接字中的connect连结函数。

       再回到发送端,发送端的MsgMngTcp_sd一直在监听,当有connect连接事,就会触发WM_TCPEVENT消息,这时调用TMainWin::TcpEvent()函数,根据消息中的lparma中的FD_ACCEPT字段信息,响应对应的处理:

ConnectInfo   tmpInfo, *info;

                     if (msgMng->Accept(hWnd, &tmpInfo))

                     {

                            if (CheckConnectInfo(&tmpInfo))

                            {

                                   info = new ConnectInfo(tmpInfo);

                                   connectList.AddObj(info);

                            }

                            else ::closesocket(tmpInfo.sd);

                     }

主要是调用MsgMng::Accept()函数,建立TCP连结。并且将connectinfo信息加入connectList链表中。

 

再回到接收端,当发送端触发Accept后,socket收到信息后,会触发WM_TCPEVENT消息,然后消息传递后,调用RecvDlg::TcpEvent()函数,根据消息的Lparma值为FD_CONNECT,执行对应的代码:StartRecvFile()

StartRecvFile()函数主要是开启接收传输的函数,它开始向发送端发送TCP信息,根据接收的是文件夹还是文件决定发送IPMSG_GETDIRFILES还是IPMSG_GETFILEDATA消息。然后调用MsgMng::ConnectDone();然后创建一个缓冲区,然后开启一个线程接收数据

::CreateThread(NULL, 0, RecvFileThread, this, 0, &id)

线程体是RecvFileThread函数,这个函数会根据接收文件的属性,来确定是调用RecvDirFile还是RecvFile,也就是确定是接收文件夹还是文件。

 

       回到发送端,当TCP建立起连结后,SOCKET会触发WM_TCPEVENT消息,当调用TMainWin::TcpEvent函数后,根据消息的lParam的值为FD_READ,开启StartSendFile()函数,开始传输数据。在StartSendFile函数中,发送端也调用connectdone通知已经建立连结,三次握手完毕,然后开始准备传输数据,根据文件的属性和大小,设置fileobj。然后开启一个线程发送数据::CreateThread(NULL, 0, SendFileThread, obj, 0, &id)

这个线程的函数体是SendFileThread(),这个函数会根据文件的属性,确定是调用SendDirFile还是调用SendFile函数来区分传输文件或者文件夹。

      

  经过传输后,文件传送完毕,接收段收到发送完毕的TCP消息,然后触发WM_TCPEVENT消息,消息传递后调用RecvDlg::Tcpevent函数,根据消息Lpararm的值为FD_CLOSE,调用EndRecvFile();在EndRecvFile函数中,首先调用CloseHandle(hThread);删除接收线程的句柄,线程在RecvFileThread最后已经退出了。之后再调用SetTransferButtonText函数创建一个发送完毕对话框。然后closesocket关闭接收端得socket。删除接收缓冲区啊。保存接收的文件。

 

回到发送端,传输结束后,发送端的SOCKET套接字也会发送WM_TCPEVENT消息,传递后,处理函数调用TMainWin::TcpEvent函数,根据消息的Lparam的值为FD_CLOSE,执行对应的语句:

SendFileObj *obj;

                     if ((obj = FindSendFileObj(sd)) != NULL)

                            EndSendFile(obj);

                     else

                            ::closesocket(sd);

执行EndSendFile函数,EndSendFIle函数中,开始也是Close发送线程的句柄,接着是关闭socket套接字。接着关闭发送的文件。然后删除在sendList中的fileobj

这就是传输一个文件的全部过程。

 

本文只是本人研究飞鸽传输代码时的一点点小心得,有错误或者不对的地方,请大家多多指教。谢谢

 已同步至 yyq224444的微博

刚表态过的朋友 (0 人)

评论 (0 个评论)

facelist

您需要登录后才可以评论 登录 | 注册
验证问答 换一个 验证码 换一个

关于LUPA|人才芯片工程|人才招聘|LUPA认证|LUPA教育|LUPA开源社区 ( 浙B2-20090187 浙公网安备 33010602006705号   

返回顶部