事先说明,   Server 和 Client  同步通讯。   Server 监听数量100 个(可以更大)   Server Accept() 一个客户端后,就处理,处理完成后,再Accept()下一个(这里不讨论多线程)   问题就在于,如果客户端Socket被服务端Accept()之前就断开了,当然断开之前客户端是向服务端发了数据的。之前的高手的办法均无法检测到这个问题。
 (不要用向客户端发数据确认异常的办法,这个=自宫)   像什么Available ,Connected 之流的就不要说了。
   尝试的“经典”办法有:   s=SockeServer.Accept();
   if (s.Poll(-1, SelectMode.SelectRead))  //第一步判断
   {
       byte[] b = new byte[16];
       int nRead =s.Receive(b,System.Net.Sockets.SocketFlags.Peek );
       if (nRead == 0)
       {
          //socket连接已断开
       }
       else//第二步判断
       {
          //socket连接仍然有效??
       }
   }
   实际测试的结果,居然第一步能够过,第二步也能够过, 那socket连接仍然有效??
//------------------------------------------------------------------------------------
   如果你要测试的话, 你建立两个最简单的客户服务端程序。      测试的过程是:服务端程序的listen(..)  到 Accept()的代码间加一个延时20秒      客户端在服务端程序正在延时(20秒)的时候,连上服务器后发一串任意数据,
      然后立即 ClientSocket.close(),确保在服务端Accept()之前就断开.      这种情况下,服务端如何确定Client 已经断开??
     

