首页
社区
课程
招聘
[求助]关于SOCKET发送文件大小不一的问题[已经附上源码]
发表于: 2008-6-22 22:24 8818

[求助]关于SOCKET发送文件大小不一的问题[已经附上源码]

2008-6-22 22:24
8818
最近在写一个程序,是在线发送和接收文件的,和“飞鸽传书”差不多吧。
由于自己只有一台电脑,所以只好又当服务器又当客户端,这样的情况下,传什么文件都没问题。
结果昨天有空拿到局域网上去尝试了一下,发现传有的文件没问题,传有的文件问题很大,比如说30KB的文件传过来变成了100多MB。
采取的是MFC的CSocket来编程的,不是直接调用WIN32 API,数据流形式。
【1】
记得看网上有人说,传递文件超过一定大小时,系统会自动给你分包发送(我发现确实是这样,因为接收端的OnReceive函数被调用了多次),问题是这个“一定大小”是指多少?
【2】
我采用的方法是把要发送的文件分成若干个包,每个包1KB或者更小,这个1KB似乎不会导致系统给你自动分包,那这样为什么还是有错呢?

[培训]《安卓高级研修班(网课)》月薪三万计划,掌握调试、分析还原ollvm、vmp的方法,定制art虚拟机自动化脱壳的方法

收藏
免费 0
支持
分享
最新回复 (12)
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
这个是发送的代码:

void CMyServerXDlg::OnButton2()
{
        if(m_bConnected)
        {
                CFileDialog dlg(
                        true,
                        "",
                        "",
                        OFN_HIDEREADONLY|OFN_OVERWRITEPROMPT,
                        "所有文件(*.*)|*.*||",
                        NULL);
                int structsize=0;
                DWORD dwVersion=::GetVersion();
                DWORD dwWindowMajorVersion,
                        dwWindowMinorVersion;
                dwWindowMajorVersion=(DWORD)(LOBYTE(LOWORD(dwVersion)));
                dwWindowMinorVersion=(DWORD)(HIBYTE(LOWORD(dwVersion)));
                if(dwVersion<0x80000000)
                        structsize=88;
                else
                        structsize=76;
                dlg.m_ofn.lStructSize=structsize;
                dlg.m_ofn.Flags=dlg.m_ofn.Flags|OFN_SHOWHELP|OFN_LONGNAMES;
                CString FilePathName;
                if(dlg.DoModal()==IDOK)
                {
                        FilePathName=dlg.GetPathName();
                        CFile myFile;
                        myFile.Open((LPTSTR)(LPCTSTR)FilePathName,CFile::modeRead|CFile::typeBinary,NULL);
                        int len=myFile.GetLength();
                        for(int i=0;i<len;i+=1024)
                        {
                                if(i+1024>len)
                                {
                                        int byteNum=len-i;
                                        if(byteNum==1)
                                        {
                                                socket.Send(&byteNum,sizeof(int),0);
                                                BYTE* buffer=new BYTE[byteNum];
                                                memset(buffer,0,byteNum);
                                                myFile.ReadHuge(buffer,byteNum);                                       
                                                socket.Send(buffer,byteNum,0);
                                                delete []buffer;
                                                break;
                                        }
                                        else if(byteNum>1)
                                        {
                                                int byteNumX=byteNum-1;
                                                socket.Send(&byteNumX,sizeof(int),0);
                                                BYTE* buffer=new BYTE[byteNumX];
                                                memset(buffer,0,byteNumX);
                                                myFile.ReadHuge(buffer,byteNumX);                                       
                                                socket.Send(buffer,byteNumX,0);
                                                delete []buffer;
                                                byteNumX=1;
                                                socket.Send(&byteNumX,sizeof(int),0);
                                                buffer=new BYTE[byteNumX];
                                                memset(buffer,0,byteNumX);
                                                myFile.ReadHuge(buffer,byteNumX);                                       
                                                socket.Send(buffer,byteNumX,0);
                                                delete []buffer;
                                                break;
                                        }
                                        else
                                                AfxMessageBox("传输发生错误!",MB_OK);
                                }
                                else if(i+1024<=len)
                                {
                                        const int byteNum=1024;
                                        socket.Send(&byteNum,sizeof(int),0);
                                        BYTE* buffer=new BYTE[1024];
                                        memset(buffer,0,1024);
                                        myFile.ReadHuge(buffer,1024);
                                        socket.Send(buffer,1024,0);
                                        delete []buffer;
                                }       
                                else
                                        AfxMessageBox("传输发生错误!",MB_OK);
                        }
                        myFile.Close();
                }
                else
                        return;
        }
        else
                AfxMessageBox("服务器尚未连接!",MB_OK);
}
2008-6-22 22:25
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
接收的代码:

