如题,TCP协议是面向对象的数据,先发的先到,后发的后到!但我个人理解是把每次的buffer数据拆开后的TCP包进行的比较,我用下面的代码连续发送三次数据,在网络环境极端不好的情况下,对于这种分别使用三次BeginSend发送的buffer数据,对方也使用BeginReceive这种异步接收模式,有可能导致第三次发送的buffer[2]数据较前面的数据buffer[0]和buffer[1]先到客户端吗?
      int i=0;
                while (i < 3)
                {
                    Client.BeginSend(
                      buffer[i],
                      0,
                      buffer[i].Length,
                      0,
                      new AsyncCallback(SendCallback),
                      Client);//开启异步发送
                    i++;
                }

解决方案 »

  1.   

    昨天说了你还没明白什么是异步。TCP发送原理看看WINDOWS网络编程,不是你写buffer[i],他就按一个个buffer[i]发送的。
      

  2.   


    你在哪一个异步Receive的demo上见过这种写法Client.BeginSend(buffer1,0,buffer1.Length,0,
    new AsyncCallback(SendCallback),Client);
    Client.BeginSend(buffer2,0,buffer2.Length,0,
    new AsyncCallback(SendCallback),Client);
    Client.BeginSend(buffer3,0,buffer3.Length,0,
    new AsyncCallback(SendCallback),Client);
      

  3.   

    你在哪一个异步Send的demo上见过这种写法Client.BeginSend(buffer1,0,buffer1.Length,0,new AsyncCallback(SendCallback),Client);
    Client.BeginSend(buffer2,0,buffer2.Length,0,new AsyncCallback(SendCallback),Client);
    Client.BeginSend(buffer3,0,buffer3.Length,0,new AsyncCallback(SendCallback),Client);
    按照你的这种写法,你确实是顺序调用BeginSend,但是人家是异步并行发送数据,怎么可能按照这个顺序来发送数据?你在异步Receive上也是这种乱写异步代码的吗?
      

  4.   

    我再给你明确一点说,“有可能导致第三次发送的buffer[2]数据较前面的数据buffer[0]和buffer[1]先到客户端吗”这个问题是无意义的!因为按照你的写法,buffer[2]数据完全可能在buffer[0]和buffer[1]之前发送出去,根本谈不上去纠结Receive端代码的问题。
      

  5.   

    那麻烦帮我解释下异步的操作撒!?我的理解就是每次beginSEND就会再开个线程去发送数据到缓冲区?然后数据怎么处理的就不太理解了!
      

  6.   

    代码我只是举个例子,具体问题是这样的,麻烦帮忙看看!
    我这也是想进行即时通信,包括普通文字通信和文件通信。
    如果是文字我就在数据前面加个字节0X11表示后面跟的是文字,即这种形式:0X11+文字数据
    如果是文件就加0X21,即这种形式:0X21+文件数据。因为文件比较大,可能需要几次才能发完。那么我就担心这样一种情况的发生:
    如果我在RECEIVE的时候得到的数据是:一个文件包的部分+文字包+此文件包的另一部分的情况啊,那么我程序中就可能错误把文字包也当做文件包的一部分而错误的处理了!
      

  7.   

    不是“担心这样的情况的发生”,而是必定会发生这样的情况。你昨天的帖子,是纠结在Receive上的,所以看不出问题所在。今天的帖子就很明确了。同时启动三个线程去发送数据,它们实际执行发送操作的次序当然是不确定的,当然buffer2完全会先发送出去。这其实是一个常识,所以我之前没有怀疑你的代码会有这个问题。我觉得这可能对你是一个比较重要的启示,常识问题是不应该轻易犯下的。
      

  8.   

    异步操作简单的说就是我的程序在发送数据的同时还能做其他的事情,而不需要等待发送完菜能做其他的事情,而发送完后会通过回调函数通知我。异步一般是通过多线程或硬件实现的。你现在首先要明确你用异步发送数据是为了提高效率,而和你TCP发送的包的顺序没有任何关系。就好像左手做其他事情,右手把消息按顺序一条一条发出去。按你的代码就是你有N个右手,同时发,这样当然无法保证顺序了。你需要给每个发送的数据一个序号,接收后按序号来组合
      

  9.   

    N个右手的比喻非常恰当,也怪我没把问题表述清楚。
    文件一般比较大,所以要分批发送,每次在BEGINSEND之前,自然是需要读取处理一下文件后才交给BEGINSEND发送。比如一个8K的文件,我每次读1K出来。然后再给加一个块号表示是第几次读出来,然后发送给远端处理,最后把8次得数据按顺序组合形成文件。实际上这样可能就是你说的“右手把消息按顺序一条一条发出去”
    单独是文件发送的话,上面肯定没问题,可是即时通信中的聊天的数据怎么处理呢?文件数据肯定是持续发送的,如果此时突然有聊天的数据要发送,那这样一个右手是否就应付不过来了?
      

  10.   

    这个就是你设计的问题了。如果文件很大,后面的消息会排队,一直到前面的文件数据发送完了才发送。但是你可以给包设置优先级,比如聊天消息可以优先发送。当然这不是常见的做法,一般IM软件,发送消息和传送文件是不同的socket。就相当于多个人来做事了。一般来说聊天消息是通过服务器转发或者P2P,而传送文件基本都是P2P的。
      

  11.   

    在网上也看到过TCP的P2P技术,但好像挺难的。UDP穿透还好实现,我以前做过。但P2P穿透也不是所有网络环境都可以的,无论TCP还是UDP,这种情况就需要服务器转发了。我现在就是考虑这种需要服务器转发的情况下的编程。
    如果将发送信息和传送文件使用不同的SOCKET,客户端和服务器端都需要使用2个端口,服务器为保证安全,肯定需要与远程客户端有个验证过程的,那岂不是要验证多次?服务器开的端口越多,是否也就越不安全?
      

  12.   

    可不可以这样,我在每次发送信息时,检测下发送缓冲区是否为空,然后将数据写入缓冲区,并修改NODELAY属性,让数据立即发送出去,然后把NODELAY修改回来。但是这样的问题就是怎么判断发送缓冲区现在是空的呢?还有这个想法是否可行?
      

  13.   


    多此一举, TCP 协议回保证数据包顺序。
      

  14.   


    用两个 Socket, 独立的 Socket 是不会有顺序问题。。如果你把两个Socket 里面的数据搞混了,重写你的接受代码。
      

  15.   

    两个Socket自然逻辑就简单了,但服务器就是2个端口,怕不安全撒!而且每2个客户端需要传送文件,就开个SOCKET,开个端口,那服务器需要开多少端口啊!?
      

  16.   


    谁告诉你一定要开两个端口的IIS 默认用 web 服务用 80 端口,一次上 N 多多人浏览网页都行。
      

  17.   


    对,提醒的相当正确!可是服务器怎么多开2个SOCKET,分别处理文字和文件呢?能详细解释下吗?我觉得这个方法好!
      

  18.   

    传送文件一般是P2P通信,不通过服务器的。而且一般IM端有多种服务器的。又不是只有一台服务器,不同服务器提供不同服务,当然有多个接口。比如MSN就是给每个聊天窗口一个socket地址。谁说服务器不能开多个socket
      

  19.   

    分段吧
    10mb的文件
    10个线程发送
    第一个发送0-1mb
    第二个发送1-2mb
    .....如果使用一个socket不用担心到达顺序的问题,ip层已经帮你在每个包前面加了序号,到达之后就会自动排好,那片buffer【2】先到都没问题,虽然buffer【2】先到了,只有buffer【1】没到,你就读不到。你读到的都是已经拍好的。。
      

  20.   

    P2P不是所有网络环境都可以的!
    我觉得多开SOCKET好,CGabriel说的多开SOCKET好,可怎么多开呢?
      

  21.   

    当然是客户端连接服务端罗,但是并没说明怎么让服务器开2个SOCKET,分别用来处理文字和文件啊?
      

  22.   


    晕你还是去补习基础知识先把客户端用 Socket.Connect ; 服务端若 Socket.Accept,就会返回一个 Socket...客户端 Connect 多几次,服务端就有几个 Socket...
      

  23.   


    ………………………………………………………………
    如果你先发送了一个的文件,又发送了一串文本,假设总的字节长度为1000,那么你在接收端读取的时候,用一个1000字节的byte数组来读,那么你读出来的就是一个文件+一串文本,顺序不是你能控制的。只要发送的时候用的是一个socket一次发送出去的。如果你发送了两次,并且你的接收端及时的接收了,那么他们就是不会粘再一起的。
    lz还有一个误区:
    我的意思是一个线程用一个socket,这一个socket发送的数据一定能自动在接收端排列好(只要发送成功),因为一个socket对应一个端口,所以这10个线程(也就是发送端用10个socket,客户端也用10个socket)的数据在接收端是不会混乱的。每个socket的顺序都是对的,那怕你在一个socket中先发了一个文件+字符串,到了接收端也一定是文件+字符串。socket只管发送接收,他只能能保证接收的和发送的是一致的。他的作用类似于一个能保证数据准确到达的运输管道,只管传输,不管传的是什么。。
    所以lz说的,一个文件包的部分+文字包+此文件包的另一部分的情况?和socket没有关系。
    要看lz怎么处理了。
    lz说的这个问题是“粘包”
    解决方法就是|命令(4bytes)//这4个字节的获取方法就是 BitConverter.GetBytes(长度),以下的类似
    |----------------------
    |附加消息长度(4个自己)
    |----------------------
    |数据长度(4个自己)
    |----------------------
    |附加消息
    |----------------------
    |数据
    |----------------------这样就自己做了一个消息格式,前12个字节是命令,以后的字节是命令的附加消息+数据,最后我们把这条消息转成byte(),
    比如有这样一条命令:
    Login|User:admin,Pwd:admin|""
    命令是登陆,附加消息中保存了用户名和密码,数据为空(这里只是举例)
    因此可以这样做:List<byte> result = new List<byte>();
                //前四位为命令
                result.AddRange(BitConverter.GetBytes((int)Login));
                //再四位为名字长度
                if (附加消息!= null)
                {
                    result.AddRange(BitConverter.GetBytes(附加消息.Length));
                }
                else
                {
                    result.AddRange(BitConverter.GetBytes(0));
                }
                if (数据!= null)
                {
                    result.AddRange(BitConverter.GetBytes(数据.Length));
                }
                else
                {
                    result.AddRange(BitConverter.GetBytes(0));
                }
                if (附加消息!= null)
                {
                    result.AddRange(附加消息);
                }
                if (byteData != null)
                {
                    result.AddRange(数据);//上面提的的数据和附加消息要转为byte(),方法:Encoding.Unicode.GetBytes(数据);
                }
                return result.ToArray();//最后转成byte()
            }
    然后就是接收方了,他收到信息之后,看看是否大于12个字节,如果大于12个字节就取出头信息,取出命令,0-4个字节,方法BitConverter.ToInt32(msg,0),取出附加信息的长度:BitConverter.ToInt32(msg,4),取出数据长度:BitConverter.ToInt32(msg,8)
    然后把附加消息的长度+数据的长度+12(消息头长度)=消息的长度,判断你收到的消息的总长度是否>=消息的长度,如果为True则可以拆包了,如果false则再度。。这样就不会粘包了,即使接收端读的时候一次读了两个包或两个半都没有问题。这是我个人的处理方法,肯定有更好的方法。可以参考我的代码:
    http://topic.csdn.net/u/20110330/15/2bec10e1-7795-4153-b733-cfdcab17b3dd.html