解决方案 »

  1.   

    //Client端的代码:
    using System;
    using System.Drawing;
    using System.Collections;
    using System.ComponentModel;
    using System.Windows.Forms;
    using System.Data;using System.Threading;
    using System.Net;
    using System.Net.Sockets;
    using System.IO;
    namespace TestClient
    {
    /// <summary>
    /// Form1 的摘要说明。
    /// </summary>
    public class Form1 : System.Windows.Forms.Form
    {
    private System.Windows.Forms.TextBox textBox1;
    private System.Windows.Forms.Button button1;
    /// <summary>
    /// 必需的设计器变量。
    /// </summary>
    private System.ComponentModel.Container components = null; public Form1()
    {
    //
    // Windows 窗体设计器支持所必需的
    //
    InitializeComponent(); //
    // TODO: 在 InitializeComponent 调用后添加任何构造函数代码
    //
    } /// <summary>
    /// 清理所有正在使用的资源。
    /// </summary>
    protected override void Dispose( bool disposing )
    {
    if( disposing )
    {
    if (components != null) 
    {
    components.Dispose();
    }
    }
    base.Dispose( disposing );
    } #region Windows 窗体设计器生成的代码
    /// <summary>
    /// 设计器支持所需的方法 - 不要使用代码编辑器修改
    /// 此方法的内容。
    /// </summary>
    private void InitializeComponent()
    {
    this.textBox1 = new System.Windows.Forms.TextBox();
    this.button1 = new System.Windows.Forms.Button();
    this.SuspendLayout();
    // 
    // textBox1
    // 
    this.textBox1.Location = new System.Drawing.Point(-16, 83);
    this.textBox1.Multiline = true;
    this.textBox1.Name = "textBox1";
    this.textBox1.ScrollBars = System.Windows.Forms.ScrollBars.Vertical;
    this.textBox1.Size = new System.Drawing.Size(464, 152);
    this.textBox1.TabIndex = 3;
    this.textBox1.Text = "";
    // 
    // button1
    // 
    this.button1.Location = new System.Drawing.Point(320, 27);
    this.button1.Name = "button1";
    this.button1.Size = new System.Drawing.Size(128, 32);
    this.button1.TabIndex = 2;
    this.button1.Text = "Start Client";
    this.button1.Click += new System.EventHandler(this.button1_Click);
    // 
    // Form1
    // 
    this.AutoScaleBaseSize = new System.Drawing.Size(6, 14);
    this.ClientSize = new System.Drawing.Size(456, 262);
    this.Controls.Add(this.textBox1);
    this.Controls.Add(this.button1);
    this.Name = "Form1";
    this.Text = "Form1";
    this.ResumeLayout(false); }
    #endregion /// <summary>
    /// 应用程序的主入口点。
    /// </summary>
    [STAThread]
    static void Main() 
    {
    Application.Run(new Form1());
    } private void button1_Click(object sender, System.EventArgs e)
    {

    Thread td = new Thread(new ThreadStart(this.WorkClient));
     
    td.Name = "WorkClient";
    td.Start();   
    } private void WriteLog(string str)
    {
    if(str.IndexOf("\r")<0)
    {
    str+="\r";
    }
    this.textBox1.Text =this.textBox1.Text +str;

    }
     
    public void WorkClient()
    {
    Socket SocketClient  = new Socket(AddressFamily.InterNetwork, SocketType.Stream,ProtocolType.Tcp); 
    IPEndPoint ep = new IPEndPoint(IPAddress.Parse("192.170.1.110"),11000);
    try
    {
                    this.WriteLog("连接服务器...");
    SocketClient.Connect(ep);
    if(SocketClient.Connected)
    {
        byte [] StartPacket = System.Text.Encoding.Default.GetBytes("hellow world !! f820200506011030710");
        SocketClient.Send(StartPacket);
    this.WriteLog("已经发送数据hellow world !! f820200506011030710");
    SocketClient.Shutdown(System.Net.Sockets.SocketShutdown.Both);
    SocketClient.Close();

    }
    }
    catch(Exception cs)
    {
         this.WriteLog(cs.ToString());

    }


    }
    }
    }//--------------------------------------------------------------------------
      

  2.   


    //Server端的代码:using System;
    using System.Drawing;
    using System.Collections;
    using System.ComponentModel;
    using System.Windows.Forms;
    using System.Data;using System.Threading;
    using System.Net;
    using System.Net.Sockets;
    using System.IO;
    namespace TestServer
    {
    /// <summary>
    /// Form1 的摘要说明。
    /// </summary>
    public class Form1 : System.Windows.Forms.Form
    {
    private System.Windows.Forms.Button button1;
    private System.Windows.Forms.TextBox textBox1;
    /// <summary>
    /// 必需的设计器变量。
    /// </summary>
    private System.ComponentModel.Container components = null; public Form1()
    {
    //
    // Windows 窗体设计器支持所必需的
    //
    InitializeComponent(); //
    // TODO: 在 InitializeComponent 调用后添加任何构造函数代码
    //
    } /// <summary>
    /// 清理所有正在使用的资源。
    /// </summary>
    protected override void Dispose( bool disposing )
    {
    if( disposing )
    {
    if (components != null) 
    {
    components.Dispose();
    }
    }
    base.Dispose( disposing );
    } #region Windows 窗体设计器生成的代码
    /// <summary>
    /// 设计器支持所需的方法 - 不要使用代码编辑器修改
    /// 此方法的内容。
    /// </summary>
    private void InitializeComponent()
    {
    this.button1 = new System.Windows.Forms.Button();
    this.textBox1 = new System.Windows.Forms.TextBox();
    this.SuspendLayout();
    // 
    // button1
    // 
    this.button1.Location = new System.Drawing.Point(336, 8);
    this.button1.Name = "button1";
    this.button1.Size = new System.Drawing.Size(128, 32);
    this.button1.TabIndex = 0;
    this.button1.Text = "Start Server";
    this.button1.Click += new System.EventHandler(this.button1_Click);
    // 
    // textBox1
    // 
    this.textBox1.Location = new System.Drawing.Point(0, 64);
    this.textBox1.Multiline = true;
    this.textBox1.Name = "textBox1";
    this.textBox1.ScrollBars = System.Windows.Forms.ScrollBars.Vertical;
    this.textBox1.Size = new System.Drawing.Size(464, 152);
    this.textBox1.TabIndex = 1;
    this.textBox1.Text = "";
    this.textBox1.TextChanged += new System.EventHandler(this.textBox1_TextChanged);
    // 
    // Form1
    // 
    this.AutoScaleBaseSize = new System.Drawing.Size(6, 14);
    this.ClientSize = new System.Drawing.Size(472, 238);
    this.Controls.Add(this.textBox1);
    this.Controls.Add(this.button1);
    this.Name = "Form1";
    this.Text = "Form1";
    this.ResumeLayout(false); }
    #endregion /// <summary>
    /// 应用程序的主入口点。
    /// </summary>
    [STAThread]
    static void Main() 
    {
    Application.Run(new Form1());
    } private void button1_Click(object sender, System.EventArgs e)
    {

    Thread td = new Thread(new ThreadStart(this.WorkServer));
     
    td.Name = "WorkServer";
    td.Start();   
    } private void WriteLog(string str)
    {
    if(str.IndexOf("\r")<0)
    {
       str+="\r\n";
    }
    this.textBox1.Text =this.textBox1.Text +str;

    }
    public void WorkServer()
    {
        ArrayList ListenerList = new ArrayList();
    IPAddress ipAddress = IPAddress.Parse("192.170.1.110");
    IPEndPoint localEndPoint = new IPEndPoint(ipAddress, Convert.ToInt32(11000));
    Socket ServerSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
    ServerSocket.Bind(localEndPoint);
    ServerSocket.Listen(100);
        ListenerList.Add(ServerSocket);              
    while(true) // while(conditions)
    {
    ArrayList tmplist =      new ArrayList();
    foreach(object o in ListenerList)
    tmplist.Add(o);

    Thread.Sleep(8000); //这个时候 客户端显然已经完数据而且断开了。!!

    if(tmplist.Count > 0)
    {
    Socket.Select( tmplist, null, null, 200);
    if(tmplist.Count > 0 ) 
    {
    this.WriteLog("有数据!!");     Socket port=((Socket)tmplist[0]).Accept(); 

    if (port.Poll(-1, SelectMode.SelectRead ))
    {
    byte[] b = new byte[16];
     
    if(port.Receive(b,System.Net.Sockets.SocketFlags.Peek ) == 16)
    {

    System.Text.StringBuilder msg=new System.Text.StringBuilder();

    for(int t=0;t<16;t++)
    {
    msg.Append(Convert.ToChar(b[t]));
    } this.WriteLog(string.Format("try peek  {0}",msg.ToString()));
    }
    else
    {
    this.WriteLog(string.Format("Payphone has already DisconnectedAAA "));
    port.Shutdown(SocketShutdown.Both);
    port.Close();
    continue;
    }
    }
    else
    {
    if(port!= null)
    {
    port.Close();
    }
    this.WriteLog(string.Format("Payphone has already DisconnectedBBB ")); continue;
    }
     
    }
    }
    }
    } private void textBox1_TextChanged(object sender, System.EventArgs e)
    {

    }
    }
    }你只需要建立两个个简单的C#工程,将Form1.cs下的所有代码分别替换成Client代码或者Server代码
      

  3.   

    我对socket没有太深研究,一般socket是基于tcp/ip,他的输入输出,也就是说比如send以后,tcp/ip必然尽最大努力保证数据的完整性,所以socket输入输出都是针对缓冲区,msdn上也有讲解,当你用shutdown后,socket会发送和接收该套接字上的所有数据。所以客户端虽然close了但socket并没有真正的断开,或者说,tcp/ip已近把数据接收了,输入输出缓冲区还有数据,socket认为客户端还在连接,
      

  4.   

    这个没有必要吧,既然已经accept了,就可以认为这个连接已经有效了,以后再处理断开的问题。
    如果想避免维护一个无效的连接,可以让客户端的同时发送一段验证信息(vc可以做到,不知道C#怎么做),服务器收不到就认为连接失败。如果是为了防止攻击,最好使用防火墙
      

  5.   

    引用 hdt(倦怠) ( ) 信誉:120  2006-04-04 14:46:00   -->   我对socket没有.....   你的回复通常都很有责任心,在此表示感谢。   因为,我们的工程往往涉及到Socket收发数据的地方,都包装在复杂的协议过程里面,如果要在复杂的协议过程里面得出之前Accept()出来的Socket已经断开,那将浪费大量的时间和资源。   因此我希望在先判断这个Accept()出来的Socket是否连着,再决定是否启动复杂过程。
    这也是我对这个问题追根的原因。
      

  6.   

    我记得以前用vc做socket,针对大数据 ,不是一下子 recv,而是分块recv , 每次recv都检测返回值已确定是否出错。
      

  7.   

    我做的数据并不大,每个Recveive()一次铁定收一个字节, 只是协议(封装)复杂。协议中收到了一个字节后,可以决定后面的事情怎么做, 而客户端连接上了服务端后, 因为服务端非常忙,并没有马上Accept(), 实际上客户端就是进入了Listen的队列而已。 但是这个时候,客户端连接是成功的!所以客户端发数据了, 当然发送了几次,没有响应,就断开。 而当服务器有空闲了,自然会从Listen中取出Socket, 如果这个Socket能用,就建立特定的(高封装)处理流程, 如果这个Socket不能用, 则不要浪费时间或资源来为这个Socket建处理流程了。
      

  8.   

    你对socket理解有一定问题,客户端connect后一般会引起阻塞,除非blocking设为false,而是用面向连接的协议,如tcp,blocking 设为false,会引发异常,所以说,客户能send数据,服务器端必然已经accept,不存在你说的情况,
      

  9.   

    在我上面的代码中, 客户端确实能够在服务端Accept()之前就发送数据的,而且客户端断开了以后, 然后服务端再ACCEPT(),  仍然能够读取数据。 客户端的blocking 默认是为TRUE的。
      

  10.   

    仔细看了一下你的代码,服务器端代码有误,listen 只是代表服务器端诊听,并不是代表客户端有连接,
      

  11.   

    建议找一本socket基础编程的书看一看,你的代码socket处理逻辑有问题
      

  12.   

    我测试的Listen 侦听,的第一个参数tmplist,是引用带入的,
     如果没有客户端来连接接的话,
    Socket.Select( tmplist, null, null, 200);
    如果没有侦测到客户端到来的话, 200毫秒后,tmplist 会被清空,tmplist.Count 是为0的,否则>0。我之所以在Accept() 之前放Socket.Select(..) ,就是为了检测是否有连接请求来, 因为Accept() 可能导致程序挂起。
      

  13.   

    其实正常情况下, 我这个程序Listen 的队列30个, 并行数据库查询/写入60路,同时通讯的Socket线程500路, 服务端程序通过监视CPU和内存的占用情况,来决定是否Accept()新的通道。 我测试过多次, 每个Client每次通讯5分钟左右,CPU和内存几乎80% ,一个晚上能够完成50000次的通讯, 但是这个里面,就有近700次失败,失败原因Accept()后,进入处理流程之后,才发现客户端已经退出, 就浪费了资源。  我设计的程序,并不是只要Listen的队列里面有连接,就马上Accept()的,系统资源没有达到指定的门限,我狂Accept(),否则,就等待。
      

  14.   

    listen队列里的是客户的连接请求,TCP要三次握手,如果你不ACCEPT,那无握手完成,无建立面向连接的信道,那客户端又如何发数据给服务器端呢?
      

  15.   

    不知是不是你的全部代码,就你所贴,客户端会出现,
    + cs {"由于目标机器积极拒绝,无法连接。" } System.Exception
      

  16.   

    个人感觉你没有在正确的时间里accept,
      

  17.   

    回复: hdt(倦怠) ( ) 信誉:120 
    不知是不是你的全部代码,就你所贴,客户端会出现
    //---------------------------------------------------------
    服务端绑定的IP 和客户端Connect的IP 需要改成你测试电脑的IP
    ,我拷贝的代码中是“192.170.1.110”, 改成你电脑的IP 就可以了。
      

  18.   

    回复:huangsuipeng(hsp|I love foxpig) ( ) 信誉:100 
    //---------------------------------------------------------- 在服务端Accept之前,只要Listen 的队列没有占满, 客户端Connect 是可以成功的,
     实际上,我测试发现就是这样的。
      

  19.   

    我贴上去的代码,分别是客户端,服务端的完整的代码。    创建一个C#工程, 替换Form1.cs里面的内容,就可以。或者我的MSN:    [email protected] 上我可以直接传递。
      

  20.   

    "如果客户端Socket被服务端Accept()之前就断开了"
    如果是故意的话,这就是通常讲的DOS,利用TCP三次握手的漏洞,制造半连接状态,
      

  21.   

    回复huangsuipeng(hsp|I love foxpig) ( ) 信誉:100   可以理解成“故意断开”,但是对于客户端实际流程,却不是故意的, 而是客户端连接上了服务端, 发了数据,但是在指定的时间内得不到服务端的回应, 于是自动断开。结果自然和“故意断开”是一样的了.
     客户端断开后, 在服务端的Listen的队列里面这个Socket 仍然存在, 而且也能够被Accept()出来, 就在这个关键的时候, 无法判断这个被Accept()出来的Socket是否有效。
      

  22.   

    先假设Accept()出来的Socket经过了三次握手,但是现在无效了,那么看s=SockeServer.Accept();
       if (s.Poll(-1, SelectMode.SelectRead))  //第一步判断
       {
           byte[] b = new byte[16];
           int nRead =s.Receive(b,System.Net.Sockets.SocketFlags.Peek );
           if (nRead == 0)
           {
              //socket连接已断开
           }
           else//第二步判断
           {
              //socket连接仍然有效??
           }
       }第一步通过的原因是
    如果当连接断开了,关闭了,也是返回true的
    第二步通过的原因是
    如果的确在ACCEPT前已经三次握手,那就比较容易解释了,因为连接建立了,服务器端当然会BUFFER客户发过来的数据,SO你会读到数据,nRead != 0.
      

  23.   

    MAYBE为了提高性能,在listen队列里就做了三次握手吧~~以前也无考虑过这个问题~
    用CONNECTED判断是无用的,他只记住最近一次操作的成功与否,最可靠的还是从服务器端发一个byte的东西回去客户端,收不到ack,自然会有exception,然后根据exception的error codes作相应处理了
      

  24.   

    回复:huangsuipeng(hsp|I love foxpig) ( ) 信誉:100      在VC++6.0中, 我用recv(...MSG_PEEK)是可以检测到Client已经关闭。
         C#中,同样的recveive(..PEEK), 就不行。      Accept() 之后, 就是通过   应用层   增加少量协议(发 SYN =0X16  ,收ACK=0x06 as ),来确保客户端口是否有效?是吧?
      

  25.   

    (huangsuipeng:MAYBE为了提高性能,在listen队列里就做了三次握手吧~~)listen队列只是侦听传入的偿试连接的socket,并没有进行三次握手,
    三次握手时服务端需要创建新的socket与请求连接的socket进行通信,
    通信成功才返回新的socket,如果在listen时就三次握手,那就不必要accept了.所以在服务端accept之前,客户端就断开了连接了,accept是不会成功的楼主再检查检查程序,说不定什么地方出了什么逻辑错误.......
      

  26.   

    回复: kugua70708() ( ) 信誉:100 
     //-----------------------------------------------------  在服务端accept之前,客户端就断开了连接了,accept是可以成功的。
     
      上面贴代码跟踪说明了这一点, 事实上不仅Accept()成功,而且收码也成功!  另外,我在VC++6 里面用类似的代码流程, 发现Accept()成功并不能说明客户端当前是连着的。
      

  27.   

    服务端绑定的IP 和客户端Connect的IP 需要改成你测试电脑的IP
    ,我拷贝的代码中是“192.170.1.110”, 改成你电脑的IP 就可以了
    ================================================
    我确实已经改了ip还有我还是感觉是你的程序问题,第一你怎么保证客户close的socket就是,服务器能接受那次数据的socket,也就是说你怎么保证server 和 client之间的同步。
    还有
    如果你对数据完备性没有太高的要求,我这里所说的完备性指的是,客户发送数据,服务器没必要一定处理。为什么不使用udp协议。而使用面向连接的tcp?如果你一定要保证数据的完备,又不想阻塞,你为什么不利用socket的异步处理?
      

  28.   

    我只是以前为了好玩研究过socket,没有太深的了解,说的不一定对。仅供参考。
      

  29.   

    up
    各位继续。
    学习ing
      

  30.   

    Accept动作之后,做Receive动作。收到-1或者0个字节,就表示连接断开了。
      

  31.   

    我们老师把socket(udp)通信比喻成手机发短信.是发出后不管的,不需要握手的.
    只是经过层层协议包装后由internet发向目的地.可能会在中途丢失的.
    就是发到了目标端,可能也在队列中,不一定会执行啊.
    没有回送确认怎么来判断?
    说不定我发了消息后就关机了.....找一个座位先...
      

  32.   

    楼上老师说的socket(udp)就是不需要握手的。同时丢包也厉害。现在大部还是走的TCP的。这个需要握手,并一直保持连接。
      

  33.   


    try
    {
    temp="";
    back="";
    byte[] bytes=new byte[1024];//
    int bytesRec=receiveSocket.Receive(bytes);//
    if (bytesRec==0)
    {
    已断 }
    }
    catch(Exception ex)
    {
       已断
    }
    我用上面的方法来判断。很准的。
      

  34.   

    回复: hdt(倦怠) ( ) 信誉:120 
        我确实已经改了ip......
     //----------------------------------------------------------
      1.首先服务器监听的端口,只有客户端程序访问, 而且客户端的程序简化到了: 连接服务端一次,发送数据一次,关闭一次。 因此服务端那边的相应的操作也只有一次。 
      
      2.实际上程序对数据完备性要求很高, 客户端发出的数据,服务端都必须处理,如果客户端还连着的话, 不能用UDP。  3.异步处理在流程控制上改动太,客户端是专门的硬件产品, 目前应在同步处理上  4.感谢你能够测试我的测试代码, 有必要的话,服务端口的Select(..)全部都不要了,listen()后面,“延时”一段时间直接Accept()[创建,绑定,监听,Accept,通讯,关闭客户端],客户端的程序没有多少简化了[创建,连接,发送,关闭]。
      (“延时”的目的,是为了保证服务端在Accept()之前,客户端就完成了所有的动作)
      
      
      

  35.   

    回复:ruanruoshi(软若石) ( ) 信誉:100    你的做法,我已经试过, 收数据已经不是问题,
    bytesRec=receiveSocket.Receive(bytes);//
    if (bytesRec==0)
    {...}我上面贴的代码中,bytesRec >0, 而且接(第一次)收数据也不会异常。
      

  36.   

    我这里测试出来的结果是关闭了客户端,用netstat命令仍然显示TCP端口在监听中,说明握手应该先完成了吧?
      

  37.   

    楼主多给分:
      经过我仔细分析发现你的问题是Socket.Select方法使用不当,你是这样用的:Socket.Select( tmplist, null, null, 100);这样只检查是否可读,当然是可读的,因为客户端发来了数据;你应这样:Socket.Select( tmplist, tmplist, null, 100);第二参数检查可写性,当然不可写,因为已经断开。这样你的问题解决了。接分!!!
      

  38.   

    aSocket.Listen(10);
    if (!aSocket.Connected) {
       Console.WriteLine("Winsock error: "
          + Convert.ToString(System.Runtime.InteropServices.Marshal.GetLastWin32Error()));
    }
      

  39.   

    用TcpClient类.用不着socket
    因此我希望在先判断这个Accept()出来的Socket是否连着
    你这个想法是错的。tcp就是信任的连接,交互的时候还去检测是否连接,多此一举,try一下就行了。
      

  40.   

    不懂的不要乱讲,Socket.Select方法就是检查连接情况的,我发现楼主的设计结构还是满有创意的,想问楼主是为公司修改还是自己的程序??
      

  41.   

    看你的意思,实际上是想判断TCP连接后用来传输的那个SOCKET是否断开吧?这种情况下,客户端要断开TCP连接或者是超时或者是主动断开。实际上,这就是个判断TCP连接还在不在的问题。TCP连接有各种状态,例如LISTENING,SYN_SENT,SYN_ACK等等,其中ESTABLISHED表示已经成功连接,正在或者将要传输数据,如果不是这个状态,那么该连接一定是处在不稳定状态,可以看作“还未连上”或者“正在断开”。我没用过C#的SOCKET编程,不过应该不会取不到这个状态吧?你可以通过该状态来判断。
      

  42.   

    &not;&not;&not;&not;&not;&not;&not;&not;&not;&not;&not;&not;&not;&not;&not;&not;&not;&not;&not;&not;&not;&not;&not;&not;&not;&not;&not;&not;&not;&not;&not;&not;&not;&not;&not;&not;&not;&not;&not;&not;&not;&not;&not;&not;&not;&not;&not;&not;&not;&not;&not;&not;&not;&not;&not;&not;&not;&not;&not;&not;&not;&not;&not;&not;&not;&not;&not;&not;&not;&not;&not;&not;
    首先,可以肯定地说,客户端的connect并不是在服务端accept之后才返回的。只要服务端listen队列未满,connect就能“立即”返回。而accept测试的并不是连接本身,而是listen队列——只要listen队列不空,accept就立即返回。换句话说,三次握手在accept之前就完成了。其次,即使是这样,服务端仍然可以通过accept返回的socket判断连接是否断开。LZ的问题在于,对recv的理解较片面——并非在任何情况下客户端关闭,recv都返回0的,也可能返回负值,可以借此判断客户端是否断开:
       s=SockeServer.Accept();
       if (s.Poll(-1, SelectMode.SelectRead))  //第一步判断
       {
           byte[] b = new byte[16];
           int nRead =s.Receive(b,System.Net.Sockets.SocketFlags.Peek );
           if (nRead <= 0)   //注意这里,==改成了<=
           {
              //socket连接已断开
           }
           else//第二步判断
           {
              //socket连接仍然有效??
           }
       }
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
      

  43.   

    嗯,试了一下,确实出现楼主的问题,由于socket  recv  和 write 是针对缓冲区的,只有在缓冲区满时,才会引发异常,建议楼主从这点想办法。
      

  44.   

    请看:http://jll.cnblogs.com/archive/2005/07/29/203090.html
      

  45.   

    回复:GXY2005(不好!我看見豬在天上飛) 
       
         是为公司做程序(我贴的代码,可以说明,我的程序会遇到这个问题)
         本来我也可以不理这些事情的,只是想扩大服务器的容量和效率,或者抱着研究的想法去做这个事情。
      

  46.   

    帮忙回答一下这个问题吧,超高手的
    http://community.csdn.net/Expert/topic/4665/4665682.xml?temp=.6007807
      

  47.   

    我发现我上面的建议也是无效,经过重新调试,找到一条解决办法(本人最近刚做了个多人聊天系统所以对socket热情很高,望见谅):
    Socket.Select和port.Poll都没用,只能这样保证马上检测出离线,Accept后面加上写数据,
    this.WriteLog("有数据!!");Socket port=((Socket)tmplist[0]).Accept();
    if (port.Poll(100, SelectMode.SelectWrite))
    {
    string s="yes ok"; byte[] bt=new byte[1];
    bt=System.Text.Encoding.UTF8.GetBytes(s);
    port.Send(bt,0,bt.Length,SocketFlags.None);}然后把WorkServer()里的东东都放到try catch 里,执行到port.Receive马上会报错。(楼主可以试试我说的对不对)
    所以你可以先利用这个判断这个Accept()出来的Socket是否连着,再决定是否启动复杂过程。
      

  48.   

    回复: wanguodu(足文字D) ( ) 信誉:100    .... //注意这里,==改成了<=
    //---------------------------
       是漏掉了小于0 的情况,我加上后,做了测试,nReads总可以返回>0,(比如16,取决于服务端的接收BYTE[] 的长度定义,或者客户端口发送的长度,二者取短吧)   ==改成<= 使得程序更加严谨, 真正的问题还需要继续研讨论!
      

  49.   

    回复:GXY2005(不好!我看見豬在天上飛) 
        我发现我上面的建议.......
    //---------------------------------------------------------------
       我之前也用了你说的办法, 可以,我只希望服务端口程序改程序, 客户端是现有的硬件设施,你说的办法,就是发一个探测性的数据包回应给客户端, 客户端自然会重发第一个数据包的办法。    这个办法是可以先确定SOCKET 是否有效! 如果客户端是数万现成的硬件终端, 恐怕这第一个探测性的 非协议的包真是要好好掂量!
      

  50.   

    我对Socket了解不多,不过提供若干绕过此问题的建议。1.既然你把Queue做在Accept,为什么不把Queue改在复杂处理呢?也就是用多线程Accept,但是Accept后就仅仅是把处理任务加入处理队列,处理还是单线程的。当一个处理请求从处理队列出来时,才检查该连接是否还存在,然后决定有没有必要进行此处理。2.如果这个处理真的那么强调“单线程”,那么你就应该好像满人的餐厅一样把“排队”和“吃饭”的人分开,不要让还没能接受服务的客人坐到吃饭的桌子上来。你不如改用双Socket方案,类似FTP的被动模式那样,一个Socket用于接收客户端发送指令,另一个Socket用于处理复杂问题(例如FTP的传输数据)。例如,第一Socket仅仅提供给客户端请求服务和Loop,客户端进来了就排队,然后就让它在这里Loop,直到有空闲处理进程了就告诉队列中的第一位客户端你去第二Socket的端口处理(并给它一个密钥,如果需要的话),然后该客户端就去第二Socket使用服务。
      

  51.   

    回复:cat_hsfz(http://cathsfz.5000megs.com) ( )
       我对Socket了解......
       //--------------------------------------------------------------------
       我这边的服务端,不断的Accept() Socket出来,每个Socket将分配单独的负责通讯的线程和数据库资源等。
       针对你的第一点,的最后一句是我想要的。     针对你的第二点,前面跟某些朋友提到“单线程”只是为了,贴出段个简单的代码出来, 让大家对客户端程序,服务端程序的过程更加清晰, 我的目的在于描叙,服务器Listen,客户端连接,客户端口发数据,客户端关闭,服务端Accept(), 这时服务端如何判断客户端是否还连着。   至于,多线程的问题,应该不用担心。 因为如果先就能够判断客户端已经断开,自然也省了一个线程。
      

  52.   

    不对socket做io操作是无法知道socket的真实状态的。
    select得到的socket状态,只是最后一次io操作的socket状态。
    所以楼主会发现程序判断不准确。
      

  53.   

    要判断socket连接是否已经断开,据我所知唯一正确的方法就是做receive动作,根据receive的结果来判断。
      

  54.   

    在WINDOWS上,在调用listen()之前,对listen套接字设置SO_CONDITIONAL_ACCEPT为TRUE,可以将三次握手的完成延迟到accept之后,如果使用WSAAccept的话还可以拒绝连接。
      

  55.   

    回复:wanguodu(足文字D) 
    在WINDOWS上,在调用listen()之前
    //-------------------------------------------
    VC++6.0 的int setsockopt(
      SOCKET s,
      int level,
      int optname,
      const char* optval,
      int optlen
    );
    level 为SOL_SOCKET  ,optname 为SO_CONDITIONAL_ACCEPT 可以设置!C#中
    public void SetSocketOption(
       SocketOptionLevel optionLevel,
       SocketOptionName optionName,
       int optionValue
    );
    optionLevel 为Sockets.SocketOptionLevel.Socket
    optionName  中却没有看到合适的选项,
    而且我发现C#中的选项数目,明显少于C++6.0中的选项数目。
      

  56.   

    回复 wwwsq(wwwsq) ( )
    不对socket做io操作是无法知道socket的真实状态的......
    //-------------------------------
    其实,据我所知,也和你一样。 就是用IO操作来进行探测。
    不过,我只是希望,这样的事情,应该有一个更加“干净”的办法解决。
      

  57.   

    “道不行,乘桴浮于海”,换一种思路:既然CPU占用率很高,那么服务端主动拒掉一些连接就是合理的,可以通过重新调用listen()将侦听队列减小(比如为1)来达到此目的,同时使得后续连接变慢,一旦CPU空下来之后再恢复侦听队列为原来大小。
      

  58.   

    wanguodu(足文字D) 
      “道不行,乘桴浮于海”,换一种思路....
       //--------------------------------------   你的说的这个办法,改变Listen的大小,之前我 Listen(100),后面加上Listen(x),假设x=1,实际测试这个没有起到作用。
       就算用  ServerSocket.SetSocketOption(..,..., SocketOptionName.MaxConnections,1)
       也不行。
       当然, “道不行,乘桴浮于海”,换一种思路 
       这句话比办法的本身更加有含义!
      

  59.   

    首先,“先判断连接状态再来进行io操作”是没有意义的。因为在你判断之后和开始io之前,连接可能断了。
    其次,select模型的设计思想就是间接查询,这有助于提高效率。每次io之后更新一下状态就可以了,不用每次select的时候去底层取status,而且取来也没什么用。这和“干净”无关,主要是一个习惯和理解的问题。
      

  60.   

    想补充的一点是:socket通讯断了,通讯双方都未必会得到OnDisconnect通知,这是由于TCP协议底层就没有这个确保的机制。就好比古代的驿道一样,你不派人走一遍,就没法确证驿道是通畅的。
      

  61.   

    回复:wwwsq(wwwsq) 
    //----------------------------------------------
      TCP协议底层就没有这个确保的机制”--你回答的很明确。  其实我发帖子的中心思想也就是希望协议底层能够 正确判断出连接的状态。   这几天做了很多的测试,如果不通过IO 操作,用Select,Poll 等等,
      均无法真正的得到状态!当然因为项目的计划问题, 我不会停留在这一个问题上。
      
      增加IO 操作来进行初期判断的事情,我就不做了,因为现成的硬件终端不可能再为此增加
      协议了。 计算机忙的时候,我Accept()一个, 就关一个,本身就没有时间处理请求,
      倒不如取出来关闭。长痛不如短痛 ^_^ ,我将采用这种办法。      
      

  62.   

    今天结贴。
      
       hdt(倦怠)                   
       积极跟帖 ,抛砖引玉, 得分20   huangsuipeng(hsp|I love foxpig)  
       将问题引入到 TCP 三次握手,深化主题 得分20   GXY2005(不好!我看見豬在天上飛)
       多次测试这个问题,实事求是,得分20   wanguodu(足文字D) 
       “道不行,乘桴浮于海”这句话比办法的本身更加有含义! 得分20   wwwsq(wwwsq) 
       引用你的“这是由于TCP协议底层就没有这个确保的机制”,一针见血!得分20
       
       另外也感谢其他朋友能够参与讨论。
      

  63.   

    if(sock.Send(new Byte[4]{2,0,0,0})!=4) throw new Exception("Failed to ping client socket");