C#完成端口IOCP:(02) 封装NetworkStream支持IOCP操作

前一篇文章我们对SocketAsyncEventArgs进行了简单的封装:https://blog.csdn.net/moasp/article/details/120271060

现在我们实现一个继承NetworkStream的类,重写NetworkStreamBeginReadBeginWrite方法,实现IOCP操作。
主要就是用了前篇文章封装的TcpSocketAsyncEventArgs

0、实现BeginRead/BeginWrite

当调用ReadAsync时,就会执行重写的BeginRead逻辑,实现IOCP操作。

/// <summary>
/// 实现异步IOCP读取数据
/// </summary>
/// <param name="buffer">缓冲区</param>
/// <param name="offset">数据在缓冲区的位置</param>
/// <param name="size">准备读取的数据大小</param>
/// <param name="callback">回调方法</param>
/// <param name="state">用户状态</param>
/// <returns></returns>
public override IAsyncResult BeginRead(byte[] buffer, int offset, int size, AsyncCallback callback, object state)
{
    //从栈中弹出一个TcpSocketAsyncEventArgs用来读取数据
    //参考:https://github.com/hooow-does-it-work/iocp-sharp/blob/main/TcpSocketEventargs.cs
    TcpSocketAsyncEventArgs e = TcpSocketAsyncEventArgs.Pop();

    //实现一个IAsyncResult
    //参考:https://github.com/hooow-does-it-work/iocp-sharp/blob/main/IocpReadWriteResult.cs
    IocpReadWriteResult asyncResult = new IocpReadWriteResult(callback, state, buffer, offset, size);

    try
    {
        //读取数据,完成后调用回调函数AfterRead
        //将TcpSocketAsyncEventArgs和IocpReadWriteResult传递给回调函数。
        //ReadWriteArgs是一个简单的封装,保存上面两个参数。
        //private class ReadWriteArgs
        //{
        //    public TcpSocketAsyncEventArgs TcpSocketAsyncEventArgs;
        //    public IocpReadWriteResult AsyncResult;
        //    public ReadWriteArgs(TcpSocketAsyncEventArgs e, IocpReadWriteResult asyncResult)
        //    {
        //        TcpSocketAsyncEventArgs = e;
        //        AsyncResult = asyncResult;
        //    }

        //    ~ReadWriteArgs()
        //    {
        //        TcpSocketAsyncEventArgs = null;
        //        AsyncResult = null;
        //    }
        //}
        e.ReadAsync(Socket, buffer, offset, size, AfterRead, new ReadWriteArgs(e, asyncResult));

        return asyncResult;
    }
    catch(SocketException ex)
    {
        asyncResult.SetFailed(ex);

        //回收TcpSocketAsyncEventArgs
        TcpSocketAsyncEventArgs.Push(e);
        return asyncResult;
    }
    catch
    {
        asyncResult.Dispose();
        //回收TcpSocketAsyncEventArgs
        TcpSocketAsyncEventArgs.Push(e);
        throw;
    }
}

/// <summary>
/// 基础流读取到数据后
/// </summary>
/// <param name="bytesReceived"></param>
/// <param name="errorCode"></param>
/// <param name="state"></param>
private void AfterRead(int bytesReceived, int errorCode, object state)
{
    ReadWriteArgs args = state as ReadWriteArgs;
    IocpReadWriteResult result = args.AsyncResult;

    if (errorCode != 0)
    {
        //如果IOCP返回了错误代码,设置IAsyncResult为失败状态
        result.SetFailed(new SocketException(errorCode));
    }
    else
    {
        //成功读取数据,反馈给IAsyncResult
        result.BytesTransfered = bytesReceived;
        result.CallUserCallback();
    }

    //回收TcpSocketAsyncEventArgs
    TcpSocketAsyncEventArgs.Push(args.TcpSocketAsyncEventArgs);
}

