游戏开发论坛

 找回密码
 立即注册
搜索
楼主: tonykee

tcp中拼包,拆包,粘包,这些问题怎么处理??

[复制链接]

149

主题

4981

帖子

5033

积分

论坛元老

Rank: 8Rank: 8

积分
5033
QQ
发表于 2007-10-29 17:54:00 | 显示全部楼层

Re:tcp中拼包,拆包,粘包,这些问题怎么处理??

read/write的时候只是读出或写入尽量多的数据,把数据读出来之后再考虑拆包。一般一个包都是len-data这样的结构,包的结构对于网络操作来说就只需要知道这么多。

119

主题

1367

帖子

1393

积分

金牌会员

Rank: 6Rank: 6

积分
1393
 楼主| 发表于 2007-10-29 20:56:00 | 显示全部楼层

Re:tcp中拼包,拆包,粘包,这些问题怎么处理??

看了一下这篇文章受了点启发:
『请问完成端口中客户端如何向服务器端上传文件』
http://mfc.365dev.net/19879.html

有个人的回帖是这样的:
------------------------
如果在客户端使用完成端口模型来WSASEND到服务端,对于传输文件,不需要分包,直接WSASend所有的字节,利用IOCP模型内部的机制会把它传递到socket缓冲区,并且send到服务端。因为是异步的,所以WSASend不会堵塞,主线程和界面不会受到影响,只不过在客户端的工作线程中,得到的完成通知的已发送字节数不一定是所有的,这里就需要再次提交WSASend剩下的字节数(一定是剩下的所有字节数)。
这样做的好处是,一次投递所有字节,会保证字节的相对顺序,而且IOCP内部的调度比起你自己分包WSASend要高效和具有伸缩性。
如果你对一个客户端,还使用分包同时投递多次WSASend,根本就没有必要用IOCP,IOCP内部已经协调的很好了,他的目的就是要你把要传得数据一次投递,根据完成通知来决定是否继续投递为完成发送的字节数。
----------------------------
对于文件这样大的东西都不需要拆分的?那wsaSend那边的代码怎么写呢?

用这样的思路:
alltransbytes=getPackbyte(...);
transfered=0
while(1)
{
WSASend(lpPer_io_data->socket, &lpPer_io_data->wDataBuf + transfered, 1, &lpPer_io_data->dwBytes, MSG_PARTIAL, &lpPer_io_data->Overlapped, NULL);

transfered+=lpPer_io_data->dwBytes;
if(transfered>=alltransbytes)
break;
}

还是发一次WSASend,然后在workthread里面得到op_write,表明发送完成了,然后判断发送了多少,然后继续发送?

6

主题

471

帖子

1047

积分

金牌会员

Rank: 6Rank: 6

积分
1047
发表于 2007-10-30 10:37:00 | 显示全部楼层

Re:tcp中拼包,拆包,粘包,这些问题怎么处理??

wsasend时只使用wsabuf中的数据进行操作,不会产生新的数据copy.
所以多线程操作发送最好分块,不然出现1,2两种情况肯定出问题,你可以一次发个上百MB文件试试.

119

主题

1367

帖子

1393

积分

金牌会员

Rank: 6Rank: 6

积分
1393
 楼主| 发表于 2007-10-30 11:51:00 | 显示全部楼层

Re:tcp中拼包,拆包,粘包,这些问题怎么处理??

ls的说法我已经体会到了,每次发的长度还是固定的好,不然就容易粘包了。我已经出现了这个问题,正在用ls的方法进行修正。

149

主题

4981

帖子

5033

积分

论坛元老

Rank: 8Rank: 8

积分
5033
QQ
发表于 2007-10-30 13:04:00 | 显示全部楼层

Re:tcp中拼包,拆包,粘包,这些问题怎么处理??

奇怪……tcp中的粘包不是通过字节计数之类的方法来解决的吗……跟一次发送的长度没关系吧……

119

主题

1367

帖子

1393

积分

金牌会员

Rank: 6Rank: 6

积分
1393
 楼主| 发表于 2007-10-30 13:08:00 | 显示全部楼层

Re:tcp中拼包,拆包,粘包,这些问题怎么处理??

