MFC 大文件传输

(>^ω^<)喵上树 2014-06-04 09:32:51
使用TCP SOCKET传输文件和字符消息。
在传输文件时文件过大程序会死。传不完整。

一个文件2048*10字节。
我的发送缓冲区2048字节。前面加上文件名和标记位"FILE"。
这样至少要send 11次。
这样有点浪费资源。还不稳定。有没有什么好的方法??
...全文
346 22 打赏 收藏 转发到动态 举报
写回复
用AI写文章
22 条回复
切换为时间正序
请发表友善的回复…
发表回复
赵4老师 2014-06-12
  • 打赏
  • 举报
回复
结束或退出时Close
赵4老师 2014-06-12
  • 打赏
  • 举报
回复
引用 20 楼 u012889234 的回复:
客户端recv 数据,写入文件。

int ret = recv(tcpsock, tmp, 1024*64, 0);
        if ( ret < 0)
        {
            break;
        }
///////////////////////////////////////////////////////////////
else if(strstr(tmp, "FILE") != NULL)
            {
                strcpy(buf, strtok(tmp, "FILE"));         
                sscanf(buf, "%llu%s", &file_size, file_name);
                strcat(file_path,file_name);
                /*AfxMessageBox(file_path);*/
                length = strlen(buf) + 4;
                if(re_file.Open(file_path,CFile::modeCreate|CFile::modeNoTruncate|CFile::modeWrite) == FALSE)
                {
                    AfxMessageBox("打开文件失败!");
                } 
                re_file.SeekToEnd();
                re_file.Write((tmp + length), (ret - length));
                recved_size += (ret - length);
                re_file.Close();
                if(recved_size == file_size)
                {          
                    CString str1;
                    str_commom_info.Format("文件名    :%s\r\n",   file_name);
                    str1.Format(   "文件大小 :%llu\r\n", file_size);
                    str_commom_info += str1;
                    str_commom_info += "接收成功!";
                    GetDlgItem(IDC_COMMOM_INFO)->SetWindowTextA(str_commom_info);
                }
            }
}
为什么有时候会是打开文件失败? 怎么做 能让它只打开一次,指定写完就关闭文件。
初始化时Open 写数据时SeekToEnd,Write,Flush 结束或退出时CLose
(>^ω^<)喵上树 2014-06-11
  • 打赏
  • 举报
回复
客户端recv 数据,写入文件。

int ret = recv(tcpsock, tmp, 1024*64, 0);
        if ( ret < 0)
        {
            break;
        }
///////////////////////////////////////////////////////////////
else if(strstr(tmp, "FILE") != NULL)
            {
                strcpy(buf, strtok(tmp, "FILE"));         
                sscanf(buf, "%llu%s", &file_size, file_name);
                strcat(file_path,file_name);
                /*AfxMessageBox(file_path);*/
                length = strlen(buf) + 4;
                if(re_file.Open(file_path,CFile::modeCreate|CFile::modeNoTruncate|CFile::modeWrite) == FALSE)
                {
                    AfxMessageBox("打开文件失败!");
                } 
                re_file.SeekToEnd();
                re_file.Write((tmp + length), (ret - length));
                recved_size += (ret - length);
                re_file.Close();
                if(recved_size == file_size)
                {          
                    CString str1;
                    str_commom_info.Format("文件名    :%s\r\n",   file_name);
                    str1.Format(   "文件大小 :%llu\r\n", file_size);
                    str_commom_info += str1;
                    str_commom_info += "接收成功!";
                    GetDlgItem(IDC_COMMOM_INFO)->SetWindowTextA(str_commom_info);
                }
            }
}
为什么有时候会是打开文件失败? 怎么做 能让它只打开一次,指定写完就关闭文件。
(>^ω^<)喵上树 2014-06-11
  • 打赏
  • 举报
回复
问题在这里。。

 ULONGLONG  endp = my_file.SeekToEnd();
文件指针移向 了文件尾, 应该加一句

my_file.SeektoBegin();
(>^ω^<)喵上树 2014-06-10
  • 打赏
  • 举报
