当前位置:必发365电子游戏 > 操作系统 > 那后生可畏篇将执教ISocketHandler的兑现,互连网层把数据拉长到IP地址
那后生可畏篇将执教ISocketHandler的兑现,互连网层把数据拉长到IP地址
2019-12-19

网络通讯七层参照他事他说加以考查模型介绍:

上一篇中,大家编辑了客户端功用。

物理层: HUB,网线

必发365电子游戏,那后生可畏篇将教师ISocketHandler的落实。

链路层: MAC,ARP,交换机

再来回想一下ISocketHandler接口。

网络层:IP,ICMP,IGMP,路由器

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);
}

传输层: TCP,UDP

做三个类SocketHandler世袭ISocketHandler接口

会话层: HTTP,SMTP,FTP,POP3

/// <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)
    {

    }
}

表示层: SOAP,SSL

充实七个属性与布局函数。

应用层:HTTP,POP3等

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

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

 

StateSet能够保留我们的异步调用结果等数码

TCP和Socket的区别:

SendQueue用来做贰个出殡和下葬队列

Socket是对互联网层操作。

接下去大家从发送数据开头。

TcpClient是对传输层的操作。

是因为需求用到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; }
}

用Socket访问HTTP服务:

因为我们必要再次回到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; }
}

用TcpClient访问HTTP服务:

下一场最初编制发送数据相关函数。

传输层 --> 会话层

此处自身将发送数据的深浅限定为最大65535。

 

只需发送长度为2的头音信就能够把多少长度发送到对方。

互联网层层的商谈是IP,传输层的商酌是TCP。最本质的区分就是,互连网层把数据增加到IP地址,IP为持有的系统端口服务,传输层只增加到端口,即操作系统的服务端口。网络层/输出层提供了面向连接和无连接的劳务方法。

    /// <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;
    }

 

接下去是接纳数据的有关方法。

C#中TcpClient和Socket的接纳接纳:

    /// <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;
    }

只酌量:主机,端口,数据传输时,用TcpClient,或UdpClient;

迄今,SocketHandler的意义已经落到实处。

那后生可畏篇将执教ISocketHandler的兑现,互连网层把数据拉长到IP地址。要思忖:IP封包,路由,IP数据包时,用Socket;

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

 

原版的书文地址: