TCP协议实现文件传输

TCP协议实现文件传输

C++基础 2009-11-02 20:38:06 阅读159 评论0 字号:

 

 使用TCP协议实现传输文件

    程序分为发送端和接收端。首先在传输文件数据之前,发送端会把将装有文件名称和文件长度等

信息的数据包发送至接收端。接收端收到文件名称和文件长度信息后会创建好空白文件。接着开始传输

文件数据。下面介绍实现功能的主要过程:

1.创建套接字、绑定、监听、连接、接受连接

//创建TCP协议的套接字

    m_Socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

    if(SOCKET_ERROR == m_Socket)

        AfxMessageBox("Create Socket Error! ", 0, 0);

//绑定与监听

    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!", 0, 0);

//连接

    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, 1, 1, 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(0, 0, SendDataThrad, 0, 0, &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;

}

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值