[C#]手把手教你打造Socket的TCP通讯连接(二)

上一篇中,我们编写了客户端功能。

这一篇将讲解ISocketHandler的实现。

再来回顾一下ISocketHandler接口。

public interface ISocketHandler
{
    /// <summary>
    /// 开始接收
    /// </summary>
    /// <param name="stream">Socket网络流</param>
    /// <param name="callback">回调函数</param>
    /// <param name="state">自定义状态</param>
    /// <returns>异步结果</returns>
    IAsyncResult BeginReceive(Stream stream, AsyncCallback callback, object state);
    /// <summary>
    /// 结束接收
    /// </summary>
    /// <param name="asyncResult">异步结果</param>
    /// <returns>接收到的数据</returns>
    byte[] EndReceive(IAsyncResult asyncResult);
    /// <summary>
    /// 开始发送
    /// </summary>
    /// <param name="data">要发送的数据</param>
    /// <param name="offset">数据偏移</param>
    /// <param name="count">发送长度</param>
    /// <param name="stream">Socket网络流</param>
    /// <param name="callback">回调函数</param>
    /// <param name="state">自定义状态</param>
    /// <returns>异步结果</returns>
    IAsyncResult BeginSend(byte[] data, int offset, int count, Stream stream, AsyncCallback callback, object state);
    /// <summary>
    /// 结束发送
    /// </summary>
    /// <param name="asyncResult">异步结果</param>
    /// <returns>发送是否成功</returns>
    bool EndSend(IAsyncResult asyncResult);
}

做一个类SocketHandler继承ISocketHandler接口

/// <summary>
/// Socket处理程序
/// </summary>
public class SocketHandler : ISocketHandler
{
    /// <summary>
    /// 开始接收
    /// </summary>
    /// <param name="stream">Socket网络流</param>
    /// <param name="callback">回调函数</param>
    /// <param name="state">自定义状态</param>
    /// <returns>异步结果</returns>
    public IAsyncResult BeginReceive(Stream stream, AsyncCallback callback, object state)
    {

    }

    /// <summary>
    /// 结束接收
    /// </summary>
    /// <param name="asyncResult">异步结果</param>
    /// <returns>接收到的数据</returns>
    public byte[] EndReceive(IAsyncResult asyncResult)
    {

    }

    /// <summary>
    /// 开始发送
    /// </summary>
    /// <param name="data">要发送的数据</param>
    /// <param name="offset">数据偏移</param>
    /// <param name="count">发送长度</param>
    /// <param name="stream">Socket网络流</param>
    /// <param name="callback">回调函数</param>
    /// <param name="state">自定义状态</param>
    /// <returns>异步结果</returns>
    public IAsyncResult BeginSend(byte[] data, int offset, int count, Stream stream, AsyncCallback callback, object state)
    {

    }

    /// <summary>
    /// 结束发送
    /// </summary>
    /// <param name="asyncResult">异步结果</param>
    /// <returns>发送是否成功</returns>
    public bool EndSend(IAsyncResult asyncResult)
    {

    }
}

增加两个属性与构造函数。

    //异步处理关系集合
    private Dictionary<IAsyncResult, SocketHandlerState> StateSet;
    //发送队列
    private List<SocketHandlerState> SendQueue;

    /// <summary>
    /// 实例化Socket处理程序
    /// </summary>
    public SocketHandler()
    {
        StateSet = new Dictionary<IAsyncResult, SocketHandlerState>();
        SendQueue = new List<SocketHandlerState>();
    }

StateSet可以保存我们的异步调用结果等数据

SendQueue用来做一个发送队列

接下来我们从发送数据开始。

由于需要用到Stream的异步方法,我们需要定义一个State类。

internal class SocketHandlerState
{
    /// <summary>
    /// 数据
    /// </summary>
    public byte[] Data { get; set; }
    /// <summary>
    /// 异步结果
    /// </summary>
    public IAsyncResult AsyncResult { get; set; }
    /// <summary>
    /// Socket网络流
    /// </summary>
    public Stream Stream { get; set; }
    /// <summary>
    /// 异步回调函数
    /// </summary>
    public AsyncCallback AsyncCallBack { get; set; }
    /// <summary>
    /// 是否完成
    /// </summary>
    public bool Completed { get; set; }
    /// <summary>
    /// 数据长度
    /// </summary>
    public int DataLength { get; set; }
}

因为我们需要返回IAsyncResult,所以我们继承该接口做一个SocketAsyncResult类。

/// <summary>
/// Socket异步操作状态
/// </summary>
public class SocketAsyncResult : IAsyncResult
{
    /// <summary>
    /// 实例化Socket异步操作状态
    /// </summary>
    /// <param name="state"></param>
    public SocketAsyncResult(object state)
    {
        AsyncState = state;
        AsyncWaitHandle = new AutoResetEvent(false);
    }

    /// <summary>
    /// 获取用户定义的对象,它限定或包含关于异步操作的信息。
    /// </summary>
    public object AsyncState { get; private set; }

    /// <summary>
    /// 获取用于等待异步操作完成的 System.Threading.WaitHandle。
    /// </summary>
    public WaitHandle AsyncWaitHandle { get; private set; }

    /// <summary>
    /// 获取一个值,该值指示异步操作是否同步完成。
    /// </summary>
    public bool CompletedSynchronously { get { return false; } }

    /// <summary>
    /// 获取一个值,该值指示异步操作是否已完成。
    /// </summary>
    public bool IsCompleted { get; internal set; }
}

然后开始编写发送数据相关函数。

这里我将发送数据的大小限制为最大65535。

只需发送长度为2的头信息即可把数据长度发送到对方。

    /// <summary>
    /// 开始发送
    /// </summary>
    /// <param name="data">要发送的数据</param>
    /// <param name="offset">数据偏移</param>
    /// <param name="count">发送长度</param>
    /// <param name="stream">Socket网络流</param>
    /// <param name="callback">回调函数</param>
    /// <param name="state">自定义状态</param>
    /// <returns>异步结果</returns>
    public IAsyncResult BeginSend(byte[] data, int offset, int count, Stream stream, AsyncCallback callback, object state)
    {
        //data不能为null
        if (data == null)
            throw new ArgumentNullException("data");
        //offset不能小于0和超过data长度
        if (offset > data.Length || offset < 0)
            throw new ArgumentOutOfRangeException("offset");
        //count不能大于65535
        if (count <= 0 || count > data.Length - offset || count > ushort.MaxValue)
            throw new ArgumentOutOfRangeException("count");
        //stream不能为null
        if (stream == null)
            throw new ArgumentNullException("stream");
        //回调函数不能为null
        if (callback == null)
            throw new ArgumentNullException("callback");
        //stream异常
        if (!stream.CanWrite)
            throw new ArgumentException("stream不支持写入。");

        SocketAsyncResult result = new SocketAsyncResult(state);

        //初始化SocketHandlerState
        SocketHandlerState shs = new SocketHandlerState();
        shs.Data = data;
        shs.AsyncResult = result;
        shs.Stream = stream;
        shs.AsyncCallBack = callback;
        shs.DataLength = 0;

        //锁定SendQueue
        //避免多线程同时发送数据
        lock (SendQueue)
        {
            //添加状态
            SendQueue.Add(shs);
            //如果SendQueue数量大于1,则表示有数据尚未发送完成
            if (SendQueue.Count > 1)
                return result;
        }

        //获取数据长度
        //ushort的最大值为65535
        //转换为byte[]长度为2
        var dataLength = BitConverter.GetBytes((ushort)data.Length);
        //向对方发送长度为2的头信息,表示接下来要发送的数据长度
        stream.Write(dataLength, 0, dataLength.Length);
        //开始异步发送数据
        stream.BeginWrite(shs.Data, 0, shs.Data.Length, EndWrite, shs).AsyncWaitHandle.WaitOne();

        return result;
    }

    //stream异步结束写入
    private void EndWrite(IAsyncResult ar)
    {
        SocketHandlerState state = (SocketHandlerState)ar.AsyncState;

        //锁定StateSet
        lock (StateSet)
            StateSet.Add(state.AsyncResult, state);

        try
        {
            state.Stream.EndWrite(ar);
        }
        catch
        {
            //出现Socket异常,发送失败
            state.Completed = false;
            //允许等待线程继续
            ((AutoResetEvent)state.AsyncResult.AsyncWaitHandle).Set();
            //执行异步回调函数
            state.AsyncCallBack(state.AsyncResult);
            return;
        }
        //发送成功
        state.Completed = true;
        //允许等待线程继续
        ((AutoResetEvent)state.AsyncResult.AsyncWaitHandle).Set();
        //执行异步回调函数
        state.AsyncCallBack(state.AsyncResult);

        //锁定SendQueue
        lock (SendQueue)
        {
            SocketHandlerState prepare = null;
            //移除当前发送完成的数据
            SendQueue.Remove(state);
            //如果SendQueue还有数据存在,则继续发送
            if (SendQueue.Count > 0)
            {
                prepare = SendQueue[0];
            }
            if (prepare != null)
            {
                //获取数据长度
                //ushort的最大值为65535
                //转换为byte[]长度为2
                var dataLength = BitConverter.GetBytes((ushort)prepare.Data.Length);
                //向对方发送长度为2的头信息,表示接下来要发送的数据长度
                prepare.Stream.Write(dataLength, 0, dataLength.Length);
                //开始异步发送数据
                prepare.Stream.BeginWrite(prepare.Data, 0, prepare.Data.Length, EndWrite, prepare).AsyncWaitHandle.WaitOne();
            }
        }
    }

    /// <summary>
    /// 结束发送
    /// </summary>
    /// <param name="asyncResult">异步结果</param>
    /// <returns>发送是否成功</returns>
    public bool EndSend(IAsyncResult asyncResult)
    {
        //判断异步操作状态是否属于当前处理程序
        if (!StateSet.ContainsKey(asyncResult))
            throw new ArgumentException("无法识别的asyncResult。");
        SocketHandlerState state = StateSet[asyncResult];
        lock (StateSet)
            StateSet.Remove(asyncResult);
        return state.Completed;
    }

接下来是接收数据的相关方法。

    /// <summary>
    /// 开始接收
    /// </summary>
    /// <param name="stream">Socket网络流</param>
    /// <param name="callback">回调函数</param>
    /// <param name="state">自定义状态</param>
    /// <returns>异步结果</returns>
    public IAsyncResult BeginReceive(Stream stream, AsyncCallback callback, object state)
    {
        //stream不能为null
        if (stream == null)
            throw new ArgumentNullException("stream");
        //回调函数不能为null
        if (callback == null)
            throw new ArgumentNullException("callback");
        //stream异常
        if (!stream.CanRead)
            throw new ArgumentException("stream不支持读取。");

        SocketAsyncResult result = new SocketAsyncResult(state);

        //初始化SocketHandlerState
        SocketHandlerState shs = new SocketHandlerState();
        shs.Data = new byte[2];
        shs.AsyncResult = result;
        shs.Stream = stream;
        shs.AsyncCallBack = callback;
        shs.Completed = true;
        //开始异步接收长度为2的头信息
        //该头信息包含要接收的主要数据长度
        stream.BeginRead(shs.Data, 0, 2, EndRead, shs);
        return result;
    }

    //stream异步结束读取
    private void EndRead(IAsyncResult ar)
    {
        SocketHandlerState state = (SocketHandlerState)ar.AsyncState;
        int dataLength;
        try
        {
            dataLength = state.Stream.EndRead(ar);
        }
        catch
        {
            dataLength = 0;
        }
        //dataLength为0则表示Socket断开连接
        if (dataLength == 0)
        {
            lock (StateSet)
                StateSet.Add(state.AsyncResult, state);
            //设定接收到的数据位空byte数组
            state.Data = new byte[0];
            //允许等待线程继续
            ((AutoResetEvent)state.AsyncResult.AsyncWaitHandle).Set();
            //执行异步回调函数
            state.AsyncCallBack(state.AsyncResult);
            return;
        }

        //如果是已完成状态,则表示state.Data的数据是头信息
        if (state.Completed)
        {
            //设定状态为未完成
            state.Completed = false;
            //已接收得数据长度为0
            state.DataLength = 0;
            //获取主要数据长度
            var length = BitConverter.ToUInt16(state.Data, 0);
            //初始化数据的byte数组
            state.Data = new byte[length];
            try
            {
                //开始异步接收主要数据
                state.Stream.BeginRead(state.Data, 0, length, EndRead, state);
            }
            catch
            {
                //出现Socket异常
                lock (StateSet)
                    StateSet.Add(state.AsyncResult, state);
                state.Data = new byte[0];
                ((AutoResetEvent)state.AsyncResult.AsyncWaitHandle).Set();
                state.AsyncCallBack(state.AsyncResult);
            }
            return;
        }
        //接收到主要数据
        else
        {
            //判断是否接收了完整的数据
            if (dataLength + state.DataLength != state.Data.Length)
            {
                //增加已接收数据长度
                state.DataLength += dataLength;
                try
                {
                    //继续接收数据
                    state.Stream.BeginRead(state.Data, state.DataLength, state.Data.Length - state.DataLength, EndRead, state);
                }
                catch
                {
                    //出现Socket异常
                    lock (StateSet)
                        StateSet.Add(state.AsyncResult, state);
                    state.Data = new byte[0];
                    ((AutoResetEvent)state.AsyncResult.AsyncWaitHandle).Set();
                    state.AsyncCallBack(state.AsyncResult);
                    return;
                }
                return;
            }
            //接收完成
            state.Completed = true;
            lock (StateSet)
                StateSet.Add(state.AsyncResult, state);
            ((AutoResetEvent)state.AsyncResult.AsyncWaitHandle).Set();
            state.AsyncCallBack(state.AsyncResult);
        }
    }

    /// <summary>
    /// 结束接收
    /// </summary>
    /// <param name="asyncResult">异步结果</param>
    /// <returns>接收到的数据</returns>
    public byte[] EndReceive(IAsyncResult asyncResult)
    {
        //判断异步操作状态是否属于当前处理程序
        if (!StateSet.ContainsKey(asyncResult))
            throw new ArgumentException("无法识别的asyncResult。");
        SocketHandlerState state = StateSet[asyncResult];
        lock (StateSet)
            StateSet.Remove(asyncResult);
        return state.Data;
    }

至此,SocketHandler的功能已经实现。

下一篇将为大家讲解服务器端的实现。

 

原文地址:http://www.cnblogs.com/Kation/archive/2013/03/06/2947145.html

posted @ 2013-03-06 22:49  Kation  阅读(10031)  评论(10编辑  收藏  举报