zoukankan      html  css  js  c++  java
  • UDP文件传输的实现

     

    /*******************************************************
    *       FilePoster关键代码
    *E-mail:    andy.zhshg@163.com
    *日期:        2008.12.25
    *
    *程序描述:
    *FilePoster是基于Win32平台的网络文件传输程序。开发平台为
    *Visual C++6.0。
    *程序采用服务器/客户机模式,服务器用于接收数据,客户机负
    责发送数据。利用windows多线程原理,集接收和发送功能于一
    体。
    *网络传输采用UDP原理,为解决UDP传输的不可靠性,用Windows
    *消息对收发的双方进行同步,即发送方先发送消息请求传送文件
    *同时发送要传文件的基本信息,收取方收到后回复发送发一个确
    *认消息,然后发送方依次发送被分成固定大小块(256字节)的文
    *件数据,收取方每收到一个数据块就写入自己的文件并回复发送
    *方一个接收完毕消息,以使得发送方发送下一个数据块。发送方
    *发送最后一个数据块时添加发送结束标记,接受方接收完毕后关
    *闭文件,至此发送过程结束。
    *
    *问题阐述:
    *程序的缺陷在于只能用于具有固定IP的主机之间的文件传输。在
    *处于不同的内网中的主机无能为力。因为对于内网中的计算机其
    *IP是主机通过映射后分配来的所以如果不借助于服务器很难从外
    *网访问内网的计算机。不过对于处于同一局域网的计算机,本程
    *序还是能够完全应付的。
    *限于篇幅,与文件传输无关的代码省略,以"......"代替。
    *********************************************************/
    
    
    /*********************************************************
    *程序的初始化函数
    *********************************************************/
    BOOL CFilePosterApp::InitInstance()
    {
        //加载套接字库 
        if(!AfxSocketInit())
        {
            AfxMessageBox("加载套接字库失败!");
            return FALSE;
        }
        ......
    }
    
    /**********************************************************
    *主窗口的初始化函数
    *struct RECVPARAM 用于传递线程数据
    **********************************************************/
    struct RECVPARAM
    {
        HWND hWnd;
        SOCKET sock;
    };
    BOOL CFilePosterDlg::OnInitDialog()
    {
        ......
        //设定进度条的步进值 
        m_progress.SetStep(1);
        m_progress_r.SetStep(1);
        //创建套接字 
        m_socket=socket(AF_INET,SOCK_DGRAM,0);
        if(INVALID_SOCKET==m_socket)
        {
            MessageBox("套接字创建失败!");
            return FALSE;
        }
        SOCKADDR_IN addrSock;
        addrSock.sin_family=AF_INET;
        addrSock.sin_port=htons(6800);
        addrSock.sin_addr.S_un.S_addr=htonl(INADDR_ANY);
        //绑定套接字 
        int retval;
        retval=bind(m_socket, (SOCKADDR*)&addrSock,
        sizeof(SOCKADDR));
        if(SOCKET_ERROR==retval)
        {
            closesocket(m_socket);
            MessageBox("绑定失败!");
            return FALSE;
        }
        //产生一个用于接收数据的线程 
        struct RECVPARAM *pRecvParam=new RECVPARAM;
        pRecvParam->sock=m_socket;
        pRecvParam->hWnd=m_hWnd;
        HANDLE hThread=CreateThread(NULL, 0, RecvProc,
            (LPVOID)pRecvParam, 0, NULL);
        CloseHandle(hThread);
        ......
    }
    /**************************************************************
    *接收线程的回调函数
    *发送的数据块结构解析:
    *类别 字节号     内容
    *H      1           通信头,申请发送
    *H      2~257       文件名
    *H      258~...     文件大小
    *D      1           通信头,拒绝接收
    *R      1           通信头,同意接收
    *F      1           通信头,发送文件块
    *F      2~17        保留
    *F      18~...      文件数据块
    *E      1           通信头,文件尾发送
    *E      2~17        块大小
    *E      18~...      文件数据块
    **************************************************************/
    
    DWORD WINAPI CFilePosterDlg::RecvProc(LPVOID lpParameter)
    {
        SOCKET sock=((RECVPARAM*)lpParameter)->sock;
        HWND hWnd=((RECVPARAM*)lpParameter)->hWnd;
        delete lpParameter; 
    
        SOCKADDR_IN addrFrom;
        int len=sizeof(SOCKADDR);
        char recvBuf[0x112];        //256+17字节的接受缓冲数组 
        char fileName[0x100];       //256字节的文件名存储区 
        int retval, i;
        FILE* file = NULL;
    
        while(TRUE)
        {
            //接受UDP数据 
            retval=recvfrom(sock, recvBuf, 0x112, 0,
                (SOCKADDR*)&addrFrom, &len);
            
            if(SOCKET_ERROR == retval)
                break;
            //收到消息头为'R',即对方同意让你继续发送数据 
            if (recvBuf[0] == 'R')
            {
                char wParam = 'R';
                ::PostMessage(hWnd, WM_READY_TO_RECEIVE,
                    (WPARAM)&wParam, 0);
            }
            //收到消息头为'D',即对方拒绝让你继续发送数据 
            else if (recvBuf[0] == 'D')
            {
                char wParam = 'D';
                ::PostMessage(hWnd, WM_READY_TO_RECEIVE,
                    (WPARAM)&wParam, 0);
            }
            //收到消息头为'H',即对方申请给你发送信息,并送来文件的信息 
            else if (recvBuf[0] == 'H')
            {   
                //从收到的数据中提取文件名信息         
                for (i = 1; i <= 0x100 && recvBuf[i] != '/0'; i++)
                    fileName[i-1] = recvBuf[i];
                fileName[i-1] = '/0';
                //从收到的数据中提取文件大小信息 
                CString recvMsg;
                nFileSize = atoi(&recvBuf[0x101]);
                recvMsg.Format("收到来自于(%s)的文件:%s/n文件大小:%i字节/n是否接收?",
                inet_ntoa(addrFrom.sin_addr), fileName, nFileSize);
                //用消息框提示用户有人要发送文件 
                if (IDOK == AfxMessageBox(recvMsg, MB_OKCANCEL))
                {
                    //若用户同意接收,提供一个文件保存对话框用于设定保存的路径 
                    CFileDialog saveDlg(false, NULL, fileName);
                    if (IDOK == saveDlg.DoModal())
                    {
                        //创建一个文件用于复制接收的文件数据 
                        if (!(file = fopen(saveDlg.GetPathName(), "wb")))
                        {
                            AfxMessageBox("创建本地文件失败!");
                            continue;
                        }
                        char wParam = 'H';
                        ::PostMessage(hWnd, WM_READY_TO_RECEIVE, 
                            (WPARAM)&wParam, (LPARAM)&addrFrom);                    
                    }
                    else
                    {
                        char wParam = 'C';
                        ::PostMessage(hWnd, WM_READY_TO_RECEIVE, 
                            (WPARAM)&wParam, (LPARAM)&addrFrom);
                    }
                }
                else    //用户拒绝接收 
                {
                    char wParam = 'C';
                    ::PostMessage(hWnd, WM_READY_TO_RECEIVE, 
                        (WPARAM)&wParam, (LPARAM)&addrFrom);
                }
            }
            //收到的消息头为'F',即对方发来的是文件数据 
            else if (recvBuf[0] == 'F')
            {
                //将文件数据写入本地文件中 
                fwrite(&recvBuf[0x12], 1, 0x100, file);
                char wParam = 'F';
                ::PostMessage(hWnd, WM_READY_TO_RECEIVE, 
                    (WPARAM)&wParam, (LPARAM)&addrFrom);
            }
            //收到的消息头为'E',即对方发来最后一个数据块 
            else if (recvBuf[0] == 'E')
            {
                //获取数据块的大小 
                int bufSize = atoi(&recvBuf[1]);
                //将数据块写入本地文件,并关闭文件 
                fwrite(&recvBuf[0x12], 1, bufSize, file);
                fclose(file);
                char wParam = 'E';
                ::PostMessage(hWnd, WM_READY_TO_RECEIVE, 
                    (WPARAM)&wParam, (LPARAM)&addrFrom);
            }
            //收到未定义的数据头 
            else
                AfxMessageBox("传送数据过程中出现错误!");
        }
        return (DWORD)NULL;
    }
    /*************************************************************
    *按下发送键,发出文件信息的函数
    *************************************************************/
    void CFilePosterDlg::OnOK() 
    {
        if (m_posting)  //bool m_posting 表示程序是否正在发送文件 
        {
            MessageBox("数据发送中,请稍候再试。");
            return;
        }
    
        UpdateData();
        
        if (m_filePath == "")
        {
            MessageBox("请输入要发送的文件路径!");
            return;
        }
    
        if (m_IPAddr.IsBlank())
        {
            MessageBox("请添入接收者的IP地址。");
            return;
        }
    
        WIN32_FIND_DATA FindFileData;
    
        if (INVALID_HANDLE_VALUE == FindFirstFile(m_filePath, &FindFileData))
        {
            MessageBox("文件路径错误或文件不存在!/n请重新指定文件路径。");
            return;
        }
    
        DWORD dwIP;
        m_IPAddr.GetAddress(dwIP);
    
        SOCKADDR_IN addrTo;
        addrTo.sin_family=AF_INET;
        addrTo.sin_port=htons(6800);
        addrTo.sin_addr.S_un.S_addr=htonl(dwIP);
        //构建文件信息数据块 
        char sendBuf[0x112];
        int i;
        //消息头 
        sendBuf[0] = 'H';
        //文件名 
        for (i = 1; i <= 0x100 && FindFileData.cFileName[i-1] != '/0'; i++)
            sendBuf[i] = FindFileData.cFileName[i-1];
        sendBuf[i] = '/0';
        //文件大小 
        _itoa(FindFileData.nFileSizeLow, &sendBuf[0x101], 10);
        sendBuf[0x111] = '/0';
        //发送数据块 
        sendto(m_socket, sendBuf, 0x112, 0,
            (SOCKADDR*)&addrTo, sizeof(SOCKADDR));
        //打开文件,等待读取 
        if (!(m_file = fopen(m_filePath, "rb")))
        {
            MessageBox("读取文件失败!");
        }
        m_nSend = 0;        //已发送的块数 
        m_nFileSize_s = FindFileData.nFileSizeLow;  //文件大小 
        m_progress.SetRange(0, m_nFileSize_s/0x100+1);//设置发送进度条 
        m_posting = true;   //标明发送正进行 
    }
    /************************************************************
    *消息响应函数
    *响应自定义消息WM_READY_TO_RECEIVE
    ************************************************************/
    void CFilePosterDlg::OnReadyToRecv(WPARAM wParam,LPARAM lParam)
    {
        char sendBuf[0x112];
        DWORD dwIP;
        m_IPAddr.GetAddress(dwIP);
    
        SOCKADDR_IN addrTo;
        addrTo.sin_family=AF_INET;
        addrTo.sin_port=htons(6800);
        addrTo.sin_addr.S_un.S_addr=htonl(dwIP);
    
        int nRead;
    
        switch (*(char*)wParam)
        {
        //对方拒绝接收文件,关闭已打开的文件 
        case 'D':
            MessageBox("对方拒绝接受你发送的文件!");
            fclose(m_file);
            m_posting = false;
            break;
        //对方同意接收文件 
        case 'R':
            nRead = fread(&sendBuf[0x12], 1, 0x100, m_file);
            //读取的文件小于256字节,则读到文件尾 
            if (nRead < 0x100)
            {
                sendBuf[0] = 'E';
                _itoa(nRead, &sendBuf[1], 10);
                sendto(m_socket, sendBuf, nRead+0x12, 0,
                    (SOCKADDR*)&addrTo, sizeof(SOCKADDR));
                fclose(m_file);
                m_progress.SetPos(m_nFileSize_s/0x100+1);
                m_posting = false;
                m_send = "发送进度:(100%)";
                UpdateData(false);
                MessageBox("发送完毕!");
                m_progress.SetPos(0);
                m_send = "发送进度:";
                UpdateData(false);
            }
            //读到文件等于256字节,则文件还未读完 
            else
            {
                sendBuf[0] = 'F';
                sendto(m_socket, sendBuf, 0x112, 0,
                    (SOCKADDR*)&addrTo, sizeof(SOCKADDR));
                m_progress.StepIt();
                m_nSend++;
                m_send.Format("发送进度:(%.1f%%)",
                    (float)m_nSend/(m_nFileSize_s/0x100+1)*100);
                UpdateData(false);
            }
            break;
        //同意接收对方文件 
        case 'H':
            m_progress_r.SetRange(0, nFileSize/0x100+1);
            m_nRecv = 0;
        case 'F':
            sendto(m_socket, "R", 2, 0,
                    (SOCKADDR*)lParam, sizeof(SOCKADDR));
            m_progress_r.StepIt();
            m_nRecv++;
            m_recv.Format("接收进度:(%.1f%%)", (float)m_nRecv/(nFileSize/0x100+1)*100);
            UpdateData(false);
            break;
        //接受完毕,提示用户 
        case 'E':
            m_progress_r.SetPos(nFileSize/0x100+1);
            m_recv = "接收进度:(100%)";
            UpdateData(false);
            MessageBox("接收完毕!");
            m_recv = "接收进度:";
            m_progress_r.SetPos(0);
            UpdateData(false);
            break;
        //拒绝接收,通知对方 
        case 'C':
            sendto(m_socket, "D", 2, 0,
                    (SOCKADDR*)lParam, sizeof(SOCKADDR));
            break;
        }
    }
    

     

     

     


  • 相关阅读:
    实验 3:Mininet 实验——测量路径的损耗率
    软件工程——第一次作业:自我介绍
    实验 2:Mininet 实验——拓扑的命令脚本生成
    实验1:Mininet源码安装和可视化拓扑工具
    软工作业第一次
    031802417 林宇杰
    导航控制器
    Xcode 简易计算器 (修改版)
    WSY的博客向导
    2021年8月11日训练笔记
  • 原文地址:https://www.cnblogs.com/jinsedemaitian/p/5589161.html
Copyright © 2011-2022 走看看