void CMySocket::OnReceive(int nErrorCode)
{
        CMyServerXDlg* pDlg=(CMyServerXDlg*)AfxGetApp()->GetMainWnd();
        pDlg->m_edit1.SetWindowText("开始接收客户端信息……");
        int byteNum=0;
        Receive(&byteNum,sizeof(int),0);
        BYTE* buffer=new BYTE[byteNum];
        memset(buffer,0,byteNum);
        Receive(buffer,byteNum,0);       
        myFile.WriteHuge(buffer,byteNum);
        delete []buffer;
        if(byteNum==1)
        {
                myFile.Close();
                pDlg->m_edit1.SetWindowText("信息接收完毕!");
        }
        CSocket::OnReceive(nErrorCode);
}
2008-6-22 22:26
0
雪    币: 466
活跃值: (165)
能力值: ( LV9,RANK:190 )
在线值:
发帖
回帖
粉丝
4
不是消息缓存大小的问题。你的程序本身有问题。
两个问题:
1、接收到100M的问题:
考虑如下情况:server 发一个包,bytenum =8196(4 + 8192) ,网络传输分成4096 + 2052。
第一次 CMySocket::OnReceive( int nErrorCode)时, 收到4096,固然好。然后第二次 CMySocket::OnReceive(int nErrorCode)时, 第一个Receive(sizeof(int) )收到的是消息的第 4097~4010 这四个字节。第二个第一个Receive()就取决了这四个字节值的大小了。
2、 CSocket::OnReceive(nErrorCode) 不能主动调用。它是个虚函数,在有消息到达时,它自动被系统调用,它再调用继承它的CMySocket::OnReceive(int nErrorCode)。你这里其实是一个递归循环。

解决办法自己研究吧。有问题的话把源程序附件上来
2008-6-23 13:34
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
5
我懂您的意思,我就是为了避免这种情况,专门把要发送的文件自己分了包。每个包的大小都是1024个字节(当然最后一个包可能不够这个数目),然后再循环地一个个发送出去。(详细可见我红色部分的代码)
但是这样做不出问题的前提是:系统不会再对1024字节大小的包进行再分包,如果再分的话,立刻就会出现您说的问题。
目前这个代码,对大多数文件的传输没啥问题,但是小部分文件还是会出现错误,原因不明。
2008-6-23 16:21
0
雪    币: 466
活跃值: (165)
能力值: ( LV9,RANK:190 )
在线值:
发帖
回帖
粉丝
6
给你一个“发一次错一次,发十次错十次”的办法。你看看你的程序是不是“大多数文件的传输没啥问题”。

比如:你要发一个大小为10,内容为字串"0000000000"的文件,你按下面的办法发送:
先发送一个整数10, 再发送6个字节,再发送4个字节。那么你客户端写的文件就是3158064+ 10 个字节。3兆。

比如:你要发一个大小为10,内容为字串"0000001111"的文件,你按下面的办法发送:
先发送一个整数10, 再发送6个字节,再发送4个字节。那么你客户端写的文件就是51458353 + 10 个字节。50兆。
2008-6-23 18:27
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
7
厄。。。这样不对啊
根据接收方的代码
先发送整数10,那么接收方申请缓冲区10字节,后面再发送的6个字节就被填入该缓冲区内(不足的补0)。
再发送4字节,刚好又是一个int类型的长度,但是由于字串都是"0000000000",所以接收方不会申请缓冲区的……
算了,干脆附上源码
上传的附件:
2008-6-23 22:50
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
8
有人指教下吗?
2008-6-24 18:05
0
雪    币: 211
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
9
建议这样的程序使用一个单独的报头来定义文件的大小,报头有固定长度,接收到报头以后,再开始文件的传输.

一个TCP包大小一般为1500byte.像这样的程序楼主可以去www.vckbase.com上看,有很多例子.
2008-6-24 22:12
0
雪    币: 466
活跃值: (165)
能力值: ( LV9,RANK:190 )
在线值:
发帖
回帖
粉丝
10
void CMySocket::OnReceive(int nErrorCode)
{
        CMyServerXDlg* pDlg=(CMyServerXDlg*)AfxGetApp()->GetMainWnd();
        pDlg->m_edit1.SetWindowText("开始接收客户端信息……");
        static bool newMsg = true;
        static int byteNum= 0;
        static int len = 0;
        static BYTE* buffer = NULL;
       
        int templen;
        // 新的文件传输,则先收一下文件长度
        if ( newMsg )
        {
                newMsg = false;
                Receive(&byteNum,sizeof(int),0);       
                length = byteNum;
                buffer = new BYTE[byteNum];
        }
        //接收剩下的部分文件 byteNum - len 是应该收的长度,templen是实际收到的长充
        templen = Receive( buffer, byteNum - len , 0 );
        myFile.WriteHuge( buffer, templen );       
        len += templen;
        if (  len ==  byteNum  )
        //一个文件接收完毕
        {
                delete []buffer;       
                myFile.Close();
                pDlg->m_edit1.SetWindowText("信息接收完毕!");
                //准备接收新文件
                newMsg = true;
        }

}
上传的附件:
2008-6-24 23:40
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
11
多谢这位大虾
看了你的代码,确实比我的优秀不少啊
2008-6-26 18:43
0
雪    币: 242
活跃值: (14)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
12
楼上各位:TCP编程中没有“包”的概念!
用UDP编程的方法写TCP程序,简单网络或许没问题,在稍微复杂的网络中,比如跨网段、使用中介代理服务、跨介质等情况,会出现乱七八糟的问题的。

如果你们为了实现应用协议非要用“包”,请不要嫌麻烦,在发送和接收时参照TCP的特性处理好块大小、块确认、块接收,否则有你们受罪的时候。

一点建议,认为我说错了,就当我放屁吧
2008-6-27 14:25
0
雪    币: 222
活跃值: (10)
能力值: ( LV8,RANK:130 )
在线值:
发帖
回帖
粉丝
13
UDP的话,缓冲窗口,块顺序,块分片,包重发,包重复检测等都是要自己做的
2008-6-29 04:36
0
游客
登录 | 注册 方可回帖
返回
//