回复
引用 16 楼 shenyi0106 的回复:
[quote=引用 15 楼 u012889234 的回复:] [quote=引用 14 楼 shenyi0106 的回复:] [quote=引用 12 楼 u012889234 的回复:] [quote=引用 11 楼 shenyi0106 的回复:] 缓冲区可调啊,没看到是宏定义么?也可以改成变量,只要你想那么做,就能那么做。 而且,如果用这种方式的话,那么你的文件长度,文件名已经文件数据为什么还要分开发呢? 你应该还没理解我这个文件头的作用。。。。。。
不是。。 在程序运行时你无法判断是文件数据包,还是其他的。。怎么调。。。他们都是一个socket通信。[/quote] 包头不一样啊,比如你在设计一个如下的包头: struct packet_header { int iCmd; //命令 int iPktLen; //包长度 }; 这样采用packet_header + file_header + data的方式,就完美了。通过包头中的命令来判断是什么数据,然后在分离后面的数据头,在分离数据部分。 TCP/IP协议就是这么干的,不要指望一种结构能包含千万种变化,那是不现实的[/quote] 嗯。这个比较好!!!!!赞。。。。赞。。。。

my_file.Open(file_path, CFile::modeRead) 
file_size = my_file.GetLength();
 ULONGLONG  endp = my_file.SeekToEnd();
 char * pszDate = (char *)malloc(MAX_BUF_SIZE);
 while(my_file.GetPosition() <= endp)
 { 
        memset(pszDate, 0, MAX_BUF_SIZE);
        tFhdr * pFhdr = (tFhdr *)pszDate;
        strcpy(pFhdr->filename, file_name.GetBuffer());     //文件名
        pFhdr->totallen = file_size;                        //文件总长度
        char * pDateBuf = (char *)pFhdr->date;              //指向数据部分
        if (file_size - total < MAX_BUF_SIZE - sizeof(tFhdr))
        {
            my_file.Read(pDateBuf, file_size - total);
            cur_len = send(tcpsock, pDateBuf, (file_size - total), 0);
        }
 }
//这里Read后pDateBuf还是空,怎么回事啊!


[/quote] 注意!!!! struct file_header结构必须是字节对齐的(编译器默认是8字节对齐),否则使用0长数组就会出现问题。 可以去google 一下0长数组,看看相关介绍。 还有,你确定你读文件传递的参数正确?file_size - total 是不是小于0了?[/quote] 不是。。好像是

  char * pDateBuf = (char *)pFhdr->date;              //指向数据部分
        if (file_size - total < MAX_BUF_SIZE - sizeof(tFhdr))
        {
            my_file.Read(pDateBuf, file_size - total);
            cur_len = send(tcpsock, pDateBuf, (file_size - total), 0);
            AfxMessageBox(pDateBuf);
        }
// char * pDateBuf = (char *)pFhdr->date;              //指向数据部分
//  这样好像不能往pFhdr的date中存数据。
//  但前面分配了内存啊??
KevinHo 2014-06-10
  • 打赏
  • 举报
回复
应该格式化数据包,我感觉数据包也应该加头,接收方需要对数据头进行解析
shenyi0106 2014-06-09
  • 打赏
  • 举报
回复
引用 15 楼 u012889234 的回复:
[quote=引用 14 楼 shenyi0106 的回复:] [quote=引用 12 楼 u012889234 的回复:] [quote=引用 11 楼 shenyi0106 的回复:] 缓冲区可调啊,没看到是宏定义么?也可以改成变量,只要你想那么做,就能那么做。 而且,如果用这种方式的话,那么你的文件长度,文件名已经文件数据为什么还要分开发呢? 你应该还没理解我这个文件头的作用。。。。。。
不是。。 在程序运行时你无法判断是文件数据包,还是其他的。。怎么调。。。他们都是一个socket通信。[/quote] 包头不一样啊,比如你在设计一个如下的包头: struct packet_header { int iCmd; //命令 int iPktLen; //包长度 }; 这样采用packet_header + file_header + data的方式,就完美了。通过包头中的命令来判断是什么数据,然后在分离后面的数据头,在分离数据部分。 TCP/IP协议就是这么干的,不要指望一种结构能包含千万种变化,那是不现实的[/quote] 嗯。这个比较好!!!!!赞。。。。赞。。。。

my_file.Open(file_path, CFile::modeRead) 
file_size = my_file.GetLength();
 ULONGLONG  endp = my_file.SeekToEnd();
 char * pszDate = (char *)malloc(MAX_BUF_SIZE);
 while(my_file.GetPosition() <= endp)
 { 
        memset(pszDate, 0, MAX_BUF_SIZE);
        tFhdr * pFhdr = (tFhdr *)pszDate;
        strcpy(pFhdr->filename, file_name.GetBuffer());     //文件名
        pFhdr->totallen = file_size;                        //文件总长度
        char * pDateBuf = (char *)pFhdr->date;              //指向数据部分
        if (file_size - total < MAX_BUF_SIZE - sizeof(tFhdr))
        {
            my_file.Read(pDateBuf, file_size - total);
            cur_len = send(tcpsock, pDateBuf, (file_size - total), 0);
        }
 }
//这里Read后pDateBuf还是空,怎么回事啊!


[/quote] 注意!!!! struct file_header结构必须是字节对齐的(编译器默认是8字节对齐),否则使用0长数组就会出现问题。 可以去google 一下0长数组,看看相关介绍。 还有,你确定你读文件传递的参数正确?file_size - total 是不是小于0了?
(>^ω^<)喵上树 2014-06-09
  • 打赏
  • 举报
回复
引用 14 楼 shenyi0106 的回复:
[quote=引用 12 楼 u012889234 的回复:] [quote=引用 11 楼 shenyi0106 的回复:] 缓冲区可调啊,没看到是宏定义么?也可以改成变量,只要你想那么做,就能那么做。 而且,如果用这种方式的话,那么你的文件长度,文件名已经文件数据为什么还要分开发呢? 你应该还没理解我这个文件头的作用。。。。。。
不是。。 在程序运行时你无法判断是文件数据包,还是其他的。。怎么调。。。他们都是一个socket通信。[/quote] 包头不一样啊,比如你在设计一个如下的包头: struct packet_header { int iCmd; //命令 int iPktLen; //包长度 }; 这样采用packet_header + file_header + data的方式,就完美了。通过包头中的命令来判断是什么数据,然后在分离后面的数据头,在分离数据部分。 TCP/IP协议就是这么干的,不要指望一种结构能包含千万种变化,那是不现实的[/quote] 嗯。这个比较好!!!!!赞。。。。赞。。。。

my_file.Open(file_path, CFile::modeRead) 
file_size = my_file.GetLength();
 ULONGLONG  endp = my_file.SeekToEnd();
 char * pszDate = (char *)malloc(MAX_BUF_SIZE);
 while(my_file.GetPosition() <= endp)
 { 
        memset(pszDate, 0, MAX_BUF_SIZE);
        tFhdr * pFhdr = (tFhdr *)pszDate;
        strcpy(pFhdr->filename, file_name.GetBuffer());     //文件名
        pFhdr->totallen = file_size;                        //文件总长度
        char * pDateBuf = (char *)pFhdr->date;              //指向数据部分
        if (file_size - total < MAX_BUF_SIZE - sizeof(tFhdr))
        {
            my_file.Read(pDateBuf, file_size - total);
            cur_len = send(tcpsock, pDateBuf, (file_size - total), 0);
        }
 }
//这里Read后pDateBuf还是空,怎么回事啊!


shenyi0106 2014-06-09
  • 打赏
  • 举报
回复
引用 12 楼 u012889234 的回复:
[quote=引用 11 楼 shenyi0106 的回复:] 缓冲区可调啊,没看到是宏定义么?也可以改成变量,只要你想那么做,就能那么做。 而且,如果用这种方式的话,那么你的文件长度,文件名已经文件数据为什么还要分开发呢? 你应该还没理解我这个文件头的作用。。。。。。
不是。。 在程序运行时你无法判断是文件数据包,还是其他的。。怎么调。。。他们都是一个socket通信。[/quote] 包头不一样啊,比如你在设计一个如下的包头: struct packet_header { int iCmd; //命令 int iPktLen; //包长度 }; 这样采用packet_header + file_header + data的方式,就完美了。通过包头中的命令来判断是什么数据,然后在分离后面的数据头,在分离数据部分。 TCP/IP协议就是这么干的,不要指望一种结构能包含千万种变化,那是不现实的
HUSTYZHY 2014-06-09
  • 打赏
  • 举报
回复
通常采用定长方式,每个字段都固定长度,长度不足时填充空格 简单方便 有时也采用准TLV的方式,每个值由3部分组成:一个tag、一个长度、内容,这样值就不用采用固定的长度来存放,节省空间 如果采用结束符标识,那必须保证结束符的唯一性了 不管采用哪种方式,一个表示报文总长度的字段还是必须的,据以读取报文。
(>^ω^<)喵上树 2014-06-09
  • 打赏
  • 举报
回复
引用 11 楼 shenyi0106 的回复:
缓冲区可调啊,没看到是宏定义么?也可以改成变量,只要你想那么做,就能那么做。 而且,如果用这种方式的话,那么你的文件长度,文件名已经文件数据为什么还要分开发呢? 你应该还没理解我这个文件头的作用。。。。。。
不是。。 在程序运行时你无法判断是文件数据包,还是其他的。。怎么调。。。他们都是一个socket通信。
shenyi0106 2014-06-09
  • 打赏
  • 举报
回复
缓冲区可调啊,没看到是宏定义么?也可以改成变量,只要你想那么做,就能那么做。 而且,如果用这种方式的话,那么你的文件长度,文件名已经文件数据为什么还要分开发呢? 你应该还没理解我这个文件头的作用。。。。。。
(>^ω^<)喵上树 2014-06-09
  • 打赏
  • 举报
回复
引用 9 楼 shenyi0106 的回复:

#define MAX_BUFFER_SIZE 64 * 1024
//File Transmit Header
struct file_head
{
	char              filename[32];             //文件名(发送一个文件过程中,该字段固定不变)
	UINT64        totallen;                      //文件总长度(发送一个文件过程中,该字段固定不变)
	UINT64        datalen;                     //数据长度
	UINT64        offset;                         //数据偏移
	UINT64        checksum;                //效验和(针对TCP,这个字段也可以不用)
	char              data[0];                      //数据缓冲区(利用0长数组来表示数据缓冲区)
};

char *pszData = (char*)malloc(MAX_BUFFER_SIZE );
ZeroMemory(pszData, 0, MAX_BUFFER_SIZE );

struct file_head *pFileHeader = (struct file_head*)pszData;
strcpy(pFileHeader ->filename, "你的文件名.txt");
pFileHeader ->totallen = 文件总长度;

char *pDataBuf = pFileHeader  + 1;
while (xxxx)
{
     pFileHeader->datalen = 本次数据长度;
     pFileHeader->offset    = 本次数据在整个文件中的位置;
     memcpy(pFileHeader->data, 文件数据, pFileHeader->datalen);
    
    send(pszData , MAX_BUFFER_SIZE);
}
接收到数据以后,直接根据文件名找到文件句柄,然后Seek到offset处,直接write数据结束。 数据传输中间,不需要额外管理;这是我的大文件传输方式。仅供参考
那我每次recv 的缓冲区也要是 64 * 1024啊! 这对于前两种是不是有点浪费!!!
shenyi0106 2014-06-09
  • 打赏
  • 举报
回复

#define MAX_BUFFER_SIZE 64 * 1024
//File Transmit Header
struct file_head
{
	char              filename[32];             //文件名(发送一个文件过程中,该字段固定不变)
	UINT64        totallen;                      //文件总长度(发送一个文件过程中,该字段固定不变)
	UINT64        datalen;                     //数据长度
	UINT64        offset;                         //数据偏移
	UINT64        checksum;                //效验和(针对TCP,这个字段也可以不用)
	char              data[0];                      //数据缓冲区(利用0长数组来表示数据缓冲区)
};

char *pszData = (char*)malloc(MAX_BUFFER_SIZE );
ZeroMemory(pszData, 0, MAX_BUFFER_SIZE );

struct file_head *pFileHeader = (struct file_head*)pszData;
strcpy(pFileHeader ->filename, "你的文件名.txt");
pFileHeader ->totallen = 文件总长度;

char *pDataBuf = pFileHeader  + 1;
while (xxxx)
{
     pFileHeader->datalen = 本次数据长度;
     pFileHeader->offset    = 本次数据在整个文件中的位置;
     memcpy(pFileHeader->data, 文件数据, pFileHeader->datalen);
    
    send(pszData , MAX_BUFFER_SIZE);
}
接收到数据以后,直接根据文件名找到文件句柄,然后Seek到offset处,直接write数据结束。 数据传输中间,不需要额外管理;这是我的大文件传输方式。仅供参考
(>^ω^<)喵上树 2014-06-09
  • 打赏
  • 举报
回复
引用 2 楼 zhao4zhong1 的回复:
_lseeki64 不知道有多少前人掉在TCP Socket send(人多)send(病少)send(财富) recv(人多病)recv(少财富) 陷阱里面啊! http://bbs.csdn.net/topics/380167545
看了下你的这个链接。。

 
           
             memset(buf, 0, sizeof(buf));
		len = recv(wParam, buf, 2048, 0);
		if (len < 0)  break;

          if(buf[1]==0x4d && buf[2]==0x52 && buf[3]==0x44)           
        {
          phead = (tMrHead *)buf;
         if(phead->type == 2)   //reflect 回应
           {
                pdata = (tGMrData *)phead->data;
               inet_ntop(AF_INET, &pdata->refex_addr, refex_addr, sizeof(refex_addr));
                str_mrdata.Format(_T("Reflex ip:     %s\r\nReflex port:  %d"),refex_addr, ntohs(pdata->refex_port));                                                 
                GetDlgItem(IDC_MR_INFO)->SetWindowTextA(str_mrdata);              
            }
        }
        else 
        {

            if(buf[0] == 'G' && buf[1] =='N')    //文本标记    用于传信息
            {
               str_mydata.Format("%s", buf + 2);
               GetDlgItem(IDC_MY_STRING)->SetWindowTextA(str_mydata);
            }
           else if((ptr = strstr(buf, "FILE")) != NULL)  //文件标记
           {
                 //.....
           }
要处理这几种情况。 我开始在发送数据的时候是没错send 1024字节。 长度 + 文件名 + FILE(标记) + 文件数据 --------- 没次都这样的结构。直到数据发完。 后来看了一个别人写的是 先发数据长度 (一次) 再发文件名(一次) 最后是文件内容 在接收的时候用的 int flag = 1; switch(lParam) { case 1: break; //接收文件长度 case 2: break; //接收文件名 case 3: break; //接收文件内容 } 用这中方法实现我的所有需求,,有点问题,怎么可以兼容。。 这两种方法哪个好。。。 有没有更好的方法,,可以穿几G的文件,不卡的方法。。
(>^ω^<)喵上树 2014-06-09
  • 打赏
  • 举报
回复

                CString info = "";
                info.Format(_T("文件名:%s\r\n文件大小:%d\r\n接收成功!"), file_name, file_size);
                GetDlgItem(IDC_COMMOM_INFO)->SetWindowTextA(info);

               /*文件名:gnLog.h文件大小:2095接收成功!*/ 
为什么不换行,,我使用的多字节字符集。
(>^ω^<)喵上树 2014-06-09
  • 打赏
  • 举报
回复



这是什么原因?
版主大哥 2014-06-04
  • 打赏
  • 举报
回复
发送端:打开文件,读入内存,组装自定义的协议包(加上开始或结束标记),发送 接收端:收到包,查看标记位是否结束,不是结束就缓存起来并且发送告知对端已经接收,否则将缓存保存到文件
版主大哥 2014-06-04
  • 打赏
  • 举报
回复
引用 4 楼 lhl1158612009 的回复:
[quote=引用 2 楼 zhao4zhong1 的回复:] _lseeki64 不知道有多少前人掉在TCP Socket send(人多)send(病少)send(财富) recv(人多病)recv(少财富) 陷阱里面啊! http://bbs.csdn.net/topics/380167545
赵老师天天泡 CSDN 吗?每次都看到你。特别好奇你职业是啥。能花这么时间在 CSDN 上。 不耽搁工作吗?[/quote] 他是大神,你懂的~~~ 见过大神一直在敲代码吗? 一直敲代码是我们这些码农
大_猫 2014-06-04
  • 打赏
  • 举报
回复
引用 2 楼 zhao4zhong1 的回复:
_lseeki64 不知道有多少前人掉在TCP Socket send(人多)send(病少)send(财富) recv(人多病)recv(少财富) 陷阱里面啊! http://bbs.csdn.net/topics/380167545
赵老师天天泡 CSDN 吗?每次都看到你。特别好奇你职业是啥。能花这么时间在 CSDN 上。 不耽搁工作吗?
加载更多回复(2)

18,356

社区成员

发帖
与我相关
我的任务
社区描述
VC/MFC 网络编程
c++c语言开发语言 技术论坛(原bbs)
社区管理员
  • 网络编程
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