以前学过 MFC, 我用过的它里面的包装 Winsock 的两个类 CSocket 和 CAsyncSocket, 我一直觉得它哪个事件通知的功能比较不错,特别是在连接的两方收发数据没有一定的规律的时候比较有用,虽然不用它的事件通知也可以实现功能,但是你需要循环检测套接字的状态或者阻塞等待,如果每次都要做这些琐碎的工作岂不是很麻烦,于是我就想对这些功能作一次封装。当然在 .net 里面采用 delegate 和 event 是最好的选择了。下面就是我实现的一些细节:
先讲一下思路:其实这个还是很简单了,估计高手们会不屑一顾了 ^_^ 。当套接字开始工作,比如开始监听、开始连接的时候,启动一个线程不停的检测套接字的状态,当某一事件的状态条件满足时就触发此事件,要知道具体检测套接字状态的方法,请往下看。
平常用套接字编成的时候可能需要用到的功能就是:连接成功或者失败的通知、在监听的套接字上有挂起的连接的通知、有数据可以接受的通知、套接字关闭的通知、还有套接字空闲可以发送数据的通知等,我也就使对上面几个功能作了封装,不过我觉得最后面一个功能用处不大 ???
我的 Socket 类是从 System.Net.Sockets.Socket 继承下来的,类名 TcpEventSocket, 实现如下:
先申明事件的委托类型,具体看代码
public delegate void AcceptConnectionHandler();
public delegate void ConnectCompletedHandler( bool connected); //connected 表明连接成功与否
public delegate void DataCanSendHandler();
public delegate void DataCanReceiveHandler( int buffersize); // 当前可以接受的数据量
public delegate void SocketClosedHandler();
这些类型具体什么意思应该能看出来吧,另外还要申明对应的事件
public event AcceptConnectionHandler AcceptConnection;
public event ConnectCompletedHandler ConnectCompleted;
public event DataCanSendHandler DataCanSend;
public event DataCanReceiveHandler DataCanReceive;
public event SocketClosedHandler SocketClosed;
另外加了几个虚方法,这几个方法用来触发事件
protected virtual void OnAcceptConnection();
protected virtual void OnConnectCompleted(bool connected)
protected virtual void OnDataCanSend()
protected virtual void OnDataCanReceive(int buffersize)
protected virtual void OnSocketClosed()
为了在适当的事机启动检测线程,我重写了几个基类的方法:
new public void Listen( int backlog)
{
base .Listen(backlog);
sockState = SocketState.Listenning;
if (!checkThread.IsAlive)
checkThread.Start();
}
new public void Connect(EndPoint remoteEP)
{
try
{
base .Connect(remoteEP);
this .Blocking = false ; // 设置非阻塞状态,以便事件通知的效率
if (!checkThread.IsAlive)
checkThread.Start();
}
catch (SocketException)
{
OnConnectCompleted( false );
}
}
这两个方法实际编成中都应该首先被调用的,所以有他们启动检测线程比较合适,另外线程启动了必然也要中止,于是我重写了 Close 方法
new public void Close()
{
if (checkThread.IsAlive) // 先中止线程再关闭连接
checkThread.Abort();
base .Close();
sockState = SocketState.Disconnected;
OnSocketClosed();
}
于是剩下的工作就是怎么检测套接字了, Socket 类有个 Select 的静态方法,它可以检测很多套接字的状态,不过这里只需要检测一个,所以直接用 Socket 的 Poll 方法, Poll 的具体用法可以看 MSDN ,我这里用代码说明我对套接字的检测方法
while ( true ) // 循环检查
{
if (sockState == SocketState.Disconnected) // 如果当前没有连接
{
if (Poll(500, SelectMode.SelectWrite))
OnConnectCompleted( true ); // 如果为可写状态,则表示连接成功
}
else if (sockState == SocketState.Listenning)
{
if (Poll(500, SelectMode.SelectRead)) // 如果在监听状态发现套接字有数据可读则表示已经有人连接上来可以调用 Accept 接受连接
OnAcceptConnection();
}
else // 这里 sockState = SocketState.Connected
{
if (Poll(500, SelectMode.SelectWrite)) // 如果有可写状态则表示可以发送数据
OnDataCanSend();
if (Poll(500, SelectMode.SelectRead)) // 如果有可读状态
{
if (Available > 0) // 如果有数据可读表示可以调用 Receive 接受数据
OnDataCanReceive(Available);
else
{
OnSocketClosed(); // 没数据可读表示连接已经关闭
break ;
}
}
}
// 如果没有连接并且有错误状态,则连接失败
if (sockState == SocketState.Disconnected && Poll(500, SelectMode.SelectError))
OnConnectCompleted( false );
}
这里的 OnXXX 方法就是执行事件通知的方法,并且派生类中可以重载这些方法直接获得事件通知而不需要挂界别的事件通知处理函数 ( 类似于 MFC 的 OnAccept 等虚函数 ) 。不过派生的函数集的调用基类的相应方法哦。很可惜没有重写 Socket.Accept 方法,让它返回一个 TcpEventSocket, 这样应该就比较完整了,只可惜我不知道怎么做 ^_^, 如果谁知道的话欢迎指教。
事件通知机制已经基本完成,所缺乏的只是大量的测试(我做了几下最简单的测试,惭愧!! ^_^ )
如果有问题请联系 [email protected]