IAsyncIOService(对应代码里的IAsyncConnector)
这是一个异步IO服务接口,含有一下几个方法。
IAsyncConnector
public
interface
IAsyncConnector : IDisposable
{
void
Connect(EndPoint endPoint);
ConnectionStatus ConnectionStatus {
get
; }
void
Send
<
T
>
(T msg);
void
Close();
void
Close(Boolean isImmediately);
void
Abort();
}
异步模型
win32支持多种异步I/O模型,比如从最简单的select到复杂但高效的IOCP,.NET将这些底层进行了很好的封装,为开发人员提供了非常方便好用异步API。对于.NET上异步Socket的使用这里就不复述了,大家可以参阅MSDN上的相关内容。
在使用异步I/O的时候有些地方需要注意:一是网络上每次传来的数据并不是我们想读多少就传多少,并且不同于阻塞I/O编程,我们read 10个字节的数据,只有读完10个字节后才会进行下一步,对于.NET异步编程,,我们要自己去计算每个消息的完整性并记录未读完的数据以防止连包、粘包的发生。二是SocketAsyncEventArgs对象的复用性,我们需要适当使用池化来减少频繁的新对象创建。其实这个框架稍加改动既是一个服务器框架。
池化SocketAsyncEventArgs
.NET的异步Socket API需要频繁使用SocketAsyncEventArgs实例,我们需要设计一个池来存储、提供SocketAsyncEventArgs实例的复用,GenericPool<T>是我写的一个简单的泛型池实现。这里有个细节需要注意,每个对象入池和从池中去除前都可能需要一些清理/初始化之类的操作,一种方案是让每个池化的类型都实现一个接口,但是这种方式过于暴力和死板,我们定义一个IPoolAspectHandler<T>来解除Pool和池化对象间的耦合性。
public
interface
IPoolAspectHandler
<
T
>
{
void
OnInitialize(T obj);
void
OnPut(T obj);
void
OnResolve(T obj);
void
OnClear(T obj);
}
PoolAspectHandlerAdapter
public
class
PoolAspectHandlerAdapter
<
T
>
: IPoolAspectHandler
<
T
>
{
#region
IPoolAspectHandler<T> Members
public
void
OnInitialize(T obj)
{
}
public
void
OnPut(T obj)
{
}
public
void
OnResolve(T obj)
{
}
public
void
OnClear(T obj)
{
}
#endregion
}
SocketRecvEventArgsAspectHandler
class
SocketRecvEventArgsAspectHandler : IPoolAspectHandler
<
SocketAsyncEventArgs
>
{
public
Action
<
SocketAsyncEventArgs
>
OnInitializeEvent;
public
Action
<
SocketAsyncEventArgs
>
OnClearupEventReference;
#region
IPoolAspectHandler<SocketAsyncEventArgs> Members
private
static
readonly
int
RECV_BUFFER_SIZE
=
10
;
public
void
OnInitialize(SocketAsyncEventArgs obj)
{
if
(OnInitializeEvent
!=
null
)
OnInitializeEvent(obj);
var buffer
=
FastBuffer.Allocate(
512
);
buffer.AutoExtend
=
true
;
var bufferStatus
=
new
BufferStatus(RECV_BUFFER_SIZE)
{
Buffer
=
buffer
};
obj.UserToken
=
bufferStatus;
var recvBuffer
=
new
byte
[RECV_BUFFER_SIZE];
obj.SetBuffer(recvBuffer,
0
, recvBuffer.Length);
}
public
void
OnPut(SocketAsyncEventArgs obj)
{
}
public
void
OnResolve(SocketAsyncEventArgs obj)
{
}
public
void
OnClear(SocketAsyncEventArgs obj)
{
obj.BufferList
=
null
;
obj.UserToken
=
null
;
}
#endregion
}
SocketSendEventArgsAspectHandler
class
SocketSendEventArgsAspectHandler : IPoolAspectHandler
<
SocketAsyncEventArgs
>
{
public
Action
<
SocketAsyncEventArgs
>
OnInitializeEvent;
public
Action
<
SocketAsyncEventArgs
>
OnClearupEventReference;
#region
IPoolAspectHandler<SocketAsyncEventArgs> Members
public
void
OnInitialize(SocketAsyncEventArgs obj)
{
if
(OnInitializeEvent
!=
null
)
OnInitializeEvent(obj);
}
public
void
OnPut(SocketAsyncEventArgs obj)
{
obj.UserToken
=
null
;
}
public
void
OnResolve(SocketAsyncEventArgs obj)
{
}
public
void
OnClear(SocketAsyncEventArgs obj)
{
obj.SetBuffer(
0
,
0
);
obj.BufferList
=
null
;
}
#endregion
}
GenericPool
public
class
GenericPool
<
T
>
{
public
static
readonly
int
DEFAULT_MAXSIZE
=
1000
;
private
ConcurrentQueue
<
T
>
_store
=
new
ConcurrentQueue
<
T
>
();
private
IPoolAspectHandler
<
T
>
_aspectHandler;
private
int
_maxSize
=
DEFAULT_MAXSIZE;
public
static
readonly
GenericPool
<
T
>
Instance;
static
GenericPool()
{
Instance
=
new
GenericPool
<
T
>
();
}
public
GenericPool()
:
this
(DEFAULT_MAXSIZE,
new
PoolAspectHandlerAdapter
<
T
>
())
{
}
public
GenericPool(
int
maxSize, IPoolAspectHandler
<
T
>
aspectHandler)
{
this
._maxSize
=
maxSize;
this
._aspectHandler
=
aspectHandler;
}
public
T Resolve()
{
T result
=
default
(T);
_store.TryDequeue(
out
result);
if
(result
==
null
)
{
lock
(_store)
{
_store.TryDequeue(
out
result);
if
(result
==
null
)
{
var obj
=
Activator.CreateInstance
<
T
>
();
this
._aspectHandler.OnInitialize(obj);
if
(Put(obj))
_store.TryDequeue(
out
result);
else
throw
new
InvalidOperationException(
"
池已满
"
);
}
}
}
return
result;
}
public
bool
Put(T obj)
{
if
(_store.Count
<
_maxSize)
{
this
._aspectHandler.OnPut(obj);
_store.Enqueue(obj);
return
true
;
}
else
return
false
;
}
public
void
Empty()
{
while
(
!
_store.IsEmpty)
{
var obj
=
Resolve();
this
._aspectHandler.OnClear(obj);
}
}
public
int
Count
{
get
{
return
this
._store.Count;
}
}
public
void
Clear(T obj)
{
this
._aspectHandler.OnClear(obj);
}
}
IDecoder和IEncoder
每当从网络上获取数据时,都将数据与上一次接消息残留的数据进行拼接并交由IDecode进行处理。通过IDecode接口的两个方法:Decodable和Decode,Decodable判断数据是否完整,返回3种枚举值(OK,NEED_DATA,BAD_DATA),如果读完或者拼接成了一个完整的数据则返回OK,如果缺少数据返回NEED_DATA,如果数据有错误返回BAD_DATA。
IEncoder<T>的作用是将类型为T的对象按照协议序编码为为byte[]数据。
public
interface
IDecoder
{
DecodeResult Decodable(
byte
[] data);
void
Decode(
byte
[] data,
out
int
usedSize,
out
object
msg);
}
public
enum
DecodeResult
{
OK,
NOT_OK,
NEED_DATA
}
public
interface
IEncoder
<
T
>
{
byte
[] Encode(T msg);
}
IoHandler
IoHandler接口有六个方法,SessionOpened,SessionCreated,MessageSent,MessageReceived,ExceptionCaught,SessionClosed。
分别对应连接打开、创建、发送、接受、异常、关闭六种情况。我们可以再SessionCreated中队连接进行安全性审查或是初始化session。
在MessageReceived中进行业务处理,在SessionClosed中持久化用户的会话数据。
IoHandler
public
interface
IoHandler
{
void
SessionOpened();
void
SessionCreated();
void
MessageSent(
object
msg);
void
MessageReceived(
object
msg);
void
ExceptionCaught();
void
SessionClosed();
}
对于丢包、粘包、数据拼接的处理
这就是为什么我们需要消息头首先告诉我们整个消息的长度是多少的原因。我们知道了消息长度,就可以判断第一次收到的消息是否完整或者包含写一条消息的数据。将多余的数据存在一个FastBuffer中,在下一次接受到数据时将新数据追加到这个FashBuffer中,再提交给IDecode.Decodable处理即可。
我们来看其完整的实现:
AsyncConnector
public
class
AsyncConnector : IAsyncConnector
{
#region
async sockets
private
Socket _socket;
#endregion
#region
reused socketasynceventargs
private
GenericPool
<
SocketAsyncEventArgs
>
_asyncSendArgsPool
=
new
GenericPool
<
SocketAsyncEventArgs
>
();
private
GenericPool
<
SocketAsyncEventArgs
>
_asyncRecvArgsPool
=
new
GenericPool
<
SocketAsyncEventArgs
>
();
#endregion
#region
fields
//
private IoSession session = new IoSession();
private
ConnectionStatus _connectionStatus
=
ConnectionStatus.Unused;
#endregion
#region
ctors
public
AsyncConnector()
{
var sendAspectHandler
=
new
SocketSendEventArgsAspectHandler();
sendAspectHandler.OnClearupEventReference
=
(p)
=>
{
p.Completed
-=
_socketArgs_Completed;
};
_asyncSendArgsPool
=
new
GenericPool
<
SocketAsyncEventArgs
>
(GenericPool
<
SocketAsyncEventArgs
>
.DEFAULT_MAXSIZE, sendAspectHandler);
var recvAspectHandler
=
new
SocketRecvEventArgsAspectHandler();
recvAspectHandler.OnInitializeEvent
+=
(p)
=>
p.Completed
+=
_socketArgs_Completed;
recvAspectHandler.OnClearupEventReference
=
(p)
=>
p.Completed
-=
_socketArgs_Completed;
_asyncRecvArgsPool
=
new
GenericPool
<
SocketAsyncEventArgs
>
(GenericPool
<
SocketAsyncEventArgs
>
.DEFAULT_MAXSIZE, recvAspectHandler);
}
#endregion
#region
Property
/*
public IoSession Session
{
get
{
return session;
}
}
*/
public
event
ConnectionStatusChangedEventHandler OnConnectionStatusChanged;
public
IoHandler Handler
{
get
;
set
;
}
public
IProtocolFactory ProtocolFactory
{
get
;
set
;
}
#endregion
public
void
Connect(EndPoint endPoint)
{
_socket
=
new
Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
SocketAsyncEventArgs _socketArgs
=
new
SocketAsyncEventArgs();
_socketArgs.Completed
+=
new
EventHandler
<
SocketAsyncEventArgs
>
(_socketArgs_Connected);
_socketArgs.RemoteEndPoint
=
endPoint;
ConnectionStatus
=
IO.ConnectionStatus.Connecting;
if
(
!
_socket.ConnectAsync(_socketArgs))
{
_socketArgs_Connected(_socket, _socketArgs);
}
}
public
ConnectionStatus ConnectionStatus
{
get
{
return
_connectionStatus;
}
set
{
if
(value
==
_connectionStatus)
return
;
if
(OnConnectionStatusChanged
!=
null
)
OnConnectionStatusChanged(_connectionStatus, value);
_connectionStatus
=
value;
}
}
public
void
Close()
{
_socket.Close();
ConnectionStatus
=
IO.ConnectionStatus.Closed;
}
public
void
Close(
bool
isImmediately)
{
_socket.Close();
ConnectionStatus
=
IO.ConnectionStatus.Closed;
}
public
void
Abort()
{
_socket.Close();
ConnectionStatus
=
IO.ConnectionStatus.Closed;
}
public
void
Dispose()
{
if
(_socket
!=
null
)
{
if
(_socket.Connected)
{
_socket.Shutdown(SocketShutdown.Both);
_socket.Close();
_socket.Dispose();
_socket
=
null
;
ConnectionStatus
=
IO.ConnectionStatus.Closed;
}
_asyncRecvArgsPool.Empty();
_asyncSendArgsPool.Empty();
}
//
session.Dispose();
}
public
void
Send
<
T
>
(T msg)
{
var args
=
_asyncSendArgsPool.Resolve();
byte
[] ret
=
ProtocolFactory.GetEncoder
<
T
>
().Encode(msg);
args.SetBuffer(ret,
0
, ret.Length);
_socket.SendAsync(args);
}
private
void
_socketArgs_Connected(
object
sender, SocketAsyncEventArgs args)
{
args.Completed
-=
_socketArgs_Connected;
if
(args.SocketError
==
SocketError.Success)
{
ConnectionStatus
=
IO.ConnectionStatus.Connected;
var recvArgs
=
_asyncRecvArgsPool.Resolve();
if
(
!
_socket.ReceiveAsync(recvArgs))
_socketArgs_Received(sender, recvArgs);
}
else
{
ConnectionStatus
=
IO.ConnectionStatus.Unavailable;
}
}
private
void
_socketArgs_Completed(
object
sender, SocketAsyncEventArgs args)
{
switch
(args.LastOperation)
{
case
SocketAsyncOperation.Send:
_socketArgs_Sended(sender, args);
break
;
case
SocketAsyncOperation.Receive:
_socketArgs_Received(sender, args);
break
;
}
}
private
void
_socketArgs_Received(
object
sender, SocketAsyncEventArgs args)
{
var socket
=
sender
as
Socket;
if
(args.SocketError
==
SocketError.Success)
{
//
check if the buffer is full used(ByteTransfered, OffSet)
var bufferStatus
=
args.UserToken
as
BufferStatus;
bufferStatus.Buffer.Append(args.Buffer, args.Offset
/*
bufferStatus.ReadOffset
*/
, args.BytesTransferred);
bufferStatus.IncreaseTransfered(args.BytesTransferred);
var decoder
=
ProtocolFactory.GetDecoder();
var data
=
bufferStatus.Buffer.copyAvaliableBytes();
var result
=
decoder.Decodable(data);
switch
(result)
{
case
DecodeResult.NEED_DATA:
socket.ReceiveAsync(args);
break
;
case
DecodeResult.NOT_OK:
throw
new
BadImageFormatException();
case
DecodeResult.OK:
int
usedSize;
object
netMsg
=
null
;
decoder.Decode(data,
out
usedSize,
out
netMsg);
ThreadPool.QueueUserWorkItem(Handler.MessageReceived, netMsg);
bufferStatus.Buffer.Reset();
bufferStatus.Buffer.Append(data, usedSize, data.Length
-
usedSize);
socket.ReceiveAsync(args);
break
;
}
}
}
private
void
_socketArgs_Sended(
object
sender, SocketAsyncEventArgs args)
{
if
(args.SocketError
==
SocketError.Success)
{
}
else
{
}
_asyncSendArgsPool.Put(args);
}
}
代码下载
http://files.cnblogs.com/wJiang/ClientConnLib.rar