在客户端的窗口上有两个按钮点击button1就开始连接服务端:
tcpClient = new TcpClient("192.168.1.3", 8080);点击button2开始发数据到服务端:
NetworkStream stream = tcpClient.GetStream();
...
...
...
(通讯完毕后,并没有关闭tcpClient)>>>>>当第一次点击button2的时候正常运行,当第二次点击它的时候就抛出异常:“不允许对非连接的套接字执行该操作 at System.Net.Sockets.TcpClient.GetStream()”各位老大,到底是什么问题引起的呀?应该怎么修改呢?我的目的是想客户端与服务断连接之后,连续通讯,直到一方退出为止...

解决方案 »

  1.   

    用TcpListener和TcpClient想要连续通讯的话,应该怎样做呀?
      

  2.   

    想问一下各位,有没有现成的TcpListener和TcpClient连续通讯知道一方退出的例子呢?现在找到的几乎全部都是用Socket类写的
      

  3.   

    NetworkStream stream = tcpClient.GetStream();当以 GetStream方法得到 stream 对象实体以后,第2次发送信息时不用再次调用 GetStream方法。
    第一次调用之后,以后直到关闭连接之前的所有通信,直接对stream 读写即可。
      

  4.   

    to eplox:按照你的方法做了,还是不行;第二次发数据到服务端之后再读服务端的返回数据就会失去反应服务端我是用一个循环去监听来自客户端的请求的:
    while(true)
    {
       TcpClient client = tcpListener.AcceptTcpClient();  
       NetworkStream stream = client.GetStream();
       if(stream.CanRead)
       {
          readBuffer = new Byte[10240];      stream.BeginRead(readBuffer, 0, readBuffer.Length, new AsyncCallback(ReadCallback), stream);
       }
    }private void ReadCallback(IAsyncResult ar)
    {
    NetworkStream n_stream = (NetworkStream)ar.AsyncState;int read_size = n_stream.EndRead(ar);
    ...
    ...
    n_stream.BeginWrite(byteData, 0, byteData.Length, new AsyncCallback(WriteCallback), n_stream);
    }public static void WriteCallback(IAsyncResult ar)
    {
    NetworkStream n_stream = (NetworkStream)ar.AsyncState;n_stream.EndWrite(ar);//n_stream.Close();
    }请问是不是我的服务端写得不好呢?
      

  5.   

    还有,在private void ReadCallback(IAsyncResult ar)里设置了断点,客户端第二次发数据过来,根本就没有进入到private void ReadCallback(IAsyncResult ar),请问是怎么回事呢?
      

  6.   

    你的客户端发送数据完毕后马上开始接受,但是如果你的接收操作不是另外一个单独线程来做,而是放在主线程里,那么如果没有得到服务器传来的数据时,你的客户端程序便会被 block ,而停止响应。直到收完服务器传来的数据。
      

  7.   

    你的服务器端也有问题。while(true)
    {
       TcpClient client = tcpListener.AcceptTcpClient();  
       NetworkStream stream = client.GetStream();
       
      while(true)
      {   if(stream.CanRead)
       {
          readBuffer = new Byte[10240];      stream.BeginRead(readBuffer, 0, readBuffer.Length, new AsyncCallback(ReadCallback), stream);
       }
       }
    }
    试试看这样行不?
      

  8.   

    恩谢谢 “eplox(090-7523-38**)” 现在按照你上面的方法试试看。
      

  9.   

    while(true)
    {
       TcpClient client = tcpListener.AcceptTcpClient();  
       NetworkStream stream = client.GetStream();
       
      while(true)
      {   if(stream.CanRead)
       {
          readBuffer = new Byte[10240];      stream.BeginRead(readBuffer, 0, readBuffer.Length, new AsyncCallback(ReadCallback), stream);
       }
       }
    }用这种方法不行呀当一接收到客户端连接,就跑到第二个while里面去了,然后服务端和客户端都没反应了
      

  10.   

    服务器端要用到线程将各个 Client 分开才行的。
    刚才简单写了个,看看你能不能参考一下using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Text;
    using System.Windows.Forms;
    using System.Net.Sockets;
    using System.Threading;
    using System.Net;namespace csdn
    {
        public partial class Form1 : Form
        {
            public Form1()
            {
                InitializeComponent();
            }        TcpListener tcpc;
            private void button1_Click(object sender, EventArgs e)
            {
                Thread t = new Thread(new ThreadStart(MyServer));
                t.IsBackground = true;
                t.Start();
            }        private void MyServer()
            {
                tcpc = new TcpListener(IPAddress.Parse("127.0.0.1"), 2000);
                
                tcpc.Start(20);
                while (true)
                {
                    TcpClient client = tcpc.AcceptTcpClient();
                    ClientClass cc = new ClientClass(client);
                    cc.work();
                }
            }
        }
        public class ClientClass
        {
            private TcpClient tcpc;
            public ClientClass(TcpClient t)
            {
                this.tcpc = t;
            }
            public void work()
            {
                Thread t = new Thread(new ThreadStart(run));
                t.IsBackground = true;
                t.Start();
            }
            private void run()
            {
                NetworkStream nstream = tcpc.GetStream();
                while (true)
                {
                    // 在这里写你的收发代码,比如下面:
                    byte[] bb = Encoding.ASCII.GetBytes("hello");
                    nstream.Write(bb, 0, bb.Length);
                }
            }
        }
    }
      

  11.   

    嗯...好象找到解决方法了,修改服务端代码,在NetworkStream.EndWrite()的回调函数里面执行NetwordStream.BeginRead()方法,经过测试,好象没有问题public static void WriteCallback(IAsyncResult ar)
    {
    NetworkStream n_stream = (NetworkStream)ar.AsyncState;n_stream.EndWrite(ar);n_stream.BeginRead(readBuffer, 0, readBuffer.Length, new AsyncCallback(ReadCallback), n_stream);
    //n_stream.Close();
    }请各位大大看看解决方法是不是这样呀?
      

  12.   

    是的,如果使用异步,在end时再回调begin大概也可以吧。是一种递归式的手法。
    若不用异步,用同步加线程的话,像我上面的那样就可以了。
      

  13.   

    to eplox:"若不用异步,用同步加线程的话,像我上面的那样就可以了。"如果是同步的话,想你上面所说的方法,进入到第二个while(true),程序会不会也失去反应呀?如果有N个客户端,那么服务端在while(true)的同时怎么去响应其他客户端的数据呢?我刚用SOCKET没几天,之前对网络通讯方面也没经验,很多东西都不懂,望赐教.
      

  14.   

    to eplox:不好意思,原来你又发了一段代码上来,没看见我在14点28分问的问题,不用回答我了。不过我还有一个疑问,如果使用的你“同步+线程”的方式,假设现在有2个客户端已经连接到了服务器,那么在服务端已经打为他们各打开了A和B总共两个线程,那么既然是同步的话,是不是A线程中在收/发数据,B线程就处于等待状态等A收/发完毕再继续?“同步 + 线程”的运作方式到底是怎么样的?
      

  15.   

    和SOCKET异步到底有哪些区别呀?
      

  16.   

    to eplox:如果有N个客户端已经连接了服务端,可不可以令服务端在某一时间内只处理其中一个客户端?如果可以那么应该如何实现呢?
      

  17.   

    to eplox:如果有N(A,B,C,D)个客户端已经连接了服务端,可不可以令服务端在某一时间内只处理其中一个客户端(A)?其他的客户端处于等待状态,等服务端处理完A之后在处理下一个请求?如果可以那么应该如何实现呢?
      

  18.   

    CSDN真麻烦,只可以连续回复三次to eplox:我是楼主,换了马甲上来了.上面的意思是说,在你给的范例的基础上可不可以实现:如果有N(A,B,C,D)个客户端已经连接了服务端,可不可以令服务端在某一时间内只处理其中一个客户端(A)?其他的客户端处于等待状态,等服务端处理完A之后在处理下一个请求?其实如果不用线程,WHILE{} 每执行一次就把当前的客户端连接断开就可以达到上面的目的,但是我现在又要求服务端与客户端的连接不能断开,看又要求服务端那边同一时间只响应一个客户端的请求(因为上司说怕影响数据的完整性,因为客户端的请求可能是要触发同一个EVENT)
      

  19.   

    关于你的最后的要求,可以用同步和异步的方法来解决同步的话比较直观:tcpListener...
    while (true) { // 循环接收多个client
       TcpClient client;
       try { client = tcpListener.AcceptTcpClient(); }
       catch { // 如果接收出错(例如外面有代码关闭了tcpListener)
         break; // 就终止服务,跳出外循环
       }
       NetworkStream stream = client.GetStream();
       try {
          while (true) { // 循环相应client的数据
              ..... // 同步读
              .... // 处理
              .... // 同步写
          }
       } catch { } // 当出错(例如client已断开)时便可跳出内循环
       finally {
         try { stream.Close(); } catch {} // 始终关闭
         try { client.Clise(); } catch {} // 始终关闭
       }
    }异步的话编起来比较麻烦,但是比较符合良好程序设计,因为充分调用了可用的资源首先是侦听的线程:private AutoResetEvent next = new AutoResetEvent(false);
    private TcpListener tcpListener;
    private TcpClient tcpClient; // 把局部变量都提到类字段
    private NetworkStream stream;// 外部可以调用这个方法来终止侦听
    public void StopListening() {
      try { tcpListener.Close(); } catch { }
      next.Set();
    }public void Listen() {
      tcpListener = new .......
      while (true) {
        try { client = tcpListener.AcceptTcpClient(); }
        catch { // 如果接收出错(例如外面有代码关闭了tcpListener)
          break; // 就终止服务,跳出
        }
        stream = client.GetStream();
        BeginRead();
        next.WaitOne(); // 等待当前连接的client完成
      }
    }void Next() {
      try { stream.Close(); } catch { }
      try { tcpClient.Close(); } catch { }
      next.Set();
    }void BeginRead() {
      readBuffer = new Byte[10240];
      try {
        stream.BeginRead(readBuffer, 0, readBuffer.Length, new AsyncCallback(ReadCallback), stream);
      } catch {
        Next();
      }
    }然后 ReadCallback 和 WriteCallback 里都改成类似上面这个BeginRead的结构,也就是:try {
        // 原来的事情
    } catch {
      Next();
    }在WriteCallback的catch{}之后,再次调用BeginRead();
      

  20.   

    to netmicro(麦) :谢谢你的代码!不过有不明白的地方:(1)void Next() {
      try { stream.Close(); } catch { }
      try { tcpClient.Close(); } catch { }
      next.Set();
    }这个Next()把NetworkStream和TcpClient都关闭了,那么也就是服务端和客户端已经断开了连接,但我是想两者不断开连接,连续通讯的而且所有的Next()都是在Catch{}里面执行,那么读写stream不抛出异常的话,next.Set()就没办法执行了,那其他的客户端不就得不到处理了?(2)stream = client.GetStream();
    BeginRead();
    next.WaitOne(); // 等待当前连接的client完成在这里执行next.WaitOne()的话,如果之前连接的客户端不发送数据过来的话,线程会一直处于等待状态,其他的客户端也是发不了数据的
      

  21.   

    NetworkStraem的异步其实就是Socket的异步操作封装.有没有现成的TcpListener和TcpClient连续通讯知道一方退出的例子呢?
    可以通过SocketExecption来确定,如果发生这类异常那连接基本是断开了.
    我自己编写的连接池就是通过这方来释放连接的(不过我比较喜欢直接用Socket).“同步 + 线程”的运作方式到底是怎么样的?
    通常的做法是开一个新的线程来维护一个Socket请求和发送.多个连接其实可以同时进行同步接收和发送.只是工作在各自的线程中.如果有N(A,B,C,D)个客户端已经连接了服务端,可不可以令服务端在某一时间内只处理其中一个客户端(A)?其他的客户端处于等待状态,等服务端处理完A之后在处理下一个请求?
    不知道为什么你需要这样需求,因为现实中一个任务可能是多次请求发送数据.
    如果你想只能有一个连接操作业务逻辑,那你可以对业务逻辑进行同步.
      

  22.   

    不知道为什么你需要这样需求,因为现实中一个任务可能是多次请求发送数据.
    如果你想只能有一个连接操作业务逻辑,那你可以对业务逻辑进行同步.>>>因为现在的需求是客户端只发一次数据,然后等带服务端返回结果,所以就想实现这种方式的通讯,其实我也知这样设计是不合理的,但没办法,时间比较紧迫,所以最简单的方法就是一次只接受一个客户端的数据...另外,想请教楼上的各位大大,如果服务端和客户端要长期保持连接,然后服务端要按照一定的时间间隔主动发送消息到已连接的客户端(如检测客户端是否已经断开连接或者发送其他数据).我看过一些SOCKET通讯的例子,都是当客户端连接的时候把客户端的SOCKET保存在一个ARRAY动态数组里面,然后在需要发信息的时候就在数组里找到对应的SOCKET发送消息,但如果我是用TCPLISTENER和TCPCLIENT这种方式,那么服务端是把AcceptTcpClient()得到的TCPCLIENT还是AcceptTcpSocket()得到的SOCKET还是NetworkStream保存到数组里面呀?
      

  23.   

    你描述的其实就是业务逻辑同步,把查询的业务逻辑同步锁就可以了.首先你也了解TcpClient,NetworkStraem,Socket的层次.
    其调用关系
    TcpClient<->NetworkStream<->Socket(数据发送和接收).
    其实三个都一样,只是封装使用的层次不同,当然Socket是最灵活的.
      

  24.   

    首先你也了解TcpClient,NetworkStraem,Socket的层次.
    其调用关系
    TcpClient<->NetworkStream<->Socket(数据发送和接收).
    其实三个都一样,只是封装使用的层次不同,当然Socket是最灵活的.>>>恩,那么说我只要在服务端用数组将连接上来的TcpClient对象保存起来,然后等到需要的时候再对其进行操作就可以了,是吗?因为之前完全没做过网络方便的编程,所以各位大大不要谦虚我问的问题菜,嘿嘿....我把我的想法说出来,麻烦大家斧正:ArrayList clientList = new ArrayList();//服务端启动侦听:
    tcpListener.Start();while(true)
    {
       TcpClient client = tcpc.AcceptTcpClient();
       clientList.Add(client);
       NetworkStream stream = client.GetStream();   readBuffer = new Byte[10240];   stream.BeginRead(readBuffer, 0, readBuffer.Length, new AsyncCallback(ReadCallback), stream);}//服务端需要主动发消息给指定的客户端
    sendMsgToClient(clientList[x]);private sendMsgToClient(TcpClient _client)
    {
       NetworkStream _stream = _client.GetStream();
       _stream.Write("要发送的消息");
    }//服务端要关闭指定的客户端的连接
    closeClient(clientList[x]);private sendMsgToClient(TcpClient _client)
    {
       _client.Close();
    }上面是我的想法,不知道是否是正确的,请大大们指点指点呀!
      

  25.   

    是的可以这样做。
    但有些不太合理的地方,基于索引来维护连接很多时候有不确定性(到底这个索引的Cleitn是谁?)。
    最好是用类包装一下,提供UserName等相关属性用hashtable 来维护连接。这样服务器想主动发信息给谁就很明确了。
      

  26.   

    最好是用类包装一下>>>嗯...我也有这样的打算,因为看别人的代码,他们都是这样做的.另外还有一个问题,还要请教henryfan1:如我上面的代码:closeClient(clientList[x]);当执行完这个函数,因他会调用clientList[x].Close(),是不是这样就可以断开服务端与该TcpClient的通讯,该客户也不能再与服务端通讯?还需不需要关闭之前已经取得的该TcpClient的NetworkString呢?
      

  27.   

    clientList[x].Close()方法基实已经关闭相应的Socket对象,这个时候两端就不能再通信了.
    不需做其他工作.
    不过关闭后你还必须把连接从池中移走。
      

  28.   

    根据以上的对话,你要做的事情超过你现在的能力太多了首先要知道,TCP是“慢速连接”,通过牺牲速度来保证信息的完整传送
    这样,就有可能出现这种情况:一个14.4K modem连接的用户正在用龟速发几MB的数据
    如果你的服务器是“一次只服务一个客户端”,那么它就必须等待n分钟
    即使1秒之后,每隔两三秒就有一个快速的连接用光速发了几KB的数据,
    这些数据也必须等待几分钟,甚至因为等待超时了而被网络层强迫中断所以,一次只服务一个客户端的想法就不对了其次,TCP连接在.NET里的体现是“网络流”,也就是可以作为一个流Stream来读取/写入数据
    这样的话,两次连续的数据之间哪里是分界线?或者同一次数据因为网络堵塞而中间停顿了几秒钟,如何判断这次数据还没有传完?最后还有,假设使用了业务模型,你还要注意客户端故障断掉了,如何取消这个业务(最起码,如何解锁,以便其他客户端可以处理他们的业务)网络世界有太多的不稳定性最后说说我对各个方面的各自的想法:1)关于TcpListener/TcpClient/NetworkStream/Socket:前3者是后面的Socket的超级繁复的包装。在这3者里面,只有TcpListener好用;TcpClient和NetworkStream之间的关系很不明白,特别是两者都有Close,不调用好像又没有释放好资源,两者都调用的话,好像能释放的资源就只有一个——Socket,所以一般情况下,特别是很复杂的服务器端,我只用Socket,有时也用一下TcpListener2)关于将所有已连接的Socket放在一个IList里面:有一个Socket.Select方法可以在三个IList里面过滤剩下符合条件的Socket,这个模型和方法非常适合单线程编程使用,过程类似:while(服务器未停止){测试可读的IList = 程序管理的已连接Socket的IList.副本();
    测试可写的IList = 程序管理的已连接Socket的IList.选择写出缓冲区有数据的子集();
    测试有错的IList = 程序管理的已连接Socket的IList.副本();Select(测试可读的IList, 测试可写的IList, 测试有错的IList, 1);如果可读IList有剩余(即这些剩余Socket有等待接收的数据)-->
      对于每个可读Socket:
        try{
          检查 Socket.Available 属性确定有多少数据可读
          读取这些数据,放入程序管理的单独为此Socket创建的读入缓冲区
          如果刚才的读取得到0字节-->
            // 根据MSDN,“可读”并且读出0字节意味着对方已中断
            从已连接的Socket的IList里面将此Socket删除
        } catch (读取时出错) {
          从已连接的Socket的IList里面将此Socket删除
          释放该Socket的所有资源
        }
        如果读入缓冲区里面的数据形成最起码一条完整的信息-->
          处理这个信息
        循环直到读入缓冲区没有完整的信息
      循环直到可读IList完成遍历
    如果可写IList有剩余-->
      对于每个可写Socket:
        从写出缓冲区获取并移除一小截数据(最大约几KB)
        try { 写出去 }
        catch (写数据时出错) {
          从已连接的Socket的IList里面将此Socket删除
          释放该Socket的所有资源    }
      循环直到可写IList完成遍历
    如果出错IList有剩余-->
      对于每个出错的Socket:
        从已连接的Socket的IList里面将此Socket删除
        释放该Socket的所有资源
      循环直到出错IList完成遍历}这个过程负责所有低级的操作。仔细阅读可以发现两个“缓冲区”,一个“读入缓冲区”,一个“写出缓冲区”。这两个缓冲区就是用来防止我这个回复第一个内容里描述的情况。在“处理这个信息”的过程里,如果服务器有消息要回复给客户端,就把消息写到该Socket的写出缓冲区,下一个循环的时候就会把它们写出去。整个过程都在单一进程内完成,所以不会出现死锁或者资源争用情况,但是如果某个处理过程太漫长,一些客户端就可能会抱怨连接不够快了。但是因为你本身就想一次只处理一个客户端的事务,你就可以跟客户们说这是“设计使然”,抱怨无效。以此推断,这个模型可能更适合你现在的能力。你只需要研制出一个有“读入缓冲区”和“写出缓冲区”的可以与单一Socket连接起来的类就可以了。示范类结构:class SocketCollection { // 管理已连接的Sockets
      Hashtable sockets; // Key = Socket; Value = SocketBuffers
      public IList GetSocketsCopy() {
        // 返回 sockets.Keys 集合的复制品,用于可读测试和出错测试IList
      }
      public IList GetSocketsCopyWithOutputData() {
        // 返回 sockets.Keys 集合的复制品,其中没有输出数据的都先去掉
        // 用于可写测试
      }
      public void AddSocket(Socket s) { // 添加一个已连接Socket
        sockets.Add(s, new SocketBuffers());
      }
      public void RemoveSocket(Socket s) { // 删除一个Socket并将其断开、释放资源
        sockets.Remove(s);
        try { s.Shutdown(SocketShutdown.Both); } catch { }
        try { s.Close(); } catch { }
      }
    }
    class SocketBuffers { // 一个包含读入缓冲区和写出缓冲区的类
      public InputBuffer InputBuffer { get; }
      public OutputBuffer OutputBuffer { get; }
    }
    class InputBuffer { // 读入缓冲区
      public void AppendData(byte[] data); // 从Socket读出的数据会写入这个缓冲区
      public bool HasCompletePieceOfData(); // 判断是否有完整的数据
      public byte[] GetCompletePieceOfData(); // 获取一组完整的数据并从缓冲区删除
    }
    class OutputBuffer { // 写出缓冲区
      public void AppendData(byte[] data); // 从主过程写给客户段的数据会首先写入这里
      public bool HasData(); // 判断是否有数据要发送出去
      public byte[] GetData(int maxBytes); // 获取一截数据并从缓冲区删除
    }
      

  29.   

    to netmicro(麦):非常感谢你的回复!实在是太有用了,先按照你的建议写个测试程序试试先!
      

  30.   

    对于每个可写Socket:
        从写出缓冲区获取并移除一小截数据(最大约几KB)
    >>>请问这个作用是什么?
      

  31.   

    因为你现在整个模型都用同步处理,于是如果有几MB的数据准备发送,而你一次过就全部发出去的话,程序就会“死”在这里——要等几MB的数据都发完了才能继续所以每次都只截取一小段来发,达到“公平使用资源”的原则
      

  32.   

    晚上逛了一下博客员,查了一些关于Socket编程的东东看,其中一遍文章,有一个这样的回复:"NetworkStream是在同步的情况下使用的,异步通信的话,不要使用此类。"以我现在的水平来看,又迷惑了,NetworkStream不是提供BeginXXX和EndXXX的异步读写吗?
    为什么还说它是在同步情况下使用的呢?疑惑ing...望各位大大解惑...
      

  33.   

    因为你现在整个模型都用同步处理,于是如果有几MB的数据准备发送,而你一次过就全部发出去的话,程序就会“死”在这里——要等几MB的数据都发完了才能继续所以每次都只截取一小段来发,达到“公平使用资源”的原则>>>明白了!也就是数据分块的问题吧?请教一下"netmicro(麦)":如你所说,如果我有几MB的数据需要发送给客户端,而服务端每次只发送很小的一块,那么这些小块怎么在另外一端重组呢?可否提供一些相关的资料或代码看看?实在是惭愧,因为之前在公司都只是做针对数据库的开发,一做网络方面的东西就晕头了...
      

  34.   

    chunked包在发送之前都会象小学生排队一样给打上标签,到了目的地根据标签重新组合,这就是协议了。如果楼主想要相关http协议资料,下面这些连接会很有帮助:
    简易入门:
    http://www.jmarshall.com/easy/http/HTTP1.0
    http://www.ics.uci.edu/pub/ietf/http/rfc1945.htmlHTTP1.1
    http://www.w3.org/Protocols/rfc2616/rfc2616.html
      

  35.   

    不知道为什么楼上忽然提到HTTP反正,回答楼主问题:
    1) NetworkStream 的同步问题:
    的确有人提到过,.NET 1.x 里面的NetworkStream,它所提供的BeginXXX和EndXXX其实是另开一个线程同步执行,于是如果大量使用的话就会造成线程池枯竭。但是.NET 2.0已经将其改为真正的异步了。2) 常见的做法,是在数据的开头附上数据长度
    一般来说,2字节的ushort已经够大,因为一条数据64KB已经比较恐怖
    但是你可能还会担心会超出,所以就写int吧,一条数据怎么也超不过2GB吧
    但是还是要小心恶意客户端用这个int值造成拒绝服务攻击(占用2GB内存的缓冲区?!)
    无论如何,以下用4字节的长度标识来举例;具体应该用几个字节,应该怎么在可扩展性和安全性之间权衡,是你自己要思考的事情。从二进制网络流的角度看,互相传送的数据的格式都是:[4字节][     n 字 节   ][4字节][     n 字 节   ][4字节][     n 字 节   ]...其中所有的[4字节]都表示紧接着的真正的数据的长度
    这样也方便缓冲区的分配
    首先读取4个字节,然后用BitConverter类将其转成int,
    然后在接着的读取中,用一个累加器时刻监测读够这个数字没有
    读够了就可以开始处理了(但是在截取这条完整的消息的时候,小心不要丢掉“读得太多”的部分——那是下一条消息的长度标识和数据的开头)这样的话,即使一条大数据被分成无数块,而且因为网络堵塞需要一天才能传完,也因为双方“心里有数”而不会贸然开始处理收了一半的数据
      

  36.   

    to netmicro(麦):从二进制网络流的角度看,互相传送的数据的格式都是:[4字节][     n 字 节   ][4字节][     n 字 节   ][4字节][     n 字 节   ]...其中所有的[4字节]都表示紧接着的真正的数据的长度>>>>>>(1)按照我的理解“[4字节][     n 字 节   ]”是当前发送端发送出的数据格式,其中[4字节]应该是后面的[   n字节   ]的长度,是这样吧?还是指整个数据的长度(如完整数据需要发送3次)?(2)如果N个“[4字节][     n 字 节   ]”发送到了接收端,由于没有编号,接收端怎么吧它们重新组合成完整可用的数据呢?对数据分块传输和组合,听就听得多,但不清楚是怎么实现得望不吝解答,谢谢!
      

  37.   

    1) NetworkStream 的同步问题:
    的确有人提到过,.NET 1.x 里面的NetworkStream,它所提供的BeginXXX和EndXXX其实是另开一个线程同步执行,于是如果大量使用的话就会造成线程池枯竭。但是.NET 2.0已经将其改为真正的异步了。>>>>>>>按照我的理解,不单是NetworkStream的BeginXXX和EndXXX是另开一个线程同步执行,我觉得Socket类所提供的BeginXXX和EndXXX也是另开一个线程同步执行...不知道我是不是又理解错了...那么真正的异步又是什么概念呢?
      

  38.   

    1)[4字节][         n字节            ] 表示一段完整的数据;
    这一段数据可能会被斩件传送;甚至连最开头的4字节也可能被斩断之所以不怕被斩件,是因为另一头知道它首先一定要读完4个字节,得到了下一条数据的长度,然后就一定要读完这n字节,才是一条完整的数据。2)使用TCP通讯不需要担心重组;TCP会保证数据会按照发送的顺序到达对方。3)如果你知道.NET里面所有委托都可以用.NET自己的异步调用机制来异步调用,就大概可以明白为什么NetworkStream被别人称为“是同步的东西”。它利用的就是.NET的异步调用委托的功能,另开一个线程同步收发数据但是Socket就不同了;它是直接封装操作系统级别的异步I/O,即你调用BeginXXXX之后,它把必要的东西封装好之后就直接把调用封送到系统级别,自己就退出了。到系统通知它I/O完成之后,它就把结果反封送回来,所以是“不独占一个线程”的
      

  39.   

    2)使用TCP通讯不需要担心重组;TCP会保证数据会按照发送的顺序到达对方。>>>如果我有一条数据([4字节][   n字节(n = 1020)   ])共1024字节需要由A发送到B,每次发送其中的256字节,共分4次(分别是P01,P02,P03,P04)发送完毕,按照你上面的意思,就是说TCP/IP协议会按照P01 - P04的顺序发送到B,而在B用一个_buffer(Byte[] _buffer = new Byte[1024])经过4次读取存储它们,那么是不需要关心它们的顺序的,因为它们在发送的时候已经按 P01 - 04的顺序发送它们发送过来全部都保存在_buffer之后,肯定是以"[4字节][   n字节(n = 1020)   ]"的形式出现的?PS:这个帖子我暂时还不想结,另开一个帖给分,呵呵...
      

  40.   


    其实,因为你现在已经预先知道要读多少了,所以读的流程就可以简化成这样:
    (假设从0或者从一条新的数据开始)首先的任务,是读取4个字节所以,
    buffer = new byte[4]; (byte[] buffer 在某个用来包装的类里面已经定义好)
    read = 0; (int read 在同一个类里面已经定义好;意义:已读取的字节数;read是过去时)
    length = 4; (int length 表示本消息的总长度)
    isReadingLength = true; (bool isReadingLength 表示当前是否正在读取数据长度)然后在可读的时候:int available = Socket.Available;
    if (available == 0) // 可读而且有0个字节=连接死掉了
      throw new SocketException(10035); // 这个只是用来代码重用;
                                        // 在外边的catch()里面有中断连接的代码
    int remaining = length - read; // 当前还有这么多字节没有读
    int toRead = available > remaining ? remaining : available; // 决定要读多少
    int received = Socket.Receive(buffer, read, toRead, SocketFlags.None); // 将可用的数据接入缓冲区的适当的位置
    read += received; // 累加
    if (read == length) // 已经读完
    {
      if (isReadingLength) // 读完的是下个数据的长度
      {
         read = 0;
         length = BitConverter.ToInt32(buffer, 0); // 将读到的4个字节转成 int
                // 提示:写出去的时候,用的是 byte[] BitConverter.GetBytes(int)
         buffer = new byte[length];
         isReadingLength = false;
         // 这样就准备好接收真正的数据了
      }
      else { ... } // 这里就可以处理保存在buffer里面的信息了
    }再啰嗦一句,int做长度还是有点危险……要么换成ushort(System.UInt16),要么在读完length之后判断一下是否太夸张,例如在 if (read == length) if (isReadingLength) 里面,将 length 计算出来之后,if (length > 1000000 /* 1MB */) throw new NotSupportedException();
      

  41.   

    补充一下:第一行“Socket.Available”:Socket要改成当前正在处理的Socket的对象,不要当作静态属性了;int toRead = available > remaining ? remaining : available;这一行的作用就是将属于下一段(下一个数据的长度标识,或者当前长度标识所指的数据)的内容留在Socket自己的缓冲区,这样就不会“读得太多”,让属于下次的东西下次再处理