send方法如果同时有两个在运行那么传到缓冲区的数据会不会相互交叉现象
比如第一个send方法传送aaaaaaaaaaa,同时第二个send方法(可能是另一个线程的)传送bbbbbbbbb,那么缓冲区会不会字节队列为aaaaabbbbbaaaaabbbbbb呢?

解决方案 »

  1.   

    不会,TCP/IP协议对传送的内容是以包来传送的,即使对于比较大的包,该协议内容也支持内部的分包和组包的过程,不会存在某个包的一部分和别的包混淆的事情。
      

  2.   

    是不是每一个send方法对应一个tcp包?
      

  3.   

    .net 的socket基于字节数据流,你只能针对高层次的buffer操作,其实你看不到真正的数据包。我们用包这个词往往只是用来解释一些类似的编程概念,而并不是真的对底层数据包做操作。回答lz的问题:如果两个send是同一个socket实例对象发出的,或者即使是两个对象但是它们相互之间复用了本地地址(和端口),那么接收端在网络稍微繁忙一点时肯定会经常发现同时再一次receive时受到两个send数据中的部分的情况。
      

  4.   

    我打算开一个线程,每10秒发个包,可是这样的话就可能出现两个send方法同时进行的情况,如果两个send发送的数据交叉就完了,请指教
      

  5.   

    如果两个send是分别隶属两个socket,并且它是不同的本地地址或者本地端口,只不过访问相同的远程地址和端口,那么服务器端在Accept的时候就区分开了不同会话对象,因此这才不可能在同一个receive中取到相互(首尾数据)组合的部分。
      

  6.   


    这有很多种可能的做法。一种:每次发数据其实都new一个新的socket实例,并且Bind本地IPEndPoint时使用 IPEndPoint.Any(既随机分配一个未使用的本地端口),然后连接远程IPEndPoint,然后send。这样,服务器端在不同的Accept处理中作为不同的socket来处理的,不会在同一个Receive操作时有任何交叉。当然,客户端send完了数据,接收返回值,之后就可以立即关闭连接。另一种:多线程但是共享同一个socket实例,显然send之后并不会关闭连接。这样服务器端就会在同一个接收动作中完全可能包含不只一个send内容。服务器端在Receive时,不管buffer设置的有多少,它都要循环读取tcp底层的数据,直到socket.Avaliable不大于0,写成代码类似于using(var dest=new MemoryStream())
    {
        while(socket.Avaliable>0)
        {
            len=socket.Receive(buffer);
            dest.Write(buffer,0,len);
        };
        送去处理接收到的数据(dest.ToArray());
    };
    而“送去处理处理接收到的数据”并不一定仅仅有客户端一次send的值,有可能有两次send的值,甚至当网络实在是非常繁忙并且接收端的buffer的大小比发送端的buffer的大小更小时完全可能连一个send值都没有接受完全就已经满足socket.Avaliable==0的条件了!因此dest中接收到的需要放入一个更大的缓冲区中,然后从这个更大的缓冲区中取出第一个消息内容(因此设计师需要预先设计通信协议,并且从一堆字节中解析出一条单独的消息),处理消息。而取出了完整消息之后,剩下的字节仍然在缓冲区中,等待下一次再送来新的数据添加到缓冲区的时候,在去检查是否有完整的消息需要处理。基本上,第一种短连接的方式及其简单方便,并且更加可靠。缺点是可能不如第二种快速,因为tcp socket的发起、发送数据、握手和接收数据的整个机制都像一个慢性子的人,目的是为了减少网络上的数据包数量同时又提供可靠的连接。第二种长连接方式显然更复杂,需要更多编程。
    最后,其实可以使用udp传送数据,udp不存在这种多个send交叉如一个receice的问题。但是udp是不可靠的通讯,不能保证数据真的传送到位(你可以假设成功率只有50%并且即使对方收不到数据你的程序也不会有任何异常抛出),因此需要你自己设计更多的业务逻辑来进行机器之间的协调控制。
      

  7.   


    心跳是应用层,完全看设计师怎么设计通信协议。假设服务器端只需要知道客户端的IPEndPoint就能记录客户端所绑定的应用程序(或者在线用户)的活跃时间(超过一定时间则可以认为是离线了),那么其实可以设计一个“什么都不做”的指令,例如一个简单的“换行回车”,客户端发送一个什么都不做的指令,服务器只是用来更新客户端的状态记录,而不会执行任何其它的动作。而所有其它业务指令其实都可以具有更新客户端活跃状态记录的作用,只不过我们特意设计一个“什么都不做”的空指令特别地叫做心跳消息罢了。
      

  8.   

    我主要是想避免两个send同时进行,准备设个参数当第一个线程send时参数为0,此时第二个线程不send,当第二个线程send时,参数设为1,第一个线程不send可以吗
      

  9.   


    短连接比较简单,我写两三句短连接代码:客户端,假设我们要把一个门禁刷卡信息发送到服务器,这个指令的协议格式是:110 刷卡机编号 刷卡者的描述信息 刷卡时间,其中110是指令的标识,然后所有信息之间都用换行回车分割(信息内部不能包含换行回车),最后以一个换行回车结束,那么发送消息可以写(让我们使用.net专门为tcp封装的更高级一点的对象来操作):public void 发送门禁刷卡消息(long m,string p,DateTime t)
    {
        var x = new TcpClient(AddressFamily.InterNetwork);
        x.Connect(host, port);  //远程服务器地址host和port,在class中定义。
        var sendString=string.Format("110/r/n{0}/r/n{1}/r/n{2}/r/n/r/n",m,p,t);
        var sendDatas=Encoding.UTF8.GetBytes(sendString);
        var st = x.GetStream();
        st.Write(sendDatas, 0, sendDatas.Length);
        st.Flush();
        return st;
    }而服务器端每当处理Accept,然后使用所得到的socket对象进行Receive操作,得到的就是(并且只是!)一个完整的指令,在上述“送去处理接收到的数据"操作时只要判断所接收到的数据最后是两个换行回车即可逐行取出信息,并且根据第一个指令标识(这里是110)送去不同的命令处理程序处理余下的参数。
      

  10.   

    短连接方式,每一个访问跟从不同进程上(甚至不同机器上)访问服务器没有什么两样,所以服务器不会有任何“数据交叉”的问题。而长连接方式,例如你的读线程共享同一个socket对象并且不确定何时哪一个线程会执行一个send,这时服务器就要做关于粘包信息的检查,只是代码更多一些。实际上短连接方式很适合你开发的服务器支持大量并发的情况,假设有1000台客户端电脑,每台电脑上有好几个不同种类的客户端进程,每一个客户端进程都可能并行地使用多个线程向服务器不定时地发送消息,那么服务器异步接收这些不同进程发送的,就比让客户端进程中各个线程的消息排着队以长连接方式发送给服务器要好(特别是有的消息要服务器处理完并返回结果需要很长时间,而有的则极短时间的情况)。
      

  11.   

    例如你的读线程共享同一个socket对象  -->  例如你的多个线程共享同一个socket对象