zoukankan      html  css  js  c++  java
  • TCP协议实现文件传输

     使用TCP协议实现传输文件
        程序分为发送端和接收端。首先在传输文件数据之前,发送端会把将装有文件名称和文件长度等
    信息的数据包发送至接收端。接收端收到文件名称和文件长度信息后会创建好空白文件。接着开始传输
    文件数据。下面介绍实现功能的主要过程:

    1.创建套接字、绑定、监听、连接、接受连接
    //创建TCP协议的套接字
        m_Socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
        
    if(SOCKET_ERROR == m_Socket)
            AfxMessageBox(
    "Create Socket Error! "00);

    //绑定与监听
        SOCKADDR_IN   addrSrv;   
        addrSrv.sin_addr.s_addr 
    = inet_addr(sIP);
        addrSrv.sin_family   
    =   AF_INET;   
        addrSrv.sin_port   
    =   htons(Port);   
        
    int   ret   =   bind(m_Socket,   (SOCKADDR   *)&addrSrv,   sizeof(SOCKADDR));   
        
    if(ret==SOCKET_ERROR)   
            AfxMessageBox(
    "Bind Socket Error!"00);

    //连接
        SOCKADDR_IN ServerAddr;
        ServerAddr.sin_addr.s_addr 
    = inet_addr(ServerAddr_in);
        ServerAddr.sin_family 
    = AF_INET;
        ServerAddr.sin_port 
    = htons(ServerPort);
        
    int Result = connect(m_Socket, (struct sockaddr*)&ServerAddr, sizeof(struct sockaddr));
        
    if(SOCKET_ERROR == Result)
            AfxMessageBox(
    "Connet Failed!");

    //接受连接
        SOCKADDR_IN ClientAddr;
        
    int len = sizeof(SOCKADDR_IN);
        SOCKET ClientSock 
    = accept(m_Socket, (struct sockaddr*)&ClientAddr, &len);
        
    if(SOCKET_ERROR == ClientSock)
            AfxMessageBox(
    "Accept Failed!");

    2.声明宏和结构体
    声明套接字缓冲区和一次发送文件数据的缓冲区大小
    #define SOCKET_BUFF 80000    //套接字缓冲区大小
    #define PACK_BUFF 50000        //数据包缓冲区大小


    声明文件I
    /O缓冲区和最大文件路径长度
    #define FILE_NAME_MAX 100       //文件路径最大长度
    #define FILE_IO_BUFF PACK_BUFF    //文件IO缓冲区    


    //文件信息
    typedef struct _FileInfor    
    {
        u_long ulFileLen;
        
    char sFileName[ FILE_NAME_MAX ];
    }_FileInfor;

    //数据包
    typedef struct _DataPack
    {
        
    char cType;        //'D'为数据  'M'为文件信息
        int nPackLen;
        
    char sContent[ PACK_BUFF ];            //数据包缓冲区
        u_long nPosition;                //数据在文件中的位置
        int nContentLen;                //数据字节数
        _FileInfor    FileInfor;        //文件信息
    }_DataPack;



    3.发送端
    //发送线程需要的全局变量
    char sPath[FILE_NAME_MAX];        //文件地址
    u_long FileByteCount;            //文件大小
    SOCKET ClientSocket;            //


    (
    1)设置套接字发送缓冲区大小,在32位Windows XP环境下,系统为每个套接字分配的默认发送数据缓
    冲区为8192字节。由于传输的文件很大,可能几十兆,或者更大。那么系统为每个套接字分配的默认
    缓冲区显然过小。为此在创建套接字之后,需要修改套接字发送数据缓冲尺寸。在这里我修改为80k,
    差不多可以够用了。
        
    //设置套接字发送缓冲区
        int nBuf = SOCKET_BUFF;
        
    int nBufLen = sizeof(nBuf);
        
    int nRe = setsockopt(ClientSock, SOL_SOCKET, SO_SNDBUF, (char*)&nBuf, nBufLen);
        
    if(SOCKET_ERROR == nRe)
            AfxMessageBox(
    "setsockopt error!");    
        
    //检查缓冲区是否设置成功
        nRe = getsockopt(ClientSock, SOL_SOCKET, SO_SNDBUF, (char*)&nBuf, &nBufLen);
        
    if(SOCKET_BUFF != nBuf)
            AfxMessageBox(
    "检查缓冲区:setsockopt error!");

    (
    2)测量文件大小并发送文件大小和名称给客户端

        首先使用C库函数对源文件进行测量
        
    //得到文件地址
        LPTSTR lpPath =     m_sPath.GetBuffer(    m_sPath.GetLength ());
        
    //打开文件
        FILE *File = fopen(lpPath, "rb"); 
        
    if(NULL == File)
            AfxMessageBox(
    "打开文件失败!");
        
    //测量文件大小
        char Buff[PACK_BUFF];
        u_long ulFaceReadByte;
        FileByteCount 
    = 0;
        fseek(File, 
    0, SEEK_SET);
        
    while(!feof(File))
        {
            ulFaceReadByte 
    = fread(Buff, 11, File);
            FileByteCount 
    += ulFaceReadByte;
        }
        
    //关闭文件
        int nRe = fclose(File);
        
    if(nRe)
            AfxMessageBox(
    "关闭文件失败!");
            
        此时以获取源文件的长度,我们将文件长度和文件名称放到数据包中,设置数据包为
    'M'类型。
        
    //打包
        _DataPack Pack;
        Pack.cType 
    = 'M';
        Pack.nPackLen 
    = sizeof(Pack);
        
    //取得文件名
        ZeroMemory(Pack.FileInfor.sFileName, FILE_NAME_MAX);
        GetFIieNameFromPath(lpPath, Pack.FileInfor.sFileName);
        Pack.FileInfor.ulFileLen 
    = FileByteCount;
        
        接着使用send()将打包完成的数据包发送给接收端,把发送线程的全局变量初始化,并创建发送线
    程,文件数据将由发送线程负责发送
        
    //发送数据包 文件大小和名称
        nRe = send(m_ClientSockFd.fd_array[0], (char*)&Pack, Pack.nPackLen, 0);
        
    if(SOCKET_ERROR == nRe)
                AfxMessageBox(
    "Send File Size Failed!");
        
    //线程准备全局变量
        strcpy(sPath, m_sPath);
        ClientSocket 
    = m_ClientSockFd.fd_array[0];
        
    //启动线程发送文件
        DWORD ID;
        m_hSendThread 
    = CreateThread(00, SendDataThrad, 00&ID);


    (
    3)发送文件数据线程。先打开源文件,为了一次读取大量数据将文件缓冲区设置为FILE_IO_BUFF大小,
    然后将读取的数据打包并发送。这样不断地读、不断地发送,直到将整个文件发送完为止。

    DWORD __stdcall CServerDlg::SendDataThrad(LPVOID LpP)
    {
        
    //打开文件
        FILE *File = fopen(sPath, "rb");
        
    if(NULL == File)
        {
            AfxMessageBox(
    "SendDataThrad中打开文件失败!");
            
    return 1;
        }
        
    //设置文件缓冲区
        int nBuff = FILE_IO_BUFF;
        
    if(setvbuf(File, (char*)&nBuff, _IOFBF, sizeof(nBuff)))
            AfxMessageBox(
    "设置文件缓冲区失败!");
        
    //读取文件数据并发送
        u_long ulFlagCount = 0;            //记录读了多少数据
        u_long FaceReadByte = 0;    //一次实际读取的字节数
        char sBuff[PACK_BUFF];    
        ZeroMemory(sBuff, PACK_BUFF);
        fseek(File, 
    0, SEEK_SET);
        
    while (!feof(File))
        {
            FaceReadByte 
    = fread(sBuff, 1, PACK_BUFF, File);
            
    //打包
            _DataPack DataPack;
            DataPack.cType 
    = 'D';
            DataPack.nPackLen 
    = sizeof(DataPack);
            DataPack.nContentLen 
    = FaceReadByte;
            CopyMemory(DataPack.sContent, sBuff, FaceReadByte);
            DataPack.nPosition 
    = ulFlagCount;
            
    //发送
            int nResult = send(ClientSocket, (char*)&DataPack, DataPack.nPackLen, 0);
            
    if (SOCKET_ERROR == nResult)
            {
                AfxMessageBox(
    "SendDataThrad中发送数据失败!");
            }
    else
                ulFlagCount 
    += FaceReadByte;        //记录发送字节数
        }
        AfxMessageBox(
    "发送结束");
        
    //关闭
        int nRe = fclose(File);
        
    if(nRe)
            AfxMessageBox(
    "SendDataThrad中关闭文件失败!");
        
    return 0;
    }

    4.接收端
    //接收线程用的全局变量
    _FileInfor FileInfor;        //文件信息
    u_long ulWriteByte;            //记录总共写入的字节
    char lpPath[FILE_NAME_MAX];    //文件路径
    char sFilePathAndName[FILE_NAME_MAX];        //完整的文件路径

    (
    1)设置套接字接收缓冲区大小。
    //设置套接字接收缓冲区
        int nBuf = SOCKET_BUFF;
        
    int nBufLen = sizeof(nBuf);
        SOCKET ClientSock 
    = m_Sock.GetSocket();
        
    int nRe = setsockopt(ClientSock, SOL_SOCKET, SO_RCVBUF, (char*)&nBuf, nBufLen);
        
    if(SOCKET_ERROR == nRe)
                AfxMessageBox(
    "setsockopt error!");
        
    //检查缓冲区是否设置成功
        nRe = getsockopt(ClientSock, SOL_SOCKET, SO_RCVBUF, (char*)&nBuf, &nBufLen);
        
    if(SOCKET_BUFF != nBuf)
            AfxMessageBox(
    "检查缓冲区:setsockopt error!");
            
    (
    2)接收文件信息和文件数据线程。先判断数据包是属于文件信息还是文件类型,如果是文件信息就根
    据信息创建空文件,如果是文件数据就将数据已追加的方式写进目的文件中。

    DWORD __stdcall CClientDlg::ReceiveDataPro(LPVOID LpP)
    {
        CSocket_Win32 
    *pCSock = (CSocket_Win32*)LpP;
        u_long ulWriteByteCount 
    = 0;
        
    while (1)
        {
            _DataPack DataPack;
            ZeroMemory(
    &DataPack, sizeof(DataPack));
            
    if(!(*pCSock).Receive(&DataPack, sizeof(DataPack)))
            {
                AfxMessageBox(
    "Receive DataPack Failed!");
            }
            
    //判断数据包类型
            if('M' == DataPack.cType)    //包为文件信息
            {
                
    //接收文件信息
                FileInfor.ulFileLen = DataPack.FileInfor.ulFileLen;  //获取文件长度
                strcpy(FileInfor.sFileName, DataPack.FileInfor.sFileName); //获取文件名称
                
    //得到文件目录
                char sFilePath[FILE_NAME_MAX];
                ZeroMemory(sFilePath, FILE_NAME_MAX);
                strcpy(sFilePath, lpPath);
                strcat(sFilePath, FileInfor.sFileName);
                strcat(sFilePathAndName, sFilePath);    
    //保存完整的文件路径
                
    //创建文件
                SECURITY_ATTRIBUTES attr;
                attr.nLength 
    = FileInfor.ulFileLen;
                attr.lpSecurityDescriptor 
    = NULL;
                HANDLE hRe 
    = CreateFile(sFilePath, GENERIC_WRITE, FILE_SHARE_WRITE, &attr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
                
    if(INVALID_HANDLE_VALUE == hRe)
                    AfxMessageBox(
    "创建文件失败!");
                
    bool bRe = ::CloseHandle(hRe);
                
    //可以开始接受文件
                bIsStartReceive = true;
            }
    else if('D' == DataPack.cType)        //包为文件数据
            {
                
    //打开文件
                char sPath[FILE_NAME_MAX];
                strcpy(sPath, sFilePathAndName);
                FILE 
    *File = fopen(sPath, "ab");
                
    if(0 == File)
                    AfxMessageBox(
    "打开文件失败!");
                
    //设置文件缓冲区
                int nBuff = FILE_IO_BUFF;
                
    if(setvbuf(File, (char*)&nBuff, _IOFBF, sizeof(nBuff)))
                    AfxMessageBox(
    "设置文件缓冲区失败!");
                
    //定位文件
                u_long nPosition = DataPack.nPosition;
                
    int nRe = fseek(File, nPosition, SEEK_SET);
                
    if(nRe)
                    AfxMessageBox(
    "SendDataThrad中定位失败!");
                
    //写文件
                u_long nNumberOfBytesWritten = fwrite(&DataPack.sContent, 1, DataPack.nContentLen, File);
                
    if(DataPack.nContentLen != nNumberOfBytesWritten)
                    AfxMessageBox(
    "写文件失败!");
                
    else
                {
                    ulWriteByteCount 
    += nNumberOfBytesWritten;
                }
                fflush(File);                                
    //清除文件缓冲区
                
    //关闭文件
                nRe = fclose(File);
                
    if(nRe)
                    AfxMessageBox(
    "关闭文件失败!");

                
    if(ulWriteByteCount >= FileInfor.ulFileLen)
                {
                    AfxMessageBox(
    "接收结束");
                    
    break;
                }    
            }
        }
        
    return 0;
    }

    兴趣是学习的动力。
  • 相关阅读:
    PHP遍历数组元素
    In PHP5, what is the difference between using self and $this? When is each appropriate?
    了解如何构建 Metro 样式的应用程序
    HTML5在路上
    meta 之 viewport
    dede数据库连接文件
    windows 环境安装wamp软件实现php开发环境
    域名空间那些事
    Javascript编程风格
    apache 服务器修改网站默认首页
  • 原文地址:https://www.cnblogs.com/yunboy4/p/1538865.html
Copyright © 2011-2022 走看看