我也不太清楚,发送和接受的per_io_buffer_size长度不一样,那么重新用套接字发其他的数据回来的时候包数据就乱了,很奇怪,我现在改用13楼的朋友的方法了。每次发定长的数据就没有问题。

6

主题

471

帖子

1047

积分

金牌会员

Rank: 6Rank: 6

积分
1047
发表于 2007-10-30 14:00:00 | 显示全部楼层

Re:tcp中拼包,拆包,粘包,这些问题怎么处理??

所谓粘包就是服务器多次发数据,到客户端这里变成了一个大包,或者只到了第一个整包和第二个包的一半.
本身拆包就是写TCP程序必做的步骤,只不过大部分人还没搞懂,或者想偷懒.
希望简单的服务器发一次,客户端就收一次,出现2个包一起过来过来就傻眼了.

119

主题

1367

帖子

1393

积分

金牌会员

Rank: 6Rank: 6

积分
1393
 楼主| 发表于 2007-10-30 16:03:00 | 显示全部楼层

Re:tcp中拼包,拆包,粘包,这些问题怎么处理??

我现在就出现了后发的包含有前一个包的内容。而且顺序是乱的,也说不上来是什么错误
还在调。唉。。郁闷,异步传输不好搞啊。

119

主题

1367

帖子

1393

积分

金牌会员

Rank: 6Rank: 6

积分
1393
 楼主| 发表于 2007-10-30 23:25:00 | 显示全部楼层

Re:tcp中拼包,拆包,粘包,这些问题怎么处理??

终于搞定了,服务器和客户端都是异步传输,而且都是完成端口,算是领悟了完成端口的本质,就是一个很优良的IO模型而已,我的错误在于不断的发出数据接受的请求,前一次发送没有完成是不能发接受请求的
        virtual void OnWrite(const DWORD dwByteCount, Session* lpSession, LPPER_IO_DATA PerIoData)
{
               
                lpSession->lpBufEnd += dwByteCount; //更新已经发完的部分
                int sended = lpSession->lpBufEnd - lpSession->arrayDataBuf; //已发的长度
                short int paclen = *(short int*) ( lpSession->arrayDataBuf + 3); //后面两位表示数据包的长度

       
                if(sended < paclen) //还没发完
                {
                        //还没发完继续发...
                        PerIoData->wDataBuf.buf = (char *)lpSession->lpBufEnd; //发剩下部分
                        PerIoData->wDataBuf.len = paclen - sended;
                        PerIoData->dwFlags  = 0;
                        PerIoData->dwOpCode = OP_WRITE;

                        if(WSASend(PerIoData->socket, &amperIoData->wDataBuf, 1, &PerIoData->dwBytes,
                                MSG_PARTIAL, &PerIoData->Overlapped, NULL) == SOCKET_ERROR)
                        {
                                if(WSAGetLastError() != ERROR_IO_PENDING)
                                {
                                        g_Log.Log(DEBUG_LV, "WSASend() failed with error- %d\n", WSAGetLastError());
                                        return ;
                                }
                        }
                }
                else
                {
                        lpSession->lpBufEnd = lpSession->arrayDataBuf;
                        ZeroMemory(lpSession->arrayDataBuf,sizeof(lpSession->arrayDataBuf));
            SendRev(PerIoData, IO_BUFFER_LONGTH); //表明数据发送完成了,这个时候投递请求好了
                }

这才是本质,现在的问题都解决了,我采用了不定长的发送方式,还是12楼的做法
领会了:
=============================================================================
只不过在客户端的工作线程中,得到的完成通知的已发送字节数不一定是所有的,这里就需要再次提交WSASend剩下的字节数(一定是剩下的所有字节数)。
===========================
以及:
=====================================
一次投递所有字节,会保证字节的相对顺序,而且IOCP内部的调度比起你自己分包WSASend要高效和具有伸缩性。
============================================================================
这两句话的含义,按照这样的方法发送数据,性能才是优良的。

0

主题

7

帖子

38

积分

注册会员

Rank: 2

积分
38
发表于 2017-8-8 13:54:08 | 显示全部楼层
怎么回帖还要审核了。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

作品发布|文章投稿|广告合作|关于本站|游戏开发论坛 ( 闽ICP备17032699号-3 )

GMT+8, 2025-2-24 17:59

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表