/// <summary>
/// 结束读取数据
/// </summary>
/// <param name="asyncResult"></param>
/// <returns>读取数据的大小</returns>
public override int EndRead(IAsyncResult asyncResult)
{
    if (asyncResult is not IocpReadWriteResult result) throw new InvalidOperationException("asyncResult");

    //如果同步调用了EndRead,等待IAsyncResult完成
    //不建议同步调用。
    if (result.IsCompleted) result.AsyncWaitHandle.WaitOne();

    //失败,抛出异常
    if (result.Exception != null) throw result.Exception;

    //返回读取到的数据
    return result.BytesTransfered;
}

BeginWrite的逻辑同BeginRead一样,不赘述了。
整个类的完整实现参考:https://github.com/hooow-does-it-work/iocp-sharp/blob/main/IocpNetworkStream.cs

1、测试

TcpSocketAsyncEventArgsReadAsyncWriteAsync方法和IocpNetworkStreamBeginReadBeginWrite方法增加控制台输出,查看效果。
下面代码通过向csdn服务器的80端口发送一个简单的HTTP请求,来测试异步。


//实例化Socket
Socket client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

//连接服务器
client.Connect("www.csdn.net", 80);

//实例化IocpNetworkStream
NetworkStream stream = new IocpNetworkStream(client, true);

//向服务器发送个Http请求头
string header = "GET / HTTP/1.1\r\nHost: www.csdn.net\r\nConnection: close\r\n\r\n";
byte[] streamBuffer = Encoding.ASCII.GetBytes(header);

//使用异步方法,Stream内部会调用我们重写的BeginWrite、EndWrite方法生成异步任务
stream.WriteAsync(streamBuffer, 0, streamBuffer.Length).Wait();

//随便从服务器读取点数据
byte[] buffer = new byte[4096];

//使用异步方法,Stream内部会调用我们重写的BeginRead、EndRead方法生成异步任务
var task = stream.ReadAsync(buffer, 0, buffer.Length);
task.Wait();
stream.Close();

//输出从服务器返回的内容
string response = Encoding.UTF8.GetString(buffer, 0, task.Result);

Console.WriteLine(response);

在这里插入图片描述
可以看到控制台输出,上面部分四行是分别调用了对应的异步方法。
下面返回了HTTP响应,CSDN会强制跳转SSL,所以响应了301 Moved Permanently状态。

2、总结

程序用到的IocpReadWriteResult类实现了IAsyncResult接口给BeginXXX和EndXXX使用。
.Net核心的内部也有类似的实现,并且我也作了一些参考。
封装完成后,正常使用流的各种方法即可,下游应用程序无需关心实现细节。

IocpNetworkStream实现ISocketBasedStream接口,暴露出了BaseSocket给下游程序使用,注意安全

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
使用IOCP完成端口Socket封装的异步TCP类是一种成熟的技术,它能够有效地处理大量并发请求,并且能够提高系统的性能和效率。首先,IOCP能够充分利用系统资源,通过将I/O操作交给内核来完成,可以实现高效的异步操作。而端口Socket封装则可以简化开发者的编程工作,提供更加友好的接口和操作方法,从而提高开发效率和代码的可读性。 此外,使用IOCP完成端口Socket封装的异步TCP类还具有良好的扩展性和灵活性。通过合理设计类的结构和接口,可以实现类的继承和重载,满足不同应用场景和需求。同时,IOCP完成端口也可以与其他技术和框架结合,如线程池、事件驱动等,从而实现更加强大和复杂的系统功能。 然而,使用IOCP完成端口Socket封装的异步TCP类也存在一些局限性。比如对于初学者来说,可能需要一定的学习成本,需要掌握一定的操作和调试技巧。同时,如果应用不当,可能会导致系统资源的浪费和性能下降。因此,在使用这种技术时,需要开发者具有一定的经验和技术水平,可以充分发挥其优势,避免其劣势的影响。 总的来说,使用IOCP完成端口Socket封装的异步TCP类是一种成熟的技术,它具有高效、灵活、可扩展等优势,能够满足大规模并发请求的处理需求,为系统的性能和可靠性提供了良好的支持

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Anlige

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值