事先说明, 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 已经断开??
(不要用向客户端发数据确认异常的办法,这个=自宫) 像什么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 已经断开??
解决方案 »
- object [ ] values = List.ToAray(typeof(object))
- C#应用程序怎么样在属性栏中自定义个imagelist属性
- 全角半角的问题
- 如何实现多个应用程序对文件的读写操作????高手们来帮忙啊。。。
- 怎样判断在TREEVIEW控件中的checkbox是否被选中?
- 有人用过google里gmail邮箱吗?
- 有人能搞定吗?再来一贴:为什么System.Environment.后面没有SpecialFolder?我新建一个项目后又能找到SpecialFolder了?好奇怪!急急急
- C#,DATAGRID保存数据问题。高分求解.
- 怎么查找AD中某个域用户的其他信息?
- 怎样在窗体(winform)上画线?
- 关于类的写法
- 如何通过网络访问ACCESS数据库?
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());
}
}
}
}//--------------------------------------------------------------------------
//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代码
如果想避免维护一个无效的连接,可以让客户端的同时发送一段验证信息(vc可以做到,不知道C#怎么做),服务器收不到就认为连接失败。如果是为了防止攻击,最好使用防火墙
这也是我对这个问题追根的原因。
如果没有客户端来连接接的话,
Socket.Select( tmplist, null, null, 200);
如果没有侦测到客户端到来的话, 200毫秒后,tmplist 会被清空,tmplist.Count 是为0的,否则>0。我之所以在Accept() 之前放Socket.Select(..) ,就是为了检测是否有连接请求来, 因为Accept() 可能导致程序挂起。
+ cs {"由于目标机器积极拒绝,无法连接。" } System.Exception
不知是不是你的全部代码,就你所贴,客户端会出现
//---------------------------------------------------------
服务端绑定的IP 和客户端Connect的IP 需要改成你测试电脑的IP
,我拷贝的代码中是“192.170.1.110”, 改成你电脑的IP 就可以了。
//---------------------------------------------------------- 在服务端Accept之前,只要Listen 的队列没有占满, 客户端Connect 是可以成功的,
实际上,我测试发现就是这样的。
如果是故意的话,这就是通常讲的DOS,利用TCP三次握手的漏洞,制造半连接状态,
客户端断开后, 在服务端的Listen的队列里面这个Socket 仍然存在, 而且也能够被Accept()出来, 就在这个关键的时候, 无法判断这个被Accept()出来的Socket是否有效。
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.
用CONNECTED判断是无用的,他只记住最近一次操作的成功与否,最可靠的还是从服务器端发一个byte的东西回去客户端,收不到ack,自然会有exception,然后根据exception的error codes作相应处理了
C#中,同样的recveive(..PEEK), 就不行。 Accept() 之后, 就是通过 应用层 增加少量协议(发 SYN =0X16 ,收ACK=0x06 as ),来确保客户端口是否有效?是吧?
三次握手时服务端需要创建新的socket与请求连接的socket进行通信,
通信成功才返回新的socket,如果在listen时就三次握手,那就不必要accept了.所以在服务端accept之前,客户端就断开了连接了,accept是不会成功的楼主再检查检查程序,说不定什么地方出了什么逻辑错误.......
//----------------------------------------------------- 在服务端accept之前,客户端就断开了连接了,accept是可以成功的。
上面贴代码跟踪说明了这一点, 事实上不仅Accept()成功,而且收码也成功! 另外,我在VC++6 里面用类似的代码流程, 发现Accept()成功并不能说明客户端当前是连着的。
,我拷贝的代码中是“192.170.1.110”, 改成你电脑的IP 就可以了
================================================
我确实已经改了ip还有我还是感觉是你的程序问题,第一你怎么保证客户close的socket就是,服务器能接受那次数据的socket,也就是说你怎么保证server 和 client之间的同步。
还有
如果你对数据完备性没有太高的要求,我这里所说的完备性指的是,客户发送数据,服务器没必要一定处理。为什么不使用udp协议。而使用面向连接的tcp?如果你一定要保证数据的完备,又不想阻塞,你为什么不利用socket的异步处理?
各位继续。
学习ing
只是经过层层协议包装后由internet发向目的地.可能会在中途丢失的.
就是发到了目标端,可能也在队列中,不一定会执行啊.
没有回送确认怎么来判断?
说不定我发了消息后就关机了.....找一个座位先...
try
{
temp="";
back="";
byte[] bytes=new byte[1024];//
int bytesRec=receiveSocket.Receive(bytes);//
if (bytesRec==0)
{
已断 }
}
catch(Exception ex)
{
已断
}
我用上面的方法来判断。很准的。
我确实已经改了ip......
//----------------------------------------------------------
1.首先服务器监听的端口,只有客户端程序访问, 而且客户端的程序简化到了: 连接服务端一次,发送数据一次,关闭一次。 因此服务端那边的相应的操作也只有一次。
2.实际上程序对数据完备性要求很高, 客户端发出的数据,服务端都必须处理,如果客户端还连着的话, 不能用UDP。 3.异步处理在流程控制上改动太,客户端是专门的硬件产品, 目前应在同步处理上 4.感谢你能够测试我的测试代码, 有必要的话,服务端口的Select(..)全部都不要了,listen()后面,“延时”一段时间直接Accept()[创建,绑定,监听,Accept,通讯,关闭客户端],客户端的程序没有多少简化了[创建,连接,发送,关闭]。
(“延时”的目的,是为了保证服务端在Accept()之前,客户端就完成了所有的动作)
bytesRec=receiveSocket.Receive(bytes);//
if (bytesRec==0)
{...}我上面贴的代码中,bytesRec >0, 而且接(第一次)收数据也不会异常。
经过我仔细分析发现你的问题是Socket.Select方法使用不当,你是这样用的:Socket.Select( tmplist, null, null, 100);这样只检查是否可读,当然是可读的,因为客户端发来了数据;你应这样:Socket.Select( tmplist, tmplist, null, 100);第二参数检查可写性,当然不可写,因为已经断开。这样你的问题解决了。接分!!!
if (!aSocket.Connected) {
Console.WriteLine("Winsock error: "
+ Convert.ToString(System.Runtime.InteropServices.Marshal.GetLastWin32Error()));
}
因此我希望在先判断这个Accept()出来的Socket是否连着
你这个想法是错的。tcp就是信任的连接,交互的时候还去检测是否连接,多此一举,try一下就行了。
首先,可以肯定地说,客户端的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连接仍然有效??
}
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
是为公司做程序(我贴的代码,可以说明,我的程序会遇到这个问题)
本来我也可以不理这些事情的,只是想扩大服务器的容量和效率,或者抱着研究的想法去做这个事情。
http://community.csdn.net/Expert/topic/4665/4665682.xml?temp=.6007807
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是否连着,再决定是否启动复杂过程。
//---------------------------
是漏掉了小于0 的情况,我加上后,做了测试,nReads总可以返回>0,(比如16,取决于服务端的接收BYTE[] 的长度定义,或者客户端口发送的长度,二者取短吧) ==改成<= 使得程序更加严谨, 真正的问题还需要继续研讨论!
我发现我上面的建议.......
//---------------------------------------------------------------
我之前也用了你说的办法, 可以,我只希望服务端口程序改程序, 客户端是现有的硬件设施,你说的办法,就是发一个探测性的数据包回应给客户端, 客户端自然会重发第一个数据包的办法。 这个办法是可以先确定SOCKET 是否有效! 如果客户端是数万现成的硬件终端, 恐怕这第一个探测性的 非协议的包真是要好好掂量!
我对Socket了解......
//--------------------------------------------------------------------
我这边的服务端,不断的Accept() Socket出来,每个Socket将分配单独的负责通讯的线程和数据库资源等。
针对你的第一点,的最后一句是我想要的。 针对你的第二点,前面跟某些朋友提到“单线程”只是为了,贴出段个简单的代码出来, 让大家对客户端程序,服务端程序的过程更加清晰, 我的目的在于描叙,服务器Listen,客户端连接,客户端口发数据,客户端关闭,服务端Accept(), 这时服务端如何判断客户端是否还连着。 至于,多线程的问题,应该不用担心。 因为如果先就能够判断客户端已经断开,自然也省了一个线程。
select得到的socket状态,只是最后一次io操作的socket状态。
所以楼主会发现程序判断不准确。
在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中的选项数目。
不对socket做io操作是无法知道socket的真实状态的......
//-------------------------------
其实,据我所知,也和你一样。 就是用IO操作来进行探测。
不过,我只是希望,这样的事情,应该有一个更加“干净”的办法解决。
“道不行,乘桴浮于海”,换一种思路....
//-------------------------------------- 你的说的这个办法,改变Listen的大小,之前我 Listen(100),后面加上Listen(x),假设x=1,实际测试这个没有起到作用。
就算用 ServerSocket.SetSocketOption(..,..., SocketOptionName.MaxConnections,1)
也不行。
当然, “道不行,乘桴浮于海”,换一种思路
这句话比办法的本身更加有含义!
其次,select模型的设计思想就是间接查询,这有助于提高效率。每次io之后更新一下状态就可以了,不用每次select的时候去底层取status,而且取来也没什么用。这和“干净”无关,主要是一个习惯和理解的问题。
//----------------------------------------------
TCP协议底层就没有这个确保的机制”--你回答的很明确。 其实我发帖子的中心思想也就是希望协议底层能够 正确判断出连接的状态。 这几天做了很多的测试,如果不通过IO 操作,用Select,Poll 等等,
均无法真正的得到状态!当然因为项目的计划问题, 我不会停留在这一个问题上。
增加IO 操作来进行初期判断的事情,我就不做了,因为现成的硬件终端不可能再为此增加
协议了。 计算机忙的时候,我Accept()一个, 就关一个,本身就没有时间处理请求,
倒不如取出来关闭。长痛不如短痛 ^_^ ,我将采用这种办法。
hdt(倦怠)
积极跟帖 ,抛砖引玉, 得分20 huangsuipeng(hsp|I love foxpig)
将问题引入到 TCP 三次握手,深化主题 得分20 GXY2005(不好!我看見豬在天上飛)
多次测试这个问题,实事求是,得分20 wanguodu(足文字D)
“道不行,乘桴浮于海”这句话比办法的本身更加有含义! 得分20 wwwsq(wwwsq)
引用你的“这是由于TCP协议底层就没有这个确保的机制”,一针见血!得分20
另外也感谢其他朋友能够参与讨论。