diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..3e8a1553f --- /dev/null +++ b/.gitignore @@ -0,0 +1,341 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUNIT +*.VisualState.xml +TestResult.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# JustCode is a .NET coding add-in +.JustCode + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ +# ASP.NET Core default setup: bower directory is configured as wwwroot/lib/ and bower restore is true +**/wwwroot/lib/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# JetBrains Rider +.idea/ +*.sln.iml + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# BeatPulse healthcheck temp database +healthchecksdb diff --git a/LICENSE b/LICENSE new file mode 100644 index 000000000..b09cd7856 --- /dev/null +++ b/LICENSE @@ -0,0 +1,201 @@ +Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/README.md b/README.md new file mode 100644 index 000000000..831734694 --- /dev/null +++ b/README.md @@ -0,0 +1,397 @@ +

+

+

+图片名称 +

+ +
+ +[![NuGet version (RRQMSocket)](https://img.shields.io/nuget/v/RRQMSocket.svg?style=flat-square)](https://www.nuget.org/packages/RRQMSocket/) +[![License](https://img.shields.io/badge/license-Apache%202-4EB1BA.svg)](https://www.apache.org/licenses/LICENSE-2.0.html) +[![Download](https://img.shields.io/nuget/dt/RRQMSocket)](https://www.nuget.org/packages/RRQMSocket/) +[![star](https://gitee.com/dotnetchina/RRQMSocket/badge/star.svg?theme=gvp)](https://gitee.com/dotnetchina/RRQMSocket/stargazers) +[![fork](https://gitee.com/dotnetchina/RRQMSocket/badge/fork.svg?theme=gvp)](https://gitee.com/dotnetchina/RRQMSocket/members) + +QQ + +
+ +
+ +合抱之木,生于毫末;九层之台,起于垒土。 + +
+
+图片名称 +
+ +## 💿描述 +| 名称 |描述| +|---|---| +|[![NuGet version (RRQMSocket)](https://img.shields.io/nuget/v/RRQMSocket.svg?label=RRQMSocket)](https://www.nuget.org/packages/RRQMSocket/)| **RRQMSocket**是一个整合性的、超轻量级的、可以免费商用使用的网络通信服务框架。
它具有 **高并发连接** 、 **高并发处理** 、 **事件订阅** 、 **插件式扩展** 、
**多线程处理** 、 **内存池** 、 **对象池** 等特点,
让使用者能够更加简单的、快速的搭建网络框架。| +|[![NuGet version](https://img.shields.io/nuget/v/RRQMSocketFramework.svg?label=RRQMSocketFramework)](https://www.nuget.org/packages/RRQMSocketFramework/)| **RRQMSocketFramework**是RRQMSocket系列的企业版,
两者在功能上几乎没有区别,但是RRQMSocketFramework无任何依赖,
且可以提供专属的定制功能。后续也会加入企业已定制的优秀功能,希望大家多多支持。| +| [![NuGet version (RRQMSocket.FileTransfer)](https://img.shields.io/nuget/v/RRQMSocket.FileTransfer.svg?label=RRQMSocket.FileTransfer)](https://www.nuget.org/packages/RRQMSocket.FileTransfer/) | RRQMSocket.FileTransfer是一个高性能的文件传输框架,
您可以用它传输**任意大小**的文件,它可以完美支持**上传下载混合式队列传输**、
**断点续传**、 **快速上传** 、**传输限速**、**获取文件信息**、**删除文件**等。
在实时测试中,它的传输速率可达500Mb/s。 | +|[![NuGet version (RRQMSocket.RPC)](https://img.shields.io/nuget/v/RRQMSocket.RPC.svg?label=RRQMSocket.RPC)](https://www.nuget.org/packages/RRQMSocket.RPC/) |RPC是一个超轻量、高性能、可扩展的微服务管理平台框架,
目前已完成开发**RRQMRPC**、**XmlRpc**、**JsonRpc**、**WebApi**部分。
**RRQMRPC**部分使用RRQM专属协议,支持客户端**异步调用**,
服务端**异步触发**、以及**out**和**ref**关键字,**函数回调**等。
在调用效率上也是非常强悍,在调用空载函数,且返回状态时,
**10w**次调用仅用时**3.8**秒,不返回状态用时**0.9**秒。
其他协议调用性能详看性能评测。| +|[![NuGet version (RRQMSocket.RPC.WebApi)](https://img.shields.io/nuget/v/RRQMSocket.RPC.WebApi.svg?label=RRQMSocket.RPC.WebApi)](https://www.nuget.org/packages/RRQMSocket.RPC.WebApi/)| WebApi是一个扩展于RRQMSocket.RPC的WebApi组件,
可以通过该组件创建WebApi服务解析器,让桌面端、Web端、移动端可以跨语言调用RPC函数。
功能支持路由、Get传参、Post传参等。| +|[![NuGet version (RRQMSocket.RPC.XmlRpc)](https://img.shields.io/nuget/v/RRQMSocket.RPC.XmlRpc.svg?label=RRQMSocket.RPC.XmlRpc)](https://www.nuget.org/packages/RRQMSocket.RPC.XmlRpc/)| XmlRpc是一个扩展于RRQMSocket.RPC的XmlRpc组件,
可以通过该组件创建XmlRpc服务解析器,完美支持XmlRpc数据类型,类型嵌套,
Array等,也能与CookComputing.XmlRpcV2完美对接。不限Web,Android等平台。| +| [![NuGet version (RRQMSocket.RPC.JsonRpc)](https://img.shields.io/nuget/v/RRQMSocket.RPC.JsonRpc.svg?label=RRQMSocket.RPC.JsonRpc)](https://www.nuget.org/packages/RRQMSocket.RPC.JsonRpc/)| JsonRpc是一个扩展于RRQMSocket.RPC的JsonRpc组件,
可以通过该组件创建JsonRpc服务解析器,支持JsonRpc全部功能,可与Web,Android等平台无缝对接。| +| [![NuGet version (RRQMSocket.Http)](https://img.shields.io/nuget/v/RRQMSocket.Http.svg?label=RRQMSocket.Http)](https://www.nuget.org/packages/RRQMSocket.Http/) | RRQMSocket.Http是一个能够简单解析Http的服务组件,
能够快速响应Http服务请求。| + + +## 🖥支持环境 +- .NET Framework4.5及以上。 +- .NET Core3.1及以上。 +- .NET Standard2.0及以上。 + +## 🥪支持框架 +- WPF +- Winform +- Blazor +- Xamarin +- Mono +- Unity +- 其他(即所有C#系) + +## 🌴RRQMSocket特点速览 + +#### 对象池 + +对象池在RRQMSocket有很多应用,最主要的两个就是**连接对象池**和**处理对象池**。连接对象池就是当客户端成功连接时,首先会去连接对象池中找SocketClient,然后没有的话,才会创建。如果哪个客户端掉线了,它的SocketClient就会被回收。 + +然后就是处理对象池,在RRQMSocket中,接收数据的线程和IOCP内核线程是分开的(也可以设置拥塞接收),也就是比如说客户端给服务器发送了1w条数据,但是服务器收到后处理起来很慢,那传统的iocp肯定会放慢接收速率,然后通知客户端的tcp窗口,发生拥塞,然后让客户端暂缓发送。但是在RRQMSocket中会把收到的数据通过队列全都存起来,首先不影响iocp的接收,同时再分配线程去处理收到的报文信息,这样就相当于一个“泄洪湖泊”,能很大程度的提高处理数据的能力。 + +#### 多线程 + +由于有**处理对象池**的存在,使多线程处理变得简单。在客户端连接完成时,会自动分配该客户端辅助类(TcpSocketClient)的消息处理逻辑线程,假如服务器线程数量为10,则第一个连接的客户端会被分配到0号线程中,第二个连接将被分配到1号线程中,以此类推,循环分配。当某个客户端收到数据时,会将数据排入当前线程所独自拥有的队列当中,并唤醒线程执行。 + +#### 传统IOCP和RRQMSocket + +RRQMSocket的IOCP和传统也不一样,就以微软官方示例为例,使用MemoryBuffer开辟一块内存,均分,然后给每个会话分配一个区接收,等收到数据后,再**复制**源数据,然后把复制的数据进行处理。而RRQMSocket是每次接收之前,从内存池拿一个可用内存块,然后**直接用于接收**,等收到数据以后,直接就把这个内存块抛出处理,这样就避免了**复制操作**,虽然只是细小的设计,但是在传输**1000w**次**64kb**的数据时,性能相差了**10倍**。 + +#### 数据处理适配器 + +相信大家都使用过其他的Socket产品,例如HPSocket,SuperSocket等,那么RRQMSocket在设计时也是借鉴了其他产品的优秀设计理念,数据处理适配器就是其中之一,但和其他产品的设计不同的是,RRQMSocket的适配器功能更加强大,它不仅可以提前解析数据包,还可以解析数据对象。例如:可以使用固定包头对数据进行预处理,从而解决数据分包、粘包的问题。也可以直接解析HTTP协议,经过适配器处理后传回一个HttpRequest对象等。 + +#### 粘包、分包解决 + +在RRQMSocket中处理TCP粘包、分包问题是非常简单的。只需要更改不同的**数据处理适配器**即可。例如:使用**固定包头**,只需要给SocketClient和TcpClient配置注入**FixedHeaderDataHandlingAdapter**的实例即可。同样对应的处理器也有**固定长度** 、 **终止字符分割** 等。 + +#### 兼容性与适配 + +RRQMSocket提供多种框架模型,能够完全兼容基于TCP、UDP协议的所有协议。例如:TcpService与TcpClient,其基础功能和Socket一模一样,只是增强了框架的**坚固性**和**并发性**,将**连接**和**接收数据**通过事件的形式抛出,让使用者能够更加友好的使用。 + +其次,RRQMSocket也提供了一些特定的服务器和客户端,如TokenService和TokenClient,这两个就必须配套使用,不然在验证Token时会被主动断开。 + +## 🔗联系作者 + +- [CSDN博客主页](https://blog.csdn.net/qq_40374647) +- [哔哩哔哩视频](https://space.bilibili.com/94253567) +- [源代码仓库主页](https://gitee.com/RRQM_Home) +- 交流QQ群:234762506 + +## 🍻RRQM系产品 + +| 名称| 版本(Nuget Version)|下载(Nuget Download)| 描述 | +|------|----------|-------------|-------| +| [RRQMCore](https://gitee.com/RRQM_OS/RRQMCore) | [![NuGet version (RRQMCore)](https://img.shields.io/nuget/v/RRQMCore.svg?style=flat-square)](https://www.nuget.org/packages/RRQMCore/) | [![Download](https://img.shields.io/nuget/dt/RRQMCore)](https://www.nuget.org/packages/RRQMCore/) | RRQMCore是为RRQM系提供基础服务功能的库,其中包含:**内存池**、**对象池**、**等待逻辑池**、**AppMessenger**、**3DES加密**、**Xml快速存储**、**运行时间测量器**、**文件快捷操作**、**高性能序列化器**、**规范日志接口**等。 | +| [RRQMMVVM](https://gitee.com/RRQM_OS/RRQMMVVM) | [![NuGet version (RRQMMVVM)](https://img.shields.io/nuget/v/RRQMMVVM.svg?style=flat-square)](https://www.nuget.org/packages/RRQMMVVM/) | [![Download](https://img.shields.io/nuget/dt/RRQMMVVM)](https://www.nuget.org/packages/RRQMMVVM/) | RRQMMVVM是超轻简的MVVM框架,但是麻雀虽小,五脏俱全。| +| [RRQMSkin](https://gitee.com/RRQM_OS/RRQMSkin) | [![NuGet version (RRQMSkin)](https://img.shields.io/nuget/v/RRQMSkin.svg?style=flat-square)](https://www.nuget.org/packages/RRQMSkin/) | [![Download](https://img.shields.io/nuget/dt/RRQMSkin)](https://www.nuget.org/packages/RRQMSkin/) | RRQMSkin是WPF的控件样式库,其中包含: **无边框窗体** 、 **圆角窗体** 、 **水波纹按钮** 、 **输入提示筛选框** 、 **控件拖动效果** 、**圆角图片框**、 **弧形文字** 、 **扇形元素** 、 **指针元素** 、 **饼图** 、 **时钟** 、 **速度表盘** 等。| + +## 一、TCP框架 + +#### 1.1 说明 + +TCP框架是RRQMSocket最基础的框架,它定制了后继成员的创建、管理,维护、使用等一系列的规则,让使用者无需关心连接、掉线、失活检测、多线程安全等问题,能够专注于数据处理。 + +#### 1.2 安装 + +工具 ➨ Nuegt包管理器 ➨ 程序包管理器控制台 + +```CSharp +Install-Package RRQMSocket +``` + +#### 1.3 特点 + +- 简单易用。 +- 多线程。 +- **多地址监听**(可以一次性监听多个IP及端口) +- 适配器预处理,一键式解决**分包**、**粘包**、对象解析(如HTTP,Json)等。 +- 超简单的同步发送、异步发送、接收等操作。 +- 基于事件驱动,让每一步操作尽在掌握。 +- 高性能(服务器每秒可接收200w条信息) +- **独立线程内存池**(每个线程拥有自己的内存池) + +#### 1.4 应用场景 + +- C/S服务器开发。 +- 制造业自动化控制服务器。 +- 物联网数据采集服务器。 +- 游戏服务器开发。 + +#### 1.5 API文档 + +[RRQMSocket API文档](https://gitee.com/RRQM_OS/RRQM/wikis/pages?sort_id=3984527&doc_id=1402901) + +#### 1.6 Demo + +[RRQMBox](https://gitee.com/RRQM_OS/RRQMBox) + +## 二、Token框架 + +#### 2.1 说明 + +TokenService框架是RRQMSocket提供的派生自TcpService的基础框架,它在TCP基础之上,通过验证Token的方式,可以规范、筛选连接者。这样可以很大程度的**保护服务器**不疲于非法连接者的攻击。 + +#### 2.2 安装 + +工具➨Nuegt包管理器 ➨ 程序包管理器控制台 + +```CSharp +Install-Package RRQMSocket +``` + +#### 2.3 特点 + +- **规范**、**筛选**连接者,保护服务器。 +- 客户端与服务器必须**配套**使用。 + +#### 2.4 应用场景 + +- C/S服务器开发。 +- 制造业自动化控制服务器。 +- 游戏服务器开发。 + +#### 2.5 API文档 + +[RRQMSocket API文档](https://gitee.com/RRQM_OS/RRQM/wikis/pages?sort_id=3984517&doc_id=1402901) + +#### 2.6 Demo + +[RRQMBox](https://gitee.com/RRQM_OS/RRQMBox) + +## 三、Protocol框架 + +#### 3.1 说明 + +ProtocolService框架是RRQMSocket提供的派生自TokenService的基础框架,它在Token基础之上,提供**协议+数据**的形式发送,其中还包括**协议冲突检测**、**协议数据占位**等。 + +#### 3.2 安装 + +工具 ➨ Nuegt包管理器 ➨ 程序包管理器控制台 + +```CSharp +Install-Package RRQMSocket +``` + +#### 3.3 特点 + +- 支持**ID同步**。 +- 快捷**协议发送**。 + +#### 3.4 应用场景 + +- C/S服务器开发。 +- 制造业自动化控制服务器。 +- 游戏服务器开发。 + +#### 3.5 API文档 + +[RRQMSocket API文档](https://gitee.com/RRQM_OS/RRQM/wikis/pages?sort_id=3984517&doc_id=1402901) + +#### 3.6 Demo + +[RRQMBox](https://gitee.com/RRQM_OS/RRQMBox) + +## 四、RPCService框架 + +#### 4.1 说明 + +RPCService框架是所有远程过程调用的微服务调用管理平台,在该平台的托管下,使多种协议、多种序列化方式调用成为可能。目前可使用RRQMRPC、WebApi、XmlRpc、JsonRpc共同调用。 + +#### 4.2 RPC解析器 + +**说明:** RPCService仅仅是对调用的服务进行管理和维护,并不参与实质性的通信过程。实际上由于通信协议、序列化方式的不同,需要创建相对应的解析器才能完成调用操作。 + +#### 4.3 RPC解析器之RRQMRPC + +##### 4.3.1 说明 + +RRQMRPC是基于Protocol框架、固定包头解析的远程调用框架,也是RRQM中性能最强悍、使用最简单、功能最强大的RPC框架。 + +##### 4.3.2 特点 + +- 支持**自定义**类型参数。 +- 支持具有**默认值**的参数设定。 +- 支持**out、ref** 关键字参数。 +- 支持服务器**回调客户端** 。 +- 支持**客户端**之间**相互调用**。 +- 支持TCP、UDP等不同的协议调用相同服务。 +- 支持异步调用。 +- 支持权限管理,让非法调用死在萌芽时期。 +- 支持**静态织入调用**,**静态编译调用**,也支持**方法名+参数**调用。 +- 支持**调用配置**(类似MQTT的AtMostOnce,AtLeastOnce,ExactlyOnce)。 +- **支持EventBus**(企业版支持)。 +- 支持**自定义序列化**。 +- **全异常反馈** ,服务器调用状态会完整的反馈到客户端(可以设置不反馈)。 +- 高性能,在保证送达但不返回的情况下,10w次调用用时0.8s,在返回的情况下,用时3.9s。 + +##### 4.3.3 安装 + +工具 ➨ Nuegt包管理器 ➨ 程序包管理器控制台 + +```CSharp +Install-Package RRQMSocket.RPC +``` + +##### 4.3.4 RRQMRPC性能测试 + + **说明:** +图一、图二、图三分别为`UDP无反馈调用`、`TCP有反馈调用`、`TCP连接池有反馈调用`。调用次数均为10w次,调用性能非常nice。在无反馈中,吞吐量达14.28w,在有反馈中达2.72w。 + +![输入图片说明](https://images.gitee.com/uploads/images/2021/0409/191343_e5827d04_8553710.png "屏幕截图.png") + +![输入图片说明](https://images.gitee.com/uploads/images/2021/0409/191501_abec9e45_8553710.png "屏幕截图.png") + +![输入图片说明](https://images.gitee.com/uploads/images/2021/0409/191531_d7f0a8d4_8553710.png "屏幕截图.png") + + + +#### 4.4 RPC解析器之WebApi + +##### 4.4.1 说明 + +使用WebApi解析器,就可以在RPCService中通过WebApi的调用方式直接调用服务。 + +##### 4.4.2 特点 + +- 高性能,100个客户端,10w次调用,仅用时17s。 +- **全异常反馈** 。 +- 支持大部分路由规则。 +- 支持js、Android等调用。 + +##### 4.4.3 安装 + +工具 ➨ Nuegt包管理器 ➨ 程序包管理器控制台 + +```CSharp +Install-Package RRQMSocket.RPC.WebApi +``` + +#### 4.5 RPC解析器之XmlRpc + +##### 4.5.1 说明 + +使用XmlRpc解析器,就可以在RPCService中通过XmlRpc的调用方式直接调用服务,客户端可以使用**CookComputing.XmlRpcV2**进行对接。 + +##### 4.5.2 特点 + +- **异常反馈** 。 +- 支持自定义类型。 +- 支持类型嵌套。 +- 支持Array及自定义Array嵌套。 +- 支持js、Android等调用。 + +##### 4.5.3 安装 + +工具 ➨ Nuegt包管理器 ➨ 程序包管理器控制台 + +```CSharp +Install-Package RRQMSocket.RPC.XmlRpc +``` + +#### 4.6 RPC解析器之JsonRpc + +##### 4.6.1 说明 + +使用JsonRpc解析器,就可以在RPCService中通过Json字符串直接调用服务。 + +##### 4.6.2 特点 + +- **异常反馈** 。 +- 支持自定义类型。 +- 支持类型嵌套。 +- 支持js、Android等调用。 + +##### 4.6.3 安装 + +工具 ➨ Nuegt包管理器 ➨ 程序包管理器控制台 + +```CSharp +Install-Package RRQMSocket.RPC.JsonRpc +``` + +#### 4.7 API文档 + +[RRQMSocket API文档](https://gitee.com/RRQM_OS/RRQM/wikis/pages?sort_id=3984517&doc_id=1402901) + +#### 4.8 Demo + +[RRQMBox](https://gitee.com/RRQM_OS/RRQMBox) + +## 五、文件传输框架 + +#### 5.1 说明 + +RRQMSocket.FileTransfer是一个高性能的文件传输框架,由于它派生自RRQMRPC,所以也具备RPC的全部特性。 + +#### 5.2 特点 + +- 简单易用。 +- 多线程处理。 +- 高性能,传输速度可达**500Mb/s**。 +- 超简单的**传输限速**设置,1k-10Gb 无级调节。 +- 超简单的传输速度、传输进度获取。 +- 随心所欲的暂停、继续、停止传输。 +- 系统化的权限管理,让敏感文件只允许**私有化下载**。 +- **RPC交互**,让客户端和服务器交流不延迟。 +- 基于**事件驱动**,让每一步操作尽在掌握。 +- 可视化的文件块流,可以实现像迅雷一样的**填充式进度条**。 +- 超简单的**断点续传**设置,为大文件传输保驾护航。 +- 无状态上传断点续传设置,让同一个文件,在不同客户端之间**接力上传**。 +- **断网续传**(企业版支持) +- 已经上传的文件,再次上传时,可实现**快速上传**。 +- 极少的GC释放。 + +#### 5.3 安装 + +工具 ➨ Nuegt包管理器 ➨ 程序包管理器控制台 + +```CSharp +Install-Package RRQMSocket.FileTransfer +``` + +#### 5.4 Demo示例 + + **Demo位置:** [RRQMBox](https://gitee.com/RRQM_OS/RRQMBox) + +#### 5.5 性能测试 + + **说明:** 可以看到,图一正在上传一个Window的系统镜像文件,大约4.2Gb,传输速度已达到346Mb/s,这是因为服务器和客户端在同一电脑上,磁盘性能限制导致的。其次,GC基本上没有释放,性能非常强悍,图二是下载文件,性能依旧非常强悍。 + +![上传文件](https://images.gitee.com/uploads/images/2021/0409/190350_92a2ad36_8553710.png "上传文件") +![下载文件](https://images.gitee.com/uploads/images/2021/0409/190954_a212982d_8553710.png "下载文件") + + +## 致谢 + +谢谢大家对我的支持,如果还有其他问题,请加群QQ:234762506讨论。 + + +## 💕 支持本项目 +您的支持就是我不懈努力的动力。 + +#### 爱心赞助名单(以下排名只按照打赏时间顺序) + + 1. Bobo Joker(200¥) + 2. UnitySir(66¥) + 3. Coffee(100¥) + 4. Ninety(50¥) + 5. *琼(100¥) + 6. **安(5¥) + +#### 商业采购名单(以下排名只按照商业采购时间顺序) +1.凯斯得****有限公司 + +图片名称 + diff --git a/RRQMSocket.FileTransfer/Common/FileBlock.cs b/RRQMSocket.FileTransfer/Common/FileBlock.cs new file mode 100644 index 000000000..5d8216cf6 --- /dev/null +++ b/RRQMSocket.FileTransfer/Common/FileBlock.cs @@ -0,0 +1,40 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ + +namespace RRQMSocket.FileTransfer +{ + /// + /// 文件块 + /// + public class FileBlock + { + /// + /// 文件快索引 + /// + public int Index { get; internal set; } + + /// + /// 文件流位置 + /// + public long Position { get; internal set; } + + /// + /// 文件块长度 + /// + public long UnitLength { get; internal set; } + + /// + /// 请求状态 + /// + public RequestStatus RequestStatus { get; internal set; } + } +} \ No newline at end of file diff --git a/RRQMSocket.FileTransfer/Common/FileHashGenerator.cs b/RRQMSocket.FileTransfer/Common/FileHashGenerator.cs new file mode 100644 index 000000000..e964c7c43 --- /dev/null +++ b/RRQMSocket.FileTransfer/Common/FileHashGenerator.cs @@ -0,0 +1,80 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +using RRQMCore.IO; +using System.IO; +using System.Security.Cryptography; + +namespace RRQMSocket.FileTransfer +{ + /// + /// 文件Hash校验 + /// + public static class FileHashGenerator + { + private static FileHashType fileCheckType; + + /// + /// 文件 + /// + public static FileHashType FileCheckType + { + get { return fileCheckType; } + set { fileCheckType = value; } + } + + /// + /// 获取文件Hash + /// + /// + /// + public static string GetFileHash(string path) + { + using (FileStream fileStream = new FileStream(path, FileMode.Open, FileAccess.Read)) + { + return GetFileHash(fileStream); + } + } + + /// + /// 获取文件Hash + /// + /// + /// + public static string GetFileHash(FileStream fileStream) + { + HashAlgorithm hash; + switch (fileCheckType) + { + case FileHashType.MD5: + hash = MD5.Create(); + break; + + case FileHashType.SHA1: + hash = SHA1.Create(); + break; + + case FileHashType.SHA256: + hash = SHA256.Create(); + break; + + case FileHashType.SHA512: + hash = SHA512.Create(); + break; + + default: + hash = null; + break; + } + return FileControler.GetStreamHash(fileStream, hash); + } + } +} \ No newline at end of file diff --git a/RRQMSocket.FileTransfer/Common/FileStreamPool.cs b/RRQMSocket.FileTransfer/Common/FileStreamPool.cs new file mode 100644 index 000000000..08dd010b7 --- /dev/null +++ b/RRQMSocket.FileTransfer/Common/FileStreamPool.cs @@ -0,0 +1,250 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +using RRQMCore.ByteManager; +using RRQMCore.Exceptions; +using System; +using System.Collections.Concurrent; +using System.Threading; + +namespace RRQMSocket.FileTransfer +{ + /// + /// 文件流池 + /// + public static class FileStreamPool + { + internal static ConcurrentDictionary pathStream = new ConcurrentDictionary(); + private readonly static object locker = new object(); + private static ReaderWriterLockSlim lockSlim = new ReaderWriterLockSlim(); + + internal static bool CheckAllFileBlockFinished(string path) + { + lock (locker) + { + RRQMStream stream; + if (!pathStream.TryGetValue(path, out stream)) + { + return false; + } + if (stream.StreamType == StreamOperationType.Read) + { + return false; + } + foreach (var block in stream.Blocks) + { + if (block.RequestStatus != RequestStatus.Finished) + { + return false; + } + } + return true; + } + } + + internal static void DisposeReadStream(string path) + { + RRQMStream stream; + if (pathStream.TryGetValue(path, out stream)) + { + if (Interlocked.Decrement(ref stream.reference) == 0) + { + if (pathStream.TryRemove(path, out stream)) + { + stream.Dispose(); + } + } + } + } + + internal static void DisposeWriteStream(string path, bool finished) + { + RRQMStream stream; + if (pathStream.TryGetValue(path, out stream)) + { + if (Interlocked.Decrement(ref stream.reference) == 0) + { + if (pathStream.TryRemove(path, out stream)) + { + if (finished) + { + stream.FinishStream(); + } + else + { + stream.Dispose(); + } + } + } + } + } + + internal static bool GetFreeFileBlock(string path, out FileBlock fileBlock, out string mes) + { + lock (locker) + { + RRQMStream stream; + if (!pathStream.TryGetValue(path, out stream)) + { + mes = "没有此路径的写入信息"; + fileBlock = null; + return false; + } + if (stream.StreamType == StreamOperationType.Read) + { + mes = "该路径的流为只读"; + fileBlock = null; + return false; + } + foreach (var block in stream.Blocks) + { + if (block.RequestStatus == RequestStatus.Hovering) + { + block.RequestStatus = RequestStatus.InProgress; + fileBlock = block; + mes = null; + return true; + } + } + fileBlock = null; + mes = null; + return true; + } + } + + internal static bool LoadReadStream(ref UrlFileInfo urlFileInfo, out string mes) + { + RRQMStream stream; + if (pathStream.TryGetValue(urlFileInfo.FilePath, out stream)) + { + Interlocked.Increment(ref stream.reference); + mes = null; + return true; + } + else + { + if (RRQMStream.CreateReadStream(out stream, ref urlFileInfo, out mes)) + { + Interlocked.Increment(ref stream.reference); + pathStream.TryAdd(urlFileInfo.FilePath, stream); + return true; + } + else + { + return false; + } + } + } + + internal static bool LoadWriteStream(ref ProgressBlockCollection blocks, bool onlySearch, out string mes) + { + RRQMStream stream; + string rrqmPath = blocks.UrlFileInfo.SaveFullPath + ".rrqm"; + if (!pathStream.TryGetValue(rrqmPath, out stream)) + { + if (RRQMStream.CreateWriteStream(out stream, ref blocks, out mes)) + { + Interlocked.Increment(ref stream.reference); + mes = null; + return pathStream.TryAdd(rrqmPath, stream); + } + else + { + return false; + } + } + else + { + if (onlySearch) + { + blocks = stream.Blocks; + mes = null; + return true; + } + mes = "该文件流正在被其他客户端拥有"; + return false; + } + } + + internal static bool ReadFile(string path, out string mes, long beginPosition, ByteBlock byteBlock, int offset, int length) + { + lockSlim.EnterReadLock(); + try + { + if (pathStream.TryGetValue(path, out RRQMStream stream)) + { + stream.FileStream.Position = beginPosition; + + if (byteBlock.Buffer.Length < length + offset) + { + byteBlock.SetBuffer(new byte[length + offset]); + } + + int r = stream.FileStream.Read(byteBlock.Buffer, offset, length); + if (r == length) + { + byteBlock.Position = offset + length; + byteBlock.SetLength(offset + length); + mes = null; + return true; + } + } + mes = "没有找到该路径下的流文件"; + return false; + } + catch (Exception ex) + { + mes = ex.Message; + return false; + } + finally + { + lockSlim.ExitReadLock(); + } + } + + internal static void SaveProgressBlockCollection(string path) + { + RRQMStream stream; + if (pathStream.TryGetValue(path, out stream)) + { + stream.SaveProgressBlockCollection(); + } + else + { + throw new RRQMException("没有找到该路径下的流文件"); + } + } + + internal static bool WriteFile(string path, out string mes, out RRQMStream stream, long streamPosition, byte[] buffer, int offset, int length) + { + try + { + if (pathStream.TryGetValue(path, out stream)) + { + stream.FileStream.Position = streamPosition; + stream.FileStream.Write(buffer, offset, length); + stream.FileStream.Flush(); + mes = null; + return true; + } + mes = "未找到该路径下的流"; + return false; + } + catch (Exception ex) + { + mes = ex.Message; + stream = null; + return false; + } + } + } +} \ No newline at end of file diff --git a/RRQMSocket.FileTransfer/Common/FileTransferErrorExceptionMapping.cs b/RRQMSocket.FileTransfer/Common/FileTransferErrorExceptionMapping.cs new file mode 100644 index 000000000..f1f92fbe1 --- /dev/null +++ b/RRQMSocket.FileTransfer/Common/FileTransferErrorExceptionMapping.cs @@ -0,0 +1,28 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +using RRQMCore.Exceptions; + +namespace RRQMSocket.FileTransfer +{ + /// + /// 文件传输错误码映射 + /// + public class FileTransferErrorExceptionMapping : ErrorExceptionMapping + { + private static FileTransferErrorExceptionMapping _instance = new FileTransferErrorExceptionMapping(); + + /// + /// 默认实例 + /// + public static FileTransferErrorExceptionMapping Default { get => _instance; } + } +} \ No newline at end of file diff --git a/RRQMSocket.FileTransfer/Common/FileWaitResult.cs b/RRQMSocket.FileTransfer/Common/FileWaitResult.cs new file mode 100644 index 000000000..b52fa4483 --- /dev/null +++ b/RRQMSocket.FileTransfer/Common/FileWaitResult.cs @@ -0,0 +1,20 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +using RRQMCore.Run; + +namespace RRQMSocket.FileTransfer +{ + internal class FileWaitResult : WaitResult + { + public PBCollectionTemp PBCollectionTemp { get; set; } + } +} \ No newline at end of file diff --git a/RRQMSocket.FileTransfer/Common/PBCollectionTemp.cs b/RRQMSocket.FileTransfer/Common/PBCollectionTemp.cs new file mode 100644 index 000000000..353a177c7 --- /dev/null +++ b/RRQMSocket.FileTransfer/Common/PBCollectionTemp.cs @@ -0,0 +1,64 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +using System.Collections.Generic; + +namespace RRQMSocket.FileTransfer +{ + /// + /// 临时序列化 + /// + public class PBCollectionTemp + { + /// + /// 文件信息 + /// + public UrlFileInfo UrlFileInfo { get; internal set; } + + /// + /// 块集合 + /// + public List Blocks { get; internal set; } + + /// + /// 从文件块转换 + /// + /// + /// + public static PBCollectionTemp GetFromProgressBlockCollection(ProgressBlockCollection progressBlocks) + { + if (progressBlocks == null) + { + return null; + } + PBCollectionTemp collectionTemp = new PBCollectionTemp(); + collectionTemp.UrlFileInfo = progressBlocks.UrlFileInfo; + collectionTemp.Blocks = new List(); + collectionTemp.Blocks.AddRange(progressBlocks); + return collectionTemp; + } + + /// + /// 转换为ProgressBlockCollection + /// + /// + public ProgressBlockCollection ToPBCollection() + { + ProgressBlockCollection progressBlocks = new ProgressBlockCollection(); + progressBlocks.UrlFileInfo = this.UrlFileInfo; + if (this.Blocks != null) + { + progressBlocks.AddRange(this.Blocks); + } + return progressBlocks; + } + } +} \ No newline at end of file diff --git a/RRQMSocket.FileTransfer/Common/ProgressBlockCollection.cs b/RRQMSocket.FileTransfer/Common/ProgressBlockCollection.cs new file mode 100644 index 000000000..3eb8e2f46 --- /dev/null +++ b/RRQMSocket.FileTransfer/Common/ProgressBlockCollection.cs @@ -0,0 +1,106 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +using RRQMCore.Serialization; +using System; +using System.IO; + +namespace RRQMSocket.FileTransfer +{ + /// + /// 文件进度块集合 + /// + public class ProgressBlockCollection : ReadOnlyList + { + /// + /// 文件信息 + /// + public UrlFileInfo UrlFileInfo { get; internal set; } + + private static int blockLength = 1024 * 1024 * 10; + + /// + /// 分块长度,min=1024*1024*5 + /// + public static int BlockLength + { + get { return blockLength; } + set + { + if (value < 1024 * 1024 * 5) + { + value = 1024 * 1024 * 5; + } + blockLength = value; + } + } + + /// + /// 保存 + /// + /// + internal void Save(string path) + { + if (File.Exists(path)) + { + File.Delete(path); + } + byte[] buffer = SerializeConvert.RRQMBinarySerialize(this, true); + using (FileStream fileStream = new FileStream(path, FileMode.OpenOrCreate, FileAccess.ReadWrite)) + { + fileStream.Write(buffer, 0, buffer.Length); + } + } + + /// + /// 读取 + /// + /// + /// + internal static ProgressBlockCollection Read(string path) + { + try + { + using (FileStream stream = File.OpenRead(path)) + { + byte[] buffer = new byte[stream.Length]; + stream.Read(buffer, 0, buffer.Length); + return SerializeConvert.RRQMBinaryDeserialize(buffer, 0); + } + } + catch (Exception) + { + return null; + } + } + + internal static ProgressBlockCollection CreateProgressBlockCollection(UrlFileInfo urlFileInfo) + { + ProgressBlockCollection blocks = new ProgressBlockCollection(); + blocks.UrlFileInfo = urlFileInfo; + long position = 0; + long surLength = urlFileInfo.FileLength; + int index = 0; + while (surLength > 0) + { + FileBlock block = new FileBlock(); + block.Index = index++; + block.RequestStatus = RequestStatus.Hovering; + block.Position = position; + block.UnitLength = surLength > blockLength ? blockLength : surLength; + blocks.Add(block); + position += block.UnitLength; + surLength -= block.UnitLength; + } + return blocks; + } + } +} \ No newline at end of file diff --git a/RRQMSocket.FileTransfer/Common/RRQMStream.cs b/RRQMSocket.FileTransfer/Common/RRQMStream.cs new file mode 100644 index 000000000..e03d7fc3b --- /dev/null +++ b/RRQMSocket.FileTransfer/Common/RRQMStream.cs @@ -0,0 +1,157 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +using RRQMCore.Serialization; +using System; +using System.IO; + +namespace RRQMSocket.FileTransfer +{ + internal class RRQMStream + { + internal int reference; + + private ProgressBlockCollection blocks; + + private FileStream fileStream; + private string rrqmPath; + private StreamOperationType streamType; + + private UrlFileInfo urlFileInfo; + + private RRQMStream() + { + } + + public ProgressBlockCollection Blocks { get { return blocks; } } + + public FileStream FileStream + { + get { return fileStream; } + } + + public StreamOperationType StreamType + { + get { return streamType; } + } + + public UrlFileInfo UrlFileInfo { get => urlFileInfo; } + + internal static bool CreateReadStream(out RRQMStream stream, ref UrlFileInfo urlFileInfo, out string mes) + { + stream = new RRQMStream(); + try + { + stream.streamType = StreamOperationType.Read; + stream.fileStream = File.OpenRead(urlFileInfo.FilePath); + urlFileInfo.FileLength = stream.fileStream.Length; + stream.urlFileInfo = urlFileInfo; + mes = null; + return true; + } + catch (Exception ex) + { + stream.Dispose(); + stream = null; + mes = ex.Message; + return false; + } + } + + internal static bool CreateWriteStream(out RRQMStream stream, ref ProgressBlockCollection blocks, out string mes) + { + stream = new RRQMStream(); + stream.rrqmPath = blocks.UrlFileInfo.SaveFullPath + ".rrqm"; + stream.urlFileInfo = blocks.UrlFileInfo; + stream.streamType = StreamOperationType.Write; + try + { + if (blocks.UrlFileInfo.Flags.HasFlag(TransferFlags.BreakpointResume) && File.Exists(stream.rrqmPath)) + { + stream.fileStream = new FileStream(stream.rrqmPath, FileMode.OpenOrCreate, FileAccess.ReadWrite); + int blocksLength = (int)(stream.fileStream.Length - blocks.UrlFileInfo.FileLength); + if (blocksLength > 0) + { + stream.fileStream.Position = blocks.UrlFileInfo.FileLength; + byte[] buffer = new byte[blocksLength]; + stream.fileStream.Read(buffer, 0, buffer.Length); + try + { + PBCollectionTemp readBlocks = SerializeConvert.RRQMBinaryDeserialize(buffer); + if (readBlocks.UrlFileInfo != null && blocks.UrlFileInfo != null && readBlocks.UrlFileInfo.FileHash != null) + { + if (readBlocks.UrlFileInfo.FileHash == blocks.UrlFileInfo.FileHash) + { + stream.blocks = blocks = readBlocks.ToPBCollection(); + mes = null; + return true; + } + } + } + catch + { + } + } + stream.fileStream.Dispose(); + } + stream.blocks = blocks; + if (File.Exists(stream.rrqmPath)) + { + File.Delete(stream.rrqmPath); + } + stream.fileStream = new FileStream(stream.rrqmPath, FileMode.Create, FileAccess.ReadWrite); + stream.SaveProgressBlockCollection(); + mes = null; + return true; + } + catch (Exception ex) + { + mes = ex.Message; + stream.Dispose(); + stream = null; + return false; + } + } + + internal bool FinishStream() + { + this.fileStream.SetLength(this.urlFileInfo.FileLength); + this.fileStream.Flush(); + UrlFileInfo info = this.urlFileInfo; + this.Dispose(); + if (File.Exists(info.SaveFullPath)) + { + File.Delete(info.SaveFullPath); + } + File.Move(info.SaveFullPath + ".rrqm", info.SaveFullPath); + return true; + } + + internal void Dispose() + { + this.blocks = null; + this.urlFileInfo = null; + if (this.fileStream != null) + { + this.fileStream.Dispose(); + this.fileStream = null; + } + } + + internal void SaveProgressBlockCollection() + { + byte[] dataBuffer = SerializeConvert.RRQMBinarySerialize(PBCollectionTemp.GetFromProgressBlockCollection(blocks), true); + this.fileStream.Position = this.urlFileInfo.FileLength; + this.fileStream.WriteAsync(dataBuffer, 0, dataBuffer.Length); + this.fileStream.Flush(); + } + } +} \ No newline at end of file diff --git a/RRQMSocket.FileTransfer/Common/ReadOnlyList.cs b/RRQMSocket.FileTransfer/Common/ReadOnlyList.cs new file mode 100644 index 000000000..ca2da9cab --- /dev/null +++ b/RRQMSocket.FileTransfer/Common/ReadOnlyList.cs @@ -0,0 +1,93 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +using System; +using System.Collections; +using System.Collections.Generic; + +namespace RRQMSocket.FileTransfer +{ + /// + /// 只读 + /// + /// + + public class ReadOnlyList : IEnumerable + { + private List list = new List(); + + internal void Add(T block) + { + list.Add(block); + } + + internal void AddRange(IEnumerable collection) + { + list.AddRange(collection); + } + + internal void Remove(T block) + { + list.Remove(block); + } + + internal void RemoveAt(int index) + { + list.RemoveAt(index); + } + + internal void RemoveAll(Predicate match) + { + list.RemoveAll(match); + } + + internal void RemoveRange(int index, int range) + { + list.RemoveRange(index, range); + } + + internal void Clear() + { + list.Clear(); + } + + internal void Insert(int index, T item) + { + list.Insert(index, item); + } + + internal void InsertRange(int index, IEnumerable collection) + { + list.InsertRange(index, collection); + } + + /// + /// 返回迭代器 + /// + /// + public IEnumerator GetEnumerator() + { + return this.list.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return this.list.GetEnumerator(); + } + + /// + /// 获取对象 + /// + /// + /// + public T this[int index] { get { return list[index]; } } + } +} \ No newline at end of file diff --git a/RRQMSocket.FileTransfer/Common/Speed.cs b/RRQMSocket.FileTransfer/Common/Speed.cs new file mode 100644 index 000000000..19951c904 --- /dev/null +++ b/RRQMSocket.FileTransfer/Common/Speed.cs @@ -0,0 +1,22 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +namespace RRQMSocket.FileTransfer +{ + /// + /// + /// + internal class Speed + { + internal static long downloadSpeed; + internal static long uploadSpeed; + } +} \ No newline at end of file diff --git a/RRQMSocket.FileTransfer/Common/TransferCollection.cs b/RRQMSocket.FileTransfer/Common/TransferCollection.cs new file mode 100644 index 000000000..38d847c6f --- /dev/null +++ b/RRQMSocket.FileTransfer/Common/TransferCollection.cs @@ -0,0 +1,92 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +using System.Collections; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace RRQMSocket.FileTransfer +{ + /// + /// 传输集合 + /// + public class TransferCollection : IEnumerable + { + internal TransferCollection() + { + list = new List(); + } + + internal event RRQMMessageEventHandler OnCollectionChanged; + + private List list; + + /// + /// 返回一个循环访问集合的枚举器 + /// + /// + public IEnumerator GetEnumerator() + { + return list.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return list.GetEnumerator(); + } + + internal void Add(UrlFileInfo fileInfo) + { + this.list.Add(fileInfo); + Task.Run(() => + { + OnCollectionChanged?.Invoke(null, new MesEventArgs("添加")); + }); + } + + internal void Clear() + { + this.list.Clear(); + Task.Run(() => + { + OnCollectionChanged?.Invoke(null, new MesEventArgs("清空")); + }); + } + + internal bool Remove(UrlFileInfo fileInfo) + { + Task.Run(() => + { + OnCollectionChanged?.Invoke(null, new MesEventArgs("移除")); + }); + return this.list.Remove(fileInfo); + } + + internal bool GetFirst(out UrlFileInfo fileInfo) + { + lock (this) + { + if (this.list.Count > 0) + { + fileInfo = this.list[0]; + this.list.RemoveAt(0); + Task.Run(() => + { + OnCollectionChanged?.Invoke(null, new MesEventArgs("进入传输")); + }); + return true; + } + fileInfo = null; + return false; + } + } + } +} \ No newline at end of file diff --git a/RRQMSocket.FileTransfer/Common/TransferFileHashDictionary.cs b/RRQMSocket.FileTransfer/Common/TransferFileHashDictionary.cs new file mode 100644 index 000000000..524f0d120 --- /dev/null +++ b/RRQMSocket.FileTransfer/Common/TransferFileHashDictionary.cs @@ -0,0 +1,203 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +using System.Collections.Concurrent; +using System.IO; + +namespace RRQMSocket.FileTransfer +{ + /// + /// 传输文件Hash暂存字典 + /// + public static class TransferFileHashDictionary + { + private static ConcurrentDictionary fileHashAndInfo = new ConcurrentDictionary(); + private static ConcurrentDictionary filePathAndInfo = new ConcurrentDictionary(); + + /// + /// 字典存储文件Hash的最大数量,默认为10000 + /// + public static int MaxCount { get; set; } = 10000; + + /// + /// 添加文件信息 + /// + /// + /// + /// + public static UrlFileInfo AddFile(string filePath, bool breakpointResume = true) + { + UrlFileInfo urlFileInfo = new UrlFileInfo(); + using (FileStream stream = File.OpenRead(filePath)) + { + urlFileInfo.FilePath = filePath; + urlFileInfo.FileLength = stream.Length; + urlFileInfo.FileName = Path.GetFileName(filePath); + if (breakpointResume) + { + urlFileInfo.FileHash = FileHashGenerator.GetFileHash(stream); + } + } + AddFile(urlFileInfo); + return urlFileInfo; + } + + /// + /// 添加文件信息 + /// + /// + public static void AddFile(UrlFileInfo urlFileInfo) + { + if (urlFileInfo == null) + { + return; + } + filePathAndInfo.AddOrUpdate(urlFileInfo.FilePath, urlFileInfo, (key, oldValue) => + { + return urlFileInfo; + }); + + if (!string.IsNullOrEmpty(urlFileInfo.FileHash)) + { + fileHashAndInfo.AddOrUpdate(urlFileInfo.FileHash, urlFileInfo, (key, oldValue) => + { + return urlFileInfo; + }); + } + + if (filePathAndInfo.Count > MaxCount) + { + foreach (var item in filePathAndInfo.Keys) + { + if (filePathAndInfo.TryRemove(item, out _)) + { + break; + } + } + } + + if (fileHashAndInfo.Count > MaxCount) + { + foreach (var item in fileHashAndInfo.Keys) + { + if (fileHashAndInfo.TryRemove(item, out _)) + { + break; + } + } + } + } + + /// + /// 清除全部 + /// + public static void ClearDictionary() + { + if (filePathAndInfo == null) + { + return; + } + filePathAndInfo.Clear(); + } + + /// + /// 获取文件信息 + /// + /// + /// + /// + /// + public static bool GetFileInfo(string filePath, out UrlFileInfo urlFileInfo, bool breakpointResume) + { + if (filePathAndInfo == null) + { + urlFileInfo = null; + return false; + } + if (filePathAndInfo.ContainsKey(filePath)) + { + urlFileInfo = filePathAndInfo[filePath]; + if (File.Exists(filePath)) + { + using (FileStream stream = File.OpenRead(filePath)) + { + if (urlFileInfo.FileLength == stream.Length) + { + if (breakpointResume && urlFileInfo.FileHash == null) + { + urlFileInfo.FileHash = FileHashGenerator.GetFileHash(stream); + AddFile(urlFileInfo); + } + return true; + } + } + } + } + + urlFileInfo = null; + return false; + } + + /// + /// 通过FileHash获取文件信息 + /// + /// + /// + /// + public static bool GetFileInfoFromHash(string fileHash, out UrlFileInfo urlFileInfo) + { + if (fileHashAndInfo == null) + { + urlFileInfo = null; + return false; + } + if (string.IsNullOrEmpty(fileHash)) + { + urlFileInfo = null; + return false; + } + + if (fileHashAndInfo.TryGetValue(fileHash, out urlFileInfo)) + { + if (urlFileInfo.FileHash == fileHash) + { + if (File.Exists(urlFileInfo.FilePath)) + { + using (FileStream stream = File.OpenRead(urlFileInfo.FilePath)) + { + if (urlFileInfo.FileLength == stream.Length) + { + return true; + } + } + } + } + } + + urlFileInfo = null; + return false; + } + + /// + /// 移除 + /// + /// + /// + public static bool Remove(string filePath) + { + if (filePathAndInfo == null) + { + return false; + } + return filePathAndInfo.TryRemove(filePath, out _); + } + } +} \ No newline at end of file diff --git a/RRQMSocket.FileTransfer/Common/UrlFileInfo.cs b/RRQMSocket.FileTransfer/Common/UrlFileInfo.cs new file mode 100644 index 000000000..9b63ab055 --- /dev/null +++ b/RRQMSocket.FileTransfer/Common/UrlFileInfo.cs @@ -0,0 +1,166 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ + +using System.IO; + +namespace RRQMSocket.FileTransfer +{ + /// + /// 文件信息类 + /// + public class UrlFileInfo + { + private string saveFullPath = string.Empty; + + private int timeout = 30 * 1000; + + /// + /// 文件哈希值 + /// + public string FileHash { get; internal set; } + + /// + /// 文件大小 + /// + public long FileLength { get; internal set; } + + /// + /// 文件名 + /// + public string FileName { get; internal set; } + + /// + /// 文件路径 + /// + public string FilePath { get; internal set; } + + /// + /// 传输标识 + /// + public TransferFlags Flags { get; set; } + + /// + /// 携带消息 + /// + public string Message { get; set; } + + /// + /// 存放目录 + /// + public string SaveFullPath + { + get { return saveFullPath; } + set + { + if (value == null) + { + value = string.Empty; + } + saveFullPath = value; + } + } + + /// + /// 超时时间,默认30*1000 ms + /// + public int Timeout + { + get { return timeout; } + set { timeout = value; } + } + + /// + /// 请求传输类型 + /// + public TransferType TransferType { get; internal set; } + + /// + /// 生成下载请求必要信息 + /// + /// + /// + /// + public static UrlFileInfo CreateDownload(string path, TransferFlags flags) + { + UrlFileInfo fileInfo = new UrlFileInfo(); + fileInfo.FilePath = path; + fileInfo.Flags = flags; + fileInfo.FileName = Path.GetFileName(path); + fileInfo.TransferType = TransferType.Download; + return fileInfo; + } + + /// + /// 生成上传请求必要信息 + /// + /// + /// + /// + public static UrlFileInfo CreateUpload(string path, TransferFlags flags) + { + UrlFileInfo fileInfo = new UrlFileInfo(); + fileInfo.TransferType = TransferType.Upload; + using (FileStream stream = File.OpenRead(path)) + { + fileInfo.Flags = flags; + fileInfo.FilePath = path; + if (flags.HasFlag(TransferFlags.BreakpointResume) || flags.HasFlag(TransferFlags.QuickTransfer)) + { + fileInfo.FileHash = FileHashGenerator.GetFileHash(stream); + } + fileInfo.FileLength = stream.Length; + fileInfo.FileName = Path.GetFileName(path); + } + + return fileInfo; + } + + /// + /// 复制 + /// + /// + public void CopyFrom(UrlFileInfo urlFileInfo) + { + this.FileHash = urlFileInfo.FileHash; + this.FileLength = urlFileInfo.FileLength; + this.FileName = urlFileInfo.FileName; + this.FilePath = urlFileInfo.FilePath; + } + + /// + /// 判断参数是否相同 + /// + /// + /// + public bool Equals(UrlFileInfo urlFileInfo) + { + if (urlFileInfo.FileHash != this.FileHash) + { + return false; + } + if (urlFileInfo.FileLength != this.FileLength) + { + return false; + } + if (urlFileInfo.FileName != this.FileName) + { + return false; + } + if (urlFileInfo.FilePath != this.FilePath) + { + return false; + } + + return true; + } + } +} \ No newline at end of file diff --git a/RRQMSocket.FileTransfer/Config/FileClientConfig.cs b/RRQMSocket.FileTransfer/Config/FileClientConfig.cs new file mode 100644 index 000000000..18d2b0868 --- /dev/null +++ b/RRQMSocket.FileTransfer/Config/FileClientConfig.cs @@ -0,0 +1,85 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +using RRQMCore.Dependency; + +namespace RRQMSocket.FileTransfer +{ + /// + /// 文件客户端配置 + /// + public class FileClientConfig : TokenClientConfig + { + /// + /// 构造函数 + /// + public FileClientConfig() + { + this.BufferLength = 64 * 1024; + } + + /// + /// 默认接收文件的存放目录 + /// + public string ReceiveDirectory + { + get { return (string)GetValue(ReceiveDirectoryProperty); } + set + { + if (value == null) + { + value = string.Empty; + } + SetValue(ReceiveDirectoryProperty, value); + } + } + + /// + /// 默认接收文件的存放目录, 所需类型 + /// + public static readonly DependencyProperty ReceiveDirectoryProperty = + DependencyProperty.Register("ReceiveDirectory", typeof(string), typeof(FileClientConfig), string.Empty); + + /// + /// 单次请求超时时间 min=5000,max=60*1000 ms + /// + public int Timeout + { + get { return (int)GetValue(TimeoutProperty); } + set + { + SetValue(TimeoutProperty, value); + } + } + + /// + /// 单次请求超时时间 min=5000,max=60*1000 ms, 所需类型 + /// + public static readonly DependencyProperty TimeoutProperty = + DependencyProperty.Register("Timeout", typeof(int), typeof(FileClientConfig), 10*1000); + + /// + /// 数据包尺寸 + /// + public int PacketSize + { + get { return (int)GetValue(PacketSizeProperty); } + set { SetValue(PacketSizeProperty, value); } + } + + /// + /// 数据包尺寸, 所需类型 + /// + [RRQMCore.Range] + public static readonly DependencyProperty PacketSizeProperty = + DependencyProperty.Register("PacketSize", typeof(int), typeof(FileClientConfig), 1024 * 1024); + } +} \ No newline at end of file diff --git a/RRQMSocket.FileTransfer/Config/FileServiceConfig.cs b/RRQMSocket.FileTransfer/Config/FileServiceConfig.cs new file mode 100644 index 000000000..40d0d60f0 --- /dev/null +++ b/RRQMSocket.FileTransfer/Config/FileServiceConfig.cs @@ -0,0 +1,59 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +using RRQMCore.Dependency; + +namespace RRQMSocket.FileTransfer +{ + /// + /// 文件服务器配置 + /// + public class FileServiceConfig : TokenServiceConfig + { + /// + /// 构造函数 + /// + public FileServiceConfig() + { + this.BufferLength = 64 * 1024; + } + + /// + /// 最大下载速度 + /// + public long MaxDownloadSpeed + { + get { return (long)GetValue(MaxDownloadSpeedProperty); } + set { SetValue(MaxDownloadSpeedProperty, value); } + } + + /// + /// 最大下载速度, 所需类型 + /// + public static readonly DependencyProperty MaxDownloadSpeedProperty = + DependencyProperty.Register("MaxDownloadSpeed", typeof(long), typeof(FileServiceConfig), 1024 * 1024L); + + /// + /// 最大上传速度 + /// + public long MaxUploadSpeed + { + get { return (long)GetValue(MaxUploadSpeedProperty); } + set { SetValue(MaxUploadSpeedProperty, value); } + } + + /// + /// 最大上传速度, 所需类型 + /// + public static readonly DependencyProperty MaxUploadSpeedProperty = + DependencyProperty.Register("MaxUploadSpeed", typeof(long), typeof(FileServiceConfig), 1024 * 1024L); + } +} \ No newline at end of file diff --git a/RRQMSocket.FileTransfer/Delegate/DelegateCollection.cs b/RRQMSocket.FileTransfer/Delegate/DelegateCollection.cs new file mode 100644 index 000000000..a205109a9 --- /dev/null +++ b/RRQMSocket.FileTransfer/Delegate/DelegateCollection.cs @@ -0,0 +1,28 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ + +namespace RRQMSocket.FileTransfer +{ + /// + /// 传输文件操作处理 + /// + /// + /// + public delegate void RRQMFileOperationEventHandler(object sender, FileOperationEventArgs e); + + /// + /// 传输文件消息 + /// + /// + /// + public delegate void RRQMTransferFileMessageEventHandler(object sender, TransferFileMessageArgs e); +} \ No newline at end of file diff --git a/RRQMSocket.FileTransfer/Enum/FileHashType.cs b/RRQMSocket.FileTransfer/Enum/FileHashType.cs new file mode 100644 index 000000000..63e78a7ed --- /dev/null +++ b/RRQMSocket.FileTransfer/Enum/FileHashType.cs @@ -0,0 +1,39 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +namespace RRQMSocket.FileTransfer +{ + /// + /// 文件Hash检验类型 + /// + public enum FileHashType : byte + { + /// + /// MD5 + /// + MD5, + + /// + /// SHA1 + /// + SHA1, + + /// + /// SHA256 + /// + SHA256, + + /// + /// SHA512 + /// + SHA512 + } +} \ No newline at end of file diff --git a/RRQMSocket.FileTransfer/Enum/RequestStatus.cs b/RRQMSocket.FileTransfer/Enum/RequestStatus.cs new file mode 100644 index 000000000..9853e83d1 --- /dev/null +++ b/RRQMSocket.FileTransfer/Enum/RequestStatus.cs @@ -0,0 +1,34 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +namespace RRQMSocket.FileTransfer +{ + /// + /// 请求状态 + /// + public enum RequestStatus : byte + { + /// + /// 未开始 + /// + Hovering, + + /// + /// 正在进行 + /// + InProgress, + + /// + /// 完成 + /// + Finished + } +} \ No newline at end of file diff --git a/RRQMSocket.FileTransfer/Enum/StreamOperationType.cs b/RRQMSocket.FileTransfer/Enum/StreamOperationType.cs new file mode 100644 index 000000000..228181223 --- /dev/null +++ b/RRQMSocket.FileTransfer/Enum/StreamOperationType.cs @@ -0,0 +1,29 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +namespace RRQMSocket.FileTransfer +{ + /// + /// 流操作类型 + /// + public enum StreamOperationType : byte + { + /// + /// 读 + /// + Read, + + /// + /// 写 + /// + Write + } +} \ No newline at end of file diff --git a/RRQMSocket.FileTransfer/Enum/TransferFlags.cs b/RRQMSocket.FileTransfer/Enum/TransferFlags.cs new file mode 100644 index 000000000..4a6e1cd52 --- /dev/null +++ b/RRQMSocket.FileTransfer/Enum/TransferFlags.cs @@ -0,0 +1,37 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +using System; + +namespace RRQMSocket.FileTransfer +{ + /// + /// 传输标识 + /// + [Flags] + public enum TransferFlags + { + /// + /// 无任何标识 + /// + None = 0, + + /// + /// 断点续传 + /// + BreakpointResume = 1, + + /// + /// 快速传输 + /// + QuickTransfer = 2 + } +} \ No newline at end of file diff --git a/RRQMSocket.FileTransfer/Enum/TransferStatus.cs b/RRQMSocket.FileTransfer/Enum/TransferStatus.cs new file mode 100644 index 000000000..2f9d84fa4 --- /dev/null +++ b/RRQMSocket.FileTransfer/Enum/TransferStatus.cs @@ -0,0 +1,44 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +namespace RRQMSocket.FileTransfer +{ + /// + /// 传输类型 + /// + public enum TransferStatus + { + /// + /// 无下载 + /// + None, + + /// + /// 上传 + /// + Upload, + + /// + /// 下载 + /// + Download, + + /// + /// 暂停下载状态 + /// + PauseDownload, + + /// + /// 暂停上传状态 + /// + PauseUpload + } +} \ No newline at end of file diff --git a/RRQMSocket.FileTransfer/Enum/TransferType.cs b/RRQMSocket.FileTransfer/Enum/TransferType.cs new file mode 100644 index 000000000..d8a1c0159 --- /dev/null +++ b/RRQMSocket.FileTransfer/Enum/TransferType.cs @@ -0,0 +1,30 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ + +namespace RRQMSocket.FileTransfer +{ + /// + /// 传输类型 + /// + public enum TransferType + { + /// + /// 上传 + /// + Upload, + + /// + /// 下载 + /// + Download, + } +} \ No newline at end of file diff --git a/RRQMSocket.FileTransfer/EventArgs/FileEventArgs.cs b/RRQMSocket.FileTransfer/EventArgs/FileEventArgs.cs new file mode 100644 index 000000000..04083ea11 --- /dev/null +++ b/RRQMSocket.FileTransfer/EventArgs/FileEventArgs.cs @@ -0,0 +1,26 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +using RRQMCore.Event; + +namespace RRQMSocket.FileTransfer +{ + /// + /// 文件事件 + /// + public class FileEventArgs : RRQMEventArgs + { + /// + /// 文件信息 + /// + public UrlFileInfo UrlFileInfo { get; internal set; } + } +} \ No newline at end of file diff --git a/RRQMSocket.FileTransfer/EventArgs/FileOperationEventArgs.cs b/RRQMSocket.FileTransfer/EventArgs/FileOperationEventArgs.cs new file mode 100644 index 000000000..aba8cbd17 --- /dev/null +++ b/RRQMSocket.FileTransfer/EventArgs/FileOperationEventArgs.cs @@ -0,0 +1,24 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +namespace RRQMSocket.FileTransfer +{ + /// + /// 操作文件事件类 + /// + public class FileOperationEventArgs : TransferFileMessageArgs + { + /// + /// 是否允许操作 + /// + public bool IsPermitOperation { get; set; } + } +} \ No newline at end of file diff --git a/RRQMSocket.FileTransfer/EventArgs/TransferFileMessageArgs.cs b/RRQMSocket.FileTransfer/EventArgs/TransferFileMessageArgs.cs new file mode 100644 index 000000000..8e964b77d --- /dev/null +++ b/RRQMSocket.FileTransfer/EventArgs/TransferFileMessageArgs.cs @@ -0,0 +1,29 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +namespace RRQMSocket.FileTransfer +{ + /// + /// 文件传输消息 + /// + public class TransferFileMessageArgs : FileEventArgs + { + /// + /// 信息 + /// + public string Message { get; internal set; } + + /// + /// 传输类型 + /// + public TransferType TransferType { get; internal set; } + } +} \ No newline at end of file diff --git a/RRQMSocket.FileTransfer/Exceptions/RRQMTransferErrorException.cs b/RRQMSocket.FileTransfer/Exceptions/RRQMTransferErrorException.cs new file mode 100644 index 000000000..688f354fd --- /dev/null +++ b/RRQMSocket.FileTransfer/Exceptions/RRQMTransferErrorException.cs @@ -0,0 +1,48 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +using RRQMCore.Exceptions; + +namespace RRQMSocket.FileTransfer +{ + /// + /// 传输错误 + /// + + public class RRQMTransferErrorException : RRQMException + { + /// + /// + /// + public RRQMTransferErrorException() : base() { } + + /// + /// + /// + /// + public RRQMTransferErrorException(string message) : base(message) { } + + /// + /// + /// + /// + /// + public RRQMTransferErrorException(string message, System.Exception inner) : base(message, inner) { } + + /// + /// + /// + /// + /// + protected RRQMTransferErrorException(System.Runtime.Serialization.SerializationInfo info, + System.Runtime.Serialization.StreamingContext context) : base(info, context) { } + } +} \ No newline at end of file diff --git a/RRQMSocket.FileTransfer/Exceptions/RRQMTransferingException.cs b/RRQMSocket.FileTransfer/Exceptions/RRQMTransferingException.cs new file mode 100644 index 000000000..4483a684e --- /dev/null +++ b/RRQMSocket.FileTransfer/Exceptions/RRQMTransferingException.cs @@ -0,0 +1,52 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +using RRQMCore.Exceptions; + +namespace RRQMSocket.FileTransfer +{ + /* + 若汝棋茗 + */ + + /// + /// 没有传输任务异常 + /// + + public class RRQMTransferingException : RRQMException + { + /// + /// + /// + public RRQMTransferingException() : base() { } + + /// + /// + /// + /// + public RRQMTransferingException(string message) : base(message) { } + + /// + /// + /// + /// + /// + public RRQMTransferingException(string message, System.Exception inner) : base(message, inner) { } + + /// + /// + /// + /// + /// + protected RRQMTransferingException(System.Runtime.Serialization.SerializationInfo info, + System.Runtime.Serialization.StreamingContext context) : base(info, context) { } + } +} \ No newline at end of file diff --git a/RRQMSocket.FileTransfer/Interface/IFileClient.cs b/RRQMSocket.FileTransfer/Interface/IFileClient.cs new file mode 100644 index 000000000..f39f32ce1 --- /dev/null +++ b/RRQMSocket.FileTransfer/Interface/IFileClient.cs @@ -0,0 +1,40 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ + +namespace RRQMSocket.FileTransfer +{ + /// + /// 文件终端接口 + /// + public interface IFileClient + { + /// + /// 获取当前传输文件信息 + /// + UrlFileInfo TransferFileInfo { get; } + + /// + /// 获取当前传输进度 + /// + float TransferProgress { get; } + + /// + /// 获取当前传输速度 + /// + long TransferSpeed { get; } + + /// + /// 获取当前传输状态 + /// + TransferStatus TransferStatus { get; } + } +} \ No newline at end of file diff --git a/RRQMSocket.FileTransfer/Interface/IFileService.cs b/RRQMSocket.FileTransfer/Interface/IFileService.cs new file mode 100644 index 000000000..e09bb3db2 --- /dev/null +++ b/RRQMSocket.FileTransfer/Interface/IFileService.cs @@ -0,0 +1,30 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ + +namespace RRQMSocket.FileTransfer +{ + /// + /// 服务器接口 + /// + public interface IFileService + { + /// + /// 最大下载速度 + /// + long MaxDownloadSpeed { get; set; } + + /// + /// 最大上传速度 + /// + long MaxUploadSpeed { get; set; } + } +} \ No newline at end of file diff --git a/RRQMSocket.FileTransfer/LICENSE b/RRQMSocket.FileTransfer/LICENSE new file mode 100644 index 000000000..5a9bb6217 --- /dev/null +++ b/RRQMSocket.FileTransfer/LICENSE @@ -0,0 +1,201 @@ +Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license procotol you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/RRQMSocket.FileTransfer/RRQM.ico b/RRQMSocket.FileTransfer/RRQM.ico new file mode 100644 index 000000000..6465fb16e Binary files /dev/null and b/RRQMSocket.FileTransfer/RRQM.ico differ diff --git a/RRQMSocket.FileTransfer/RRQM.png b/RRQMSocket.FileTransfer/RRQM.png new file mode 100644 index 000000000..1fc567ad9 Binary files /dev/null and b/RRQMSocket.FileTransfer/RRQM.png differ diff --git a/RRQMSocket.FileTransfer/RRQMSocket.FileTransfer.csproj b/RRQMSocket.FileTransfer/RRQMSocket.FileTransfer.csproj new file mode 100644 index 000000000..9997bd263 --- /dev/null +++ b/RRQMSocket.FileTransfer/RRQMSocket.FileTransfer.csproj @@ -0,0 +1,77 @@ + + + net45;netcoreapp3.1;netstandard2.0 + RRQM.ico + true + RRQM.pfx + 5.5.0 + 若汝棋茗 + Copyright © 2021 若汝棋茗 + 介绍:RRQMSocket.FileTransfer是一个轻量级文件传输框架,您可以用它传输任意大小的文件,它可以完美支持断点续传、快速上传、传输限速等。在实时测试中,它的传输速率可达1000Mb/s。 + +更新说明: +优化:传输逻辑。 +优化:临时文件存储逻辑。 +优化:断点续传逻辑。 +优化:文件验证支持MD5、SHA1、SHA256、SHA512。 +修改:UrlFileInfo属性特性,可以一键式设置文件上传,下载路径。 +修改:扩大客户端权限,传输的封包可以由配置PacketSize设置。 +修改:超时事件单位由“秒”调整为“毫秒”。 + +API:https://gitee.com/RRQM_OS/RRQM/wikis/pages +Demo:https://gitee.com/RRQM_OS/RRQMBox + https://gitee.com/dotnetchina/RRQMSocket + + true + RRQM.png + 若汝棋茗 + true + LICENSE + FileTransfer,TCP,IOCP,BreakpointResume + + + + bin\Debug\netstandard2.0\RRQMSocket.FileTransfer.xml + + + + + bin\Release\netstandard2.0\RRQMSocket.FileTransfer.xml + + + + + bin\Debug\net45\RRQMSocket.FileTransfer.xml + + + + + bin\Release\net45\RRQMSocket.FileTransfer.xml + + + + + bin\Debug\netcoreapp3.1\RRQMSocket.FileTransfer.xml + + + + + bin\Release\netcoreapp3.1\RRQMSocket.FileTransfer.xml + + + + + + True + + + + True + + + + + + + + diff --git a/RRQMSocket.FileTransfer/Socket/FileClient.cs b/RRQMSocket.FileTransfer/Socket/FileClient.cs new file mode 100644 index 000000000..7abc66e67 --- /dev/null +++ b/RRQMSocket.FileTransfer/Socket/FileClient.cs @@ -0,0 +1,1030 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +using RRQMCore; +using RRQMCore.ByteManager; +using RRQMCore.Exceptions; +using RRQMCore.Log; +using RRQMCore.Run; +using RRQMCore.Serialization; +using RRQMSocket.RPC.RRQMRPC; +using System; +using System.IO; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace RRQMSocket.FileTransfer +{ + /// + /// 通讯客户端主类 + /// + public class FileClient : TcpRpcClient, IFileClient, IDisposable + { + private ProgressBlockCollection fileBlocks; + + private TransferCollection fileTransferCollection; + + private int packetSize; + + private long position; + + private float progress; + + private string receiveDirectory = string.Empty; + + private EventWaitHandle receiveWaitHandle; + + private string rrqmPath; + + private long speed; + + private bool stop; + + private long tempLength; + + private int timeout = 10 * 1000; + + private TransferStatus transferStatus; + + private UrlFileInfo transferUrlFileInfo; + + private EventWaitHandle transferWaitHandle; + + private WaitData waitDataSend; + + static FileClient() + { + AddUsedProtocol(110, "同步设置"); + AddUsedProtocol(111, "通用信道返回"); + AddUsedProtocol(112, "请求下载"); + AddUsedProtocol(113, "下载文件分块"); + AddUsedProtocol(114, "退出下载通道"); + AddUsedProtocol(115, "确认下载完成"); + AddUsedProtocol(116, "请求上传"); + AddUsedProtocol(117, "上传分块"); + AddUsedProtocol(118, "停止上传"); + AddUsedProtocol(119, "确认上传完成"); + AddUsedProtocol(120, "智能包调节"); + for (short i = 121; i < 200; i++) + { + AddUsedProtocol(i, "保留协议"); + } + } + + /// + /// 无参数构造函数 + /// + public FileClient() + { + this.waitDataSend = new WaitData(); + this.transferWaitHandle = new AutoResetEvent(false); + this.receiveWaitHandle = new AutoResetEvent(false); + this.fileTransferCollection = new TransferCollection(); + this.transferStatus = TransferStatus.None; + this.FileTransferCollection.OnCollectionChanged += this.FileTransferCollection_OnCollectionChanged; + } + + /// + /// 传输文件之前 + /// + public event RRQMFileOperationEventHandler BeforeFileTransfer; + + /// + /// 当文件传输集合更改时 + /// + public event RRQMMessageEventHandler FileTransferCollectionChanged; + + /// + /// 当文件传输完成时 + /// + public event RRQMTransferFileMessageEventHandler FinishedFileTransfer; + + /// + /// 传输文件错误 + /// + public event RRQMTransferFileMessageEventHandler TransferFileError; + + /// + /// 获取当前传输文件包 + /// + public ProgressBlockCollection FileBlocks { get { return fileBlocks == null ? null : fileBlocks; } } + + /// + /// 文件传输队列集合 + /// + public TransferCollection FileTransferCollection + { + get { return fileTransferCollection; } + } + + /// + /// 默认接收文件的存放目录 + /// + public string ReceiveDirectory + { + get { return receiveDirectory; } + } + + /// + /// 单次请求超时时间 min=5000,max=60*1000 ms + /// + public int Timeout + { + get { return timeout; } + } + + /// + /// 获取当前传输文件信息 + /// + public UrlFileInfo TransferFileInfo { get { return this.transferUrlFileInfo; } } + + /// + /// 获取当前传输进度 + /// + public float TransferProgress + { + get + { + if (fileBlocks == null) + { + return 0; + } + if (fileBlocks.UrlFileInfo != null) + { + this.progress = fileBlocks.UrlFileInfo.FileLength > 0 ? (float)position / fileBlocks.UrlFileInfo.FileLength : 0;//计算下载完成进度 + } + else + { + this.progress = 0; + } + return progress <= 1 ? progress : 1; + } + } + + /// + /// 获取当前传输速度 + /// + public long TransferSpeed + { + get + { + this.speed = tempLength; + tempLength = 0; + return speed; + } + } + + /// + /// 获取当前传输状态 + /// + public TransferStatus TransferStatus + { + get { return transferStatus; } + } + + /// + /// 请求下载文件 + /// + /// + /// IP及端口 + /// 验证令箭 + /// 完成时回调 + /// + /// + public static FileClient RequestFile(UrlFileInfo urlFileInfo, string host, string verifyToken = null, + RRQMTransferFileMessageEventHandler finishedCallBack = null, + RRQMTransferFileMessageEventHandler errorCallBack = null) + { + FileClient fileClient = new FileClient(); + try + { + var config = new FileClientConfig(); + config.SetValue(TcpClientConfig.RemoteIPHostProperty, new IPHost(host)) + .SetValue(TokenClientConfig.VerifyTokenProperty, verifyToken); + + fileClient.Setup(config); + fileClient.Connect(); + + fileClient.FinishedFileTransfer += + (object sender, TransferFileMessageArgs e) => + { + fileClient.Dispose(); + finishedCallBack?.Invoke(sender, e); + }; + fileClient.TransferFileError += + (object sender, TransferFileMessageArgs e) => + { + fileClient.Dispose(); + errorCallBack?.Invoke(sender, e); + }; + fileClient.RequestTransfer(urlFileInfo); + return fileClient; + } + catch (Exception ex) + { + fileClient.Dispose(); + throw new RRQMException(ex.Message); + } + } + + /// + /// 取消指定传输任务 + /// + /// + /// + public bool CancelTransfer(UrlFileInfo fileInfo) + { + return this.FileTransferCollection.Remove(fileInfo); + } + + /// + /// 断开连接 + /// + public override void Disconnect() + { + base.Disconnect(); + this.ResetVariable(); + } + + /// + /// 释放资源 + /// + public override void Dispose() + { + base.Dispose(); + if (this.transferStatus == TransferStatus.Download) + { + this.StopDownload(); + } + else if (this.transferStatus == TransferStatus.Upload) + { + this.StopUpload(); + } + this.waitDataSend.Dispose(); + this.transferStatus = TransferStatus.None; + this.progress = 0; + this.speed = 0; + } + + /// + /// 暂停传输 + /// + public void PauseTransfer() + { + if (this.transferStatus == TransferStatus.Download) + { + this.transferStatus = TransferStatus.PauseDownload; + } + else if (this.transferStatus == TransferStatus.Upload) + { + this.transferStatus = TransferStatus.PauseUpload; + } + } + + /// + /// 请求传输文件 + /// + /// + public void RequestTransfer(UrlFileInfo fileInfo) + { + this.FileTransferCollection.Add(fileInfo); + BeginTransfer(); + } + + /// + /// 恢复传输 + /// + /// 是否有任务成功继续 + public bool ResumeTransfer() + { + if (this.transferStatus == TransferStatus.PauseDownload) + { + this.transferStatus = TransferStatus.Download; + } + else if (this.transferStatus == TransferStatus.PauseUpload) + { + this.transferStatus = TransferStatus.Upload; + } + else + { + return false; + } + + return this.transferWaitHandle.Set(); + } + + /// + /// 终止所有传输 + /// + public void StopAllTransfer() + { + this.stop = true; + this.FileTransferCollection.Clear(); + } + + /// + /// 终止当前传输 + /// + /// + public void StopThisTransfer() + { + this.stop = true; + this.BeginTransfer(); + } + + /// + /// 文件客户端处理其他协议 + /// + /// + /// + protected virtual void FileClientHandleDefaultData(short? procotol, ByteBlock byteBlock) + { + this.OnHandleDefaultData(procotol, byteBlock); + } + + /// + /// 加载配置 + /// + /// + protected override void LoadConfig(TcpClientConfig clientConfig) + { + base.LoadConfig(clientConfig); + this.receiveDirectory = (string)clientConfig.GetValue(FileClientConfig.ReceiveDirectoryProperty); + this.packetSize = (int)clientConfig.GetValue(FileClientConfig.PacketSizeProperty); + this.timeout = (int)clientConfig.GetValue(FileClientConfig.TimeoutProperty); + } + + /// + /// 密封方法 + /// + /// + /// + protected sealed override void RPCHandleDefaultData(short? procotol, ByteBlock byteBlock) + { + switch (procotol) + { + case 111: + { + this.waitDataSend.Set(byteBlock); + this.receiveWaitHandle.WaitOne(); + break; + } + default: + { + FileClientHandleDefaultData(procotol, byteBlock); + break; + } + } + } + + private void BeginTransfer() + { + Task.Run(() => + { + lock (locker) + { + if (this.transferStatus == TransferStatus.None) + { + this.receiveWaitHandle.Set(); + if (this.FileTransferCollection.GetFirst(out UrlFileInfo urlFileInfo)) + { + try + { + if (urlFileInfo.TransferType == TransferType.Download) + { + this.DownloadFile(urlFileInfo, true); + } + else + { + this.UploadFile(urlFileInfo, true); + } + } + catch (Exception ex) + { + this.OnTransferError(urlFileInfo.TransferType, ex.Message, urlFileInfo); + } + } + } + } + }); + } + + private void DownloadFile(UrlFileInfo urlFileInfo, bool triggerEvent) + { + if (!this.Online) + { + throw new RRQMException("未连接服务器"); + } + else if (this.transferStatus != TransferStatus.None) + { + throw new RRQMTransferingException("已有传输任务在进行中"); + } + + ByteBlock byteBlock = this.BytePool.GetByteBlock(this.BufferLength); + + try + { + SerializeConvert.RRQMBinarySerialize(byteBlock, urlFileInfo, true); + ByteBlock returnByteBlock = this.SingleSendWait(112, urlFileInfo.Timeout, byteBlock); + FileWaitResult waitResult = SerializeConvert.RRQMBinaryDeserialize(returnByteBlock.Buffer, 2); + if (waitResult.Status == 2) + { + throw new RRQMTransferErrorException(waitResult.Message); + } + urlFileInfo = waitResult.PBCollectionTemp.UrlFileInfo; + + ProgressBlockCollection blocks = ProgressBlockCollection.CreateProgressBlockCollection(urlFileInfo); + if (triggerEvent) + { + this.OnBeforeFileTransfer(blocks); + } + if (!FileStreamPool.LoadWriteStream(ref blocks, !triggerEvent, out string mes)) + { + this.OnTransferError(urlFileInfo.TransferType, mes, urlFileInfo); + return; + } + + this.transferStatus = TransferStatus.Download; + this.fileBlocks = blocks; + this.transferUrlFileInfo = blocks.UrlFileInfo; + this.rrqmPath = blocks.UrlFileInfo.SaveFullPath + ".rrqm"; + Thread thread_Transfer = new Thread(this.DownloadFileBlock); + thread_Transfer.IsBackground = true; + thread_Transfer.Name = "文件下载线程"; + thread_Transfer.Start(); + } + catch (Exception ex) + { + throw ex; + } + finally + { + this.receiveWaitHandle.Set(); + byteBlock.Dispose(); + } + } + + private void DownloadFileBlock() + { + this.stop = false; + while (true) + { + if (FileStreamPool.GetFreeFileBlock(this.rrqmPath, out FileBlock fileBlock, out string errorMes)) + { + if (fileBlock == null) + { + break; + } + this.position = fileBlock.Position; + long surplusLength = fileBlock.UnitLength; + int reTryCount = 0; + while (surplusLength > 0) + { + if (!this.Online) + { + fileBlock.RequestStatus = RequestStatus.Hovering; + this.OnTransferError(TransferType.Download, "客户端已断开连接!!!"); + return; + } + if (disposable || stop) + { + fileBlock.RequestStatus = RequestStatus.Hovering; + StopDownload(); + return; + } + if (this.transferStatus == TransferStatus.PauseDownload) + { + transferWaitHandle.WaitOne(); + } + + ByteBlock byteBlock = this.BytePool.GetByteBlock(this.BufferLength); + byteBlock.Write(this.position); + int requestLength = surplusLength > this.packetSize ? this.packetSize : (int)surplusLength; + byteBlock.Write(requestLength); + + try + { + ByteBlock returnByteBlock = this.SingleSendWait(113, this.timeout, byteBlock); + if (returnByteBlock.Buffer[2] == 1) + { + if (this.transferStatus != TransferStatus.Download && this.transferStatus != TransferStatus.PauseDownload) + { + fileBlock.RequestStatus = RequestStatus.Hovering; + return; + } + if (FileStreamPool.WriteFile(this.rrqmPath, out string mes, out RRQMStream _, this.position, returnByteBlock.Buffer, 3, returnByteBlock.Len - 3)) + { + tempLength += requestLength; + this.position += requestLength; + surplusLength -= requestLength; + reTryCount = 0; + } + else + { + throw new RRQMException($"文件写入错误,信息:{mes}"); + } + } + else if (returnByteBlock.Buffer[2] == 2) + { + string mes = Encoding.UTF8.GetString(returnByteBlock.Buffer, 3, returnByteBlock.Len - 3); + throw new RRQMException($"文件请求错误,信息:{mes}"); + } + } + catch (Exception ex) + { + reTryCount++; + Logger.Debug(LogType.Message, this, $"下载文件错误,信息:{ex.Message},即将进行第{reTryCount}次重试"); + + if (reTryCount > 10) + { + fileBlock.RequestStatus = RequestStatus.Hovering; + this.OnTransferError(TransferType.Download, "重试次数达到最大,详细信息请查看日志"); + return; + } + } + finally + { + this.receiveWaitHandle.Set(); + byteBlock.Dispose(); + } + } + fileBlock.RequestStatus = RequestStatus.Finished; + if (this.transferUrlFileInfo.Flags.HasFlag(TransferFlags.BreakpointResume)) + { + try + { + FileStreamPool.SaveProgressBlockCollection(this.rrqmPath); + } + catch (Exception ex) + { + this.Logger.Debug(LogType.Error, this, "保存进度文件错误", ex); + } + } + } + else + { + this.Logger.Debug(LogType.Error, this, $"获取文件块错误,信息:{errorMes}"); + } + } + if (FileStreamPool.CheckAllFileBlockFinished(this.rrqmPath)) + { + this.DownloadFinished(); + } + } + + private void DownloadFinished() + { + for (int i = 0; i < 5; i++) + { + try + { + ByteBlock resultByteBlock = this.SingleSendWait(115, this.timeout); + + if (resultByteBlock.Buffer[2] == 1) + { + OnDownloadFileFinished(); + return; + } + else + { + this.Logger.Debug(LogType.Warning, this, $"确认下载完成返回状态错误,即将进行第{i + 1}次重试"); + } + } + catch (Exception ex) + { + this.Logger.Debug(LogType.Warning, this, $"确认下载完成错误,信息:{ex.Message},即将进行第{i + 1}次重试"); + } + finally + { + this.receiveWaitHandle.Set(); + } + } + + this.OnTransferError(TransferType.Download, "确认下载完成状态重试次数已达到最大,具体信息请查看日志输出"); + } + + private void FileTransferCollection_OnCollectionChanged(object sender, MesEventArgs e) + { + this.FileTransferCollectionChanged?.Invoke(this, e); + } + + private void OnBeforeFileTransfer(ProgressBlockCollection blocks) + { + FileOperationEventArgs args = new FileOperationEventArgs(); + args.UrlFileInfo = blocks.UrlFileInfo; + if (blocks.UrlFileInfo.TransferType == TransferType.Download) + { + //下载 + if (string.IsNullOrEmpty(blocks.UrlFileInfo.SaveFullPath)) + { + blocks.UrlFileInfo.SaveFullPath = Path.Combine(this.receiveDirectory, blocks.UrlFileInfo.FileName); + } + } + + args.TransferType = blocks.UrlFileInfo.TransferType; + args.IsPermitOperation = true; + try + { + this.BeforeFileTransfer?.Invoke(this, args); + } + catch (Exception ex) + { + this.Logger.Debug(LogType.Error, this, $"在事件{nameof(BeforeFileTransfer)}中发生异常", ex); + } + if (blocks.UrlFileInfo.TransferType == TransferType.Download) + { + //下载 + blocks.UrlFileInfo.SaveFullPath = Path.GetFullPath(args.UrlFileInfo.SaveFullPath); + } + else + { + blocks.UrlFileInfo.FilePath = Path.GetFullPath(args.UrlFileInfo.FilePath); + } + } + + private void OnDownloadFileFinished() + { + try + { + FileStreamPool.DisposeWriteStream(this.rrqmPath, true); + TransferFileMessageArgs args = new TransferFileMessageArgs(); + args.UrlFileInfo = this.transferUrlFileInfo; + args.TransferType = TransferType.Download; + TransferFileHashDictionary.AddFile(this.transferUrlFileInfo); + + ResetVariable(); + try + { + this.FinishedFileTransfer?.Invoke(this, args); + } + catch (Exception ex) + { + this.Logger.Debug(LogType.Error, this, $"在事件{nameof(FinishedFileTransfer)}中发生异常", ex); + } + this.BeginTransfer(); + } + catch (Exception ex) + { + this.Logger.Debug(LogType.Error, this, $"在完成下载时中发生异常", ex); + } + } + + private void OnTransferError(TransferType transferType, string msg, UrlFileInfo urlFileInfo = null) + { + TransferFileMessageArgs args = new TransferFileMessageArgs(); + args.UrlFileInfo = urlFileInfo == null ? this.TransferFileInfo : urlFileInfo; + args.TransferType = transferType; + args.Message = msg; + switch (transferType) + { + case TransferType.Upload: + { + StopUpload(); + break; + } + case TransferType.Download: + { + StopDownload(); + break; + } + } + + try + { + this.TransferFileError?.Invoke(this, args); + } + catch (Exception ex) + { + this.Logger.Debug(LogType.Error, this, $"在事件{nameof(this.TransferFileError)}中发生异常。", ex); + } + + this.BeginTransfer(); + } + + private void OnUploadFileFinished() + { + TransferFileMessageArgs args = new TransferFileMessageArgs(); + args.UrlFileInfo = this.transferUrlFileInfo; + args.TransferType = TransferType.Upload; + + ResetVariable(); + try + { + this.FinishedFileTransfer?.Invoke(this, args); + } + catch (Exception ex) + { + this.Logger.Debug(LogType.Error, this, $"在事件{nameof(FinishedFileTransfer)}中发生异常", ex); + } + this.BeginTransfer(); + } + + private void ResetVariable() + { + this.fileBlocks = null; + this.transferStatus = TransferStatus.None; + this.transferUrlFileInfo = null; + this.progress = 0; + this.speed = 0; + if (!string.IsNullOrEmpty(this.rrqmPath)) + { + FileStreamPool.DisposeWriteStream(this.rrqmPath, false); + } + this.rrqmPath = null; + } + + private ByteBlock SingleSendWait(short procotol, int timeout, ByteBlock byteBlock = null, bool reserved = false) + { + lock (locker) + { + if (!this.Online) + { + throw new RRQMNotConnectedException("客户端未连接"); + } + if (byteBlock == null) + { + this.InternalSend(procotol, new byte[0], 0, 0); + } + else + { + this.InternalSend(procotol, byteBlock.Buffer, 0, byteBlock.Len, reserved); + } + if (!this.waitDataSend.Wait(timeout)) + { + this.receiveWaitHandle.Set(); + throw new RRQMTimeoutException("请求超时"); + } + return this.waitDataSend.WaitResult; + } + } + + private void StopDownload() + { + if (this.disposable) + { + return; + } + this.stop = true; + FileStreamPool.DisposeWriteStream(this.rrqmPath, false); + if (!this.Online) + { + ResetVariable(); + return; + } + try + { + this.SingleSendWait(114, this.timeout); + ResetVariable(); + } + catch (Exception ex) + { + throw ex; + } + finally + { + this.receiveWaitHandle.Set(); + } + } + + private void StopUpload() + { + try + { + if (this.transferUrlFileInfo == null) + { + return; + } + FileStreamPool.DisposeReadStream(this.transferUrlFileInfo.FilePath); + if (!this.Online) + { + ResetVariable(); + return; + } + ByteBlock byteBlock = this.SingleSendWait(118, this.timeout); + ResetVariable(); + } + catch (Exception ex) + { + throw ex; + } + finally + { + this.receiveWaitHandle.Set(); + } + } + + private void UploadFile(UrlFileInfo urlFileInfo, bool triggerEvent) + { + if (!this.Online) + { + throw new RRQMNotConnectedException("未连接到服务器"); + } + else if (this.transferStatus != TransferStatus.None) + { + throw new RRQMTransferingException("已有传输任务在进行中"); + } + byte[] datas = SerializeConvert.RRQMBinarySerialize(urlFileInfo, true); + ByteBlock byteBlock = this.BytePool.GetByteBlock(datas.Length); + byteBlock.Write(datas); + + try + { + ByteBlock resultByteBlock = this.SingleSendWait(116, this.timeout, byteBlock); + FileWaitResult waitResult = SerializeConvert.RRQMBinaryDeserialize(resultByteBlock.Buffer, 2); + if (waitResult.Status == 0) + { + throw new RRQMTimeoutException("等待结果超时"); + } + else if (waitResult.Status == 2) + { + throw new RRQMTransferErrorException(waitResult.Message); + } + else if (waitResult.Status == 3) + { + this.transferStatus = TransferStatus.Upload; + this.transferUrlFileInfo = urlFileInfo; + this.fileBlocks = ProgressBlockCollection.CreateProgressBlockCollection(urlFileInfo); + if (triggerEvent) + { + this.OnBeforeFileTransfer(this.fileBlocks); + } + Task.Run(() => + { + UploadFinished(); + }); + + } + else + { + if (FileStreamPool.LoadReadStream(ref urlFileInfo, out string mes)) + { + this.transferStatus = TransferStatus.Upload; + ProgressBlockCollection blocks = waitResult.PBCollectionTemp.ToPBCollection(); + blocks.UrlFileInfo = urlFileInfo; + this.transferUrlFileInfo = urlFileInfo; + this.fileBlocks = blocks; + if (triggerEvent) + { + this.OnBeforeFileTransfer(blocks); + } + + Thread thread_Transfer = new Thread(this.UploadFileBlock); + thread_Transfer.IsBackground = true; + thread_Transfer.Name = "文件上传线程"; + thread_Transfer.Start(); + } + else + { + throw new RRQMException(mes); + } + } + } + catch (Exception ex) + { + throw ex; + } + finally + { + byteBlock.Dispose(); + this.receiveWaitHandle.Set(); + } + } + + private void UploadFileBlock() + { + this.stop = false; + + foreach (FileBlock fileBlock in this.fileBlocks) + { + if (fileBlock.RequestStatus == RequestStatus.Hovering) + { + this.position = fileBlock.Position; + long surplusLength = fileBlock.UnitLength; + int reTryCount = 0; + while (surplusLength > 0) + { + if (!this.Online) + { + fileBlock.RequestStatus = RequestStatus.Hovering; + this.OnTransferError(TransferType.Download, "客户端已断开连接!!!"); + return; + } + if (disposable || stop) + { + StopUpload(); + return; + } + if (this.transferStatus == TransferStatus.PauseUpload) + { + transferWaitHandle.WaitOne(); + } + + int submitLength = surplusLength > this.packetSize ? this.packetSize : (int)surplusLength; + ByteBlock byteBlock = this.BytePool.GetByteBlock(this.packetSize + 23); + byteBlock.Position = 6; + if (this.position + submitLength == fileBlock.Position + fileBlock.UnitLength) + { + byteBlock.Write((byte)1);//1 + } + else + { + byteBlock.Write((byte)1); + } + + byteBlock.Write(fileBlock.Index);//4 + byteBlock.Write(this.position);//8 + byteBlock.Write(submitLength);//4 + //17+2+4=23 + try + { + if (this.transferStatus != TransferStatus.Upload && this.transferStatus != TransferStatus.PauseUpload) + { + return; + } + if (FileStreamPool.ReadFile(this.transferUrlFileInfo.FilePath, out string mes, this.position, byteBlock, 23, submitLength)) + { + ByteBlock returnByteBlock = this.SingleSendWait(117, this.timeout, byteBlock, true); + + if (returnByteBlock.Buffer[2] == 1) + { + reTryCount = 0; + this.tempLength += submitLength; + this.position += submitLength; + surplusLength -= submitLength; + } + else if (returnByteBlock.Buffer[2] == 2) + { + throw new RRQMException(Encoding.UTF8.GetString(returnByteBlock.Buffer, 3, returnByteBlock.Len - 3)); + } + else + { + this.OnTransferError(TransferType.Upload, "服务器无此传输信息,已终止本次传输"); + return; + } + } + else + { + throw new RRQMException(mes); + } + } + catch (Exception ex) + { + reTryCount++; + Logger.Debug(LogType.Message, this, $"上传文件错误,正在尝试第{reTryCount}次重试", ex); + if (reTryCount > 10) + { + this.OnTransferError(TransferType.Upload, "重试次数达到最大,详细信息请查看日志"); + return; + } + } + finally + { + byteBlock.Dispose(); + this.receiveWaitHandle.Set(); + } + } + } + } + UploadFinished(); + } + + private void UploadFinished() + { + for (int i = 0; i < 5; i++) + { + try + { + ByteBlock byteBlock = this.SingleSendWait(119, this.timeout); + if (byteBlock.Length == 3 && byteBlock.Buffer[2] == 1) + { + this.OnUploadFileFinished(); + return; + } + else + { + this.Logger.Debug(LogType.Warning, this, $"确认上传完成返回状态错误,即将进行第{i + 1}次重试"); + } + } + catch (Exception ex) + { + this.Logger.Debug(LogType.Warning, this, $"确认下载完成错误,信息:{ex.Message},即将进行第{i + 1}次重试"); + } + finally + { + this.receiveWaitHandle.Set(); + } + } + + this.OnTransferError(TransferType.Upload, "确认上传完成状态重试次数已达到最大,具体信息请查看日志输出"); + } + } +} \ No newline at end of file diff --git a/RRQMSocket.FileTransfer/Socket/FileService.cs b/RRQMSocket.FileTransfer/Socket/FileService.cs new file mode 100644 index 000000000..ef4a3bc71 --- /dev/null +++ b/RRQMSocket.FileTransfer/Socket/FileService.cs @@ -0,0 +1,133 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ + +using RRQMSocket.RPC.RRQMRPC; + +namespace RRQMSocket.FileTransfer +{ + /// + /// 通讯服务端主类 + /// + public class FileService : TcpParser, IFileService + { + #region 属性 + + private long maxDownloadSpeed; + + private long maxUploadSpeed; + + /// + /// 获取下载速度 + /// + public long DownloadSpeed + { + get + { + this.downloadSpeed = Speed.downloadSpeed; + Speed.downloadSpeed = 0; + return this.downloadSpeed; + } + } + + /// + /// 最大下载速度 + /// + public long MaxDownloadSpeed + { + get { return maxDownloadSpeed; } + set { maxUploadSpeed = value; } + } + + /// + /// 最大上传速度 + /// + public long MaxUploadSpeed + { + get { return maxUploadSpeed; } + set { maxUploadSpeed = value; } + } + + /// + /// 获取上传速度 + /// + public long UploadSpeed + { + get + { + this.uploadSpeed = Speed.uploadSpeed; + Speed.uploadSpeed = 0; + return this.uploadSpeed; + } + } + + #endregion 属性 + + #region 字段 + + private long downloadSpeed; + private long uploadSpeed; + + #endregion 字段 + + #region 事件 + + /// + /// 传输文件之前 + /// + public event RRQMFileOperationEventHandler BeforeFileTransfer; + + /// + /// 当文件传输完成时 + /// + public event RRQMTransferFileMessageEventHandler FinishedFileTransfer; + + #endregion 事件 + + /// + /// 载入配置 + /// + /// + protected override void LoadConfig(ServiceConfig serviceConfig) + { + base.LoadConfig(serviceConfig); + this.maxDownloadSpeed = (long)serviceConfig.GetValue(FileServiceConfig.MaxDownloadSpeedProperty); + this.maxUploadSpeed = (long)serviceConfig.GetValue(FileServiceConfig.MaxUploadSpeedProperty); + } + + /// + /// 创建完成FileSocketClient + /// + /// + /// + protected override void OnCreateSocketCliect(FileSocketClient socketClient, CreateOption creatOption) + { + base.OnCreateSocketCliect(socketClient, creatOption); + socketClient.MaxDownloadSpeed = this.MaxDownloadSpeed; + socketClient.MaxUploadSpeed = this.MaxUploadSpeed; + if (creatOption.NewCreate) + { + socketClient.BeforeFileTransfer = this.OnBeforeFileTransfer; + socketClient.FinishedFileTransfer = this.OnFinishedFileTransfer; + } + } + + private void OnBeforeFileTransfer(object sender, FileOperationEventArgs e) + { + this.BeforeFileTransfer?.Invoke(sender, e); + } + + private void OnFinishedFileTransfer(object sender, TransferFileMessageArgs e) + { + this.FinishedFileTransfer?.Invoke(sender, e); + } + } +} \ No newline at end of file diff --git a/RRQMSocket.FileTransfer/Socket/FileSocketClient.cs b/RRQMSocket.FileTransfer/Socket/FileSocketClient.cs new file mode 100644 index 000000000..5e27845ac --- /dev/null +++ b/RRQMSocket.FileTransfer/Socket/FileSocketClient.cs @@ -0,0 +1,692 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +using RRQMCore.ByteManager; +using RRQMCore.Log; +using RRQMCore.Serialization; +using RRQMSocket.RPC.RRQMRPC; +using System; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading; + +namespace RRQMSocket.FileTransfer +{ + /// + /// 已接收的客户端 + /// + public class FileSocketClient : RpcSocketClient, IFileService, IFileClient + { + /// + /// 传输文件之前 + /// + internal RRQMFileOperationEventHandler BeforeFileTransfer; + + /// + /// 当文件传输完成时 + /// + internal RRQMTransferFileMessageEventHandler FinishedFileTransfer; + + private long dataTransferLength; + + private long maxDownloadSpeed = 1024 * 1024; + + private long maxUploadSpeed = 1024 * 1024; + + private long position; + + private float progress; + + private string rrqmPath; + + private long speed; + + private Stopwatch stopwatch = new Stopwatch(); + + private long tempLength; + + private long timeTick; + + private TransferStatus transferStatus; + + private UrlFileInfo transferUrlFileInfo; + + static FileSocketClient() + { + AddUsedProtocol(110, "同步设置"); + AddUsedProtocol(111, "通用信道返回"); + AddUsedProtocol(112, "请求下载"); + AddUsedProtocol(113, "下载文件分块"); + AddUsedProtocol(114, "退出下载通道"); + AddUsedProtocol(115, "确认下载完成"); + AddUsedProtocol(116, "请求上传"); + AddUsedProtocol(117, "上传分块"); + AddUsedProtocol(118, "停止上传"); + AddUsedProtocol(119, "确认上传完成"); + AddUsedProtocol(120, "智能包调节"); + for (short i = 121; i < 200; i++) + { + AddUsedProtocol(i, "保留协议"); + } + } + + /// + /// 每秒最大下载速度(Byte),不可小于1024 + /// + public long MaxDownloadSpeed + { + get { return maxDownloadSpeed; } + set + { + if (value < 1024) + { + value = 1024; + } + this.maxDownloadSpeed = value; + } + } + + /// + /// 每秒最大上传速度(Byte),不可小于1024 + /// + public long MaxUploadSpeed + { + get { return maxUploadSpeed; } + set + { + if (value < 1024) + { + value = 1024; + } + this.maxUploadSpeed = value; + } + } + + /// + /// 获取当前传输文件信息 + /// + public UrlFileInfo TransferFileInfo { get => this.transferUrlFileInfo; } + + /// + /// 获取当前传输进度 + /// + public float TransferProgress + { + get + { + if (transferUrlFileInfo == null) + { + return 0; + } + this.progress = transferUrlFileInfo.FileLength > 0 ? (float)position / transferUrlFileInfo.FileLength : 0;//计算下载完成进度 + return progress <= 1 ? progress : 1; + } + } + + /// + /// 获取当前传输速度 + /// + public long TransferSpeed + { + get + { + this.speed = tempLength; + tempLength = 0; + return speed; + } + } + + /// + /// 获取当前传输状态 + /// + public TransferStatus TransferStatus + { + get { return transferStatus; } + } + + /// + /// 释放资源 + /// + public override void Dispose() + { + base.Dispose(); + this.ResetVariable(); + } + + /// + /// 重置 + /// + public override void Recreate() + { + base.Recreate(); + } + + /// + /// 文件辅助类处理其他协议 + /// + /// + /// + protected virtual void FileSocketClientHandleDefaultData(short? procotol, ByteBlock byteBlock) + { + this.OnHandleDefaultData(procotol, byteBlock); + } + + /// + /// 封装协议 + /// + /// + /// + protected sealed override void RPCHandleDefaultData(short? procotol, ByteBlock byteBlock) + { + byte[] buffer = byteBlock.Buffer; + + switch (procotol) + { + case 110: + { + try + { + //this.P110_SynchronizeTransferSetting(); + } + catch (Exception ex) + { + Logger.Debug(LogType.Error, this, ex.Message, ex); + } + break; + } + case 112: + { + try + { + UrlFileInfo fileInfo = SerializeConvert.RRQMBinaryDeserialize(buffer, 2); + P112_RequestDownload(fileInfo); + } + catch (Exception ex) + { + Logger.Debug(LogType.Error, this, ex.Message, ex); + } + break; + } + case 113: + { + try + { + P113_DownloadBlockData(byteBlock); + } + catch (Exception ex) + { + Logger.Debug(LogType.Error, this, ex.Message, ex); + } + break; + } + case 114: + { + try + { + P114_StopDownload(); + } + catch (Exception ex) + { + Logger.Debug(LogType.Error, this, ex.Message, ex); + } + break; + } + case 115: + { + try + { + P115_DownloadFinished(); + } + catch (Exception ex) + { + Logger.Debug(LogType.Error, this, ex.Message, ex); + } + break; + } + case 116: + { + try + { + UrlFileInfo urlFileInfo = SerializeConvert.RRQMBinaryDeserialize(buffer, 2); + P116_RequestUpload(urlFileInfo); + } + catch (Exception ex) + { + Logger.Debug(LogType.Error, this, ex.Message, ex); + } + break; + } + case 117: + { + try + { + P117_UploadBlockData(buffer); + } + catch (Exception ex) + { + Logger.Debug(LogType.Error, this, ex.Message, ex); + } + break; + } + case 118: + { + try + { + P118_StopUpload(); + } + catch (Exception ex) + { + Logger.Debug(LogType.Error, this, ex.Message, ex); + } + break; + } + case 119: + { + try + { + P119_UploadFinished(); + } + catch (Exception ex) + { + Logger.Debug(LogType.Error, this, ex.Message, ex); + } + + break; + } + default: + { + this.FileSocketClientHandleDefaultData(procotol, byteBlock); + break; + } + } + } + + /// + /// 继承 + /// + protected override void WaitReceive() + { + if (this.GetNowTick() - timeTick > 0) + { + //时间过了一秒 + this.timeTick = GetNowTick(); + this.dataTransferLength = 0; + this.dataTransferLength = 0; + stopwatch.Restart(); + } + else + { + //在这一秒中 + switch (this.transferStatus) + { + case TransferStatus.Upload: + if (this.dataTransferLength > this.maxUploadSpeed) + { + //上传饱和 + stopwatch.Stop(); + int sleepTime = 1000 - (int)stopwatch.ElapsedMilliseconds <= 0 ? 0 : 1000 - (int)stopwatch.ElapsedMilliseconds; + Thread.Sleep(sleepTime); + } + break; + + case TransferStatus.Download: + if (this.dataTransferLength > this.maxDownloadSpeed) + { + //下载饱和 + stopwatch.Stop(); + int sleepTime = 1000 - (int)stopwatch.ElapsedMilliseconds <= 0 ? 0 : 1000 - (int)stopwatch.ElapsedMilliseconds; + Thread.Sleep(sleepTime); + } + break; + } + } + } + + /// + /// 获取当前时间帧 + /// + /// + private long GetNowTick() + { + return DateTime.Now.Ticks / 10000000; + } + + private FileOperationEventArgs OnBeforeFileTransfer(UrlFileInfo urlFileInfo) + { + FileOperationEventArgs args = new FileOperationEventArgs(); + args.UrlFileInfo = urlFileInfo; + args.TransferType = urlFileInfo.TransferType; + args.IsPermitOperation = true; + if (urlFileInfo.TransferType == TransferType.Download) + { + //下载 + urlFileInfo.FilePath = Path.GetFullPath(urlFileInfo.FilePath); + } + + try + { + this.BeforeFileTransfer?.Invoke(this, args);//触发 接收文件事件 + } + catch (Exception ex) + { + this.Logger.Debug(LogType.Error, this, $"在事件{nameof(BeforeFileTransfer)}中发生异常", ex); + } + + if (urlFileInfo.TransferType == TransferType.Upload) + { + urlFileInfo.SaveFullPath = Path.GetFullPath(string.IsNullOrEmpty(urlFileInfo.SaveFullPath) ? urlFileInfo.FileName : urlFileInfo.SaveFullPath); + this.rrqmPath = urlFileInfo.SaveFullPath + ".rrqm"; + } + this.transferUrlFileInfo = urlFileInfo; + return args; + } + + private void OnFinishedFileTransfer(TransferType transferType) + { + if (!string.IsNullOrEmpty(this.transferUrlFileInfo.FileHash)) + { + TransferFileHashDictionary.AddFile(this.transferUrlFileInfo); + } + TransferFileMessageArgs args = new TransferFileMessageArgs(); + args.UrlFileInfo = this.transferUrlFileInfo; + args.TransferType = transferType; + + if (transferType == TransferType.Download) + { + FileStreamPool.DisposeReadStream(this.transferUrlFileInfo.FilePath); + } + else + { + FileStreamPool.DisposeWriteStream(this.rrqmPath, true); + this.rrqmPath = null; + } + this.transferStatus = TransferStatus.None; + this.transferUrlFileInfo = null; + try + { + this.FinishedFileTransfer?.Invoke(this, args); + } + catch (Exception ex) + { + this.Logger.Debug(LogType.Error, this, $"在事件{nameof(FinishedFileTransfer)}中发生异常", ex); + } + this.ResetVariable(); + } + + private void P112_RequestDownload(UrlFileInfo urlFileInfo) + { + FileOperationEventArgs args = OnBeforeFileTransfer(urlFileInfo); + FileWaitResult waitResult = new FileWaitResult(); + if (!args.IsPermitOperation) + { + waitResult.Message = string.IsNullOrEmpty(args.Message) ? "服务器拒绝下载" : args.Message; + waitResult.Status = 2; + this.transferStatus = TransferStatus.None; + } + else if (!File.Exists(urlFileInfo.FilePath)) + { + waitResult.Message = $"路径{urlFileInfo.FilePath}文件不存在"; + waitResult.Status = 2; + this.transferStatus = TransferStatus.None; + } + else + { + UrlFileInfo fileInfo; + + if (!TransferFileHashDictionary.GetFileInfo(urlFileInfo.FilePath, out fileInfo, urlFileInfo.Flags.HasFlag(TransferFlags.BreakpointResume))) + { + fileInfo = TransferFileHashDictionary.AddFile(urlFileInfo.FilePath, urlFileInfo.Flags.HasFlag(TransferFlags.BreakpointResume)); + } + urlFileInfo.CopyFrom(fileInfo); + if (FileStreamPool.LoadReadStream(ref urlFileInfo, out string mes)) + { + this.transferStatus = TransferStatus.Download; + waitResult.Message = null; + waitResult.Status = 1; + this.transferUrlFileInfo = urlFileInfo; + waitResult.PBCollectionTemp = new PBCollectionTemp(); + waitResult.PBCollectionTemp.UrlFileInfo = urlFileInfo; + } + else + { + waitResult.Message = mes; + waitResult.Status = 2; + } + } + + this.SendDefaultObject(waitResult); + } + + private void P113_DownloadBlockData(ByteBlock receivedByteBlock) + { + receivedByteBlock.Pos = 2; + + long position = receivedByteBlock.ReadInt64(); + int requestLength = receivedByteBlock.ReadInt32(); + ByteBlock byteBlock = this.BytePool.GetByteBlock(requestLength + 7); + if (this.transferStatus != TransferStatus.Download) + { + byteBlock.Buffer[6] = 0; + } + + string mes; + if (FileStreamPool.ReadFile(transferUrlFileInfo.FilePath, out mes, position, byteBlock, 7, requestLength)) + { + Speed.downloadSpeed += requestLength; + this.position = position + requestLength; + this.tempLength += requestLength; + this.dataTransferLength += requestLength; + byteBlock.Buffer[6] = 1; + } + else + { + byteBlock.Position = 6; + byteBlock.Write((byte)2); + byteBlock.Write(Encoding.UTF8.GetBytes(string.IsNullOrEmpty(mes) ? "未知错误" : mes)); + } + + try + { + if (this.Online) + { + this.InternalSend(111, byteBlock.Buffer, 0, byteBlock.Len, true); + } + } + finally + { + byteBlock.Dispose(); + } + } + + private void P114_StopDownload() + { + this.transferUrlFileInfo = null; + this.transferStatus = TransferStatus.None; + this.InternalSend(111, new byte[] { 1 }, 0, 1); + } + + private void P115_DownloadFinished() + { + if (this.transferStatus == TransferStatus.None) + { + this.InternalSend(111, new byte[] { 1 }, 0, 1); + return; + } + this.InternalSend(111, new byte[] { 1 }, 0, 1); + OnFinishedFileTransfer(TransferType.Download); + } + + private void P116_RequestUpload(UrlFileInfo urlFileInfo) + { + FileOperationEventArgs args = OnBeforeFileTransfer(urlFileInfo); + + FileWaitResult waitResult = new FileWaitResult(); + if (!args.IsPermitOperation) + { + waitResult.Status = 2; + waitResult.Message = string.IsNullOrEmpty(args.Message) ? "服务器拒绝上传" : args.Message; + } + else + { + this.transferStatus = TransferStatus.Upload; + this.transferUrlFileInfo = urlFileInfo; + if (urlFileInfo.Flags.HasFlag(TransferFlags.QuickTransfer) && TransferFileHashDictionary.GetFileInfoFromHash(urlFileInfo.FileHash, out UrlFileInfo oldUrlFileInfo)) + { + try + { + if (urlFileInfo.FilePath != oldUrlFileInfo.FilePath) + { + File.Copy(oldUrlFileInfo.FilePath, urlFileInfo.FilePath, true); + } + + waitResult.Status = 3; + waitResult.Message = null; + this.SendDefaultObject(waitResult); + return; + } + catch (Exception ex) + { + waitResult.Status = 2; + waitResult.Message = ex.Message; + this.Logger.Debug(LogType.Error, this, "在处理快速上传时发生异常。", ex); + } + } + + try + { + ProgressBlockCollection blocks = ProgressBlockCollection.CreateProgressBlockCollection(urlFileInfo); + + if (FileStreamPool.LoadWriteStream(ref blocks, false, out string mes)) + { + waitResult.Status = 1; + waitResult.Message = null; + waitResult.PBCollectionTemp = PBCollectionTemp.GetFromProgressBlockCollection(blocks); + } + else + { + waitResult.Status = 2; + waitResult.Message = mes; + } + } + catch (Exception ex) + { + waitResult.Status = 2; + waitResult.Message = ex.Message; + waitResult.PBCollectionTemp = null; + } + } + + this.SendDefaultObject(waitResult); + } + + private void P117_UploadBlockData(byte[] buffer) + { + ByteBlock byteBlock = this.BytePool.GetByteBlock(this.BufferLength); + try + { + if (this.transferStatus != TransferStatus.Upload) + { + byteBlock.Write((byte)4); + } + byte status = buffer[2]; + int index = BitConverter.ToInt32(buffer, 3); + long position = BitConverter.ToInt64(buffer, 7); + int submitLength = BitConverter.ToInt32(buffer, 15); + + string mes; + if (FileStreamPool.WriteFile(this.rrqmPath, out mes, out RRQMStream stream, position, buffer, 19, submitLength)) + { + this.position = position + submitLength; + this.tempLength += submitLength; + this.dataTransferLength += submitLength; + Speed.uploadSpeed += submitLength; + byteBlock.Write((byte)1); + if (status == 1) + { + FileBlock fileProgress = stream.Blocks.FirstOrDefault(a => a.Index == index); + fileProgress.RequestStatus = RequestStatus.Finished; + stream.SaveProgressBlockCollection(); + } + } + else + { + byteBlock.Write((byte)2); + byteBlock.Write(Encoding.UTF8.GetBytes(mes)); + Logger.Debug(LogType.Error, this, $"上传文件写入错误,信息:{mes}"); + } + this.InternalSend(111, byteBlock); + } + catch (Exception ex) + { + Logger.Debug(LogType.Error, this, "上传文件错误", ex); + } + finally + { + byteBlock.Dispose(); + } + } + + private void P118_StopUpload() + { + try + { + FileStreamPool.SaveProgressBlockCollection(this.rrqmPath); + FileStreamPool.DisposeWriteStream(this.rrqmPath, false); + this.ResetVariable(); + this.InternalSend(111, new byte[] { 1 }, 0, 1); + } + catch (Exception ex) + { + this.Logger.Debug(LogType.Error, this, "客户端请求停止上传时发生异常", ex); + } + } + + private void P119_UploadFinished() + { + if (this.transferStatus == TransferStatus.None) + { + this.InternalSend(111, new byte[] { 1 }, 0, 1); + return; + } + this.InternalSend(111, new byte[] { 1 }, 0, 1); + OnFinishedFileTransfer(TransferType.Upload); + } + + private void ResetVariable() + { + this.transferStatus = TransferStatus.None; + this.transferUrlFileInfo = null; + this.progress = 0; + this.speed = 0; + if (!string.IsNullOrEmpty(this.rrqmPath)) + { + FileStreamPool.DisposeWriteStream(this.rrqmPath, false); + this.rrqmPath = null; + } + } + + private void SendDefaultObject(object obj) + { + ByteBlock byteBlock = this.BytePool.GetByteBlock(this.BufferLength); + byteBlock.Write(SerializeConvert.RRQMBinarySerialize(obj, true)); + try + { + this.InternalSend(111, byteBlock); + } + finally + { + byteBlock.Dispose(); + } + } + } +} \ No newline at end of file diff --git a/RRQMSocket.Http/Common/HttpBase.cs b/RRQMSocket.Http/Common/HttpBase.cs new file mode 100644 index 000000000..73bfd7f22 --- /dev/null +++ b/RRQMSocket.Http/Common/HttpBase.cs @@ -0,0 +1,258 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +using RRQMCore.ByteManager; +using RRQMCore.Helper; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; + +namespace RRQMSocket.Http +{ + /// + /// Http基础头部 + /// + public abstract class HttpBase + { + /// + /// 构造函数 + /// + public HttpBase() + { + this.headers = new Dictionary(); + } + + /// + /// 服务器版本 + /// + public static readonly string ServerVersion = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString(); + + private string requestLine; + + /// + /// 字符数据 + /// + public string Body { get { return this.content == null ? null : this.encoding.GetString(this.content); } } + + private byte[] content; + + /// + /// 内容器 + /// + public byte[] Content + { + get { return content; } + } + + /// + /// 内容编码 + /// + public string Content_Encoding { get; set; } + + /// + /// 内容长度 + /// + public int Content_Length { get; set; } + + /// + /// 内容类型 + /// + public string Content_Type { get; set; } + + /// + /// 内容语言 + /// + public string ContentLanguage { get; set; } + + private Encoding encoding = Encoding.UTF8; + + /// + /// 编码方式 + /// + public Encoding Encoding + { + get { return encoding; } + set { encoding = value; } + } + + /// + /// 传递标识 + /// + public object Flag { get; set; } + + /// + /// 请求头集合 + /// + public Dictionary Headers { get { return this.headers; } } + + private Dictionary headers; + + /// + /// 协议名称 + /// + public string Protocols { get; set; } + + /// + /// HTTP协议版本 + /// + public string ProtocolVersion { get; set; } = "1.1"; + + /// + /// 请求行 + /// + public string RequestLine + { + get { return requestLine; } + } + + /// + /// 构建数据 + /// + /// + public abstract void Build(ByteBlock byteBlock); + + /// + /// 获取头值 + /// + /// + /// + public string GetHeader(HttpHeaders header) + { + return GetHeaderByKey(header); + } + + /// + /// 读取信息 + /// + public abstract void ReadFromBase(); + + /// + /// 从内存中读取 + /// + /// + /// + /// + public void ReadHeaders(byte[] buffer, int offset, int length) + { + string data = Encoding.UTF8.GetString(buffer, offset, length); + string[] rows = Regex.Split(data, Environment.NewLine); + + //Request URL & Method & Version + this.requestLine = rows[0]; + + //Request Headers + GetRequestHeaders(rows); + + string contentLength = this.GetHeader(HttpHeaders.ContentLength); + int.TryParse(contentLength, out int content_Length); + this.Content_Length = content_Length; + } + + /// + /// 获取头集合的值 + /// + /// + /// + protected string GetHeaderByKey(Enum header) + { + var fieldName = header.GetDescription(); + if (fieldName == null) return null; + var hasKey = Headers.ContainsKey(fieldName); + if (!hasKey) return null; + return Headers[fieldName]; + } + + /// + /// 获取头集合的值 + /// + /// + /// + protected string GetHeaderByKey(string fieldName) + { + if (string.IsNullOrEmpty(fieldName)) return null; + var hasKey = Headers.ContainsKey(fieldName); + if (!hasKey) return null; + return Headers[fieldName]; + } + + /// + /// 设置头值 + /// + protected void SetHeaderByKey(Enum header, string value) + { + var fieldName = header.GetDescription(); + if (fieldName == null) return; + var hasKey = Headers.ContainsKey(fieldName); + if (!hasKey) Headers.Add(fieldName, value); + Headers[fieldName] = value; + } + + /// + /// 设置头值 + /// + /// + /// + protected void SetHeaderByKey(string fieldName, string value) + { + if (string.IsNullOrEmpty(fieldName)) return; + var hasKey = Headers.ContainsKey(fieldName); + if (!hasKey) Headers.Add(fieldName, value); + Headers[fieldName] = value; + } + + private void GetRequestHeaders(IEnumerable rows) + { + this.headers.Clear(); + if (rows == null || rows.Count() <= 0) + { + return; + } + + foreach (var item in rows) + { + string[] kv = item.SplitFirst(':'); + if (kv.Length == 2) + { + if (!this.headers.ContainsKey(kv[0])) + { + this.headers.Add(kv[0], kv[1]); + } + } + } + } + + /// + /// 设置内容 + /// + /// + /// + public void SetContent(byte[] content) + { + this.content = content; + this.Content_Length = content.Length; + } + + /// + /// 设置内容 + /// + /// + /// + /// + public void SetContent(string content, Encoding encoding = null) + { + //初始化内容 + encoding = encoding != null ? encoding : Encoding.UTF8; + SetContent(encoding.GetBytes(content)); + } + } +} \ No newline at end of file diff --git a/RRQMSocket.Http/Common/HttpRequest.cs b/RRQMSocket.Http/Common/HttpRequest.cs new file mode 100644 index 000000000..bba873ad4 --- /dev/null +++ b/RRQMSocket.Http/Common/HttpRequest.cs @@ -0,0 +1,222 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +using RRQMCore.ByteManager; +using RRQMCore.Exceptions; +using RRQMCore.Helper; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; + +namespace RRQMSocket.Http +{ + /// + /// HTTP请求定义 + /// + public class HttpRequest : HttpBase + { + /// + /// 表单参数 + /// + public Dictionary Forms { get; set; } + + /// + /// 获取时候保持连接 + /// + public bool KeepAlive { get; set; } + + /// + /// HTTP请求方式 + /// + public string Method { get; set; } + + /// + /// URL参数 + /// + public Dictionary Params { get; set; } + + /// + /// url参数 + /// + public Dictionary Query { get; set; } + + /// + /// 相对路径(不含参数) + /// + public string RelativeURL { get; set; } + + /// + /// HTTP(S)地址 + /// + public string URL { get; set; } + + /// + /// 构建响应头部 + /// + /// + private void BuildHeader(ByteBlock byteBlock) + { + StringBuilder stringBuilder = new StringBuilder(); + stringBuilder.AppendLine($"{this.Method} / HTTP/{this.ProtocolVersion}"); + + this.SetHeader(HttpHeaders.UserAgent, "RRQMHTTP"); + + if (!string.IsNullOrEmpty(this.Content_Type)) + stringBuilder.AppendLine("Content-Type: " + this.Content_Type); + stringBuilder.AppendLine("Content-Length: " + this.Content_Length); + + foreach (var headerkey in this.Headers.Keys) + { + stringBuilder.Append($"{headerkey}: "); + stringBuilder.AppendLine(this.Headers[headerkey]); + } + + stringBuilder.AppendLine(); + byteBlock.Write(this.Encoding.GetBytes(stringBuilder.ToString())); + } + + private void BuildContent(ByteBlock byteBlock) + { + if (this.Content_Length > 0) + { + if (this.Content_Length != this.Content.Length) + { + throw new RRQMException("内容实际长度与设置长度不相等"); + } + byteBlock.Write(this.Content); + } + } + + /// + /// 构建响应数据 + /// + /// + public override void Build(ByteBlock byteBlock) + { + BuildHeader(byteBlock); + BuildContent(byteBlock); + } + + /// + /// 获取头值 + /// + /// + /// + public string GetHeader(string fieldName) + { + return GetHeaderByKey(fieldName); + } + + /// + /// 从内存中读取 + /// + public override void ReadFromBase() + { + var first = Regex.Split(this.RequestLine, @"(\s+)").Where(e => e.Trim() != string.Empty).ToArray(); + if (first.Length > 0) this.Method = first[0]; + if (first.Length > 1) + { + this.URL = Uri.UnescapeDataString(first[1]); + this.RelativeURL = first[1].Split('?')[0]; + } + if (first.Length > 2) + { + string[] ps = first[2].Split('/'); + if (ps.Length == 2) + { + this.Protocols = ps[0]; + this.ProtocolVersion = ps[1]; + } + } + + string contentLength = this.GetHeader(HttpHeaders.ContentLength); + int.TryParse(contentLength, out int content_Length); + this.Content_Length = content_Length; + + if (this.Method == "GET") + { + var isUrlencoded = this.URL.Contains('?'); + if (isUrlencoded) this.Query = GetRequestParameters(URL.Split('?')[1]); + } + if (this.ProtocolVersion == "1.1") + { + if (this.GetHeader(HttpHeaders.Connection) == "keep-alive") + { + this.KeepAlive = true; + } + else + { + this.KeepAlive = false; + } + } + else + { + this.KeepAlive = false; + } + + if (this.Method == "POST") + { + this.Content_Type = GetHeader(HttpHeaders.ContentType); + if (this.Content_Type == @"application/x-www-form-urlencoded") + { + this.Params = GetRequestParameters(this.Body); + } + } + } + + /// + /// 设置头值 + /// + /// + /// + public void SetHeader(HttpHeaders header, string value) + { + SetHeaderByKey(header, value); + } + + /// + /// 设置头值 + /// + /// + /// + public void SetHeader(string fieldName, string value) + { + SetHeaderByKey(fieldName, value); + } + + private Dictionary GetRequestParameters(string row) + { + if (string.IsNullOrEmpty(row)) + { + return null; + } + string[] kvs = row.Split('&'); + if (kvs == null || kvs.Count() == 0) + { + return null; + } + + Dictionary pairs = new Dictionary(); + foreach (var item in kvs) + { + string[] kv = item.SplitFirst('='); + if (kv.Length == 2) + { + pairs.Add(kv[0], kv[1]); + } + } + + return pairs; + } + } +} \ No newline at end of file diff --git a/RRQMSocket.Http/Common/HttpResponse.cs b/RRQMSocket.Http/Common/HttpResponse.cs new file mode 100644 index 000000000..d6ab4c2a5 --- /dev/null +++ b/RRQMSocket.Http/Common/HttpResponse.cs @@ -0,0 +1,123 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +using RRQMCore.ByteManager; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; + +namespace RRQMSocket.Http +{ + /// + /// Http响应 + /// + public class HttpResponse : HttpBase + { + /// + /// 状态码 + /// + public string StatusCode { get; set; } + + /// + /// 获取头数据 + /// + /// + /// + public string GetHeader(string fieldName) + { + return GetHeaderByKey(fieldName); + } + + /// + /// 设置头数据 + /// + /// + /// + public void SetHeader(HttpHeaders header, string value) + { + SetHeaderByKey(header, value); + } + + /// + /// 设置头数据 + /// + /// + /// + public void SetHeader(string fieldName, string value) + { + SetHeaderByKey(fieldName, value); + } + + /// + /// 构建响应头部 + /// + /// + private void BuildHeader(ByteBlock byteBlock) + { + StringBuilder stringBuilder = new StringBuilder(); + if (!string.IsNullOrEmpty(StatusCode)) + stringBuilder.AppendLine($"HTTP/{this.ProtocolVersion} {StatusCode}"); + if (!string.IsNullOrEmpty(this.Content_Type)) + stringBuilder.AppendLine("Content-Type: " + this.Content_Type); + stringBuilder.AppendLine("Content-Length: " + this.Content_Length); + foreach (var headerkey in this.Headers.Keys) + { + stringBuilder.Append($"{headerkey}: "); + stringBuilder.AppendLine(this.Headers[headerkey]); + } + + stringBuilder.AppendLine(); + byteBlock.Write(Encoding.UTF8.GetBytes(stringBuilder.ToString())); + } + + private void BuildContent(ByteBlock byteBlock) + { + if (this.Content_Length > 0) + { + byteBlock.Write(this.Content); + } + } + + /// + /// 构建响应数据 + /// + /// + public override void Build(ByteBlock byteBlock) + { + BuildHeader(byteBlock); + BuildContent(byteBlock); + } + + /// + /// 读取数据 + /// + public override void ReadFromBase() + { + var first = Regex.Split(this.RequestLine, @"(\s+)").Where(e => e.Trim() != string.Empty).ToArray(); + if (first.Length > 0) + { + string[] ps = first[0].Split('/'); + if (ps.Length == 2) + { + this.Protocols = ps[0]; + this.ProtocolVersion = ps[1]; + } + } + if (first.Length > 1) + { + this.StatusCode = first[1]; + } + string contentLength = this.GetHeader(HttpHeaders.ContentLength); + int.TryParse(contentLength, out int content_Length); + this.Content_Length = content_Length; + } + } +} \ No newline at end of file diff --git a/RRQMSocket.Http/DataAdapter/HttpDataHandlingAdapter.cs b/RRQMSocket.Http/DataAdapter/HttpDataHandlingAdapter.cs new file mode 100644 index 000000000..5e966fdc5 --- /dev/null +++ b/RRQMSocket.Http/DataAdapter/HttpDataHandlingAdapter.cs @@ -0,0 +1,185 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +using RRQMCore.ByteManager; +using RRQMCore.Helper; +using RRQMCore.Log; +using System; +using System.Text; + +namespace RRQMSocket.Http +{ + /// + /// Http数据处理适配器 + /// + public class HttpDataHandlingAdapter : DataHandlingAdapter + { + /// + /// 构造函数 + /// + /// + /// + public HttpDataHandlingAdapter(int maxSize, HttpType httpType) + { + this.MaxSize = maxSize; + this.terminatorCode = Encoding.UTF8.GetBytes("\r\n\r\n"); + this.httpType = httpType; + } + + private HttpType httpType; + private byte[] terminatorCode; + + /// + /// 允许的最大长度 + /// + public int MaxSize { get; private set; } + + private ByteBlock tempByteBlock; + private ByteBlock bodyByteBlock; + + private HttpBase httpBase; + + /// + /// 预处理 + /// + /// + protected override void PreviewReceived(ByteBlock byteBlock) + { + byte[] buffer = byteBlock.Buffer; + int r = byteBlock.Len; + if (this.tempByteBlock != null) + { + this.tempByteBlock.Write(buffer, 0, r); + buffer = this.tempByteBlock.ToArray(); + r = this.tempByteBlock.Pos; + this.tempByteBlock.Dispose(); + this.tempByteBlock = null; + } + if (this.httpBase == null) + { + this.Split(buffer, 0, r); + } + else + { + int surLen = this.httpBase.Content_Length - this.bodyByteBlock.Len; + if (r == surLen) + { + this.bodyByteBlock.Write(buffer, 0, surLen); + this.PreviewHandle(this.httpBase); + } + else if (r > surLen) + { + this.bodyByteBlock.Write(buffer, 0, surLen); + this.PreviewHandle(this.httpBase); + Split(buffer, surLen, r - surLen); + } + else + { + this.bodyByteBlock.Write(buffer, 0, r); + } + } + } + + private void Split(byte[] buffer, int offset, int length) + { + int index = buffer.IndexOfFirst(offset, length, this.terminatorCode); + if (index > 0) + { + switch (this.httpType) + { + case HttpType.Server: + this.httpBase = new HttpRequest(); + break; + + case HttpType.Client: + this.httpBase = new HttpResponse(); + break; + } + + this.httpBase.ReadHeaders(buffer, 0, index); + + if (this.httpBase.Content_Length > 0) + { + this.bodyByteBlock = this.BytePool.GetByteBlock(this.httpBase.Content_Length); + int surLength = length - (index + 1 + this.httpBase.Content_Length); + if (surLength == 0) + { + this.bodyByteBlock.Write(buffer, index + 1, this.httpBase.Content_Length); + this.PreviewHandle(this.httpBase); + } + else if (surLength >0) + { + this.bodyByteBlock.Write(buffer, index + 1, this.httpBase.Content_Length); + int indexBuffer = index + 1 + this.httpBase.Content_Length; + this.PreviewHandle(this.httpBase); + this.Split(buffer, indexBuffer, length); + } + else + { + this.bodyByteBlock.Write(buffer, index + 1, length); + } + } + else + { + this.PreviewHandle(this.httpBase); + } + } + else if (length > this.MaxSize) + { + if (this.tempByteBlock != null) + { + this.tempByteBlock.Dispose(); + this.tempByteBlock = null; + } + + Logger.Debug(LogType.Error, this, "在已接收数据大于设定值的情况下未找到终止符号,已放弃接收"); + return; + } + else if (this.tempByteBlock == null) + { + this.tempByteBlock = this.BytePool.GetByteBlock(length * 2); + this.tempByteBlock.Write(buffer,offset, length-offset); + } + } + + private void PreviewHandle(HttpBase httpBase) + { + this.httpBase = null; + try + { + if (this.bodyByteBlock != null) + { + httpBase.SetContent(this.bodyByteBlock.ToArray()); + this.bodyByteBlock.Dispose(); + this.bodyByteBlock = null; + } + httpBase.ReadFromBase(); + this.GoReceived(null, httpBase); + } + catch (Exception ex) + { + this.Logger.Debug(LogType.Error, this, "处理数据错误", ex); + } + } + + /// + /// 预处理 + /// + /// + /// + /// + /// + protected override void PreviewSend(byte[] buffer, int offset, int length, bool isAsync) + { + this.GoSend(buffer, offset, length, isAsync); + } + } +} \ No newline at end of file diff --git a/RRQMSocket.Http/Enum/HttpHeaders.cs b/RRQMSocket.Http/Enum/HttpHeaders.cs new file mode 100644 index 000000000..59086448c --- /dev/null +++ b/RRQMSocket.Http/Enum/HttpHeaders.cs @@ -0,0 +1,327 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +using System.ComponentModel; + +namespace RRQMSocket.Http +{ + /// + /// 请求头枚举 + /// + public enum HttpHeaders : byte + { + /// + /// Cache-Control 标头,指定请求/响应链上所有缓存控制机制必须服从的指令。 + /// + [Description("Cache-Control")] + CacheControl = 0, + + /// + /// Connection 标头,指定特定连接需要的选项。 + /// + [Description("Connection")] + Connection = 1, + + /// + /// Date 标头,指定开始创建请求的日期和时间。 + /// + [Description("Date")] + Date = 2, + + /// + /// Keep-Alive 标头,指定用以维护持久性连接的参数。 + /// + [Description("Keep-Alive")] + KeepAlive = 3, + + /// + /// Pragma 标头,指定可应用于请求/响应链上的任何代理的特定于实现的指令。 + /// + [Description("Pragma")] + Pragma = 4, + + /// + /// Trailer 标头,指定标头字段显示在以 chunked 传输编码方式编码的消息的尾部。 + /// + [Description("Trailer")] + Trailer = 5, + + /// + /// Transfer-Encoding 标头,指定对消息正文应用的转换的类型(如果有)。 + /// + [Description("Transfer-Encoding")] + TransferEncoding = 6, + + /// + /// Upgrade 标头,指定客户端支持的附加通信协议。 + /// + [Description("Upgrade")] + Upgrade = 7, + + /// + /// Via 标头,指定网关和代理程序要使用的中间协议。 + /// + [Description("Via")] + Via = 8, + + /// + /// Warning 标头,指定关于可能未在消息中反映的消息的状态或转换的附加信息。 + /// + [Description("Warning")] + Warning = 9, + + /// + /// Allow 标头,指定支持的 HTTP 方法集。 + /// + [Description("Allow")] + Allow = 10, + + /// + /// Content-Length 标头,指定伴随正文数据的长度(以字节为单位)。 + /// + [Description("Content-Length")] + ContentLength = 11, + + /// + /// Content-Type 标头,指定伴随正文数据的 MIME 类型。 + /// + [Description("Content-Type")] + ContentType = 12, + + /// + /// Content-Encoding 标头,指定已应用于伴随正文数据的编码。 + /// + [Description("Content-Encoding")] + ContentEncoding = 13, + + /// + /// Content-Langauge 标头,指定伴随正文数据的自然语言。 + /// + [Description("Content-Langauge")] + ContentLanguage = 14, + + /// + /// Content-Location 标头,指定可从其中获得伴随正文的 URI。 + /// + [Description("Content-Location")] + ContentLocation = 15, + + /// + /// Content-MD5 标头,指定伴随正文数据的 MD5 摘要,用于提供端到端消息完整性检查。 + /// + [Description("Content-MD5")] + ContentMd5 = 16, + + /// + /// Content-Range 标头,指定在完整正文中应用伴随部分正文数据的位置。 + /// + [Description("Content-Range")] + ContentRange = 17, + + /// + /// Expires 标头,指定日期和时间,在此之后伴随的正文数据应视为陈旧的。 + /// + [Description("Expires")] + Expires = 18, + + /// + /// Last-Modified 标头,指定上次修改伴随的正文数据的日期和时间。 + /// + [Description("Last-Modified")] + LastModified = 19, + + /// + /// Accept 标头,指定响应可接受的 MIME 类型。 + /// + [Description("Accept")] + Accept = 20, + + /// + /// Accept-Charset 标头,指定响应可接受的字符集。 + /// + [Description("Accept-Charset")] + AcceptCharset = 21, + + /// + /// Accept-Encoding 标头,指定响应可接受的内容编码。 + /// + [Description("Accept-Encoding")] + AcceptEncoding = 22, + + /// + /// Accept-Langauge 标头,指定响应首选的自然语言。 + /// + [Description("Accept-Langauge")] + AcceptLanguage = 23, + + /// + /// Authorization 标头,指定客户端为向服务器验证自身身份而出示的凭据。 + /// + [Description("Authorization")] + Authorization = 24, + + /// + /// Cookie 标头,指定向服务器提供的 Cookie 数据。 + /// + [Description("Cookie")] + Cookie = 25, + + /// + /// Expect 标头,指定客户端要求的特定服务器行为。 + /// + [Description("Expect")] + Expect = 26, + + /// + /// From 标头,指定控制请求用户代理的用户的 Internet 电子邮件地址。 + /// + [Description("From")] + From = 27, + + /// + /// Host 标头,指定所请求资源的主机名和端口号。 + /// + [Description("Host")] + Host = 28, + + /// + /// If-Match 标头,指定仅当客户端的指示资源的缓存副本是最新的时,才执行请求的操作。 + /// + [Description("If-Match")] + IfMatch = 29, + + /// + /// If-Modified-Since 标头,指定仅当自指示的数据和时间之后修改了请求的资源时,才执行请求的操作。 + /// + [Description("If-Modified-Since")] + IfModifiedSince = 30, + + /// + /// If-None-Match 标头,指定仅当客户端的指示资源的缓存副本都不是最新的时,才执行请求的操作。 + /// + [Description("If-None-Match")] + IfNoneMatch = 31, + + /// + /// If-Range 标头,指定如果客户端的缓存副本是最新的,仅发送指定范围的请求资源。 + /// + [Description("If-Range")] + IfRange = 32, + + /// + /// If-Unmodified-Since 标头,指定仅当自指示的日期和时间之后修改了请求的资源时,才执行请求的操作。 + /// + [Description("If-Unmodified-Since")] + IfUnmodifiedSince = 33, + + /// + /// Max-Forwards 标头,指定一个整数,表示此请求还可转发的次数。 + /// + [Description("Max-Forwards")] + MaxForwards = 34, + + /// + /// Proxy-Authorization 标头,指定客户端为向代理验证自身身份而出示的凭据。 + /// + [Description("Proxy-Authorization")] + ProxyAuthorization = 35, + + /// + /// Referer 标头,指定从中获得请求 URI 的资源的 URI。 + /// + [Description("Referer")] + Referer = 36, + + /// + /// Range 标头,指定代替整个响应返回的客户端请求的响应的子范围。 + /// + [Description("Range")] + Range = 37, + + /// + /// TE 标头,指定响应可接受的传输编码方式。 + /// + [Description("TE")] + Te = 38, + + /// + /// Translate 标头,与 WebDAV 功能一起使用的 HTTP 规范的 Microsoft 扩展。 + /// + [Description("Translate")] + Translate = 39, + + /// + /// User-Agent 标头,指定有关客户端代理的信息。 + /// + [Description("User-Agent")] + UserAgent = 40, + + /// + /// Accept-Ranges 标头,指定服务器接受的范围。 + /// + [Description("Accept-Ranges")] + AcceptRanges = 41, + + /// + /// Age 标头,指定自起始服务器生成响应以来的时间长度(以秒为单位)。 + /// + [Description("Age")] + Age = 42, + + /// + /// Etag 标头,指定请求的变量的当前值。 + /// + [Description("Etag")] + ETag = 43, + + /// + /// Location 标头,指定为获取请求的资源而将客户端重定向到的 URI。 + /// + [Description("Location")] + Location = 44, + + /// + /// Proxy-Authenticate 标头,指定客户端必须对代理验证其自身。 + /// + [Description("Proxy-Authenticate")] + ProxyAuthenticate = 45, + + /// + /// Retry-After 标头,指定某个时间(以秒为单位)或日期和时间,在此时间之后客户端可以重试其请求。 + /// + [Description("Retry-After")] + RetryAfter = 46, + + /// + /// Server 标头,指定关于起始服务器代理的信息。 + /// + [Description("Server")] + Server = 47, + + /// + /// Set-Cookie 标头,指定提供给客户端的 Cookie 数据。 + /// + [Description("Set-Cookie")] + SetCookie = 48, + + /// + /// Vary 标头,指定用于确定缓存的响应是否为新响应的请求标头。 + /// + [Description("Vary")] + Vary = 49, + + /// + /// WWW-Authenticate 标头,指定客户端必须对服务器验证其自身。 + /// + [Description("WWW-Authenticate")] + WwwAuthenticate = 50, + } +} \ No newline at end of file diff --git a/RRQMSocket.Http/Enum/HttpType.cs b/RRQMSocket.Http/Enum/HttpType.cs new file mode 100644 index 000000000..46292013a --- /dev/null +++ b/RRQMSocket.Http/Enum/HttpType.cs @@ -0,0 +1,30 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ + +namespace RRQMSocket.Http +{ + /// + /// 解析类型 + /// + public enum HttpType : byte + { + /// + /// 服务器 + /// + Server, + + /// + /// 客户端 + /// + Client + } +} \ No newline at end of file diff --git a/RRQMSocket.Http/Helper/EnumHelper.cs b/RRQMSocket.Http/Helper/EnumHelper.cs new file mode 100644 index 000000000..21bdd3797 --- /dev/null +++ b/RRQMSocket.Http/Helper/EnumHelper.cs @@ -0,0 +1,72 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +using System; +using System.Collections.Concurrent; +using System.ComponentModel; +using System.Linq; +using System.Reflection; + +namespace RRQMSocket.Http +{ + /// + /// 枚举扩展类 + /// + public static class EnumHelper + { + private static ConcurrentDictionary _cache = new ConcurrentDictionary(); + + /// + /// 获取DescriptionAttribute + /// + /// + /// + public static string GetDescription(this Enum @enum) + { + var result = string.Empty; + + if (@enum == null) return result; + + if (!_cache.TryGetValue(@enum, out result)) + { + var typeInfo = @enum.GetType(); + + var enumValues = typeInfo.GetEnumValues(); + + foreach (var value in enumValues) + { + if (@enum.Equals(value)) + { + MemberInfo memberInfo = typeInfo.GetMember(value.ToString()).First(); + + result = memberInfo.GetCustomAttribute().Description; + } + } + + _cache.TryAdd(@enum, result); + } + + return result; + } + + /// + /// 根据字符串获取枚举 + /// + /// + /// + /// + /// + public static bool GetEnum(string str, out T result) where T : struct + { + return Enum.TryParse(str, out result); + } + } +} \ No newline at end of file diff --git a/RRQMSocket.Http/Helper/RequestHelper.cs b/RRQMSocket.Http/Helper/RequestHelper.cs new file mode 100644 index 000000000..d379dc867 --- /dev/null +++ b/RRQMSocket.Http/Helper/RequestHelper.cs @@ -0,0 +1,58 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +namespace RRQMSocket.Http +{ + /// + /// 请求辅助类 + /// + public static class RequestHelper + { + /// + /// 从Xml格式 + /// + /// + /// + /// + public static HttpRequest FromXML(this HttpRequest httpRequest, string xmlText) + { + httpRequest.SetContent(xmlText); + httpRequest.Content_Type = "text/xml"; + return httpRequest; + } + + /// + /// 从Json + /// + /// + /// + /// + public static HttpRequest FromJson(this HttpRequest httpRequest, string jsonText) + { + httpRequest.SetContent(jsonText); + httpRequest.Content_Type = "text/json"; + return httpRequest; + } + + /// + /// 从文本 + /// + /// + /// + /// + public static HttpRequest FromText(this HttpRequest httpRequest, string text) + { + httpRequest.SetContent(text); + httpRequest.Content_Type = "text/plain"; + return httpRequest; + } + } +} \ No newline at end of file diff --git a/RRQMSocket.Http/Helper/ResponseHelper.cs b/RRQMSocket.Http/Helper/ResponseHelper.cs new file mode 100644 index 000000000..048340bda --- /dev/null +++ b/RRQMSocket.Http/Helper/ResponseHelper.cs @@ -0,0 +1,100 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +using RRQMCore.Helper; +using System; + +namespace RRQMSocket.Http +{ + /// + /// 响应扩展 + /// + public static class ResponseHelper + { + ///// + ///// 从文件 + ///// + ///// + ///// + ///// + //public static HttpResponse FromFile(this HttpResponse response, string fileName) + //{ + // if (!File.Exists(fileName)) + // { + // response.SetContent("

404 -RRQM Not Found

"); + // response.StatusCode = "404"; + // response.Content_Type = "text/html"; + // return response; + // } + + // var content = File.ReadAllBytes(fileName); + // response.SetContent(content); + // return response.FromSuccess(); + //} + + /// + /// 从Xml格式 + /// + /// + /// + /// + /// + public static HttpResponse FromXML(this HttpResponse response, string xmlText, string statusCode = "200") + { + response.SetContent(xmlText); + response.Content_Type = "text/xml"; + return response.FromSuccess(statusCode); + } + + /// + /// 从Json + /// + /// + /// + /// + /// + public static HttpResponse FromJson(this HttpResponse response, string jsonText, string statusCode = "200") + { + response.SetContent(jsonText); + response.Content_Type = "text/json"; + return response.FromSuccess(statusCode); + } + + /// + /// 从文本 + /// + /// + /// + /// + /// + public static HttpResponse FromText(this HttpResponse response, string text, string statusCode = "200") + { + response.SetContent(text); + response.Content_Type = "text/plain"; + + return response.FromSuccess(statusCode); + } + + /// + /// 返回成功 + /// + /// + /// + /// + public static HttpResponse FromSuccess(this HttpResponse response, string statusCode = "200") + { + response.StatusCode = statusCode; + response.SetHeader(HttpHeaders.Server, $"RRQMSocket.Http {HttpResponse.ServerVersion}"); + response.SetHeader(HttpHeaders.Date, DateTime.Now.ToGMTString("r")); + return response; + } + } +} \ No newline at end of file diff --git a/RRQMSocket.Http/LICENSE b/RRQMSocket.Http/LICENSE new file mode 100644 index 000000000..5a9bb6217 --- /dev/null +++ b/RRQMSocket.Http/LICENSE @@ -0,0 +1,201 @@ +Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license procotol you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/RRQMSocket.Http/RRQM.ico b/RRQMSocket.Http/RRQM.ico new file mode 100644 index 000000000..6465fb16e Binary files /dev/null and b/RRQMSocket.Http/RRQM.ico differ diff --git a/RRQMSocket.Http/RRQM.png b/RRQMSocket.Http/RRQM.png new file mode 100644 index 000000000..1fc567ad9 Binary files /dev/null and b/RRQMSocket.Http/RRQM.png differ diff --git a/RRQMSocket.Http/RRQMSocket.Http.csproj b/RRQMSocket.Http/RRQMSocket.Http.csproj new file mode 100644 index 000000000..fed3391c7 --- /dev/null +++ b/RRQMSocket.Http/RRQMSocket.Http.csproj @@ -0,0 +1,70 @@ + + + net45;netcoreapp3.1;netstandard2.0 + RRQM.ico + true + RRQM.pfx + 5.5.0 + 若汝棋茗 + Copyright © 2021 若汝棋茗 + 介绍:这是一个能够简单解析HTTP的扩展库,能够为RRQMSocket扩展解析HTTP的能力。 + +RRQMSocket is an extension library that can easily parse HTTP. The RRQMSocket extension has the ability to parse HTTP. + +更新说明: +优化:优化Http适配器 + +API:https://gitee.com/RRQM_OS/RRQM/wikis/pages + https://gitee.com/dotnetchina/RRQMSocket + + true + RRQM.png + 若汝棋茗 + true + LICENSE + HTTP;IOCP + + + + bin\Debug\netstandard2.0\RRQMSocket.Http.xml + + + + + bin\Release\netstandard2.0\RRQMSocket.Http.xml + + + + + bin\Debug\net45\RRQMSocket.Http.xml + + + + + bin\Release\net45\RRQMSocket.Http.xml + + + + + bin\Debug\netcoreapp3.1\RRQMSocket.Http.xml + + + + + bin\Release\netcoreapp3.1\RRQMSocket.Http.xml + + + + + True + + + + True + + + + + + + diff --git a/RRQMSocket.RPC.JsonRpc/Attribute/JsonRpcAttribute.cs b/RRQMSocket.RPC.JsonRpc/Attribute/JsonRpcAttribute.cs new file mode 100644 index 000000000..f9724037a --- /dev/null +++ b/RRQMSocket.RPC.JsonRpc/Attribute/JsonRpcAttribute.cs @@ -0,0 +1,43 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +using System; + +namespace RRQMSocket.RPC.JsonRpc +{ + /// + /// 适用于XmlRpc的标记 + /// + [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)] + public sealed class JsonRpcAttribute : RPCAttribute + { + /// + /// 构造函数 + /// + public JsonRpcAttribute() + { + } + + /// + /// 构造函数 + /// + /// + public JsonRpcAttribute(string methodKey) + { + this.MethodKey = methodKey; + } + + /// + /// 服务唯一标识 + /// + public string MethodKey { get; private set; } + } +} \ No newline at end of file diff --git a/RRQMSocket.RPC.JsonRpc/Common/ActionMap.cs b/RRQMSocket.RPC.JsonRpc/Common/ActionMap.cs new file mode 100644 index 000000000..a600a4f94 --- /dev/null +++ b/RRQMSocket.RPC.JsonRpc/Common/ActionMap.cs @@ -0,0 +1,70 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +using System.Collections; +using System.Collections.Generic; + +namespace RRQMSocket.RPC.JsonRpc +{ + /// + /// 服务映射图 + /// + public class ActionMap : IEnumerable> + { + internal ActionMap() + { + this.actionMap = new Dictionary(); + } + + private Dictionary actionMap; + + internal void Add(string actionKey, MethodInstance methodInstance) + { + this.actionMap.Add(actionKey, methodInstance); + } + + /// + /// 服务键集合 + /// + public IEnumerable ActionKeys { get { return this.actionMap.Keys; } } + + /// + /// 通过routeUrl获取函数实例 + /// + /// + /// + /// + public bool TryGet(string actionKey, out MethodInstance methodInstance) + { + if (this.actionMap.ContainsKey(actionKey)) + { + methodInstance = this.actionMap[actionKey]; + return true; + } + methodInstance = null; + return false; + } + + /// + /// 返回迭代器 + /// + /// + public IEnumerator> GetEnumerator() + { + return this.actionMap.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return this.actionMap.GetEnumerator(); + } + } +} \ No newline at end of file diff --git a/RRQMSocket.RPC.JsonRpc/Common/JsonRequestContext.cs b/RRQMSocket.RPC.JsonRpc/Common/JsonRequestContext.cs new file mode 100644 index 000000000..0d299e8a1 --- /dev/null +++ b/RRQMSocket.RPC.JsonRpc/Common/JsonRequestContext.cs @@ -0,0 +1,31 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ + +using RRQMCore.XREF.Newtonsoft.Json.Linq; + +namespace RRQMSocket.RPC.JsonRpc +{ + /// + /// JsonRpc调用器 + /// + public class JsonRequestContext + { +#pragma warning disable + public string jsonrpc; + public string method; + public object[] parameters; + public JToken @params; + public string id; + public bool needResponse; +#pragma warning restore + } +} \ No newline at end of file diff --git a/RRQMSocket.RPC.JsonRpc/Common/JsonResponseContext.cs b/RRQMSocket.RPC.JsonRpc/Common/JsonResponseContext.cs new file mode 100644 index 000000000..e2960383e --- /dev/null +++ b/RRQMSocket.RPC.JsonRpc/Common/JsonResponseContext.cs @@ -0,0 +1,40 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ + +namespace RRQMSocket.RPC.JsonRpc +{ +#pragma warning disable CS1591 + + /// + /// JsonRpc响应器 + /// + public class JsonResponseContext + { + public string jsonrpc; + + public object result; + + public error error; + + public string id; + } + + /// + /// 错误 + /// + public class error + { + public int code; + + public string message; + } +} \ No newline at end of file diff --git a/RRQMSocket.RPC.JsonRpc/Common/JsonRpcWaitContext.cs b/RRQMSocket.RPC.JsonRpc/Common/JsonRpcWaitContext.cs new file mode 100644 index 000000000..23b44e143 --- /dev/null +++ b/RRQMSocket.RPC.JsonRpc/Common/JsonRpcWaitContext.cs @@ -0,0 +1,22 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +using RRQMCore.Run; + +namespace RRQMSocket.RPC.JsonRpc +{ + internal class JsonRpcWaitContext : WaitResult + { + internal object Return; + + internal error error; + } +} \ No newline at end of file diff --git a/RRQMSocket.RPC.JsonRpc/Config/JsonRpcClientConfig.cs b/RRQMSocket.RPC.JsonRpc/Config/JsonRpcClientConfig.cs new file mode 100644 index 000000000..da6bf2588 --- /dev/null +++ b/RRQMSocket.RPC.JsonRpc/Config/JsonRpcClientConfig.cs @@ -0,0 +1,54 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +using RRQMCore.Dependency; + +namespace RRQMSocket.RPC.JsonRpc +{ + /// + /// JsonRpcClient配置 + /// + public class JsonRpcClientConfig : TcpClientConfig + { + /// + /// 协议类型 + /// + public JsonRpcProtocolType ProtocolType + { + get { return (JsonRpcProtocolType)GetValue(ProtocolTypeProperty); } + set { SetValue(ProtocolTypeProperty, value); } + } + + /// + /// 协议类型, + /// 所需类型 + /// + public static readonly DependencyProperty ProtocolTypeProperty = + DependencyProperty.Register("ProtocolType", typeof(JsonRpcProtocolType), typeof(JsonRpcClientConfig), JsonRpcProtocolType.Tcp); + + /// + /// 最大数据包长度 + /// + public int MaxPackageSize + { + get { return (int)GetValue(MaxPackageSizeProperty); } + set { SetValue(MaxPackageSizeProperty, value); } + } + + /// + /// 最大数据包长度,所需类型 + /// + public static readonly DependencyProperty MaxPackageSizeProperty = + DependencyProperty.Register("MaxPackageSize", typeof(int), typeof(JsonRpcClientConfig), 1024); + + + } +} \ No newline at end of file diff --git a/RRQMSocket.RPC.JsonRpc/Config/JsonRpcParserConfig.cs b/RRQMSocket.RPC.JsonRpc/Config/JsonRpcParserConfig.cs new file mode 100644 index 000000000..d80f75b4d --- /dev/null +++ b/RRQMSocket.RPC.JsonRpc/Config/JsonRpcParserConfig.cs @@ -0,0 +1,55 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +using RRQMCore.Dependency; + +namespace RRQMSocket.RPC.JsonRpc +{ + /// + /// JsonRpcParser配置 + /// + public class JsonRpcParserConfig : TcpServiceConfig + { + /// + /// 协议类型 + /// + public JsonRpcProtocolType ProtocolType + { + get { return (JsonRpcProtocolType)GetValue(ProtocolTypeProperty); } + set { SetValue(ProtocolTypeProperty, value); } + } + + /// + /// 协议类型, + /// 所需类型 + /// + public static readonly DependencyProperty ProtocolTypeProperty = + DependencyProperty.Register("ProtocolType", typeof(JsonRpcProtocolType), typeof(JsonRpcParserConfig), JsonRpcProtocolType.Tcp); + + + /// + /// 最大数据包长度 + /// + public int MaxPackageSize + { + get { return (int)GetValue(MaxPackageSizeProperty); } + set { SetValue(MaxPackageSizeProperty, value); } + } + + /// + /// 最大数据包长度,所需类型 + /// + public static readonly DependencyProperty MaxPackageSizeProperty = + DependencyProperty.Register("MaxPackageSize", typeof(int), typeof(JsonRpcParserConfig), 1024); + + + } +} \ No newline at end of file diff --git a/RRQMSocket.RPC.JsonRpc/Enum/JsonRpcProtocolType.cs b/RRQMSocket.RPC.JsonRpc/Enum/JsonRpcProtocolType.cs new file mode 100644 index 000000000..ded7252d0 --- /dev/null +++ b/RRQMSocket.RPC.JsonRpc/Enum/JsonRpcProtocolType.cs @@ -0,0 +1,29 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +namespace RRQMSocket.RPC.JsonRpc +{ + /// + /// JsonRpc协议类型 + /// + public enum JsonRpcProtocolType : byte + { + /// + /// 普通TCP协议 + /// + Tcp, + + /// + /// Http协议 + /// + Http + } +} \ No newline at end of file diff --git a/RRQMSocket.RPC.JsonRpc/Interface/IJsonRpcClient.cs b/RRQMSocket.RPC.JsonRpc/Interface/IJsonRpcClient.cs new file mode 100644 index 000000000..e4f3473b1 --- /dev/null +++ b/RRQMSocket.RPC.JsonRpc/Interface/IJsonRpcClient.cs @@ -0,0 +1,22 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +using RRQMSocket.RPC.RRQMRPC; + +namespace RRQMSocket.RPC.JsonRpc +{ + /// + /// Json客户端RPC接口 + /// + public interface IJsonRpcClient : IRpcClient + { + } +} \ No newline at end of file diff --git a/RRQMSocket.RPC.JsonRpc/LICENSE b/RRQMSocket.RPC.JsonRpc/LICENSE new file mode 100644 index 000000000..b09cd7856 --- /dev/null +++ b/RRQMSocket.RPC.JsonRpc/LICENSE @@ -0,0 +1,201 @@ +Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/RRQMSocket.RPC.JsonRpc/RRQM.ico b/RRQMSocket.RPC.JsonRpc/RRQM.ico new file mode 100644 index 000000000..6465fb16e Binary files /dev/null and b/RRQMSocket.RPC.JsonRpc/RRQM.ico differ diff --git a/RRQMSocket.RPC.JsonRpc/RRQM.png b/RRQMSocket.RPC.JsonRpc/RRQM.png new file mode 100644 index 000000000..1fc567ad9 Binary files /dev/null and b/RRQMSocket.RPC.JsonRpc/RRQM.png differ diff --git a/RRQMSocket.RPC.JsonRpc/RRQMSocket.RPC.JsonRpc.csproj b/RRQMSocket.RPC.JsonRpc/RRQMSocket.RPC.JsonRpc.csproj new file mode 100644 index 000000000..4363daec7 --- /dev/null +++ b/RRQMSocket.RPC.JsonRpc/RRQMSocket.RPC.JsonRpc.csproj @@ -0,0 +1,71 @@ + + + net45;netcoreapp3.1;netstandard2.0 + RRQM.ico + true + RRQM.pfx + 5.5.0 + 若汝棋茗 + Copyright © 2021 若汝棋茗 + 介绍:这是一个扩展于RRQMSocket.RPC的JsonRpc组件,可以通过该组件直接创建JsonRpc服务解析器,让Web端、移动端可以跨语言调用RPC函数。功能支持JsonRpc全功能。 + +更新说明: +增加:配置中通过MaxPackageSize属性设定可处理数据的最大值。 +修复:内联调用。 + +Demo:https://gitee.com/RRQM_OS/RRQMBox +API:https://gitee.com/RRQM_OS/RRQM/wikis/pages + https://gitee.com/dotnetchina/RRQMSocket + + true + RRQM.png + 若汝棋茗 + true + LICENSE + JsonRpc;Socket;IOCP + + + + bin\Debug\netstandard2.0\RRQMSocket.JsonRpc.xml + + + + + bin\Release\netstandard2.0\RRQMSocket.JsonRpc.xml + + + + + bin\Debug\net45\RRQMSocket.JsonRpc.xml + + + + + bin\Release\net45\RRQMSocket.JsonRpc.xml + + + + + bin\Debug\netcoreapp3.1\RRQMSocket.JsonRpc.xml + + + + + bin\Release\netcoreapp3.1\RRQMSocket.JsonRpc.xml + + + + + True + + + + True + + + + + + + + diff --git a/RRQMSocket.RPC.JsonRpc/Socket/JsonRpcClient.cs b/RRQMSocket.RPC.JsonRpc/Socket/JsonRpcClient.cs new file mode 100644 index 000000000..f06032b6c --- /dev/null +++ b/RRQMSocket.RPC.JsonRpc/Socket/JsonRpcClient.cs @@ -0,0 +1,382 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +using RRQMCore.ByteManager; +using RRQMCore.Exceptions; +using RRQMCore.Helper; +using RRQMCore.Run; +using RRQMCore.XREF.Newtonsoft.Json; +using RRQMCore.XREF.Newtonsoft.Json.Linq; +using RRQMSocket.Http; +using RRQMSocket.RPC.RRQMRPC; +using System; +using System.Text; + +namespace RRQMSocket.RPC.JsonRpc +{ + /// + /// JsonRpc客户端 + /// + public class JsonRpcClient : TcpClient, IJsonRpcClient + { + private int maxPackageSize; + + private JsonRpcProtocolType protocolType; + + private RRQMWaitHandle waitHandle; + + /// + /// 构造函数 + /// + public JsonRpcClient() + { + waitHandle = new RRQMWaitHandle(); + } + + /// + /// 最大数据包长度 + /// + public int MaxPackageSize + { + get { return maxPackageSize; } + } + + /// + /// 协议类型 + /// + public JsonRpcProtocolType ProtocolType + { + get { return protocolType; } + } + /// + /// RPC调用 + /// + /// 方法名 + /// 调用配置 + /// 参数 + /// + /// + /// + /// + /// + public T Invoke(string method, InvokeOption invokeOption, ref object[] parameters, Type[] types) + { + JsonRpcWaitContext context = new JsonRpcWaitContext(); + WaitData waitData = this.waitHandle.GetWaitData(context); + + ByteBlock byteBlock = this.BytePool.GetByteBlock(this.BufferLength); + if (invokeOption == null) + { + invokeOption = InvokeOption.WaitInvoke; + } + try + { + JObject jobject = new JObject(); + jobject.Add("jsonrpc", JToken.FromObject("2.0")); + jobject.Add("method", JToken.FromObject(method)); + jobject.Add("params", JToken.FromObject(parameters)); + + if (invokeOption.FeedbackType == FeedbackType.WaitInvoke) + { + jobject.Add("id", JToken.FromObject(context.Sign.ToString())); + } + else + { + jobject.Add("id", null); + } + switch (this.protocolType) + { + case JsonRpcProtocolType.Tcp: + { + byteBlock.Write(Encoding.UTF8.GetBytes(jobject.ToString(Formatting.None))); + break; + } + case JsonRpcProtocolType.Http: + { + HttpRequest httpRequest = new HttpRequest(); + httpRequest.Method = "POST"; + httpRequest.FromJson(jobject.ToString(Formatting.None)); + httpRequest.Build(byteBlock); + } + break; + } + switch (invokeOption.FeedbackType) + { + case FeedbackType.OnlySend: + { + this.SendAsync(byteBlock); + } + break; + + case FeedbackType.WaitSend: + case FeedbackType.WaitInvoke: + { + this.Send(byteBlock); + } + break; + + default: + break; + } + } + catch (Exception ex) + { + throw ex; + } + finally + { + byteBlock.Dispose(); + } + + switch (invokeOption.FeedbackType) + { + case FeedbackType.OnlySend: + case FeedbackType.WaitSend: + { + this.waitHandle.Destroy(waitData); + return default; + } + case FeedbackType.WaitInvoke: + { + waitData.Wait(invokeOption.Timeout); + JsonRpcWaitContext resultContext = (JsonRpcWaitContext)waitData.WaitResult; + this.waitHandle.Destroy(waitData); + + if (resultContext.Status == 0) + { + throw new RRQMTimeoutException("等待结果超时"); + } + if (resultContext.error != null) + { + throw new RRQMRPCException(resultContext.error.message); + } + try + { + if (typeof(T).IsPrimitive || typeof(T) == typeof(string)) + { + return (T)resultContext.Return.ToString().ParseToType(typeof(T)); + } + + return JsonConvert.DeserializeObject(resultContext.Return.ToString()); + } + catch (Exception ex) + { + throw ex; + } + } + default: + return default; + } + } + + /// + /// RPC调用 + /// + /// 方法名 + /// 调用配置 + /// 参数 + /// + /// + /// + /// + public void Invoke(string method, InvokeOption invokeOption, ref object[] parameters, Type[] types) + { + JsonRpcWaitContext context = new JsonRpcWaitContext(); + WaitData waitData = this.waitHandle.GetWaitData(context); + + ByteBlock byteBlock = this.BytePool.GetByteBlock(this.BufferLength); + if (invokeOption == null) + { + invokeOption = InvokeOption.WaitInvoke; + } + try + { + JObject jobject = new JObject(); + jobject.Add("jsonrpc", JToken.FromObject("2.0")); + jobject.Add("method", JToken.FromObject(method)); + jobject.Add("params", JToken.FromObject(parameters)); + + if (invokeOption.FeedbackType == FeedbackType.WaitInvoke) + { + jobject.Add("id", JToken.FromObject(context.Sign.ToString())); + } + else + { + jobject.Add("id", null); + } + switch (this.protocolType) + { + case JsonRpcProtocolType.Tcp: + { + byteBlock.Write(Encoding.UTF8.GetBytes(jobject.ToString(Formatting.None))); + break; + } + case JsonRpcProtocolType.Http: + { + HttpRequest httpRequest = new HttpRequest(); + httpRequest.Method = "POST"; + httpRequest.FromJson(jobject.ToString(Formatting.None)); + httpRequest.Build(byteBlock); + } + break; + } + switch (invokeOption.FeedbackType) + { + case FeedbackType.OnlySend: + { + this.SendAsync(byteBlock); + } + break; + + case FeedbackType.WaitSend: + case FeedbackType.WaitInvoke: + { + this.Send(byteBlock); + } + break; + + default: + break; + } + } + catch (Exception ex) + { + throw ex; + } + finally + { + byteBlock.Dispose(); + } + + switch (invokeOption.FeedbackType) + { + case FeedbackType.OnlySend: + case FeedbackType.WaitSend: + { + this.waitHandle.Destroy(waitData); + return; + } + case FeedbackType.WaitInvoke: + { + waitData.Wait(invokeOption.Timeout); + JsonRpcWaitContext resultContext = (JsonRpcWaitContext)waitData.WaitResult; + this.waitHandle.Destroy(waitData); + + if (resultContext.Status == 0) + { + throw new RRQMTimeoutException("等待结果超时"); + } + if (resultContext.error != null) + { + throw new RRQMRPCException(resultContext.error.message); + } + return; + } + default: + return; + } + } + + /// + /// RPC调用 + /// + /// 方法名 + /// 调用配置 + /// 参数 + /// + /// + /// + public void Invoke(string method, InvokeOption invokeOption, params object[] parameters) + { + this.Invoke(method, invokeOption, ref parameters, null); + } + + /// + /// RPC调用 + /// + /// 方法名 + /// 调用配置 + /// 参数 + /// + /// + /// + /// + public T Invoke(string method, InvokeOption invokeOption, params object[] parameters) + { + return this.Invoke(method, invokeOption, ref parameters, null); + } + + /// + /// 处理数据 + /// + /// + /// + protected override void HandleReceivedData(ByteBlock byteBlock, object obj) + { + switch (this.protocolType) + { + case JsonRpcProtocolType.Tcp: + { + string jsonString = Encoding.UTF8.GetString(byteBlock.Buffer, 0, byteBlock.Len); + JsonResponseContext responseContext = (JsonResponseContext)JsonConvert.DeserializeObject(jsonString, typeof(JsonResponseContext)); + if (responseContext != null) + { + JsonRpcWaitContext waitContext = new JsonRpcWaitContext(); + waitContext.Status = 1; + waitContext.Sign = int.Parse(responseContext.id); + waitContext.error = responseContext.error; + waitContext.Return = responseContext.result; + this.waitHandle.SetRun(waitContext); + } + break; + } + + case JsonRpcProtocolType.Http: + { + HttpResponse httpResponse = (HttpResponse)obj; + JsonResponseContext responseContext = (JsonResponseContext)JsonConvert.DeserializeObject(httpResponse.Body, typeof(JsonResponseContext)); + if (responseContext != null) + { + JsonRpcWaitContext waitContext = new JsonRpcWaitContext(); + waitContext.Status = 1; + waitContext.Sign = int.Parse(responseContext.id); + waitContext.error = responseContext.error; + waitContext.Return = responseContext.result; + this.waitHandle.SetRun(waitContext); + } + break; + } + } + } + + /// + /// 载入配置 + /// + /// + protected override void LoadConfig(TcpClientConfig clientConfig) + { + base.LoadConfig(clientConfig); + this.maxPackageSize = (int)clientConfig.GetValue(JsonRpcClientConfig.MaxPackageSizeProperty); + + this.protocolType = (JsonRpcProtocolType)clientConfig.GetValue(JsonRpcClientConfig.ProtocolTypeProperty); + switch (this.protocolType) + { + case JsonRpcProtocolType.Tcp: + base.SetDataHandlingAdapter(new TerminatorDataHandlingAdapter(this.maxPackageSize, "\r\n")); + break; + + case JsonRpcProtocolType.Http: + base.SetDataHandlingAdapter(new HttpDataHandlingAdapter(this.maxPackageSize, HttpType.Client)); + break; + } + } + } +} \ No newline at end of file diff --git a/RRQMSocket.RPC.JsonRpc/Socket/JsonRpcParser.cs b/RRQMSocket.RPC.JsonRpc/Socket/JsonRpcParser.cs new file mode 100644 index 000000000..406d3aae0 --- /dev/null +++ b/RRQMSocket.RPC.JsonRpc/Socket/JsonRpcParser.cs @@ -0,0 +1,424 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +using RRQMCore.ByteManager; +using RRQMCore.Log; +using RRQMCore.XREF.Newtonsoft.Json; +using RRQMCore.XREF.Newtonsoft.Json.Linq; +using RRQMSocket.Http; +using System; +using System.Text; + +namespace RRQMSocket.RPC.JsonRpc +{ + /// + /// JsonRpcParser解析器 + /// + public class JsonRpcParser : TcpService, IRPCParser + { + private ActionMap actionMap; + + private MethodMap methodMap; + + private JsonRpcProtocolType protocolType; + + /// + /// 构造函数 + /// + public JsonRpcParser() + { + this.actionMap = new ActionMap(); + } + + /// + /// 函数键映射图 + /// + public ActionMap ActionMap { get { return this.actionMap; } } + + private int maxPackageSize; + + /// + /// 最大数据包长度 + /// + public int MaxPackageSize + { + get { return maxPackageSize; } + } + + /// + /// 函数映射 + /// + public MethodMap MethodMap + { + get { return methodMap; } + } + + /// + /// 协议类型 + /// + public JsonRpcProtocolType ProtocolType + { + get { return protocolType; } + } + + /// + /// 所属服务器 + /// + public RPCService RPCService { get; private set; } + + /// + /// 执行函数 + /// + public Action RRQMExecuteMethod { get; private set; } + + /// + /// 结束调用 + /// + /// + /// + public void OnEndInvoke(MethodInvoker methodInvoker, MethodInstance methodInstance) + { + ISocketClient socketClient = (ISocketClient)methodInvoker.Caller; + error error = new error(); + + switch (methodInvoker.Status) + { + case InvokeStatus.Success: + { + error = null; + break; + } + case InvokeStatus.UnFound: + { + error.code = -32601; + error.message = "函数未找到"; + break; + } + case InvokeStatus.UnEnable: + { + error.code = -32601; + error.message = "函数已被禁用"; + break; + } + case InvokeStatus.Abort: + { + error.code = -32601; + error.message = "函数已被中断执行"; + break; + } + case InvokeStatus.InvocationException: + { + error.code = -32603; + error.message = "函数内部异常"; + break; + } + case InvokeStatus.Exception: + { + error.code = -32602; + error.message = methodInvoker.StatusMessage; + break; + } + } + JsonRequestContext jsonRequestContext = (JsonRequestContext)methodInvoker.Flag; + if (jsonRequestContext.needResponse) + { + ByteBlock byteBlock = this.BytePool.GetByteBlock(this.BufferLength); + this.BuildResponseByteBlock(byteBlock, methodInvoker, jsonRequestContext.id, methodInvoker.ReturnParameter, error); + if (socketClient.Online) + { + try + { + string s = Encoding.UTF8.GetString(byteBlock.ToArray()); + socketClient.Send(byteBlock); + } + catch (Exception ex) + { + this.Logger.Debug(LogType.Error, this, ex.Message); + } + finally + { + byteBlock.Dispose(); + } + } + } + } + + /// + /// 初始化 + /// + /// + /// + public void OnRegisterServer(IServerProvider provider, MethodInstance[] methodInstances) + { + foreach (var methodInstance in methodInstances) + { + foreach (var att in methodInstance.RPCAttributes) + { + if (att is JsonRpcAttribute attribute) + { + if (methodInstance.IsByRef) + { + throw new RRQMRPCException($"JsonRpc服务中不允许有out及ref关键字,服务:{methodInstance.Method.Name}"); + } + string actionKey = string.IsNullOrEmpty(attribute.MethodKey) ? methodInstance.Method.Name : attribute.MethodKey; + + try + { + this.actionMap.Add(actionKey, methodInstance); + } + catch + { + throw new RRQMRPCException($"函数键为{actionKey}的方法已注册。"); + } + } + } + } + } + + /// + /// 取消注册服务 + /// + /// + /// + public void OnUnregisterServer(IServerProvider provider, MethodInstance[] methodInstances) + { + } + + /// + /// 设置执行委托 + /// + /// + public void SetExecuteMethod(Action executeMethod) + { + this.RRQMExecuteMethod = executeMethod; + } + + /// + /// 设置地图映射 + /// + /// + public void SetMethodMap(MethodMap methodMap) + { + this.methodMap = methodMap; + } + + /// + /// 设置服务 + /// + /// + public void SetRPCService(RPCService service) + { + this.RPCService = service; + } + + /// + /// 构建请求内容 + /// + /// 数据 + /// 调用服务实例 + /// + /// + protected virtual void BuildRequestContext(string jsonString, out MethodInstance methodInstance, out JsonRequestContext context) + { + try + { + context = JsonConvert.DeserializeObject(jsonString); + if (context.id != null) + { + context.needResponse = true; + } + } + catch (Exception ex) + { + context = new JsonRequestContext(); + context.needResponse = true; + throw ex; + } + + if (this.actionMap.TryGet(context.method, out methodInstance)) + { + if (context.@params == null) + { + if (methodInstance.ParameterNames.Length != 0) + { + throw new RRQMRPCException("调用参数计数不匹配"); + } + return; + } + if (context.@params.GetType() != typeof(JArray)) + { + JObject obj = (JObject)context.@params; + context.parameters = new object[methodInstance.ParameterNames.Length]; + //内联 + for (int i = 0; i < methodInstance.ParameterNames.Length; i++) + { + if (obj.TryGetValue(methodInstance.ParameterNames[i], out JToken jToken)) + { + Type type = methodInstance.ParameterTypes[i]; + context.parameters[i] = jToken.ToObject(type); + } + else if (methodInstance.Parameters[i].HasDefaultValue) + { + context.parameters[i] = methodInstance.Parameters[i].DefaultValue; + } + else + { + throw new RRQMRPCException("调用参数计数不匹配"); + } + } + } + else + { + JArray array = (JArray)context.@params; + if (array.Count != methodInstance.ParameterNames.Length) + { + throw new RRQMRPCException("调用参数计数不匹配"); + } + context.parameters = new object[methodInstance.ParameterNames.Length]; + + for (int i = 0; i < array.Count; i++) + { + context.parameters[i] = context.@params[i].ToObject(methodInstance.ParameterTypes[i]); + } + } + } + else + { + methodInstance = null; + } + } + + /// + /// 构建响应数据 + /// + /// + /// + /// + /// + /// + protected virtual void BuildResponseByteBlock(ByteBlock responseByteBlock, MethodInvoker methodInvoker, string id, object result, error error) + { + JObject jobject = new JObject(); + if (error == null) + { + //成功 + jobject.Add("jsonrpc", JToken.FromObject("2.0")); + jobject.Add("result", result == null ? null : JToken.FromObject(result)); + jobject.Add("id", id == null ? null : JToken.FromObject(id)); + } + else + { + jobject.Add("jsonrpc", JToken.FromObject("2.0")); + jobject.Add("error", JToken.FromObject(error)); + jobject.Add("id", id == null ? null : JToken.FromObject(id)); + } + switch (this.protocolType) + { + case JsonRpcProtocolType.Tcp: + { + responseByteBlock.Write(Encoding.UTF8.GetBytes(jobject.ToString(Formatting.None))); + break; + } + case JsonRpcProtocolType.Http: + { + HttpResponse httpResponse = new HttpResponse(); + httpResponse.FromJson(jobject.ToString(Formatting.None)); + httpResponse.Build(responseByteBlock); + break; + } + } + } + + /// + /// 载入配置 + /// + /// + protected override void LoadConfig(ServiceConfig serviceConfig) + { + base.LoadConfig(serviceConfig); + this.protocolType = (JsonRpcProtocolType)serviceConfig.GetValue(JsonRpcParserConfig.ProtocolTypeProperty); + this.maxPackageSize = (int)serviceConfig.GetValue(JsonRpcParserConfig.MaxPackageSizeProperty); + } + + /// + /// 创建SocketCliect + /// + /// + /// + protected override void OnCreateSocketCliect(JsonRpcSocketClient socketClient, CreateOption createOption) + { + if (createOption.NewCreate) + { + socketClient.OnReceived = this.OnReceived; + } + switch (this.protocolType) + { + case JsonRpcProtocolType.Tcp: + socketClient.SetAdapter(new TerminatorDataHandlingAdapter(this.maxPackageSize, "\r\n")); + break; + + case JsonRpcProtocolType.Http: + socketClient.SetAdapter(new HttpDataHandlingAdapter(this.maxPackageSize, HttpType.Server)); + break; + } + } + + private void OnReceived(SimpleSocketClient socketClient, ByteBlock byteBlock, object obj) + { + MethodInvoker methodInvoker = new MethodInvoker(); + methodInvoker.Caller = socketClient; + MethodInstance methodInstance = null; + JsonRequestContext context = null; + try + { + string jsonString = null; + switch (this.protocolType) + { + case JsonRpcProtocolType.Tcp: + { + jsonString = Encoding.UTF8.GetString(byteBlock.Buffer, 0, byteBlock.Len); + break; + } + case JsonRpcProtocolType.Http: + { + HttpRequest httpRequest = (HttpRequest)obj; + jsonString = httpRequest.Body; + methodInvoker.Flag = httpRequest; + break; + } + } + this.BuildRequestContext(jsonString, out methodInstance, out context); + + if (methodInstance == null) + { + methodInvoker.Status = InvokeStatus.UnFound; + } + else if (methodInstance.IsEnable) + { + methodInvoker.Parameters = context.parameters; + } + else + { + methodInvoker.Status = InvokeStatus.UnEnable; + } + } + catch (Exception ex) + { + methodInvoker.Status = InvokeStatus.Exception; + methodInvoker.StatusMessage = ex.Message; + } + + methodInvoker.Flag = context; + + this.RRQMExecuteMethod.Invoke(this, methodInvoker, methodInstance); + } + } +} \ No newline at end of file diff --git a/RRQMSocket.RPC.JsonRpc/Socket/JsonRpcSocketClient.cs b/RRQMSocket.RPC.JsonRpc/Socket/JsonRpcSocketClient.cs new file mode 100644 index 000000000..e293c95b6 --- /dev/null +++ b/RRQMSocket.RPC.JsonRpc/Socket/JsonRpcSocketClient.cs @@ -0,0 +1,29 @@ +using RRQMCore.Exceptions; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace RRQMSocket.RPC.JsonRpc +{ + /// + /// JsonRpc辅助类 + /// + public class JsonRpcSocketClient : SimpleSocketClient + { + /// + /// 禁用适配器赋值 + /// + /// + public sealed override void SetDataHandlingAdapter(DataHandlingAdapter adapter) + { + throw new RRQMException($"{nameof(JsonRpcSocketClient)}不允许设置适配器。"); + } + + internal void SetAdapter(DataHandlingAdapter adapter) + { + base.SetDataHandlingAdapter(adapter); + } + } +} diff --git a/RRQMSocket.RPC.WebApi/Attribute/RouteAttribute.cs b/RRQMSocket.RPC.WebApi/Attribute/RouteAttribute.cs new file mode 100644 index 000000000..d8bd55ebc --- /dev/null +++ b/RRQMSocket.RPC.WebApi/Attribute/RouteAttribute.cs @@ -0,0 +1,43 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +using System; + +namespace RRQMSocket.RPC.WebApi +{ + /// + /// 适用于WebApi的路由标记 + /// + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = false)] + public sealed class RouteAttribute : RPCAttribute + { + /// + /// 构造函数 + /// + public RouteAttribute() + { + } + + /// + /// 构造函数 + /// + /// + public RouteAttribute(string template) + { + this.Template = template; + } + + /// + /// 路由模板 + /// + public string Template { get; private set; } + } +} \ No newline at end of file diff --git a/RRQMSocket.RPC.WebApi/Common/ActionResult.cs b/RRQMSocket.RPC.WebApi/Common/ActionResult.cs new file mode 100644 index 000000000..d34666b80 --- /dev/null +++ b/RRQMSocket.RPC.WebApi/Common/ActionResult.cs @@ -0,0 +1,30 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ + +namespace RRQMSocket.RPC.WebApi +{ + /// + /// 结果状态 + /// + public class ActionResult + { + /// + /// 状态类型 + /// + public InvokeStatus Status { get; set; } + + /// + /// 消息 + /// + public string Message { get; set; } + } +} \ No newline at end of file diff --git a/RRQMSocket.RPC.WebApi/Common/ControllerBase.cs b/RRQMSocket.RPC.WebApi/Common/ControllerBase.cs new file mode 100644 index 000000000..c5242b240 --- /dev/null +++ b/RRQMSocket.RPC.WebApi/Common/ControllerBase.cs @@ -0,0 +1,21 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ + +namespace RRQMSocket.RPC.WebApi +{ + /// + /// ControllerBase + /// + public abstract class ControllerBase : ServerProvider + { + } +} \ No newline at end of file diff --git a/RRQMSocket.RPC.WebApi/Common/RouteMap.cs b/RRQMSocket.RPC.WebApi/Common/RouteMap.cs new file mode 100644 index 000000000..1a51b3d3c --- /dev/null +++ b/RRQMSocket.RPC.WebApi/Common/RouteMap.cs @@ -0,0 +1,70 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +using System.Collections; +using System.Collections.Generic; + +namespace RRQMSocket.RPC.WebApi +{ + /// + /// 路由映射图 + /// + public class RouteMap : IEnumerable> + { + internal RouteMap() + { + this.routeMap = new Dictionary(); + } + + private Dictionary routeMap; + + internal void Add(string routeUrl, MethodInstance methodInstance) + { + this.routeMap.Add(routeUrl, methodInstance); + } + + /// + /// 路由路径集合 + /// + public IEnumerable Urls { get { return this.routeMap.Keys; } } + + /// + /// 通过routeUrl获取函数实例 + /// + /// + /// + /// + public bool TryGet(string routeUrl, out MethodInstance methodInstance) + { + if (this.routeMap.ContainsKey(routeUrl)) + { + methodInstance = this.routeMap[routeUrl]; + return true; + } + methodInstance = null; + return false; + } + + /// + /// 返回迭代器 + /// + /// + public IEnumerator> GetEnumerator() + { + return this.routeMap.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return this.routeMap.GetEnumerator(); + } + } +} \ No newline at end of file diff --git a/RRQMSocket.RPC.WebApi/Config/WebApiParserConfig.cs b/RRQMSocket.RPC.WebApi/Config/WebApiParserConfig.cs new file mode 100644 index 000000000..edb739626 --- /dev/null +++ b/RRQMSocket.RPC.WebApi/Config/WebApiParserConfig.cs @@ -0,0 +1,53 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +using RRQMCore.Dependency; + +namespace RRQMSocket.RPC.WebApi +{ + /// + /// WebApiParser配置 + /// + public class WebApiParserConfig : TcpServiceConfig + { + /// + /// 数据转化器 + /// + public ApiDataConverter ApiDataConverter + { + get { return (ApiDataConverter)GetValue(ApiDataConverterProperty); } + set { SetValue(ApiDataConverterProperty, value); } + } + + /// + /// 数据转化器 + /// 所需类型 + /// + public static readonly DependencyProperty ApiDataConverterProperty = + DependencyProperty.Register("ApiDataConverter", typeof(ApiDataConverter), typeof(WebApiParserConfig), new JsonDataConverter()); + + /// + /// 最大数据包长度 + /// + public int MaxPackageSize + { + get { return (int)GetValue(MaxPackageSizeProperty); } + set { SetValue(MaxPackageSizeProperty, value); } + } + + /// + /// 最大数据包长度,所需类型 + /// + public static readonly DependencyProperty MaxPackageSizeProperty = + DependencyProperty.Register("MaxPackageSize", typeof(int), typeof(WebApiParserConfig), 1024); + + } +} \ No newline at end of file diff --git a/RRQMSocket.RPC.WebApi/Converter/ApiDataConverter.cs b/RRQMSocket.RPC.WebApi/Converter/ApiDataConverter.cs new file mode 100644 index 000000000..b4fcda805 --- /dev/null +++ b/RRQMSocket.RPC.WebApi/Converter/ApiDataConverter.cs @@ -0,0 +1,37 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +using RRQMSocket.Http; + +namespace RRQMSocket.RPC.WebApi +{ + /// + /// Api结果转化器 + /// + public abstract class ApiDataConverter + { + /// + /// 在调用完成时转换结果 + /// + /// + /// + /// + public abstract HttpResponse OnResult(MethodInvoker methodInvoker, MethodInstance methodInstance); + + /// + /// 在调用时 + /// + /// + /// + /// + public abstract void OnPost(HttpRequest httpRequest, ref MethodInvoker methodInvoker, MethodInstance methodInstance); + } +} \ No newline at end of file diff --git a/RRQMSocket.RPC.WebApi/Converter/JsonDataConverter.cs b/RRQMSocket.RPC.WebApi/Converter/JsonDataConverter.cs new file mode 100644 index 000000000..16bbd2bd6 --- /dev/null +++ b/RRQMSocket.RPC.WebApi/Converter/JsonDataConverter.cs @@ -0,0 +1,128 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +using RRQMCore.Helper; +using RRQMCore.XREF.Newtonsoft.Json; +using RRQMSocket.Http; + +namespace RRQMSocket.RPC.WebApi +{ + /// + /// Json数据转换器 + /// + public class JsonDataConverter : ApiDataConverter + { + /// + /// OnPost + /// + /// + /// + /// + public override void OnPost(HttpRequest httpRequest, ref MethodInvoker methodInvoker, MethodInstance methodInstance) + { + switch (httpRequest.Content_Type) + { + case "application/x-www-form-urlencoded": + { + if (httpRequest.Params != null) + { + for (int i = 0; i < methodInstance.Parameters.Length; i++) + { + if (httpRequest.Params.TryGetValue(methodInstance.ParameterNames[i], out string value)) + { + methodInvoker.Parameters[i] = value.ParseToType(methodInstance.ParameterTypes[i]); + } + else + { + methodInvoker.Parameters[i] = methodInstance.ParameterTypes[i].GetDefault(); + } + } + } + break; + } + case "application/json": + { + if (methodInstance.Parameters.Length > 0) + { + for (int i = 0; i < methodInstance.Parameters.Length; i++) + { + if (i == 0) + { + methodInvoker.Parameters[i] = JsonConvert.DeserializeObject(httpRequest.Body, methodInstance.ParameterTypes[0]); + } + else + { + methodInvoker.Parameters[i] = methodInstance.ParameterTypes[i].GetDefault(); + } + } + } + break; + } + } + } + + /// + /// 在调用完成时转换结果 + /// + /// + /// + /// + public override HttpResponse OnResult(MethodInvoker methodInvoker, MethodInstance methodInstance) + { + HttpRequest httpRequest = (HttpRequest)methodInvoker.Flag; + HttpResponse httpResponse = new HttpResponse(); + switch (methodInvoker.Status) + { + case InvokeStatus.Success: + { + if (methodInvoker.ReturnParameter != null) + { + httpResponse.FromJson(JsonConvert.SerializeObject(methodInvoker.ReturnParameter)); + break; + } + else + { + httpResponse.FromText(string.Empty); + } + + break; + } + case InvokeStatus.UnFound: + { + string jsonString = JsonConvert.SerializeObject(new ActionResult() { Status = methodInvoker.Status, Message = methodInvoker.StatusMessage }); + httpResponse.FromJson(jsonString, "404"); + break; + } + case InvokeStatus.UnEnable: + { + string jsonString = JsonConvert.SerializeObject(new ActionResult() { Status = methodInvoker.Status, Message = methodInvoker.StatusMessage }); + httpResponse.FromJson(jsonString, "405"); + break; + } + case InvokeStatus.Abort: + { + string jsonString = JsonConvert.SerializeObject(new ActionResult() { Status = methodInvoker.Status, Message = methodInvoker.StatusMessage }); + httpResponse.FromJson(jsonString, "403"); + break; + } + case InvokeStatus.InvocationException: + case InvokeStatus.Exception: + { + string jsonString = JsonConvert.SerializeObject(new ActionResult() { Status = methodInvoker.Status, Message = methodInvoker.StatusMessage }); + httpResponse.FromJson(jsonString, "422"); + break; + } + } + + return httpResponse; + } + } +} \ No newline at end of file diff --git a/RRQMSocket.RPC.WebApi/Converter/XmlDataConverter.cs b/RRQMSocket.RPC.WebApi/Converter/XmlDataConverter.cs new file mode 100644 index 000000000..de6638c86 --- /dev/null +++ b/RRQMSocket.RPC.WebApi/Converter/XmlDataConverter.cs @@ -0,0 +1,129 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +using RRQMCore.Helper; +using RRQMCore.Serialization; +using RRQMSocket.Http; + +namespace RRQMSocket.RPC.WebApi +{ + /// + /// Xml结果转换器 + /// + public class XmlDataConverter : ApiDataConverter + { + /// + /// OnPost + /// + /// + /// + /// + public override void OnPost(HttpRequest httpRequest, ref MethodInvoker methodInvoker, MethodInstance methodInstance) + { + switch (httpRequest.Content_Type) + { + case "application/x-www-form-urlencoded": + { + if (httpRequest.Params != null) + { + for (int i = 0; i < methodInstance.Parameters.Length; i++) + { + if (httpRequest.Params.TryGetValue(methodInstance.ParameterNames[i], out string value)) + { + methodInvoker.Parameters[i] = value.ParseToType(methodInstance.ParameterTypes[i]); + } + else + { + methodInvoker.Parameters[i] = methodInstance.ParameterTypes[i].GetDefault(); + } + } + } + break; + } + case "application/xml": + { + if (methodInstance.Parameters.Length > 0) + { + for (int i = 0; i < methodInstance.Parameters.Length; i++) + { + if (i == 0) + { + methodInvoker.Parameters[i] = SerializeConvert.XmlDeserializeFromBytes( + httpRequest.Encoding.GetBytes(httpRequest.Body), methodInstance.ParameterTypes[0]); + } + else + { + methodInvoker.Parameters[i] = methodInstance.ParameterTypes[i].GetDefault(); + } + } + } + break; + } + } + } + + /// + /// 在调用完成时转换结果 + /// + /// + /// + /// + public override HttpResponse OnResult(MethodInvoker methodInvoker, MethodInstance methodInstance) + { + HttpRequest httpRequest = (HttpRequest)methodInvoker.Flag; + HttpResponse httpResponse = new HttpResponse(); + switch (methodInvoker.Status) + { + case InvokeStatus.Success: + { + if (methodInvoker.ReturnParameter != null) + { + httpResponse.FromXML(SerializeConvert.XmlSerializeToString(methodInvoker.ReturnParameter)); + break; + } + else + { + httpResponse.FromText(string.Empty); + } + + break; + } + case InvokeStatus.UnFound: + { + string xmlString = SerializeConvert.XmlSerializeToString(new ActionResult() { Status = methodInvoker.Status, Message = methodInvoker.StatusMessage }); + httpResponse.FromXML(xmlString, "404"); + break; + } + case InvokeStatus.UnEnable: + { + string xmlString = SerializeConvert.XmlSerializeToString(new ActionResult() { Status = methodInvoker.Status, Message = methodInvoker.StatusMessage }); + httpResponse.FromXML(xmlString, "405"); + break; + } + case InvokeStatus.Abort: + { + string xmlString = SerializeConvert.XmlSerializeToString(new ActionResult() { Status = methodInvoker.Status, Message = methodInvoker.StatusMessage }); + httpResponse.FromXML(xmlString, "403"); + break; + } + case InvokeStatus.InvocationException: + case InvokeStatus.Exception: + { + string xmlString = SerializeConvert.XmlSerializeToString(new ActionResult() { Status = methodInvoker.Status, Message = methodInvoker.StatusMessage }); + httpResponse.FromXML(xmlString, "422"); + break; + } + } + + return httpResponse; + } + } +} \ No newline at end of file diff --git a/RRQMSocket.RPC.WebApi/LICENSE b/RRQMSocket.RPC.WebApi/LICENSE new file mode 100644 index 000000000..b09cd7856 --- /dev/null +++ b/RRQMSocket.RPC.WebApi/LICENSE @@ -0,0 +1,201 @@ +Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/RRQMSocket.RPC.WebApi/RRQM.ico b/RRQMSocket.RPC.WebApi/RRQM.ico new file mode 100644 index 000000000..6465fb16e Binary files /dev/null and b/RRQMSocket.RPC.WebApi/RRQM.ico differ diff --git a/RRQMSocket.RPC.WebApi/RRQM.png b/RRQMSocket.RPC.WebApi/RRQM.png new file mode 100644 index 000000000..1fc567ad9 Binary files /dev/null and b/RRQMSocket.RPC.WebApi/RRQM.png differ diff --git a/RRQMSocket.RPC.WebApi/RRQMSocket.RPC.WebApi.csproj b/RRQMSocket.RPC.WebApi/RRQMSocket.RPC.WebApi.csproj new file mode 100644 index 000000000..e028d0196 --- /dev/null +++ b/RRQMSocket.RPC.WebApi/RRQMSocket.RPC.WebApi.csproj @@ -0,0 +1,70 @@ + + + net45;netcoreapp3.1;netstandard2.0 + RRQM.ico + true + RRQM.pfx + 5.5.0 + 若汝棋茗 + Copyright © 2021 若汝棋茗 + 介绍:这是一个扩展于RRQMSocket.RPC的WebApi组件,可以通过该组件直接创建WebApi服务解析器,让Web端、移动端可以跨语言调用RPC函数。功能支持路由、Get传参、Post传参等。 + +更新说明: +修改为稳定版。 + +Demo:https://gitee.com/RRQM_OS/RRQMBox +API:https://gitee.com/RRQM_OS/RRQM/wikis/pages + https://gitee.com/dotnetchina/RRQMSocket + + true + RRQM.png + 若汝棋茗 + true + LICENSE + WebApi,RPC,IOCP + + + + bin\Debug\netstandard2.0\RRQMSocket.RPC.WebApi.xml + + + + + bin\Release\netstandard2.0\RRQMSocket.RPC.WebApi.xml + + + + + bin\Debug\net45\RRQMSocket.RPC.WebApi.xml + + + + + bin\Release\net45\RRQMSocket.RPC.WebApi.xml + + + + + bin\Debug\netcoreapp3.1\RRQMSocket.RPC.WebApi.xml + + + + + bin\Release\netcoreapp3.1\RRQMSocket.RPC.WebApi.xml + + + + + True + + + + True + + + + + + + + diff --git a/RRQMSocket.RPC.WebApi/Socket/WebApiParser.cs b/RRQMSocket.RPC.WebApi/Socket/WebApiParser.cs new file mode 100644 index 000000000..0ae6aa559 --- /dev/null +++ b/RRQMSocket.RPC.WebApi/Socket/WebApiParser.cs @@ -0,0 +1,280 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +using RRQMCore.ByteManager; +using RRQMCore.Helper; +using RRQMCore.Log; +using RRQMSocket.Http; +using System; +using System.Net.Sockets; +using System.Reflection; + +namespace RRQMSocket.RPC.WebApi +{ + /// + /// WebApi解析器 + /// + public class WebApiParser : TcpService, IRPCParser + { + private RouteMap routeMap; + + /// + /// 构造函数 + /// + public WebApiParser() + { + this.routeMap = new RouteMap(); + } + + /// + /// 数据转化器 + /// + public ApiDataConverter ApiDataConverter { get; private set; } + + private int maxPackageSize; + + /// + /// 最大数据包长度 + /// + public int MaxPackageSize + { + get { return maxPackageSize; } + } + + /// + /// 函数映射 + /// + public MethodMap MethodMap { get; private set; } + + /// + /// 获取路由映射图 + /// + public RouteMap RouteMap { get { return this.routeMap; } } + + /// + /// 所属服务器 + /// + public RPCService RPCService { get; private set; } + + /// + /// 执行函数 + /// + public Action RRQMExecuteMethod { get; private set; } + + /// + /// 结束调用 + /// + /// + /// + public void OnEndInvoke(MethodInvoker methodInvoker, MethodInstance methodInstance) + { + HttpRequest httpRequest = (HttpRequest)methodInvoker.Flag; + SimpleSocketClient socketClient = (SimpleSocketClient)methodInvoker.Caller; + + HttpResponse httpResponse = this.ApiDataConverter.OnResult(methodInvoker, methodInstance); + + httpResponse.ProtocolVersion = httpRequest.ProtocolVersion; + ByteBlock byteBlock = this.BytePool.GetByteBlock(this.BufferLength); + + try + { + httpResponse.Build(byteBlock); + socketClient.Send(byteBlock); + } + finally + { + byteBlock.Dispose(); + } + + if (!httpRequest.KeepAlive) + { + socketClient.Shutdown(SocketShutdown.Both); + } + } + + /// + /// 初始化 + /// + /// + /// + public void OnRegisterServer(IServerProvider provider, MethodInstance[] methodInstances) + { + foreach (var methodInstance in methodInstances) + { + if ((typeof(ControllerBase).IsAssignableFrom(methodInstance.Provider.GetType()))) + { + string controllerName; + RouteAttribute classAtt = methodInstance.Provider.GetType().GetCustomAttribute(false); + if (classAtt == null || string.IsNullOrEmpty(classAtt.Template)) + { + controllerName = methodInstance.Provider.GetType().Name; + } + else + { + controllerName = classAtt.Template.Replace("[controller]", methodInstance.Provider.GetType().Name); + } + + foreach (var att in methodInstance.RPCAttributes) + { + if (att is RouteAttribute attribute) + { + if (methodInstance.IsByRef) + { + throw new RRQMRPCException("WebApi服务中不允许有out及ref关键字"); + } + string actionUrl; + + if (controllerName.Contains("[action]")) + { + actionUrl = controllerName.Replace("[action]", methodInstance.Method.Name); + } + else + { + if (string.IsNullOrEmpty(attribute.Template)) + { + actionUrl = $"{controllerName}/{methodInstance.Method.Name}"; + } + else + { + actionUrl = $"{controllerName}/{attribute.Template.Replace("[action]", methodInstance.Method.Name)}"; + } + } + + this.routeMap.Add(actionUrl, methodInstance); + } + } + } + } + } + + /// + /// 取消注册服务 + /// + /// + /// + public void OnUnregisterServer(IServerProvider provider, MethodInstance[] methodInstances) + { + } + + /// + /// 设置执行委托 + /// + /// + public void SetExecuteMethod(Action executeMethod) + { + this.RRQMExecuteMethod = executeMethod; + } + + /// + /// 设置地图映射 + /// + /// + public void SetMethodMap(MethodMap methodMap) + { + this.MethodMap = methodMap; + } + + /// + /// 设置服务 + /// + /// + public void SetRPCService(RPCService service) + { + this.RPCService = service; + } + + /// + /// 载入配置 + /// + /// + protected override void LoadConfig(ServiceConfig serviceConfig) + { + base.LoadConfig(serviceConfig); + this.ApiDataConverter = (ApiDataConverter)serviceConfig.GetValue(WebApiParserConfig.ApiDataConverterProperty); + this.maxPackageSize = (int)serviceConfig.GetValue(WebApiParserConfig.MaxPackageSizeProperty); + } + + /// + /// 在初次接收时 + /// + /// + /// + protected override void OnCreateSocketCliect(WebApiSocketClient socketClient, CreateOption createOption) + { + if (createOption.NewCreate) + { + socketClient.OnReceived = this.OnReceived; + } + socketClient.SetAdapter(new HttpDataHandlingAdapter(this.maxPackageSize, HttpType.Server)); + } + + private void OnReceived(SimpleSocketClient socketClient, ByteBlock byteBlock, object obj) + { + HttpRequest httpRequest = (HttpRequest)obj; + MethodInvoker methodInvoker = new MethodInvoker(); + methodInvoker.Caller = socketClient; + methodInvoker.Flag = httpRequest; + + if (this.routeMap.TryGet(httpRequest.RelativeURL, out MethodInstance methodInstance)) + { + if (methodInstance.IsEnable) + { + try + { + methodInvoker.Parameters = new object[methodInstance.Parameters.Length]; + switch (httpRequest.Method) + { + case "GET": + { + if (httpRequest.Query != null) + { + for (int i = 0; i < methodInstance.Parameters.Length; i++) + { + if (httpRequest.Query.TryGetValue(methodInstance.ParameterNames[i], out string value)) + { + methodInvoker.Parameters[i] = value.ParseToType(methodInstance.ParameterTypes[i]); + } + else + { + methodInvoker.Parameters[i] = methodInstance.ParameterTypes[i].GetDefault(); + } + } + } + break; + } + case "POST": + { + this.ApiDataConverter.OnPost(httpRequest, ref methodInvoker, methodInstance); + break; + } + } + } + catch (Exception ex) + { + methodInvoker.Status = InvokeStatus.Exception; + methodInvoker.StatusMessage = ex.Message; + this.Logger.Debug(LogType.Error, this, ex.Message, ex); + } + } + else + { + methodInvoker.Status = InvokeStatus.UnEnable; + } + } + else + { + methodInvoker.Status = InvokeStatus.UnFound; + } + + this.RRQMExecuteMethod.Invoke(this, methodInvoker, methodInstance); + } + } +} \ No newline at end of file diff --git a/RRQMSocket.RPC.WebApi/Socket/WebApiSocketClient.cs b/RRQMSocket.RPC.WebApi/Socket/WebApiSocketClient.cs new file mode 100644 index 000000000..71cfa3d0f --- /dev/null +++ b/RRQMSocket.RPC.WebApi/Socket/WebApiSocketClient.cs @@ -0,0 +1,29 @@ +using RRQMCore.Exceptions; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace RRQMSocket.RPC.WebApi +{ + /// + /// WebApiSocket辅助类 + /// + public class WebApiSocketClient : SimpleSocketClient + { + /// + /// 禁用适配器赋值 + /// + /// + public sealed override void SetDataHandlingAdapter(DataHandlingAdapter adapter) + { + throw new RRQMException($"{nameof(WebApiSocketClient)}不允许设置适配器。"); + } + + internal void SetAdapter(DataHandlingAdapter adapter) + { + base.SetDataHandlingAdapter(adapter); + } + } +} diff --git a/RRQMSocket.RPC.XmlRpc/Attribute/XmlRpcAttribute.cs b/RRQMSocket.RPC.XmlRpc/Attribute/XmlRpcAttribute.cs new file mode 100644 index 000000000..2629a50db --- /dev/null +++ b/RRQMSocket.RPC.XmlRpc/Attribute/XmlRpcAttribute.cs @@ -0,0 +1,43 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +using System; + +namespace RRQMSocket.RPC.XmlRpc +{ + /// + /// 适用于XmlRpc的标记 + /// + [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)] + public sealed class XmlRpcAttribute : RPCAttribute + { + /// + /// 构造函数 + /// + public XmlRpcAttribute() + { + } + + /// + /// 构造函数 + /// + /// + public XmlRpcAttribute(string actionKey) + { + this.ActionKey = actionKey; + } + + /// + /// 服务唯一标识 + /// + public string ActionKey { get; private set; } + } +} \ No newline at end of file diff --git a/RRQMSocket.RPC.XmlRpc/Common/ActionMap.cs b/RRQMSocket.RPC.XmlRpc/Common/ActionMap.cs new file mode 100644 index 000000000..7cc41dc5e --- /dev/null +++ b/RRQMSocket.RPC.XmlRpc/Common/ActionMap.cs @@ -0,0 +1,70 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +using System.Collections; +using System.Collections.Generic; + +namespace RRQMSocket.RPC.XmlRpc +{ + /// + /// 服务映射图 + /// + public class ActionMap : IEnumerable> + { + internal ActionMap() + { + this.actionMap = new Dictionary(); + } + + private Dictionary actionMap; + + internal void Add(string actionKey, MethodInstance methodInstance) + { + this.actionMap.Add(actionKey, methodInstance); + } + + /// + /// 服务键集合 + /// + public IEnumerable ActionKeys { get { return this.actionMap.Keys; } } + + /// + /// 通过routeUrl获取函数实例 + /// + /// + /// + /// + public bool TryGet(string actionKey, out MethodInstance methodInstance) + { + if (this.actionMap.ContainsKey(actionKey)) + { + methodInstance = this.actionMap[actionKey]; + return true; + } + methodInstance = null; + return false; + } + + /// + /// 返回迭代器 + /// + /// + public IEnumerator> GetEnumerator() + { + return this.actionMap.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return this.actionMap.GetEnumerator(); + } + } +} \ No newline at end of file diff --git a/RRQMSocket.RPC.XmlRpc/Common/XmlDataTool.cs b/RRQMSocket.RPC.XmlRpc/Common/XmlDataTool.cs new file mode 100644 index 000000000..7771e86b4 --- /dev/null +++ b/RRQMSocket.RPC.XmlRpc/Common/XmlDataTool.cs @@ -0,0 +1,270 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +using RRQMCore.ByteManager; +using RRQMCore.Helper; +using RRQMSocket.Http; +using System; +using System.Collections; +using System.Reflection; +using System.Text; +using System.Xml; + +namespace RRQMSocket.RPC.XmlRpc +{ + internal static class XmlDataTool + { + public static object GetValue(XmlNode valueNode, Type type) + { + if (valueNode == null) + { + return type.GetDefault(); + } + switch (valueNode.Name) + { + case "boolean": + { + return bool.Parse(valueNode.InnerText); + } + case "i4": + case "int": + { + return int.Parse(valueNode.InnerText); + } + case "double": + { + return double.Parse(valueNode.InnerText); + } + case "dateTime.iso8601": + { + return DateTime.Parse(valueNode.InnerText); + } + case "base64": + { + return valueNode.InnerText; + } + case "struct": + { + object instance = Activator.CreateInstance(type); + foreach (XmlNode memberNode in valueNode.ChildNodes) + { + string name = memberNode.SelectSingleNode("name").InnerText; + PropertyInfo property = type.GetProperty(name); + property.SetValue(instance, GetValue(memberNode.SelectSingleNode("value").FirstChild, property.PropertyType)); + } + return instance; + } + case "arrays": + case "array": + { + if (type.GetElementType() != null) + { + XmlNode dataNode = valueNode.SelectSingleNode("data"); + Array array = Array.CreateInstance(type.GetElementType(), dataNode.ChildNodes.Count); + + int index = 0; + foreach (XmlNode arrayValueNode in dataNode.ChildNodes) + { + array.SetValue(GetValue(arrayValueNode.FirstChild, type.GetElementType()), index); + index++; + } + return array; + } + else if (type.GetGenericArguments().Length == 1) + { + XmlNode dataNode = valueNode.SelectSingleNode("data"); + IList array = (IList)Activator.CreateInstance(type); + + foreach (XmlNode arrayValueNode in dataNode.ChildNodes) + { + array.Add(GetValue(arrayValueNode.FirstChild, type.GetGenericArguments()[0])); + } + return array; + } + return type.GetDefault(); + } + default: + case "string": + { + return valueNode.InnerText; + } + } + } + + public static void CreateRequest(ByteBlock byteBlock, string host, string method, object[] parameters) + { + XmlDocument xml = new XmlDocument(); + + XmlDeclaration xmlDecl = xml.CreateXmlDeclaration("1.0", string.Empty, string.Empty); + xml.AppendChild(xmlDecl); + + XmlElement xmlElement = xml.CreateElement("methodCall"); + xml.AppendChild(xmlElement); + + XmlElement methodNameElement = xml.CreateElement("methodName"); + methodNameElement.InnerText = method; + xmlElement.AppendChild(methodNameElement); + + XmlElement paramsElement = xml.CreateElement("params"); + xmlElement.AppendChild(paramsElement); + + foreach (var param in parameters) + { + XmlElement paramElement = xml.CreateElement("param"); + paramsElement.AppendChild(paramElement); + + XmlElement valueElement = xml.CreateElement("value"); + paramElement.AppendChild(valueElement); + + CreateParam(xml, valueElement, param); + } + + ByteBlock xmlBlock = BytePool.Default.GetByteBlock(byteBlock.Capacity); + xml.Save(xmlBlock); + + StringBuilder stringBuilder = new StringBuilder(); + stringBuilder.AppendLine("POST / HTTP/1.1"); + stringBuilder.AppendLine("Content-Type: text/xml"); + stringBuilder.AppendLine($"Host: {host}"); + stringBuilder.AppendLine("User-Agent: RRQMXmlRpc"); + stringBuilder.AppendLine($"Content-Length: {xmlBlock.Length}"); + //stringBuilder.AppendLine("Connection: Close"); + stringBuilder.AppendLine("Connection: keep-alive"); + stringBuilder.AppendLine(); + try + { + byteBlock.Write(Encoding.UTF8.GetBytes(stringBuilder.ToString())); + byteBlock.Write(xmlBlock.Buffer, 0, xmlBlock.Len); + } + finally + { + xmlBlock.Dispose(); + } + } + + public static void CreateParam(XmlDocument xml, XmlNode xmlNode, object value) + { + if (value == null) + { + return; + } + if (value is int) + { + XmlElement valueElement = xml.CreateElement("i4"); + valueElement.InnerText = value.ToString(); + xmlNode.AppendChild(valueElement); + } + else if (value is bool) + { + XmlElement valueElement = xml.CreateElement("boolean"); + valueElement.InnerText = ((int)value).ToString(); + xmlNode.AppendChild(valueElement); + } + else if (value is double) + { + XmlElement valueElement = xml.CreateElement("double"); + valueElement.InnerText = ((double)value).ToString(); + xmlNode.AppendChild(valueElement); + } + else if (value is string) + { + XmlElement valueElement = xml.CreateElement("string"); + valueElement.InnerText = value.ToString(); + xmlNode.AppendChild(valueElement); + } + else if (value is DateTime) + { + XmlElement valueElement = xml.CreateElement("dateTime.iso8601"); + valueElement.InnerText = ((DateTime)value).ToString(); + xmlNode.AppendChild(valueElement); + } + else if (value is byte[]) + { + XmlElement valueElement = xml.CreateElement("base64"); + string str = Convert.ToBase64String((byte[])value); + valueElement.InnerText = str; + xmlNode.AppendChild(valueElement); + } + else if (typeof(IList).IsAssignableFrom(value.GetType())) + { + IList array = (IList)value; + XmlElement arrayElement; + + arrayElement = xml.CreateElement("array"); + + xmlNode.AppendChild(arrayElement); + + XmlElement dataElememt = xml.CreateElement("data"); + arrayElement.AppendChild(dataElememt); + + foreach (var item in array) + { + XmlElement valueElement = xml.CreateElement("value"); + dataElememt.AppendChild(valueElement); + CreateParam(xml, valueElement, item); + } + } + else + { + XmlElement valueElement = xml.CreateElement("struct"); + xmlNode.AppendChild(valueElement); + + PropertyInfo[] propertyInfos = value.GetType().GetProperties(); + foreach (var propertyInfo in propertyInfos) + { + XmlElement memberElement = xml.CreateElement("member"); + valueElement.AppendChild(memberElement); + + XmlElement nameElement = xml.CreateElement("name"); + nameElement.InnerText = propertyInfo.Name; + memberElement.AppendChild(nameElement); + + XmlElement oValueElement = xml.CreateElement("value"); + memberElement.AppendChild(oValueElement); + + object oValue = propertyInfo.GetValue(value); + CreateParam(xml, oValueElement, oValue); + } + } + } + + public static void CreatResponse(HttpResponse httpResponse, object value) + { + XmlDocument xml = new XmlDocument(); + + XmlDeclaration xmlDecl = xml.CreateXmlDeclaration("1.0", string.Empty, string.Empty); + xml.AppendChild(xmlDecl); + + XmlElement xmlElement = xml.CreateElement("methodResponse"); + + xml.AppendChild(xmlElement); + + XmlElement paramsElement = xml.CreateElement("params"); + xmlElement.AppendChild(paramsElement); + + XmlElement paramElement = xml.CreateElement("param"); + paramsElement.AppendChild(paramElement); + + XmlElement valueElement = xml.CreateElement("value"); + paramElement.AppendChild(valueElement); + + CreateParam(xml, valueElement, value); + + ByteBlock xmlBlock = BytePool.Default.GetByteBlock(1024 * 4); + xml.Save(xmlBlock); + + string xmlString = Encoding.UTF8.GetString(xmlBlock.Buffer, 0, xmlBlock.Len); + + httpResponse.FromXML(xmlString); + xmlBlock.Dispose(); + } + } +} \ No newline at end of file diff --git a/RRQMSocket.RPC.XmlRpc/Config/XmlRpcClientConfig.cs b/RRQMSocket.RPC.XmlRpc/Config/XmlRpcClientConfig.cs new file mode 100644 index 000000000..f8411356d --- /dev/null +++ b/RRQMSocket.RPC.XmlRpc/Config/XmlRpcClientConfig.cs @@ -0,0 +1,54 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +using RRQMCore.Dependency; + +namespace RRQMSocket.RPC.XmlRpc +{ + /// + /// XmlRpcClient配置 + /// + public class XmlRpcClientConfig : TcpClientConfig + { + + /// + /// 最大数据包长度 + /// + public int MaxPackageSize + { + get { return (int)GetValue(MaxPackageSizeProperty); } + set { SetValue(MaxPackageSizeProperty, value); } + } + + /// + /// 最大数据包长度,所需类型 + /// + public static readonly DependencyProperty MaxPackageSizeProperty = + DependencyProperty.Register("MaxPackageSize", typeof(int), typeof(XmlRpcClientConfig), 1024); + + + /// + /// 等待超时时间(秒) + /// + public int Timeout + { + get { return (int)GetValue(TimeoutProperty); } + set { SetValue(TimeoutProperty, value); } + } + + /// + /// 等待超时时间(秒), + /// 所需类型 + /// + public static readonly DependencyProperty TimeoutProperty = + DependencyProperty.Register("Timeout", typeof(int), typeof(XmlRpcClientConfig), 5); + } +} \ No newline at end of file diff --git a/RRQMSocket.RPC.XmlRpc/Config/XmlRpcParserConfig.cs b/RRQMSocket.RPC.XmlRpc/Config/XmlRpcParserConfig.cs new file mode 100644 index 000000000..ae4ba6bea --- /dev/null +++ b/RRQMSocket.RPC.XmlRpc/Config/XmlRpcParserConfig.cs @@ -0,0 +1,38 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ + +using RRQMCore.Dependency; + +namespace RRQMSocket.RPC.XmlRpc +{ + /// + /// XmlRpcParser配置 + /// + public class XmlRpcParserConfig : TcpServiceConfig + { + /// + /// 最大数据包长度 + /// + public int MaxPackageSize + { + get { return (int)GetValue(MaxPackageSizeProperty); } + set { SetValue(MaxPackageSizeProperty, value); } + } + + /// + /// 最大数据包长度,所需类型 + /// + public static readonly DependencyProperty MaxPackageSizeProperty = + DependencyProperty.Register("MaxPackageSize", typeof(int), typeof(XmlRpcParserConfig), 1024); + + } +} \ No newline at end of file diff --git a/RRQMSocket.RPC.XmlRpc/LICENSE b/RRQMSocket.RPC.XmlRpc/LICENSE new file mode 100644 index 000000000..b09cd7856 --- /dev/null +++ b/RRQMSocket.RPC.XmlRpc/LICENSE @@ -0,0 +1,201 @@ +Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/RRQMSocket.RPC.XmlRpc/RRQM.ico b/RRQMSocket.RPC.XmlRpc/RRQM.ico new file mode 100644 index 000000000..6465fb16e Binary files /dev/null and b/RRQMSocket.RPC.XmlRpc/RRQM.ico differ diff --git a/RRQMSocket.RPC.XmlRpc/RRQM.png b/RRQMSocket.RPC.XmlRpc/RRQM.png new file mode 100644 index 000000000..1fc567ad9 Binary files /dev/null and b/RRQMSocket.RPC.XmlRpc/RRQM.png differ diff --git a/RRQMSocket.RPC.XmlRpc/RRQMSocket.RPC.XmlRpc.csproj b/RRQMSocket.RPC.XmlRpc/RRQMSocket.RPC.XmlRpc.csproj new file mode 100644 index 000000000..27f9df34a --- /dev/null +++ b/RRQMSocket.RPC.XmlRpc/RRQMSocket.RPC.XmlRpc.csproj @@ -0,0 +1,70 @@ + + + net45;netcoreapp3.1;netstandard2.0 + RRQM.ico + true + RRQM.pfx + 5.5.0 + 若汝棋茗 + Copyright © 2021 若汝棋茗 + 介绍:这是一个扩展于RRQMSocket.RPC的XmlRpc组件,可以通过该组件直接创建XmlRpc服务解析器,让Web端、移动端可以跨语言调用RPC函数。功能支持XmlRpc全功能。 + +更新说明: +同步更新。 + +Demo:https://gitee.com/RRQM_OS/RRQMBox +API:https://gitee.com/RRQM_OS/RRQM/wikis/pages + https://gitee.com/dotnetchina/RRQMSocket + + true + RRQM.png + 若汝棋茗 + true + LICENSE + RPC;XmlRpc;Socket,IOCP + + + + bin\Debug\netstandard2.0\RRQMSocket.RPC.XmlRpc.xml + + + + + bin\Release\netstandard2.0\RRQMSocket.RPC.XmlRpc.xml + + + + + bin\Debug\net45\RRQMSocket.RPC.XmlRpc.xml + + + + + bin\Release\net45\RRQMSocket.RPC.XmlRpc.xml + + + + + bin\Debug\netcoreapp3.1\RRQMSocket.RPC.XmlRpc.xml + + + + + bin\Release\netcoreapp3.1\RRQMSocket.RPC.XmlRpc.xml + + + + + True + + + + True + + + + + + + + diff --git a/RRQMSocket.RPC.XmlRpc/Socket/XmlRpcClient.cs b/RRQMSocket.RPC.XmlRpc/Socket/XmlRpcClient.cs new file mode 100644 index 000000000..77ed19dd7 --- /dev/null +++ b/RRQMSocket.RPC.XmlRpc/Socket/XmlRpcClient.cs @@ -0,0 +1,188 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +using RRQMCore.ByteManager; +using RRQMCore.Exceptions; +using RRQMCore.Run; +using RRQMSocket.Http; +using RRQMSocket.RPC.RRQMRPC; +using System; +using System.Xml; + +namespace RRQMSocket.RPC.XmlRpc +{ + /// + /// XmlRpc客户端 + /// + public class XmlRpcClient : TcpClient, IRpcClient + { + /// + /// 构造函数 + /// + public XmlRpcClient() + { + singleWaitHandle = new WaitData(); + } + + private int maxPackageSize; + + /// + /// 最大数据包长度 + /// + public int MaxPackageSize + { + get { return maxPackageSize; } + } + + private WaitData singleWaitHandle; + + private int timeout; + + /// + /// 载入配置 + /// + /// + protected override void LoadConfig(TcpClientConfig clientConfig) + { + base.LoadConfig(clientConfig); + this.timeout = (int)clientConfig.GetValue(XmlRpcClientConfig.TimeoutProperty); + this.maxPackageSize = (int)clientConfig.GetValue(XmlRpcClientConfig.MaxPackageSizeProperty); + this.SetDataHandlingAdapter(new HttpDataHandlingAdapter(this.maxPackageSize, HttpType.Client)); + } + + /// + /// RPC调用 + /// + /// 方法名 + /// 调用配置 + /// 参数 + /// + /// + /// + /// + /// + public T Invoke(string method, InvokeOption invokeOption, ref object[] parameters, Type[] types) + { + ByteBlock byteBlock = this.BytePool.GetByteBlock(this.BufferLength); + HttpResponse response; + try + { + XmlDataTool.CreateRequest(byteBlock, this.Name, method, parameters); + response = this.WaitSend(byteBlock); + if (response.StatusCode != "200") + { + throw new RRQMException("调用错误"); + } + XmlDocument xml = new XmlDocument(); + xml.LoadXml(response.Body); + XmlNode paramNode = xml.SelectSingleNode("methodResponse/params/param"); + if (paramNode != null) + { + return (T)XmlDataTool.GetValue(paramNode.FirstChild.FirstChild, typeof(T)); + } + } + catch (Exception ex) + { + throw ex; + } + finally + { + byteBlock.Dispose(); + } + return default; + } + + /// + /// RPC调用 + /// + /// 方法名 + /// 调用配置 + /// 参数 + /// + /// + /// + /// + public void Invoke(string method, InvokeOption invokeOption, ref object[] parameters, Type[] types) + { + ByteBlock byteBlock = this.BytePool.GetByteBlock(this.BufferLength); + HttpResponse response; + try + { + XmlDataTool.CreateRequest(byteBlock, this.Name, method, parameters); + response = this.WaitSend(byteBlock); + if (response.StatusCode != "200") + { + throw new RRQMException("调用错误"); + } + } + catch (Exception ex) + { + throw ex; + } + finally + { + byteBlock.Dispose(); + } + } + + /// + /// RPC调用 + /// + /// 方法名 + /// 调用配置 + /// 参数 + /// + /// + /// + public void Invoke(string method, InvokeOption invokeOption, params object[] parameters) + { + this.Invoke(method, invokeOption, ref parameters, null); + } + + /// + /// RPC调用 + /// + /// 方法名 + /// 调用配置 + /// 参数 + /// + /// + /// + /// + public T Invoke(string method, InvokeOption invokeOption, params object[] parameters) + { + return this.Invoke(method, invokeOption, ref parameters, null); + } + + /// + /// 处理数据 + /// + /// + /// + protected override void HandleReceivedData(ByteBlock byteBlock, object obj) + { + this.singleWaitHandle.Set((HttpResponse)obj); + } + + private HttpResponse WaitSend(ByteBlock byteBlock) + { + lock (locker) + { + this.Send(byteBlock.Buffer, 0, byteBlock.Len); + if (this.singleWaitHandle.Wait(1000 * this.timeout)) + { + return this.singleWaitHandle.WaitResult; + } + throw new RRQMTimeoutException("超时接收"); + } + } + } +} \ No newline at end of file diff --git a/RRQMSocket.RPC.XmlRpc/Socket/XmlRpcParser.cs b/RRQMSocket.RPC.XmlRpc/Socket/XmlRpcParser.cs new file mode 100644 index 000000000..a001de7ed --- /dev/null +++ b/RRQMSocket.RPC.XmlRpc/Socket/XmlRpcParser.cs @@ -0,0 +1,234 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +using RRQMCore.ByteManager; +using RRQMCore.Log; +using RRQMSocket.Http; +using System; +using System.Collections.Generic; +using System.Net.Sockets; +using System.Xml; + +namespace RRQMSocket.RPC.XmlRpc +{ + /// + /// XmlRpc解析器 + /// + public class XmlRpcParser : TcpService, IRPCParser + { + private ActionMap actionMap; + + /// + /// 构造函数 + /// + public XmlRpcParser() + { + this.actionMap = new ActionMap(); + } + + /// + /// 服务键映射图 + /// + public ActionMap ActionMap { get { return this.actionMap; } } + + private int maxPackageSize; + + /// + /// 最大数据包长度 + /// + public int MaxPackageSize + { + get { return maxPackageSize; } + } + + /// + /// 函数映射 + /// + public MethodMap MethodMap { get; private set; } + + /// + /// 所属服务器 + /// + public RPCService RPCService { get; private set; } + + /// + /// 执行函数 + /// + public Action RRQMExecuteMethod { get; private set; } + + /// + /// 结束调用 + /// + /// + /// + public void OnEndInvoke(MethodInvoker methodInvoker, MethodInstance methodInstance) + { + HttpRequest httpRequest = (HttpRequest)methodInvoker.Flag; + SimpleSocketClient socketClient = (SimpleSocketClient)methodInvoker.Caller; + + HttpResponse httpResponse = new HttpResponse(); + + httpResponse.ProtocolVersion = httpRequest.ProtocolVersion; + ByteBlock byteBlock = this.BytePool.GetByteBlock(this.BufferLength); + + XmlDataTool.CreatResponse(httpResponse, methodInvoker.ReturnParameter); + try + { + httpResponse.Build(byteBlock); + socketClient.Send(byteBlock); + } + finally + { + byteBlock.Dispose(); + } + + if (!httpRequest.KeepAlive) + { + socketClient.Shutdown(SocketShutdown.Both); + } + } + + /// + /// 初始化 + /// + /// + /// + public void OnRegisterServer(IServerProvider provider, MethodInstance[] methodInstances) + { + foreach (var methodInstance in methodInstances) + { + foreach (var att in methodInstance.RPCAttributes) + { + if (att is XmlRpcAttribute attribute) + { + if (methodInstance.IsByRef) + { + throw new RRQMRPCException("XmlRpc服务中不允许有out及ref关键字"); + } + string actionKey = string.IsNullOrEmpty(attribute.ActionKey) ? $"{methodInstance.Method.Name}" : attribute.ActionKey; + + this.actionMap.Add(actionKey, methodInstance); + } + } + } + } + + /// + /// 取消注册服务 + /// + /// + /// + public void OnUnregisterServer(IServerProvider provider, MethodInstance[] methodInstances) + { + } + + /// + /// 载入配置 + /// + /// + protected override void LoadConfig(ServiceConfig serviceConfig) + { + base.LoadConfig(serviceConfig); + this.maxPackageSize = (int)serviceConfig.GetValue(XmlRpcParserConfig.MaxPackageSizeProperty); + } + + /// + /// 设置执行委托 + /// + /// + public void SetExecuteMethod(Action executeMethod) + { + this.RRQMExecuteMethod = executeMethod; + } + + /// + /// 设置地图映射 + /// + /// + public void SetMethodMap(MethodMap methodMap) + { + this.MethodMap = methodMap; + } + + /// + /// 设置服务 + /// + /// + public void SetRPCService(RPCService service) + { + this.RPCService = service; + } + + /// + /// 初始化 + /// + /// + /// + protected override void OnCreateSocketCliect(XmlRpcSocketClient socketClient, CreateOption createOption) + { + if (createOption.NewCreate) + { + socketClient.OnReceived = this.OnReceived; + } + socketClient.SetAdapter(new HttpDataHandlingAdapter(this.maxPackageSize, HttpType.Server)); + } + + private void OnReceived(SimpleSocketClient socketClient, ByteBlock byteBlock, object obj) + { + HttpRequest httpRequest = (HttpRequest)obj; + MethodInvoker methodInvoker = new MethodInvoker(); + methodInvoker.Caller = socketClient; + methodInvoker.Flag = httpRequest; + + XmlDocument xml = new XmlDocument(); + xml.LoadXml(httpRequest.Body); + XmlNode methodName = xml.SelectSingleNode("methodCall/methodName"); + string actionKey = methodName.InnerText; + + if (this.actionMap.TryGet(actionKey, out MethodInstance methodInstance)) + { + if (methodInstance.IsEnable) + { + try + { + List ps = new List(); + XmlNode paramsNode = xml.SelectSingleNode("methodCall/params"); + int index = 0; + foreach (XmlNode paramNode in paramsNode.ChildNodes) + { + XmlNode valueNode = paramNode.FirstChild.FirstChild; + ps.Add(XmlDataTool.GetValue(valueNode, methodInstance.ParameterTypes[index])); + index++; + } + + methodInvoker.Parameters = ps.ToArray(); + } + catch (Exception ex) + { + methodInvoker.Status = InvokeStatus.Exception; + methodInvoker.StatusMessage = ex.Message; + this.Logger.Debug(LogType.Error, this, ex.Message, ex); + } + } + else + { + methodInvoker.Status = InvokeStatus.UnEnable; + } + } + else + { + methodInvoker.Status = InvokeStatus.UnFound; + } + + this.RRQMExecuteMethod.Invoke(this, methodInvoker, methodInstance); + } + } +} \ No newline at end of file diff --git a/RRQMSocket.RPC.XmlRpc/Socket/XmlRpcSocketClient.cs b/RRQMSocket.RPC.XmlRpc/Socket/XmlRpcSocketClient.cs new file mode 100644 index 000000000..75cde92df --- /dev/null +++ b/RRQMSocket.RPC.XmlRpc/Socket/XmlRpcSocketClient.cs @@ -0,0 +1,29 @@ +using RRQMCore.Exceptions; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace RRQMSocket.RPC.XmlRpc +{ + /// + /// XmlRpc辅助类 + /// + public class XmlRpcSocketClient: SimpleSocketClient + { + /// + /// 禁用适配器赋值 + /// + /// + public sealed override void SetDataHandlingAdapter(DataHandlingAdapter adapter) + { + throw new RRQMException($"{nameof(XmlRpcSocketClient)}不允许设置适配器。"); + } + + internal void SetAdapter(DataHandlingAdapter adapter) + { + base.SetDataHandlingAdapter(adapter); + } + } +} diff --git a/RRQMSocket.RPC/Delegete.cs b/RRQMSocket.RPC/Delegete.cs new file mode 100644 index 000000000..4a4b7ef54 --- /dev/null +++ b/RRQMSocket.RPC/Delegete.cs @@ -0,0 +1,23 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +using RRQMCore.ByteManager; + +namespace RRQMSocket.RPC +{ + /// + /// 收到字节数据 + /// + /// + /// + /// + public delegate void RRQMReceivedProcotolEventHandler(object sender, short? procotol, ByteBlock byteBlock); +} \ No newline at end of file diff --git a/RRQMSocket.RPC/Global/Attribute/RPCAttribute.cs b/RRQMSocket.RPC/Global/Attribute/RPCAttribute.cs new file mode 100644 index 000000000..a95c615db --- /dev/null +++ b/RRQMSocket.RPC/Global/Attribute/RPCAttribute.cs @@ -0,0 +1,22 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +using System; + +namespace RRQMSocket.RPC +{ + /// + /// RPC方法属性基类 + /// + public abstract class RPCAttribute : Attribute + { + } +} \ No newline at end of file diff --git a/RRQMSocket.RPC/Global/Commond/InvokeStatus.cs b/RRQMSocket.RPC/Global/Commond/InvokeStatus.cs new file mode 100644 index 000000000..10c28f66f --- /dev/null +++ b/RRQMSocket.RPC/Global/Commond/InvokeStatus.cs @@ -0,0 +1,55 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ + +namespace RRQMSocket.RPC +{ + /// + /// 调用状态 + /// + public enum InvokeStatus : byte + { + /// + /// 就绪 + /// + Ready, + + /// + /// 未找到服务 + /// + UnFound, + + /// + /// 不可用 + /// + UnEnable, + + /// + /// 成功调用 + /// + Success, + + /// + /// 终止执行 + /// + Abort, + + /// + /// 调用内部异常 + /// + InvocationException, + + /// + /// 其他异常 + /// + Exception + } +} \ No newline at end of file diff --git a/RRQMSocket.RPC/Global/Commond/MethodInstance.cs b/RRQMSocket.RPC/Global/Commond/MethodInstance.cs new file mode 100644 index 000000000..113eb1be9 --- /dev/null +++ b/RRQMSocket.RPC/Global/Commond/MethodInstance.cs @@ -0,0 +1,77 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +using System; +using System.Reflection; + +namespace RRQMSocket.RPC +{ + /// + /// RPC函数实例 + /// + public class MethodInstance + { + /// + /// 执行此RPC的实例 + /// + public IServerProvider Provider { get; internal set; } + + /// + /// RPC函数 + /// + public MethodInfo Method { get; internal set; } + + /// + /// RPC属性集合 + /// + public RPCAttribute[] RPCAttributes { get; internal set; } + + /// + /// 方法唯一令箭 + /// + public int MethodToken { get; internal set; } + + /// + /// 返回值类型,无返回值时为Null + /// + public Type ReturnType { get; internal set; } + + /// + /// 参数类型集合,已处理out及ref,无参数时为空集合, + /// + public Type[] ParameterTypes { get; internal set; } + + /// + /// 参数集合 + /// + public ParameterInfo[] Parameters { get; internal set; } + + /// + /// 参数名集合 + /// + public string[] ParameterNames { get; internal set; } + + /// + /// 是否异步执行 + /// + public bool Async { get; internal set; } + + /// + /// 是否有引用类型 + /// + public bool IsByRef { get; internal set; } + + /// + /// 是否可用 + /// + public bool IsEnable { get; internal set; } + } +} \ No newline at end of file diff --git a/RRQMSocket.RPC/Global/Commond/MethodInvoker.cs b/RRQMSocket.RPC/Global/Commond/MethodInvoker.cs new file mode 100644 index 000000000..864971c2e --- /dev/null +++ b/RRQMSocket.RPC/Global/Commond/MethodInvoker.cs @@ -0,0 +1,50 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ + +namespace RRQMSocket.RPC +{ + /// + /// 函数调用信使 + /// + public class MethodInvoker + { + /// + /// 返回值 + /// + public object ReturnParameter { get; set; } + + /// + /// 参数值集合 + /// + public object[] Parameters { get; set; } + + /// + /// 获取调用状态 + /// + public InvokeStatus Status { get; set; } + + /// + /// 状态消息 + /// + public string StatusMessage { get; set; } + + /// + /// 可以传递其他类型的数据容器 + /// + public object Flag { get; set; } + + /// + /// 此函数执行者 + /// + public object Caller { get; set; } + } +} \ No newline at end of file diff --git a/RRQMSocket.RPC/Global/Commond/MethodMap.cs b/RRQMSocket.RPC/Global/Commond/MethodMap.cs new file mode 100644 index 000000000..cf369ad60 --- /dev/null +++ b/RRQMSocket.RPC/Global/Commond/MethodMap.cs @@ -0,0 +1,81 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +using RRQMCore; +using RRQMSocket.RPC.RRQMRPC; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; + +namespace RRQMSocket.RPC +{ + /// + /// 函数映射图 + /// + public class MethodMap + { + internal MethodMap() + { + this.methodMap = new ConcurrentDictionary(); + } + + private ConcurrentDictionary methodMap; + + internal void Add(MethodInstance methodInstance) + { + this.methodMap.TryAdd(methodInstance.MethodToken, methodInstance); + } + + /// + /// 通过methodToken获取函数实例 + /// + /// + /// + /// + public bool TryGet(int methodToken, out MethodInstance methodInstance) + { + return this.methodMap.TryGetValue(methodToken, out methodInstance); + } + + internal bool RemoveServer(Type type, out IServerProvider serverProvider, out MethodInstance[] methodInstances) + { + serverProvider = null; + bool success = false; + List keys = new List(); + foreach (var methodInstance in this.methodMap.Values) + { + if (methodInstance.Provider.GetType().FullName == type.FullName) + { + success = true; + serverProvider = methodInstance.Provider; + keys.Add(methodInstance); + } + } + + foreach (var item in keys) + { + this.methodMap.TryRemove(item.MethodToken, out _); + } + methodInstances = keys.ToArray(); + return success; + } + + /// + /// 获取所有服务函数实例 + /// + /// + public MethodInstance[] GetAllMethodInstances() + { + return this.methodMap.Values.ToArray(); + } + } +} \ No newline at end of file diff --git a/RRQMSocket.RPC/Global/Commond/RPCParserCollection.cs b/RRQMSocket.RPC/Global/Commond/RPCParserCollection.cs new file mode 100644 index 000000000..1ff891b8c --- /dev/null +++ b/RRQMSocket.RPC/Global/Commond/RPCParserCollection.cs @@ -0,0 +1,79 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +using System.Collections; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Diagnostics; + +namespace RRQMSocket.RPC +{ + /// + /// RPCParser集合 + /// + [DebuggerDisplay("Count")] + public class RPCParserCollection : IEnumerable + { + private ConcurrentDictionary parsers = new ConcurrentDictionary(); + + /// + /// 数量 + /// + public int Count { get { return parsers.Count; } } + + /// + /// 获取IRPCParser + /// + /// + /// + public IRPCParser this[string key] { get { return this.parsers[key]; } } + + /// + /// 获取IRPCParser + /// + /// + /// + /// + public bool TryGetRPCParser(string key, out IRPCParser parser) + { + return this.parsers.TryGetValue(key, out parser); + } + + internal void Add(string key, IRPCParser parser) + { + if (this.parsers.Values.Contains(parser)) + { + throw new RRQMRPCException("重复添加解析器"); + } + + this.parsers.TryAdd(key, parser); + } + + internal bool TryRemove(string key, out IRPCParser parser) + { + return this.parsers.TryRemove(key, out parser); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return this.parsers.Values.GetEnumerator(); + } + + /// + /// 返回枚举对象 + /// + /// + IEnumerator IEnumerable.GetEnumerator() + { + return this.parsers.Values.GetEnumerator(); + } + } +} \ No newline at end of file diff --git a/RRQMSocket.RPC/Global/Commond/ReadOnlyDictionary.cs b/RRQMSocket.RPC/Global/Commond/ReadOnlyDictionary.cs new file mode 100644 index 000000000..1ab419dd9 --- /dev/null +++ b/RRQMSocket.RPC/Global/Commond/ReadOnlyDictionary.cs @@ -0,0 +1,85 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +using System.Collections; +using System.Collections.Generic; + +namespace RRQMSocket.RPC +{ + /// + /// 只读字典 + /// + /// + /// + public class ReadOnlyDictionary : IEnumerable> + { + private Dictionary dic = new Dictionary(); + + /// + /// 值集合 + /// + public ICollection Values { get { return this.dic.Values; } } + + /// + /// 键集合 + /// + public ICollection Keys { get { return this.dic.Keys; } } + + internal void Add(TKey key, TValue value) + { + dic.Add(key, value); + } + + internal bool Remove(TKey key) + { + return dic.Remove(key); + } + + internal void Clear() + { + dic.Clear(); + } + + /// + /// 键值对数目 + /// + public int Count { get { return this.dic.Count; } } + + /// + /// 尝试获取值 + /// + /// + /// + /// + public bool TryGetValue(TKey key, out TValue value) + { + return this.dic.TryGetValue(key, out value); + } + + /// + /// 返回迭代器 + /// + /// + public IEnumerator> GetEnumerator() + { + return this.dic.GetEnumerator(); + } + + /// + /// 迭代器 + /// + /// + IEnumerator IEnumerable.GetEnumerator() + { + return this.dic.GetEnumerator(); + } + } +} \ No newline at end of file diff --git a/RRQMSocket.RPC/Global/Commond/ReadOnlyList.cs b/RRQMSocket.RPC/Global/Commond/ReadOnlyList.cs new file mode 100644 index 000000000..a1a404a6b --- /dev/null +++ b/RRQMSocket.RPC/Global/Commond/ReadOnlyList.cs @@ -0,0 +1,92 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +using System; +using System.Collections; +using System.Collections.Generic; + +namespace RRQMSocket.RPC +{ + /// + /// 只读 + /// + /// + public class ReadOnlyList : IEnumerable + { + private List list = new List(); + + internal void Add(T block) + { + list.Add(block); + } + + internal void AddRange(IEnumerable collection) + { + list.AddRange(collection); + } + + internal void Remove(T block) + { + list.Remove(block); + } + + internal void RemoveAt(int index) + { + list.RemoveAt(index); + } + + internal void RemoveAll(Predicate match) + { + list.RemoveAll(match); + } + + internal void RemoveRange(int index, int range) + { + list.RemoveRange(index, range); + } + + internal void Clear() + { + list.Clear(); + } + + internal void Insert(int index, T item) + { + list.Insert(index, item); + } + + internal void InsertRange(int index, IEnumerable collection) + { + list.InsertRange(index, collection); + } + + /// + /// 返回迭代器 + /// + /// + public IEnumerator GetEnumerator() + { + return this.list.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return this.list.GetEnumerator(); + } + + /// + /// 获取对象 + /// + /// + /// + public T this[int index] { get { return list[index]; } } + } +} \ No newline at end of file diff --git a/RRQMSocket.RPC/Global/Commond/ServerProvider.cs b/RRQMSocket.RPC/Global/Commond/ServerProvider.cs new file mode 100644 index 000000000..a6b0ea34e --- /dev/null +++ b/RRQMSocket.RPC/Global/Commond/ServerProvider.cs @@ -0,0 +1,62 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +using System.Reflection; + +namespace RRQMSocket.RPC +{ + /// + /// RPC范围类 + /// + public abstract class ServerProvider:IServerProvider + { + /// + /// 默认复刻程序集 + /// + public static Assembly DefaultAssembly { get; set; } + + /// + /// 该服务所属的服务器 + /// + public RPCService RPCService { get; set; } + + /// + /// RPC即将进入, + /// 若是想放弃本次执行,请抛出 + /// + /// + /// + /// + public virtual void RPCEnter(IRPCParser parser, MethodInvoker methodInvoker, MethodInstance methodInstance) + { + } + + /// + /// 执行RPC发生错误 + /// + /// + /// + /// + public virtual void RPCError(IRPCParser parser, MethodInvoker methodInvoker, MethodInstance methodInstance) + { + } + + /// + /// RPC方法执行完成 + /// + /// + /// + /// + public virtual void RPCLeave(IRPCParser parser, MethodInvoker methodInvoker, MethodInstance methodInstance) + { + } + } +} \ No newline at end of file diff --git a/RRQMSocket.RPC/Global/Commond/ServerProviderCollection.cs b/RRQMSocket.RPC/Global/Commond/ServerProviderCollection.cs new file mode 100644 index 000000000..b9e5969ad --- /dev/null +++ b/RRQMSocket.RPC/Global/Commond/ServerProviderCollection.cs @@ -0,0 +1,74 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics; + +namespace RRQMSocket.RPC +{ + /// + /// 服务集合 + /// + [DebuggerDisplay("{Count}")] + public class ServerProviderCollection : IEnumerable, IEnumerable + { + /// + /// 服务数量 + /// + public int Count { get { return this.servers.Count; } } + + private List servers = new List(); + + internal void Add(IServerProvider serverProvider) + { + foreach (var server in this.servers) + { + if (serverProvider.GetType().FullName == server.GetType().FullName) + { + throw new RRQMRPCException("相同类型的服务已添加"); + } + } + if (ServerProvider.DefaultAssembly == null) + { + ServerProvider.DefaultAssembly = serverProvider.GetType().Assembly; + } + servers.Add(serverProvider); + } + + internal void Remove(Type serverType) + { + foreach (var server in this.servers) + { + if (serverType.FullName == server.GetType().FullName) + { + this.servers.Remove(server); + return; + } + } + } + + /// + /// 返回枚举 + /// + /// + IEnumerator IEnumerable.GetEnumerator() + { + return this.servers.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return this.servers.GetEnumerator(); + } + } +} \ No newline at end of file diff --git a/RRQMSocket.RPC/Global/Exceptions/RRQMAbandonRPCException.cs b/RRQMSocket.RPC/Global/Exceptions/RRQMAbandonRPCException.cs new file mode 100644 index 000000000..c16b3b0ff --- /dev/null +++ b/RRQMSocket.RPC/Global/Exceptions/RRQMAbandonRPCException.cs @@ -0,0 +1,52 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +using RRQMCore.Exceptions; + +namespace RRQMSocket.RPC +{ + /// + /// 放弃RPC执行 + /// + + public class RRQMAbandonRPCException : RRQMException + { + /// + /// 构造函数 + /// + /// 是否反馈信息 + /// 信息 + public RRQMAbandonRPCException(bool feedback, string message) : base(message) + { + this.Feedback = feedback; + } + + /// + /// 构造函数 + /// + /// 信息 + public RRQMAbandonRPCException(string message) : this(true, message) + { + } + + /// + /// 构造函数 + /// + public RRQMAbandonRPCException() : this(true, null) + { + } + + /// + /// 是否反馈信息 + /// + public bool Feedback { get; private set; } + } +} \ No newline at end of file diff --git a/RRQMSocket.RPC/Global/Exceptions/RRQMRPCException.cs b/RRQMSocket.RPC/Global/Exceptions/RRQMRPCException.cs new file mode 100644 index 000000000..5c234ef73 --- /dev/null +++ b/RRQMSocket.RPC/Global/Exceptions/RRQMRPCException.cs @@ -0,0 +1,52 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +using RRQMCore.Exceptions; + +namespace RRQMSocket.RPC +{ + /* + 若汝棋茗 + */ + + /// + /// RPC异常 + /// + + public class RRQMRPCException : RRQMException + { + /// + /// + /// + public RRQMRPCException() : base() { } + + /// + /// + /// + /// + public RRQMRPCException(string message) : base(message) { } + + /// + /// + /// + /// + /// + public RRQMRPCException(string message, System.Exception inner) : base(message, inner) { } + + /// + /// + /// + /// + /// + protected RRQMRPCException(System.Runtime.Serialization.SerializationInfo info, + System.Runtime.Serialization.StreamingContext context) : base(info, context) { } + } +} \ No newline at end of file diff --git a/RRQMSocket.RPC/Global/Exceptions/RRQMRPCInvokeException.cs b/RRQMSocket.RPC/Global/Exceptions/RRQMRPCInvokeException.cs new file mode 100644 index 000000000..c0ef6b986 --- /dev/null +++ b/RRQMSocket.RPC/Global/Exceptions/RRQMRPCInvokeException.cs @@ -0,0 +1,48 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +using RRQMCore.Exceptions; + +namespace RRQMSocket.RPC +{ + /// + /// RPC调用异常 + /// + + public class RRQMRPCInvokeException : RRQMException + { + /// + /// + /// + public RRQMRPCInvokeException() : base() { } + + /// + /// + /// + /// + public RRQMRPCInvokeException(string message) : base(message) { } + + /// + /// + /// + /// + /// + public RRQMRPCInvokeException(string message, System.Exception inner) : base(message, inner) { } + + /// + /// + /// + /// + /// + protected RRQMRPCInvokeException(System.Runtime.Serialization.SerializationInfo info, + System.Runtime.Serialization.StreamingContext context) : base(info, context) { } + } +} \ No newline at end of file diff --git a/RRQMSocket.RPC/Global/Interface/IServerProvider.cs b/RRQMSocket.RPC/Global/Interface/IServerProvider.cs new file mode 100644 index 000000000..3c8c78e80 --- /dev/null +++ b/RRQMSocket.RPC/Global/Interface/IServerProvider.cs @@ -0,0 +1,55 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace RRQMSocket.RPC +{ + /// + /// RRQM的RPC服务接口 + /// + public interface IServerProvider + { + /// + /// 该服务所属的服务器 + /// + RPCService RPCService { get; set; } + + /// + /// RPC即将进入, + /// 若是想放弃本次执行,请抛出 + /// + /// + /// + /// + void RPCEnter(IRPCParser parser, MethodInvoker methodInvoker, MethodInstance methodInstance); + + /// + /// 执行RPC发生错误 + /// + /// + /// + /// + void RPCError(IRPCParser parser, MethodInvoker methodInvoker, MethodInstance methodInstance); + + /// + /// RPC方法执行完成 + /// + /// + /// + /// + void RPCLeave(IRPCParser parser, MethodInvoker methodInvoker, MethodInstance methodInstance); + } +} diff --git a/RRQMSocket.RPC/Global/Parser/IRPCParser.cs b/RRQMSocket.RPC/Global/Parser/IRPCParser.cs new file mode 100644 index 000000000..f142d63e3 --- /dev/null +++ b/RRQMSocket.RPC/Global/Parser/IRPCParser.cs @@ -0,0 +1,75 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +using System; + +namespace RRQMSocket.RPC +{ + /// + /// RPC解析器 + /// + public interface IRPCParser : IDisposable + { + /// + /// 获取函数映射图 + /// + MethodMap MethodMap { get; } + + /// + /// 包含此解析器的服务器实例 + /// + RPCService RPCService { get; } + + /// + /// 执行函数 + /// + Action RRQMExecuteMethod { get; } + + /// + /// 注册服务 + /// + /// + /// + void OnRegisterServer(IServerProvider provider, MethodInstance[] methodInstances); + + /// + /// 取消注册服务 + /// + /// + /// + void OnUnregisterServer(IServerProvider provider, MethodInstance[] methodInstances); + + /// + /// 结束调用 + /// + /// + /// + void OnEndInvoke(MethodInvoker methodInvoker, MethodInstance methodInstance); + + /// + /// 设置函数映射 + /// + /// + void SetMethodMap(MethodMap methodMap); + + /// + /// 设置服务 + /// + /// + void SetRPCService(RPCService service); + + /// + /// 设置执行函数 + /// + /// + void SetExecuteMethod(Action executeMethod); + } +} \ No newline at end of file diff --git a/RRQMSocket.RPC/Global/Service/RPCService.cs b/RRQMSocket.RPC/Global/Service/RPCService.cs new file mode 100644 index 000000000..214f45310 --- /dev/null +++ b/RRQMSocket.RPC/Global/Service/RPCService.cs @@ -0,0 +1,336 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +using RRQMCore; +using RRQMCore.Exceptions; +using RRQMSocket.RPC.RRQMRPC; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Threading.Tasks; + +namespace RRQMSocket.RPC +{ + /// + /// RPC服务器类 + /// + public class RPCService : IDisposable + { + /// + /// 构造函数 + /// + public RPCService() + { + this.ServerProviders = new ServerProviderCollection(); + this.RPCParsers = new RPCParserCollection(); + this.MethodMap = new MethodMap(); + } + + /// + /// 获取函数映射图实例 + /// + public MethodMap MethodMap { get; private set; } + + /// + /// 获取RPC解析器集合 + /// + public RPCParserCollection RPCParsers { get; private set; } + + /// + /// 服务实例集合 + /// + public ServerProviderCollection ServerProviders { get; private set; } + + /// + /// 添加RPC解析器 + /// + /// 名称 + /// 解析器实例 + public void AddRPCParser(string key, IRPCParser parser) + { + this.RPCParsers.Add(key, parser); + parser.SetRPCService(this); + parser.SetExecuteMethod(PreviewExecuteMethod); + parser.SetMethodMap(this.MethodMap); + } + + /// + /// 添加RPC解析器 + /// + /// 名称 + /// 解析器实例 + /// 是否应用已注册服务 + public void AddRPCParser(string key, IRPCParser parser, bool applyServer) + { + this.RPCParsers.Add(key, parser); + parser.SetRPCService(this); + parser.SetExecuteMethod(PreviewExecuteMethod); + parser.SetMethodMap(this.MethodMap); + + if (applyServer) + { + Dictionary> pairs = new Dictionary>(); + + MethodInstance[] instances = this.MethodMap.GetAllMethodInstances(); + + foreach (var item in instances) + { + if (!pairs.ContainsKey(item.Provider)) + { + pairs.Add(item.Provider, new List()); + } + + pairs[item.Provider].Add(item); + } + foreach (var item in pairs.Keys) + { + parser.OnRegisterServer(item, pairs[item].ToArray()); + } + } + } + + /// + /// 释放资源 + /// + public void Dispose() + { + foreach (var item in this.RPCParsers) + { + item.Dispose(); + } + this.RPCParsers = null; + } + + /// + /// 注册所有服务 + /// + /// 返回搜索到的服务数 + public int RegisterAllServer() + { + Type[] types = (AppDomain.CurrentDomain.GetAssemblies() + .SelectMany(s => s.GetTypes()).Where(p => typeof(ServerProvider).IsAssignableFrom(p) && p.IsAbstract == false)).ToArray(); + + foreach (Type type in types) + { + ServerProvider serverProvider = Activator.CreateInstance(type) as ServerProvider; + RegisterServer(serverProvider); + } + return types.Length; + } + + /// + /// 注册服务 + /// + /// + /// 返回T实例 + public IServerProvider RegisterServer() where T : IServerProvider + { + IServerProvider serverProvider = (IServerProvider)Activator.CreateInstance(typeof(T)); + this.RegisterServer(serverProvider); + return serverProvider; + } + + /// + /// 注册服务 + /// + /// + /// + public IServerProvider RegisterServer(Type providerType) + { + if (!typeof(IServerProvider).IsAssignableFrom(providerType)) + { + throw new RRQMRPCException("类型不相符"); + } + IServerProvider serverProvider = (IServerProvider)Activator.CreateInstance(providerType); + this.RegisterServer(serverProvider); + return serverProvider; + } + + /// + /// 注册服务 + /// + /// + public void RegisterServer(IServerProvider serverProvider) + { + serverProvider.RPCService = this; + this.ServerProviders.Add(serverProvider); + + if (this.RPCParsers.Count == 0) + { + throw new RRQMRPCException("请至少添加一种RPC解析器"); + } + MethodInstance[] methodInstances = Tools.GetMethodInstances(serverProvider, true); + + foreach (var item in methodInstances) + { + this.MethodMap.Add(item); + } + foreach (var parser in this.RPCParsers) + { + parser.OnRegisterServer(serverProvider, methodInstances); + } + } + + /// + /// 移除RPC解析器 + /// + /// + /// + public void RemoveRPCParser(string key, out IRPCParser parser) + { + if (!this.RPCParsers.TryRemove(key, out parser)) + { + throw new RRQMException("没有找到该解析器"); + } + } + + /// + /// 设置服务方法可用性 + /// + /// 方法名 + /// 可用性 + /// + public void SetMethodEnable(int methodToken, bool enable) + { + if (this.MethodMap.TryGet(methodToken, out MethodInstance methodInstance)) + { + methodInstance.IsEnable = enable; + } + else + { + throw new RRQMRPCException("未找到该方法"); + } + } + + /// + /// 获取解析器 + /// + /// + /// + /// + public bool TryGetRPCParser(string parserKey, out IRPCParser parser) + { + return this.RPCParsers.TryGetRPCParser(parserKey, out parser); + } + + /// + /// 移除注册服务 + /// + /// + /// + public int UnregisterServer(IServerProvider provider) + { + return this.UnregisterServer(provider.GetType()); + } + + /// + /// 移除注册服务 + /// + /// + /// + public int UnregisterServer(Type providerType) + { + if (!typeof(IServerProvider).IsAssignableFrom(providerType)) + { + throw new RRQMRPCException("类型不相符"); + } + this.ServerProviders.Remove(providerType); + if (this.MethodMap.RemoveServer(providerType, out IServerProvider serverProvider, out MethodInstance[] instances)) + { + foreach (var parser in this.RPCParsers) + { + parser.OnUnregisterServer(serverProvider, instances); + } + + return instances.Length; + } + return 0; + } + + /// + /// 移除注册服务 + /// + /// + /// + public int UnregisterServer() where T : ServerProvider + { + return this.UnregisterServer(typeof(T)); + } + + private void ExecuteMethod(bool isAsync, IRPCParser parser, MethodInvoker methodInvoker, MethodInstance methodInstance) + { + if (methodInvoker.Status == InvokeStatus.Ready && methodInstance != null) + { + try + { + methodInstance.Provider.RPCEnter(parser, methodInvoker, methodInstance); + if (isAsync) + { + dynamic task = methodInstance.Method.Invoke(methodInstance.Provider, methodInvoker.Parameters); + task.Wait(); + if (methodInstance.ReturnType != null) + { + methodInvoker.ReturnParameter = task.Result; + } + } + else + { + methodInvoker.ReturnParameter = methodInstance.Method.Invoke(methodInstance.Provider, methodInvoker.Parameters); + } + methodInstance.Provider.RPCLeave(parser, methodInvoker, methodInstance); + methodInvoker.Status = InvokeStatus.Success; + } + catch (RRQMAbandonRPCException e) + { + methodInvoker.Status = InvokeStatus.Abort; + methodInvoker.StatusMessage = "函数被阻止执行,信息:" + e.Message; + } + catch (TargetInvocationException e) + { + methodInvoker.Status = InvokeStatus.InvocationException; + if (e.InnerException != null) + { + methodInvoker.StatusMessage = "函数内部发生异常,信息:" + e.InnerException.Message; + } + else + { + methodInvoker.StatusMessage = "函数内部发生异常,信息:未知"; + } + methodInstance.Provider.RPCError(parser, methodInvoker, methodInstance); + } + catch (Exception e) + { + methodInvoker.Status = InvokeStatus.Exception; + methodInvoker.StatusMessage = e.Message; + methodInstance.Provider.RPCError(parser, methodInvoker, methodInstance); + } + } + + parser.OnEndInvoke(methodInvoker, methodInstance); + } + + private void PreviewExecuteMethod(IRPCParser parser, MethodInvoker methodInvoker, MethodInstance methodInstance) + { + if (methodInstance != null && methodInstance.Async) + { + Task.Run(() => + { + ExecuteMethod(true, parser, methodInvoker, methodInstance); + }); + } + else + { + ExecuteMethod(false, parser, methodInvoker, methodInstance); + } + } + } +} \ No newline at end of file diff --git a/RRQMSocket.RPC/LICENSE b/RRQMSocket.RPC/LICENSE new file mode 100644 index 000000000..5a9bb6217 --- /dev/null +++ b/RRQMSocket.RPC/LICENSE @@ -0,0 +1,201 @@ +Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license procotol you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/RRQMSocket.RPC/RRQM.ico b/RRQMSocket.RPC/RRQM.ico new file mode 100644 index 000000000..6465fb16e Binary files /dev/null and b/RRQMSocket.RPC/RRQM.ico differ diff --git a/RRQMSocket.RPC/RRQM.png b/RRQMSocket.RPC/RRQM.png new file mode 100644 index 000000000..1fc567ad9 Binary files /dev/null and b/RRQMSocket.RPC/RRQM.png differ diff --git a/RRQMSocket.RPC/RRQMRPC/Attribute/RRQMRPCAttribute.cs b/RRQMSocket.RPC/RRQMRPC/Attribute/RRQMRPCAttribute.cs new file mode 100644 index 000000000..7374ccec2 --- /dev/null +++ b/RRQMSocket.RPC/RRQMRPC/Attribute/RRQMRPCAttribute.cs @@ -0,0 +1,42 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +using System; + +namespace RRQMSocket.RPC.RRQMRPC +{ + /// + /// RPC方法标记属性类 + /// + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Event | AttributeTargets.Property, AllowMultiple = false, Inherited = false)] + public sealed class RRQMRPCAttribute : RPCAttribute + { + /// + /// 构造函数 + /// + public RRQMRPCAttribute() + { + } + + /// + /// 构造函数 + /// + /// 指定键 + public RRQMRPCAttribute(string memberKey) + { + } + + /// + /// 注册键 + /// + public string MemberKey { get; private set; } + } +} \ No newline at end of file diff --git a/RRQMSocket.RPC/RRQMRPC/Attribute/RRQMRPCCallBackMethodAttribute.cs b/RRQMSocket.RPC/RRQMRPC/Attribute/RRQMRPCCallBackMethodAttribute.cs new file mode 100644 index 000000000..9a2d60b46 --- /dev/null +++ b/RRQMSocket.RPC/RRQMRPC/Attribute/RRQMRPCCallBackMethodAttribute.cs @@ -0,0 +1,36 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +using System; + +namespace RRQMSocket.RPC.RRQMRPC +{ + /// + /// RPC方法标记属性类 + /// + [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)] + public sealed class RRQMRPCCallBackMethodAttribute : RPCAttribute + { + /// + /// 构造函数 + /// + /// 指定函数键 + public RRQMRPCCallBackMethodAttribute(int methodToken) + { + this.MethodToken = methodToken; + } + + /// + /// 注册键 + /// + public int MethodToken { get; private set; } + } +} \ No newline at end of file diff --git a/RRQMSocket.RPC/RRQMRPC/Attribute/RRQMRPCMemberAttribute.cs b/RRQMSocket.RPC/RRQMRPC/Attribute/RRQMRPCMemberAttribute.cs new file mode 100644 index 000000000..3cd017626 --- /dev/null +++ b/RRQMSocket.RPC/RRQMRPC/Attribute/RRQMRPCMemberAttribute.cs @@ -0,0 +1,23 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +using System; + +namespace RRQMSocket.RPC.RRQMRPC +{ + /// + /// 标识参数类 + /// + [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)] + public class RRQMRPCMemberAttribute : RPCAttribute + { + } +} \ No newline at end of file diff --git a/RRQMSocket.RPC/RRQMRPC/Common/CellCode.cs b/RRQMSocket.RPC/RRQMRPC/Common/CellCode.cs new file mode 100644 index 000000000..d8b7c6df3 --- /dev/null +++ b/RRQMSocket.RPC/RRQMRPC/Common/CellCode.cs @@ -0,0 +1,36 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ + +namespace RRQMSocket.RPC.RRQMRPC +{ + /// + /// 生成的单元代码 + /// + + public class CellCode + { + /// + /// 类名 + /// + public string Name { get; set; } + + /// + /// 代码本体 + /// + public string Code { get; set; } + + /// + /// 代码类型 + /// + public CodeType CodeType { get; set; } + } +} \ No newline at end of file diff --git a/RRQMSocket.RPC/RRQMRPC/Common/CodeGenerator.cs b/RRQMSocket.RPC/RRQMRPC/Common/CodeGenerator.cs new file mode 100644 index 000000000..c95030922 --- /dev/null +++ b/RRQMSocket.RPC/RRQMRPC/Common/CodeGenerator.cs @@ -0,0 +1,502 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Text; + +namespace RRQMSocket.RPC.RRQMRPC +{ + /// + /// 代码生成器 + /// + public class CodeGenerator + { + internal CodeGenerator() + { + codeString = new StringBuilder(); + } + + internal static string GetAssemblyInfo(string assemblyName, string version) + { + CodeGenerator codeMap = new CodeGenerator(); + codeMap.AppendAssemblyInfo(assemblyName, version); + return codeMap.codeString.ToString(); + } + + private StringBuilder codeString; + + internal MethodInfo[] Methods { get; set; } + internal string ClassName { get; set; } + internal static string Namespace { get; set; } + internal static PropertyCodeGenerator PropertyCode { get; set; } + + internal string GetCode() + { + codeString.AppendLine("using System;"); + codeString.AppendLine("using RRQMSocket.RPC;"); + codeString.AppendLine("using RRQMSocket.RPC.RRQMRPC;"); + codeString.AppendLine("using RRQMCore.Exceptions;"); + codeString.AppendLine("using System.Collections.Generic;"); + codeString.AppendLine("using System.Diagnostics;"); + codeString.AppendLine("using System.Text;"); + codeString.AppendLine("using System.Threading.Tasks;"); + codeString.AppendLine(string.Format("namespace {0}", Namespace)); + codeString.AppendLine("{"); + this.GetInterface("I" + this.ClassName); + this.GetClass(this.ClassName); + codeString.AppendLine("}");//空间结束 + + return codeString.ToString(); + } + + private void GetInterface(string interfaceName) + { + codeString.AppendLine(string.Format("public interface {0}", interfaceName));//类开始 + codeString.AppendLine("{"); + codeString.AppendLine("IRpcClient Client{get;}"); + AppendInterfaceMethods(); + codeString.AppendLine("}");//类结束 + } + + private void GetClass(string className) + { + codeString.AppendLine(string.Format("public class {0} :I{0}", className));//类开始 + codeString.AppendLine("{"); + codeString.AppendLine($"public {className}(IRpcClient client)"); + codeString.AppendLine("{"); + codeString.AppendLine("this.Client=client;"); + codeString.AppendLine("}"); + AppendProperties(); + AppendMethods(); + codeString.AppendLine("}");//类结束 + } + + private void AppendAssemblyInfo(string assemblyName, string version) + { + codeString.AppendLine("using System.Reflection;"); + codeString.AppendLine("using System.Runtime.CompilerServices;"); + codeString.AppendLine("using System.Runtime.InteropServices;"); + codeString.AppendLine("[assembly: AssemblyTitle(\"RRQMRPC\")]"); + codeString.AppendLine("[assembly: AssemblyProduct(\"RRQMRPC\")]"); + codeString.AppendLine("[assembly: AssemblyCopyright(\"Copyright © 2020 若汝棋茗\")]"); + codeString.AppendLine("[assembly: ComVisible(false)]"); + + codeString.AppendLine(string.Format("[assembly: AssemblyVersion(\"{0}\")]", version)); + codeString.AppendLine(string.Format("[assembly: AssemblyFileVersion(\"{0}\")]", version.ToString())); + } + + private void AppendProperties() + { + codeString.AppendLine("public IRpcClient Client{get;private set; }"); + } + + internal string GetName(Type type) + { + return PropertyCode.GetTypeFullName(type); + } + + private void AppendMethods() + { + if (Methods != null) + { + foreach (MethodInfo method in Methods) + { + bool isReturn; + bool isOut = false; + bool isRef = false; + string methodName = method.GetCustomAttribute().MemberKey == null ? method.Name : method.GetCustomAttribute().MemberKey; + + if (method.ReturnType.FullName == "System.Void" || method.ReturnType.FullName == "System.Threading.Tasks.Task") + { + isReturn = false; + codeString.Append(string.Format("public void {0} ", methodName)); + } + else + { + isReturn = true; + codeString.Append(string.Format("public {0} {1} ", this.GetName(method.ReturnType), methodName)); + } + codeString.Append("(");//方法参数 + + ParameterInfo[] parameters = method.GetParameters(); + + for (int i = 0; i < parameters.Length; i++) + { + if (i > 0) + { + codeString.Append(","); + } + if (parameters[i].ParameterType.Name.Contains("&")) + { + if (parameters[i].IsOut) + { + isOut = true; + codeString.Append(string.Format("out {0} {1}", this.GetName(parameters[i].ParameterType), parameters[i].Name)); + } + else + { + isRef = true; + codeString.Append(string.Format("ref {0} {1}", this.GetName(parameters[i].ParameterType), parameters[i].Name)); + } + } + else + { + codeString.Append(string.Format("{0} {1}", this.GetName(parameters[i].ParameterType), parameters[i].Name)); + } + + if (parameters[i].HasDefaultValue) + { + object defaultValue = parameters[i].DefaultValue; + if (defaultValue == null) + { + codeString.Append(string.Format("=null")); + } + else if (defaultValue.ToString() == string.Empty) + { + codeString.Append(string.Format("=\"\"")); + } + else if (defaultValue.GetType() == typeof(string)) + { + codeString.Append(string.Format("=\"{0}\"", defaultValue)); + } + else if (typeof(ValueType).IsAssignableFrom(defaultValue.GetType())) + { + codeString.Append(string.Format("={0}", defaultValue)); + } + } + } + if (parameters.Length > 0) + { + codeString.Append(","); + } + codeString.AppendLine("InvokeOption invokeOption = null)"); + + codeString.AppendLine("{");//方法开始 + + codeString.AppendLine("if(Client==null)"); + codeString.AppendLine("{"); + codeString.AppendLine("throw new RRQMRPCException(\"IRPCClient为空,请先初始化或者进行赋值\");"); + codeString.AppendLine("}"); + + codeString.Append($"object[] parameters = new object[]"); + codeString.Append("{"); + foreach (ParameterInfo parameter in parameters) + { + if (parameter.ParameterType.Name.Contains("&") && parameter.IsOut) + { + codeString.Append($"default({this.GetName(parameter.ParameterType)})"); + } + else + { + codeString.Append(parameter.Name); + } + if (parameter != parameters[parameters.Length - 1]) + { + codeString.Append(","); + } + } + codeString.AppendLine("};"); + + if (isOut || isRef) + { + codeString.Append($"Type[] types = new Type[]"); + codeString.Append("{"); + foreach (ParameterInfo parameter in parameters) + { + codeString.Append($"typeof({this.GetName(parameter.ParameterType)})"); + if (parameter != parameters[parameters.Length - 1]) + { + codeString.Append(","); + } + } + codeString.AppendLine("};"); + } + + if (isReturn) + { + if (isOut || isRef) + { + codeString.Append(string.Format("{0} returnData=Client.Invoke<{0}>", this.GetName(method.ReturnType))); + codeString.Append("("); + codeString.Append(string.Format("\"{0}\"", methodName)); + codeString.AppendLine(",invokeOption,ref parameters,types);"); + } + else + { + codeString.Append(string.Format("{0} returnData=Client.Invoke<{0}>", this.GetName(method.ReturnType))); + codeString.Append("("); + codeString.Append(string.Format("\"{0}\"", methodName)); + codeString.AppendLine(",invokeOption, parameters);"); + } + } + else + { + if (isOut || isRef) + { + codeString.Append("Client.Invoke("); + codeString.Append(string.Format("\"{0}\"", methodName)); + codeString.AppendLine(",invokeOption,ref parameters,types);"); + } + else + { + codeString.Append("Client.Invoke("); + codeString.Append(string.Format("\"{0}\"", methodName)); + codeString.AppendLine(",invokeOption, parameters);"); + } + } + if (isOut || isRef) + { + codeString.AppendLine("if(parameters!=null)"); + codeString.AppendLine("{"); + for (int i = 0; i < parameters.Length; i++) + { + codeString.AppendLine(string.Format("{0}=({1})parameters[{2}];", parameters[i].Name, this.GetName(parameters[i].ParameterType), i)); + } + codeString.AppendLine("}"); + if (isOut) + { + codeString.AppendLine("else"); + codeString.AppendLine("{"); + for (int i = 0; i < parameters.Length; i++) + { + if (parameters[i].IsOut) + { + codeString.AppendLine(string.Format("{0}=default({1});", parameters[i].Name, this.GetName(parameters[i].ParameterType))); + } + } + codeString.AppendLine("}"); + } + } + + if (isReturn) + { + codeString.AppendLine("return returnData;"); + } + + codeString.AppendLine("}"); + + //以下生成异步 + if (!isOut && !isRef)//没有out或者ref + { + if (method.ReturnType.FullName == "System.Void" || method.ReturnType.FullName == "System.Threading.Tasks.Task") + { + isReturn = false; + codeString.Append(string.Format("public async void {0} ", methodName + "Async")); + } + else + { + isReturn = true; + codeString.Append(string.Format("public async Task<{0}> {1} ", this.GetName(method.ReturnType), methodName + "Async")); + } + + codeString.Append("(");//方法参数 + + for (int i = 0; i < parameters.Length; i++) + { + if (i > 0) + { + codeString.Append(","); + } + + codeString.Append(string.Format("{0} {1}", this.GetName(parameters[i].ParameterType), parameters[i].Name)); + if (parameters[i].DefaultValue != System.DBNull.Value) + { + object defaultValue = parameters[i].DefaultValue; + if (defaultValue == null) + { + codeString.Append(string.Format("=null")); + } + else if (defaultValue.ToString() == string.Empty) + { + codeString.Append(string.Format("=\"\"")); + } + else if (defaultValue.GetType() == typeof(string)) + { + codeString.Append(string.Format("=\"{0}\"", defaultValue)); + } + else if (typeof(ValueType).IsAssignableFrom(defaultValue.GetType())) + { + codeString.Append(string.Format("={0}", defaultValue)); + } + } + } + + if (parameters.Length > 0) + { + codeString.Append(","); + } + codeString.AppendLine("InvokeOption invokeOption = null)"); + codeString.AppendLine("{");//方法开始 + codeString.AppendLine("if(Client==null)"); + codeString.AppendLine("{"); + codeString.AppendLine("throw new RRQMRPCException(\"RPCClient为空,请先初始化或者进行赋值\");"); + codeString.AppendLine("}"); + if (isReturn) + { + codeString.AppendLine("return await Task.Run(() =>{"); + codeString.Append(string.Format("return {0}(", methodName)); + } + else + { + codeString.AppendLine("await Task.Run(() =>{"); + codeString.Append(string.Format("{0}(", methodName)); + } + + for (int i = 0; i < parameters.Length; i++) + { + if (i > 0) + { + codeString.Append(","); + } + + codeString.Append(string.Format("{0}", parameters[i].Name)); + } + if (parameters.Length > 0) + { + codeString.Append(","); + } + codeString.Append("invokeOption);"); + codeString.AppendLine("});"); + codeString.AppendLine("}"); + } + } + } + } + + private void AppendInterfaceMethods() + { + if (Methods != null) + { + foreach (MethodInfo method in Methods) + { + bool isOut = false; + bool isRef = false; + string methodName = method.GetCustomAttribute().MemberKey == null ? method.Name : method.GetCustomAttribute().MemberKey; + + if (method.ReturnType.FullName == "System.Void" || method.ReturnType.FullName == "System.Threading.Tasks.Task") + { + codeString.Append(string.Format(" void {0} ", methodName)); + } + else + { + codeString.Append(string.Format(" {0} {1} ", this.GetName(method.ReturnType), methodName)); + } + codeString.Append("(");//方法参数 + + ParameterInfo[] parameters = method.GetParameters(); + + for (int i = 0; i < parameters.Length; i++) + { + if (i > 0) + { + codeString.Append(","); + } + if (parameters[i].ParameterType.Name.Contains("&")) + { + if (parameters[i].IsOut) + { + isOut = true; + codeString.Append(string.Format("out {0} {1}", this.GetName(parameters[i].ParameterType), parameters[i].Name)); + } + else + { + isRef = true; + codeString.Append(string.Format("ref {0} {1}", this.GetName(parameters[i].ParameterType), parameters[i].Name)); + } + } + else + { + codeString.Append(string.Format("{0} {1}", this.GetName(parameters[i].ParameterType), parameters[i].Name)); + } + + if (parameters[i].HasDefaultValue) + { + object defaultValue = parameters[i].DefaultValue; + if (defaultValue == null) + { + codeString.Append(string.Format("=null")); + } + else if (defaultValue.ToString() == string.Empty) + { + codeString.Append(string.Format("=\"\"")); + } + else if (defaultValue.GetType() == typeof(string)) + { + codeString.Append(string.Format("=\"{0}\"", defaultValue)); + } + else if (typeof(ValueType).IsAssignableFrom(defaultValue.GetType())) + { + codeString.Append(string.Format("={0}", defaultValue)); + } + } + } + if (parameters.Length > 0) + { + codeString.Append(","); + } + codeString.AppendLine("InvokeOption invokeOption = null);"); + + if (!isOut && !isRef)//没有out或者ref + { + if (method.ReturnType.FullName == "System.Void" || method.ReturnType.FullName == "System.Threading.Tasks.Task") + { + codeString.Append(string.Format("void {0} ", methodName + "Async")); + } + else + { + codeString.Append(string.Format("Task<{0}> {1} ", this.GetName(method.ReturnType), methodName + "Async")); + } + + codeString.Append("(");//方法参数 + + for (int i = 0; i < parameters.Length; i++) + { + if (i > 0) + { + codeString.Append(","); + } + + codeString.Append(string.Format("{0} {1}", this.GetName(parameters[i].ParameterType), parameters[i].Name)); + if (parameters[i].DefaultValue != System.DBNull.Value) + { + object defaultValue = parameters[i].DefaultValue; + if (defaultValue == null) + { + codeString.Append(string.Format("=null")); + } + else if (defaultValue.ToString() == string.Empty) + { + codeString.Append(string.Format("=\"\"")); + } + else if (defaultValue.GetType() == typeof(string)) + { + codeString.Append(string.Format("=\"{0}\"", defaultValue)); + } + else if (typeof(ValueType).IsAssignableFrom(defaultValue.GetType())) + { + codeString.Append(string.Format("={0}", defaultValue)); + } + } + } + + if (parameters.Length > 0) + { + codeString.Append(","); + } + codeString.AppendLine("InvokeOption invokeOption = null);"); + } + } + } + } + } +} \ No newline at end of file diff --git a/RRQMSocket.RPC/RRQMRPC/Common/CodeType.cs b/RRQMSocket.RPC/RRQMRPC/Common/CodeType.cs new file mode 100644 index 000000000..3cd6d332d --- /dev/null +++ b/RRQMSocket.RPC/RRQMRPC/Common/CodeType.cs @@ -0,0 +1,31 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ + +namespace RRQMSocket.RPC.RRQMRPC +{ + /// + /// 代码类型 + /// + + public enum CodeType + { + /// + /// 类代码 + /// + ClassArgs, + + /// + /// 服务代码 + /// + Service + } +} \ No newline at end of file diff --git a/RRQMSocket.RPC/RRQMRPC/Common/FeedbackType.cs b/RRQMSocket.RPC/RRQMRPC/Common/FeedbackType.cs new file mode 100644 index 000000000..7d43447f1 --- /dev/null +++ b/RRQMSocket.RPC/RRQMRPC/Common/FeedbackType.cs @@ -0,0 +1,35 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ + +namespace RRQMSocket.RPC.RRQMRPC +{ + /// + /// 反馈类型 + /// + public enum FeedbackType : byte + { + /// + /// 仅发送 + /// + OnlySend, + + /// + /// 等待,直到发送抵达 + /// + WaitSend, + + /// + /// 等待,直到调用完成 + /// + WaitInvoke + } +} \ No newline at end of file diff --git a/RRQMSocket.RPC/RRQMRPC/Common/InvokeOption.cs b/RRQMSocket.RPC/RRQMRPC/Common/InvokeOption.cs new file mode 100644 index 000000000..6a61dc12b --- /dev/null +++ b/RRQMSocket.RPC/RRQMRPC/Common/InvokeOption.cs @@ -0,0 +1,77 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ + +namespace RRQMSocket.RPC.RRQMRPC +{ + /// + /// RPC调用设置 + /// + public class InvokeOption + { + private static InvokeOption onlySend; + + private static InvokeOption waitInvoke; + + private static InvokeOption waitSend; + + private int timeout = 5000; + + static InvokeOption() + { + onlySend = new InvokeOption(); + onlySend.FeedbackType = FeedbackType.OnlySend; + + waitSend = new InvokeOption(); + waitSend.FeedbackType = FeedbackType.WaitSend; + + waitInvoke = new InvokeOption(); + waitInvoke.FeedbackType = FeedbackType.WaitInvoke; + } + /// + /// 默认设置。 + /// Timeout=5000 ms + /// + public static InvokeOption OnlySend { get { return onlySend; } } + /// + /// 默认设置。 + /// Timeout=5000 ms + /// + public static InvokeOption WaitInvoke { get { return waitInvoke; } } + + /// + /// 默认设置。 + /// Timeout=5000 ms + /// + public static InvokeOption WaitSend { get { return waitSend; } } + /// + /// 调用反馈 + /// + public FeedbackType FeedbackType { get; set; } + + /// + /// 调用超时, + /// min=1000,默认5000 ms + /// + public int Timeout + { + get { return timeout; } + set + { + if (value < 1000) + { + value = 1000; + } + timeout = value; + } + } + } +} \ No newline at end of file diff --git a/RRQMSocket.RPC/RRQMRPC/Common/MethodItem.cs b/RRQMSocket.RPC/RRQMRPC/Common/MethodItem.cs new file mode 100644 index 000000000..2a61fb865 --- /dev/null +++ b/RRQMSocket.RPC/RRQMRPC/Common/MethodItem.cs @@ -0,0 +1,40 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ + +namespace RRQMSocket.RPC.RRQMRPC +{ + /// + /// 方法体 + /// + public class MethodItem + { + /// + /// 服务名称 + /// + public string ServerName { get; internal set; } + + /// + /// 方法唯一标识 + /// + public int MethodToken { get; internal set; } + + /// + /// 方法名 + /// + public string Method { get; internal set; } + + /// + /// 是否含有Out或Ref + /// + public bool IsOutOrRef { get; internal set; } + } +} \ No newline at end of file diff --git a/RRQMSocket.RPC/RRQMRPC/Common/MethodStore.cs b/RRQMSocket.RPC/RRQMRPC/Common/MethodStore.cs new file mode 100644 index 000000000..273730776 --- /dev/null +++ b/RRQMSocket.RPC/RRQMRPC/Common/MethodStore.cs @@ -0,0 +1,88 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace RRQMSocket.RPC.RRQMRPC +{ + /// + /// 函数仓库 + /// + public class MethodStore + { + internal MethodStore() + { + this.tokenToMethodItem = new Dictionary(); + this.methodKeyToMethodItem = new Dictionary(); + this.propertyDic = new Dictionary(); + this.genericTypeDic = new Dictionary(); + } + + private Dictionary tokenToMethodItem; + private Dictionary methodKeyToMethodItem; + internal Dictionary propertyDic; + internal Dictionary genericTypeDic; + + /// + /// 获取所有的方法 + /// + /// + public string[] GetMethods() + { + return methodKeyToMethodItem.Keys.ToArray(); + } + + /// + /// 添加 + /// + /// + internal void AddMethodItem(MethodItem methodItem) + { + tokenToMethodItem.Add(methodItem.MethodToken, methodItem); + methodKeyToMethodItem.Add(methodItem.Method, methodItem); + } + + /// + /// 添加 + /// + /// + internal void RemoveMethodItem(int methodToken) + { + if (tokenToMethodItem.TryGetValue(methodToken, out MethodItem methodItem)) + { + tokenToMethodItem.Remove(methodToken); + methodKeyToMethodItem.Remove(methodItem.Method); + } + } + + /// + /// 获取所有 + /// + /// + public List GetAllMethodItem() + { + return this.tokenToMethodItem.Values.ToList(); + } + + /// + /// 获取函数服务 + /// + /// + /// + /// + public bool TryGetMethodItem(string method, out MethodItem methodItem) + { + return methodKeyToMethodItem.TryGetValue(method, out methodItem); + } + } +} \ No newline at end of file diff --git a/RRQMSocket.RPC/RRQMRPC/Common/PropertyCodeGenerator.cs b/RRQMSocket.RPC/RRQMRPC/Common/PropertyCodeGenerator.cs new file mode 100644 index 000000000..9ddbe4ce6 --- /dev/null +++ b/RRQMSocket.RPC/RRQMRPC/Common/PropertyCodeGenerator.cs @@ -0,0 +1,324 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +using RRQMCore.Helper; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Threading.Tasks; + +namespace RRQMSocket.RPC.RRQMRPC +{ + /// + /// 代码辅助类 + /// + public class PropertyCodeGenerator + { + private static readonly string[] listType = { "List`1", "HashSet`1", "IList`1", "ISet`1", "ICollection`1", "IEnumerable`1" }; + + private static readonly string[] dicType = { "Dictionary`2", "IDictionary`2" }; + + private static readonly Type intType = typeof(int); + private static readonly Type byteType = typeof(byte); + private static readonly Type shortType = typeof(short); + private static readonly Type longType = typeof(long); + + /// + /// 构造函数 + /// + public PropertyCodeGenerator(string nameSpace, MethodStore methodStore) + { + codeString = new StringBuilder(); + this.nameSpace = nameSpace; + this.propertyDic = methodStore.propertyDic; + this.genericTypeDic = methodStore.genericTypeDic; + } + + /// + /// 获取属性代码 + /// + public string GetPropertyCode() + { + codeString.Clear(); + + codeString.AppendLine("using System;"); + codeString.AppendLine("using RRQMSocket.RPC;"); + codeString.AppendLine("using RRQMCore.Exceptions;"); + codeString.AppendLine("using System.Collections.Generic;"); + codeString.AppendLine("using System.Diagnostics;"); + codeString.AppendLine("using System.Text;"); + codeString.AppendLine("using System.Threading.Tasks;"); + + codeString.AppendLine(string.Format("namespace {0}", nameSpace)); + + codeString.AppendLine("{"); + foreach (var item in propertyDic.Values) + { + codeString.AppendLine(item); + } + codeString.AppendLine("}"); + return codeString.ToString(); + } + + private StringBuilder codeString; + private string nameSpace; + private Dictionary propertyDic; + private Dictionary genericTypeDic; + + internal void AddTypeString(Type type) + { + if (type.IsByRef) + { + type = type.GetRefOutType(); + } + + if (!type.IsPrimitive && type != typeof(string)) + { + if (type.IsArray) + { + AddTypeString(type.GetElementType()); + } + else if (type.IsGenericType) + { + Type[] types = type.GetGenericArguments(); + foreach (Type itemType in types) + { + AddTypeString(itemType); + } + + if (listType.Contains(type.Name)) + { + string typeInnerString = this.GetTypeFullName(types[0]); + string typeString = $"System.Collections.Generic.{type.Name.Replace("`1", string.Empty)}<{typeInnerString}>"; + if (!genericTypeDic.ContainsKey(type)) + { + genericTypeDic.Add(type, typeString); + } + } + else if (dicType.Contains(type.Name)) + { + string keyString = this.GetTypeFullName(types[0]); + string valueString = this.GetTypeFullName(types[1]); + string typeString = $"System.Collections.Generic.{type.Name.Replace("`2", string.Empty)}<{keyString},{valueString}>"; + if (!genericTypeDic.ContainsKey(type)) + { + genericTypeDic.Add(type, typeString); + } + } + } + else if (type.IsInterface || type.IsAbstract) + { + throw new RRQMRPCException("服务参数类型不允许接口或抽象类"); + } + else if (type.IsEnum) + { + Type baseType = Enum.GetUnderlyingType(type); + StringBuilder stringBuilder = new StringBuilder(); + if (baseType == byteType) + { + stringBuilder.AppendLine($"public enum {type.Name}:byte"); + stringBuilder.AppendLine("{"); + Array array = Enum.GetValues(type); + foreach (object item in array) + { + string enumString = item.ToString(); + stringBuilder.AppendLine($"{enumString}={(byte)item},"); + } + } + else if (baseType == shortType) + { + stringBuilder.AppendLine($"public enum {type.Name}:short"); + stringBuilder.AppendLine("{"); + Array array = Enum.GetValues(type); + foreach (object item in array) + { + string enumString = item.ToString(); + stringBuilder.AppendLine($"{enumString}={(short)item},"); + } + } + else if (baseType == intType) + { + stringBuilder.AppendLine($"public enum {type.Name}:int"); + stringBuilder.AppendLine("{"); + Array array = Enum.GetValues(type); + foreach (object item in array) + { + string enumString = item.ToString(); + stringBuilder.AppendLine($"{enumString}={(int)item},"); + } + } + else if (baseType == longType) + { + stringBuilder.AppendLine($"public enum {type.Name}:long"); + stringBuilder.AppendLine("{"); + Array array = Enum.GetValues(type); + foreach (object item in array) + { + string enumString = item.ToString(); + stringBuilder.AppendLine($"{enumString}={(long)item},"); + } + } + + stringBuilder.AppendLine("}"); + if (!propertyDic.ContainsKey(type)) + { + propertyDic.Add(type, stringBuilder.ToString()); + } + } + else if (type.IsClass) + { + if (type.Assembly == ServerProvider.DefaultAssembly || type.GetCustomAttribute() != null) + { + StringBuilder stringBuilder = new StringBuilder(); + + stringBuilder.AppendLine(""); + stringBuilder.AppendLine($"public class {type.Name}"); + if (type.BaseType != typeof(object)) + { + AddTypeString(type.BaseType); + if (type.BaseType.IsGenericType) + { + Type[] types = type.BaseType.GetGenericArguments(); + foreach (Type itemType in types) + { + AddTypeString(itemType); + } + if (listType.Contains(type.BaseType.Name)) + { + string typeString = this.GetTypeFullName(types[0]); + stringBuilder.Append($":{type.BaseType.Name.Replace("`1", string.Empty)}<{typeString}>"); + } + else if (dicType.Contains(type.BaseType.Name)) + { + string keyString = this.GetTypeFullName(types[0]); + string valueString = this.GetTypeFullName(types[1]); + stringBuilder.Append($": {type.BaseType.Name.Replace("`2", string.Empty)}<{keyString},{valueString}>"); + } + } + else if (type.BaseType.IsClass) + { + stringBuilder.AppendLine($": {this.GetTypeFullName(type.BaseType)}"); + } + } + stringBuilder.AppendLine("{"); + PropertyInfo[] propertyInfos = type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.DeclaredOnly | BindingFlags.GetProperty | BindingFlags.SetProperty); + + foreach (PropertyInfo itemProperty in propertyInfos) + { + AddTypeString(itemProperty.PropertyType); + if (propertyDic.ContainsKey(itemProperty.PropertyType)) + { + stringBuilder.Append($"public {itemProperty.PropertyType.Name} {itemProperty.Name}"); + } + else if (itemProperty.PropertyType.IsGenericType) + { + Type[] types = itemProperty.PropertyType.GetGenericArguments(); + foreach (Type itemType in types) + { + AddTypeString(itemType); + } + + if (listType.Contains(itemProperty.PropertyType.Name)) + { + string typeString = this.GetTypeFullName(types[0]); + stringBuilder.Append($"public {itemProperty.PropertyType.Name.Replace("`1", string.Empty)}<{typeString}> {itemProperty.Name}"); + } + else if (dicType.Contains(itemProperty.PropertyType.Name)) + { + string keyString = this.GetTypeFullName(types[0]); + string valueString = this.GetTypeFullName(types[1]); + stringBuilder.Append($"public {itemProperty.PropertyType.Name.Replace("`2", string.Empty)}<{keyString},{valueString}> {itemProperty.Name}"); + } + } + else + { + AddTypeString(itemProperty.PropertyType); + stringBuilder.Append($"public {itemProperty.PropertyType.FullName} {itemProperty.Name}"); + } + + stringBuilder.AppendLine("{get;set;}"); + } + + stringBuilder.AppendLine("}"); + + if (!propertyDic.ContainsKey(type)) + { + propertyDic.Add(type, stringBuilder.ToString()); + } + } + } + } + } + + /// + /// 获取类型全名 + /// + /// + /// + public string GetTypeFullName(Type type) + { + if (type.FullName == null) + { + return type.Name.Replace("&", string.Empty); + } + else if (type == typeof(void)) + { + return null; + } + else if (typeof(Task).IsAssignableFrom(type)) + { + Type[] ts = type.GetGenericArguments(); + if (ts.Length == 1) + { + return ts[0].Name; + } + else + { + return type.Name; + } + } + else if (type.IsArray) + { + Type elementType = type.GetElementType(); + return this.GetTypeFullName(elementType) + "[]"; + } + + if (type.IsByRef) + { + string typeName = type.FullName.Replace("&", string.Empty); + type = Type.GetType(typeName); + if (type == null && ServerProvider.DefaultAssembly != null) + { + type = ServerProvider.DefaultAssembly.GetType(typeName); + } + } + + if (type.IsPrimitive || type == typeof(string)) + { + return type.FullName; + } + else if (listType.Contains(type.Name) || dicType.Contains(type.Name)) + { + return genericTypeDic[type]; + } + else if (propertyDic.ContainsKey(type)) + { + return this.nameSpace + "." + type.Name; + } + else + { + return type.FullName; + } + } + } +} \ No newline at end of file diff --git a/RRQMSocket.RPC/RRQMRPC/Common/RpcCompiler.cs b/RRQMSocket.RPC/RRQMRPC/Common/RpcCompiler.cs new file mode 100644 index 000000000..f84df0bb4 --- /dev/null +++ b/RRQMSocket.RPC/RRQMRPC/Common/RpcCompiler.cs @@ -0,0 +1,103 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +#if NET45_OR_GREATER + +using Microsoft.CSharp; +using RRQMCore.Log; +using System; +using System.CodeDom.Compiler; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace RRQMSocket.RPC.RRQMRPC +{ + /// + /// RPC编译 + /// + public static class RpcCompiler + { + private static List RefStrings = new List(); + + /// + /// 添加编译引用 + /// + /// + public static void AddRef(string refPath) + { + if (!RefStrings.Contains(refPath)) + { + RefStrings.Add(refPath); + } + } + + /// + /// 添加编译引用 + /// + /// + public static void AddRef(Type type) + { + AddRef(type.Assembly.FullName); + } + + /// + /// 清除引用 + /// + public static void ClearRef() + { + RefStrings.Clear(); + } + + /// + /// 编译代码 + /// + /// + /// + /// + public static void CompileCode(string fullPath, string[] codes) + { + CSharpCodeProvider codeProvider = new CSharpCodeProvider(); + CompilerParameters compilerParameters = new CompilerParameters(); + + compilerParameters.OutputAssembly = fullPath; + + compilerParameters.GenerateExecutable = false; + compilerParameters.GenerateInMemory = false; + compilerParameters.ReferencedAssemblies.Add("System.dll"); + compilerParameters.ReferencedAssemblies.Add("mscorlib.dll"); + compilerParameters.ReferencedAssemblies.Add(typeof(ILog).Assembly.Location); + compilerParameters.ReferencedAssemblies.Add(typeof(RPCService).Assembly.Location); + + foreach (var item in RefStrings) + { + compilerParameters.ReferencedAssemblies.Add(item); + } + + CompilerResults cr = codeProvider.CompileAssemblyFromSource(compilerParameters, codes); + if (cr.Errors.HasErrors) + { + StringBuilder stringBuilder = new StringBuilder(); + + foreach (CompilerError err in cr.Errors) + { + stringBuilder.AppendLine(err.ErrorText); + } + + throw new RRQMRPCException(stringBuilder.ToString()); + } + } + } +} + +#endif \ No newline at end of file diff --git a/RRQMSocket.RPC/RRQMRPC/Common/RpcContext.cs b/RRQMSocket.RPC/RRQMRPC/Common/RpcContext.cs new file mode 100644 index 000000000..43542211a --- /dev/null +++ b/RRQMSocket.RPC/RRQMRPC/Common/RpcContext.cs @@ -0,0 +1,74 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +using RRQMCore.ByteManager; +using RRQMCore.Run; +using System.Collections.Generic; + +namespace RRQMSocket.RPC.RRQMRPC +{ + /// + /// RPC传输类 + /// + public class RpcContext : WaitResult + { + internal int MethodToken; + internal string ID; + internal byte Feedback; + internal byte[] ReturnParameterBytes; + internal List ParametersBytes; + + internal void Serialize(ByteBlock byteBlock) + { + byteBlock.Write(this.Sign); + byteBlock.Write(this.Status); + byteBlock.Write(this.Feedback); + byteBlock.Write(this.MethodToken); + byteBlock.Write(this.ID); + byteBlock.Write(this.Message); + byteBlock.WriteBytesPackage(this.ReturnParameterBytes); + + if (this.ParametersBytes != null && this.ParametersBytes.Count > 0) + { + byteBlock.Write((byte)this.ParametersBytes.Count); + foreach (byte[] item in this.ParametersBytes) + { + byteBlock.WriteBytesPackage(item); + } + } + else + { + byteBlock.Write((byte)0); + } + } + + internal static RpcContext Deserialize(ByteBlock byteBlock) + { + RpcContext context = new RpcContext(); + context.Sign = byteBlock.ReadInt32(); + context.Status = byteBlock.ReadByte(); + context.Feedback = byteBlock.ReadByte(); + context.MethodToken = byteBlock.ReadInt32(); + context.ID = byteBlock.ReadString(); + context.Message = byteBlock.ReadString(); + context.ReturnParameterBytes = byteBlock.ReadBytesPackage(); + + context.ParametersBytes = new List(); + byte countPar = byteBlock.ReadByte(); + + for (int i = 0; i < countPar; i++) + { + context.ParametersBytes.Add(byteBlock.ReadBytesPackage()); + } + return context; + } + } +} \ No newline at end of file diff --git a/RRQMSocket.RPC/RRQMRPC/Common/RpcProxyInfo.cs b/RRQMSocket.RPC/RRQMRPC/Common/RpcProxyInfo.cs new file mode 100644 index 000000000..85b40ee14 --- /dev/null +++ b/RRQMSocket.RPC/RRQMRPC/Common/RpcProxyInfo.cs @@ -0,0 +1,42 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +using RRQMCore.Run; +using System.Collections.Generic; + +namespace RRQMSocket.RPC.RRQMRPC +{ + /// + /// RPC代理文件程序 + /// + public class RpcProxyInfo : WaitResult + { + /// + /// 程序名 + /// + public string AssemblyName { get; internal set; } + + /// + /// 数据 + /// + public byte[] AssemblyData { get; internal set; } + + /// + /// 版本号 + /// + public string Version { get; internal set; } + + /// + /// 源代码 + /// + public List Codes { get; internal set; } + } +} \ No newline at end of file diff --git a/RRQMSocket.RPC/RRQMRPC/Common/Tools.cs b/RRQMSocket.RPC/RRQMRPC/Common/Tools.cs new file mode 100644 index 000000000..0855df8bb --- /dev/null +++ b/RRQMSocket.RPC/RRQMRPC/Common/Tools.cs @@ -0,0 +1,240 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +using RRQMCore; +using RRQMCore.Helper; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Threading.Tasks; + +namespace RRQMSocket.RPC.RRQMRPC +{ + internal static class Tools + { + internal static void GetRPCMethod( + MethodInstance[] methodInstances, + string nameSpaceOld, + ref MethodStore methodStore, + Version version, + ref RpcProxyInfo proxyInfo) + { + string nameSpace = string.IsNullOrEmpty(nameSpaceOld) ? "RRQMRPC" : $"RRQMRPC.{nameSpaceOld}"; + List refs = new List(); + + PropertyCodeGenerator propertyCode = new PropertyCodeGenerator(nameSpace, methodStore); + + foreach (MethodInstance methodInstance in methodInstances) + { + foreach (RPCAttribute att in methodInstance.RPCAttributes) + { + if (att is RRQMRPCAttribute attribute) + { + if (methodInstance.ReturnType != null) + { + refs.Add(methodInstance.ReturnType.Assembly.Location); + propertyCode.AddTypeString(methodInstance.ReturnType); + } + foreach (var type in methodInstance.ParameterTypes) + { + refs.Add(type.Assembly.Location); + propertyCode.AddTypeString(type); + } + + break; + } + } + } + +#if NET45_OR_GREATER + foreach (var item in refs) + { + RpcCompiler.AddRef(item); + } +#endif + List methods = new List(); + string className = null; + + foreach (MethodInstance methodInstance in methodInstances) + { + foreach (RPCAttribute att in methodInstance.RPCAttributes) + { + if (att is RRQMRPCAttribute attribute) + { + MethodItem methodItem = new MethodItem(); + methodItem.IsOutOrRef = methodInstance.IsByRef; + methodItem.MethodToken = methodInstance.MethodToken; + methodItem.ServerName = methodInstance.Provider.GetType().Name; + methodItem.Method = string.IsNullOrEmpty(attribute.MemberKey) ? methodInstance.Method.Name : attribute.MemberKey; + try + { + methodStore.AddMethodItem(methodItem); + } + catch + { + throw new RRQMRPCKeyException($"方法键为{methodItem.Method}的服务已注册"); + } + + if (className == null) + { + className = methodInstance.Provider.GetType().Name; + } + else if (className != methodInstance.Provider.GetType().Name) + { + throw new RRQMRPCException("方法来源于不同服务"); + } + methods.Add(methodInstance.Method); + break; + } + } + } + + CodeGenerator.Namespace = nameSpace; + CodeGenerator.PropertyCode = propertyCode; + + CodeGenerator codeMap = new CodeGenerator(); + codeMap.ClassName = className; + codeMap.Methods = methods.ToArray(); + + CellCode cellCode = new CellCode(); + cellCode.Name = className; + cellCode.CodeType = CodeType.Service; + cellCode.Code = codeMap.GetCode(); + + if (proxyInfo.Codes == null) + { + proxyInfo.Codes = new List(); + } + proxyInfo.Codes.Add(cellCode); + + CellCode argesCode = proxyInfo.Codes.FirstOrDefault(a => a.Name == "ClassArgs"); + if (argesCode == null) + { + argesCode = new CellCode(); + argesCode.Name = "ClassArgs"; + argesCode.CodeType = CodeType.ClassArgs; + argesCode.Code = propertyCode.GetPropertyCode(); + proxyInfo.Codes.Add(argesCode); + } + else + { + argesCode.Code = propertyCode.GetPropertyCode(); + } + + proxyInfo.AssemblyName = $"{nameSpace}.dll"; + proxyInfo.Version = version == null ? "1.0.0.0" : version.ToString(); + } + + private static int nullReturnNullParameters = 1000000000; + private static int nullReturnExistParameters = 300000000; + private static int ExistReturnNullParameters = 500000000; + private static int ExistReturnExistParameters = 700000000; + + internal static MethodInstance[] GetMethodInstances(IServerProvider serverProvider, bool isSetToken) + { + List instances = new List(); + + MethodInfo[] methodInfos = serverProvider.GetType().GetMethods(); + + foreach (MethodInfo method in methodInfos) + { + if (method.IsGenericMethod) + { + continue; + } + IEnumerable attributes = method.GetCustomAttributes(true); + if (attributes.Count() > 0) + { + MethodInstance methodInstance = new MethodInstance(); + methodInstance.Provider = serverProvider; + methodInstance.Method = method; + methodInstance.RPCAttributes = attributes.ToArray(); + methodInstance.IsEnable = true; + methodInstance.Parameters = method.GetParameters(); + List names = new List(); + foreach (var parameterInfo in methodInstance.Parameters) + { + names.Add(parameterInfo.Name); + } + methodInstance.ParameterNames = names.ToArray(); + if (typeof(Task).IsAssignableFrom(method.ReturnType)) + { + methodInstance.Async = true; + } + + ParameterInfo[] parameters = method.GetParameters(); + List types = new List(); + foreach (var parameter in parameters) + { + types.Add(parameter.ParameterType.GetRefOutType()); + if (parameter.ParameterType.IsByRef) + { + methodInstance.IsByRef = true; + } + } + methodInstance.ParameterTypes = types.ToArray(); + + if (method.ReturnType == typeof(void) || method.ReturnType == typeof(Task)) + { + methodInstance.ReturnType = null; + + if (isSetToken) + { + if (parameters.Length == 0) + { + methodInstance.MethodToken = ++nullReturnNullParameters; + } + else + { + methodInstance.MethodToken = ++nullReturnExistParameters; + } + } + } + else + { + if (methodInstance.Async) + { + Type[] ts = method.ReturnType.GetGenericArguments(); + if (ts.Length == 1) + { + methodInstance.ReturnType = ts[0]; + } + else + { + methodInstance.ReturnType = null; + } + } + else + { + methodInstance.ReturnType = method.ReturnType; + } + + if (isSetToken) + { + if (parameters.Length == 0) + { + methodInstance.MethodToken = ++ExistReturnNullParameters; + } + else + { + methodInstance.MethodToken = ++ExistReturnExistParameters; + } + } + } + instances.Add(methodInstance); + } + } + + return instances.ToArray(); + } + } +} \ No newline at end of file diff --git a/RRQMSocket.RPC/RRQMRPC/Config/TcpRpcClientConfig.cs b/RRQMSocket.RPC/RRQMRPC/Config/TcpRpcClientConfig.cs new file mode 100644 index 000000000..ae762bb61 --- /dev/null +++ b/RRQMSocket.RPC/RRQMRPC/Config/TcpRpcClientConfig.cs @@ -0,0 +1,51 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +using RRQMCore.Dependency; + +namespace RRQMSocket.RPC.RRQMRPC +{ + /// + /// TcpRPCClient配置 + /// + public class TcpRpcClientConfig : ProtocolClientConfig + { + /// + /// 代理文件令箭 + /// + public string ProxyToken + { + get { return (string)GetValue(ProxyTokenProperty); } + set { SetValue(ProxyTokenProperty, value); } + } + + /// + /// 代理文件令箭, 所需类型 + /// + public static readonly DependencyProperty ProxyTokenProperty = + DependencyProperty.Register("ProxyToken", typeof(string), typeof(TcpRpcClientConfig), null); + + /// + /// 序列化转换器 + /// + public SerializeConverter SerializeConverter + { + get { return (SerializeConverter)GetValue(SerializeConverterProperty); } + set { SetValue(SerializeConverterProperty, value); } + } + + /// + /// 序列化转换器, 所需类型 + /// + public static readonly DependencyProperty SerializeConverterProperty = + DependencyProperty.Register("SerializeConverter", typeof(SerializeConverter), typeof(TcpRpcClientConfig), new BinarySerializeConverter()); + } +} \ No newline at end of file diff --git a/RRQMSocket.RPC/RRQMRPC/Config/TcpRpcParserConfig.cs b/RRQMSocket.RPC/RRQMRPC/Config/TcpRpcParserConfig.cs new file mode 100644 index 000000000..2c2e467fb --- /dev/null +++ b/RRQMSocket.RPC/RRQMRPC/Config/TcpRpcParserConfig.cs @@ -0,0 +1,82 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +using RRQMCore.Dependency; +using System; + +namespace RRQMSocket.RPC.RRQMRPC +{ + /// + /// RRQMRPC解析器配置 + /// + public class TcpRpcParserConfig : ProtocolServiceConfig + { + /// + /// 序列化转换器 + /// + public SerializeConverter SerializeConverter + { + get { return (SerializeConverter)GetValue(SerializeConverterProperty); } + set { SetValue(SerializeConverterProperty, value); } + } + + /// + /// 序列化转换器, 所需类型 + /// + public static readonly DependencyProperty SerializeConverterProperty = + DependencyProperty.Register("SerializeConverter", typeof(SerializeConverter), typeof(TcpRpcParserConfig), new BinarySerializeConverter()); + + /// + /// 代理源文件命名空间 + /// + public string NameSpace + { + get { return (string)GetValue(NameSpaceProperty); } + set { SetValue(NameSpaceProperty, value); } + } + + /// + /// 代理源文件命名空间, 所需类型 + /// + public static readonly DependencyProperty NameSpaceProperty = + DependencyProperty.Register("NameSpace", typeof(string), typeof(TcpRpcParserConfig), null); + + /// + /// RPC代理版本 + /// + public Version RPCVersion + { + get { return (Version)GetValue(RPCVersionProperty); } + set { SetValue(RPCVersionProperty, value); } + } + + /// + /// RPC代理版本, 所需类型 + /// + public static readonly DependencyProperty RPCVersionProperty = + DependencyProperty.Register("RPCVersion", typeof(Version), typeof(TcpRpcParserConfig), null); + + /// + /// 代理令箭,当客户端获取代理文件时需验证令箭 + /// + public string ProxyToken + { + get { return (string)GetValue(ProxyTokenProperty); } + set { SetValue(ProxyTokenProperty, value); } + } + + /// + /// 代理令箭,当客户端获取代理文件时需验证令箭, 所需类型 + /// + public static readonly DependencyProperty ProxyTokenProperty = + DependencyProperty.Register("ProxyToken", typeof(string), typeof(TcpRpcParserConfig), null); + } +} \ No newline at end of file diff --git a/RRQMSocket.RPC/RRQMRPC/Config/UdpRpcClientConfig.cs b/RRQMSocket.RPC/RRQMRPC/Config/UdpRpcClientConfig.cs new file mode 100644 index 000000000..4b43db44d --- /dev/null +++ b/RRQMSocket.RPC/RRQMRPC/Config/UdpRpcClientConfig.cs @@ -0,0 +1,51 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +using RRQMCore.Dependency; + +namespace RRQMSocket.RPC.RRQMRPC +{ + /// + /// UdpRpc + /// + public class UdpRpcClientConfig : UdpSessionConfig + { + /// + /// 代理文件令箭 + /// + public string ProxyToken + { + get { return (string)GetValue(ProxyTokenProperty); } + set { SetValue(ProxyTokenProperty, value); } + } + + /// + /// 代理文件令箭, 所需类型 + /// + public static readonly DependencyProperty ProxyTokenProperty = + DependencyProperty.Register("ProxyToken", typeof(string), typeof(UdpRpcClientConfig), null); + + /// + /// 序列化转换器 + /// + public SerializeConverter SerializeConverter + { + get { return (SerializeConverter)GetValue(SerializeConverterProperty); } + set { SetValue(SerializeConverterProperty, value); } + } + + /// + /// 序列化转换器, 所需类型 + /// + public static readonly DependencyProperty SerializeConverterProperty = + DependencyProperty.Register("SerializeConverter", typeof(SerializeConverter), typeof(UdpRpcClientConfig), new BinarySerializeConverter()); + } +} \ No newline at end of file diff --git a/RRQMSocket.RPC/RRQMRPC/Config/UdpRpcParserConfig.cs b/RRQMSocket.RPC/RRQMRPC/Config/UdpRpcParserConfig.cs new file mode 100644 index 000000000..961114725 --- /dev/null +++ b/RRQMSocket.RPC/RRQMRPC/Config/UdpRpcParserConfig.cs @@ -0,0 +1,82 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +using RRQMCore.Dependency; +using System; + +namespace RRQMSocket.RPC.RRQMRPC +{ + /// + /// UdpRPCParser + /// + public class UdpRpcParserConfig : UdpSessionConfig + { + /// + /// 代理源文件命名空间, 所需类型 + /// + public static readonly DependencyProperty NameSpaceProperty = + DependencyProperty.Register("NameSpace", typeof(string), typeof(UdpRpcParserConfig), null); + + /// + /// 代理令箭,当客户端获取代理文件时需验证令箭, 所需类型 + /// + public static readonly DependencyProperty ProxyTokenProperty = + DependencyProperty.Register("ProxyToken", typeof(string), typeof(UdpRpcParserConfig), null); + + /// + /// RPC代理版本, 所需类型 + /// + public static readonly DependencyProperty RPCVersionProperty = + DependencyProperty.Register("RPCVersion", typeof(Version), typeof(UdpRpcParserConfig), null); + + /// + /// 序列化转换器, 所需类型 + /// + public static readonly DependencyProperty SerializeConverterProperty = + DependencyProperty.Register("SerializeConverter", typeof(SerializeConverter), typeof(UdpRpcParserConfig), new BinarySerializeConverter()); + + /// + /// 代理源文件命名空间 + /// + public string NameSpace + { + get { return (string)GetValue(NameSpaceProperty); } + set { SetValue(NameSpaceProperty, value); } + } + + /// + /// 代理令箭,当客户端获取代理文件时需验证令箭 + /// + public string ProxyToken + { + get { return (string)GetValue(ProxyTokenProperty); } + set { SetValue(ProxyTokenProperty, value); } + } + + /// + /// RPC代理版本 + /// + public Version RPCVersion + { + get { return (Version)GetValue(RPCVersionProperty); } + set { SetValue(RPCVersionProperty, value); } + } + + /// + /// 序列化转换器 + /// + public SerializeConverter SerializeConverter + { + get { return (SerializeConverter)GetValue(SerializeConverterProperty); } + set { SetValue(SerializeConverterProperty, value); } + } + } +} \ No newline at end of file diff --git a/RRQMSocket.RPC/RRQMRPC/Exceptions/RRQMRPCKeyException.cs b/RRQMSocket.RPC/RRQMRPC/Exceptions/RRQMRPCKeyException.cs new file mode 100644 index 000000000..664e01f79 --- /dev/null +++ b/RRQMSocket.RPC/RRQMRPC/Exceptions/RRQMRPCKeyException.cs @@ -0,0 +1,47 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +using RRQMCore.Exceptions; + +namespace RRQMSocket.RPC.RRQMRPC +{ + /// + /// RPC添加方法键异常 + /// + public class RRQMRPCKeyException : RRQMException + { + /// + /// + /// + public RRQMRPCKeyException() : base() { } + + /// + /// + /// + /// + public RRQMRPCKeyException(string message) : base(message) { } + + /// + /// + /// + /// + /// + public RRQMRPCKeyException(string message, System.Exception inner) : base(message, inner) { } + + /// + /// + /// + /// + /// + protected RRQMRPCKeyException(System.Runtime.Serialization.SerializationInfo info, + System.Runtime.Serialization.StreamingContext context) : base(info, context) { } + } +} \ No newline at end of file diff --git a/RRQMSocket.RPC/RRQMRPC/Exceptions/RRQMRPCNoRegisterException.cs b/RRQMSocket.RPC/RRQMRPC/Exceptions/RRQMRPCNoRegisterException.cs new file mode 100644 index 000000000..2b1eab6c5 --- /dev/null +++ b/RRQMSocket.RPC/RRQMRPC/Exceptions/RRQMRPCNoRegisterException.cs @@ -0,0 +1,47 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +using RRQMCore.Exceptions; + +namespace RRQMSocket.RPC.RRQMRPC +{ + /// + /// RPC无注册异常 + /// + public class RRQMRPCNoRegisterException : RRQMException + { + /// + /// + /// + public RRQMRPCNoRegisterException() : base() { } + + /// + /// + /// + /// + public RRQMRPCNoRegisterException(string message) : base(message) { } + + /// + /// + /// + /// + /// + public RRQMRPCNoRegisterException(string message, System.Exception inner) : base(message, inner) { } + + /// + /// + /// + /// + /// + protected RRQMRPCNoRegisterException(System.Runtime.Serialization.SerializationInfo info, + System.Runtime.Serialization.StreamingContext context) : base(info, context) { } + } +} \ No newline at end of file diff --git a/RRQMSocket.RPC/RRQMRPC/Exceptions/RRQMSerializationException.cs b/RRQMSocket.RPC/RRQMRPC/Exceptions/RRQMSerializationException.cs new file mode 100644 index 000000000..665337c4e --- /dev/null +++ b/RRQMSocket.RPC/RRQMRPC/Exceptions/RRQMSerializationException.cs @@ -0,0 +1,47 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +using RRQMCore.Exceptions; + +namespace RRQMSocket.RPC.RRQMRPC +{ + /// + /// 序列化异常类 + /// + public class RRQMSerializationException : RRQMException + { + /// + /// + /// + public RRQMSerializationException() : base() { } + + /// + /// + /// + /// + public RRQMSerializationException(string message) : base(message) { } + + /// + /// + /// + /// + /// + public RRQMSerializationException(string message, System.Exception inner) : base(message, inner) { } + + /// + /// + /// + /// + /// + protected RRQMSerializationException(System.Runtime.Serialization.SerializationInfo info, + System.Runtime.Serialization.StreamingContext context) : base(info, context) { } + } +} \ No newline at end of file diff --git a/RRQMSocket.RPC/RRQMRPC/Interface/IRRQMRpcClient.cs b/RRQMSocket.RPC/RRQMRPC/Interface/IRRQMRpcClient.cs new file mode 100644 index 000000000..e44d3f94e --- /dev/null +++ b/RRQMSocket.RPC/RRQMRPC/Interface/IRRQMRpcClient.cs @@ -0,0 +1,62 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +using RRQMCore.ByteManager; +using RRQMCore.Log; + +namespace RRQMSocket.RPC.RRQMRPC +{ + /// + /// 客户端RPC接口 + /// + public interface IRRQMRpcClient : IRpcClient + { + /// + /// 获取ID + /// + string ID { get; } + + /// + /// 日志记录器 + /// + ILog Logger { get; } + + /// + /// 获取内存池实例 + /// + BytePool BytePool { get; } + + /// + /// 序列化生成器 + /// + SerializeConverter SerializeConverter { get; } + + /// + /// 获取远程服务器RPC服务文件 + /// + /// + /// + /// + RpcProxyInfo GetProxyInfo(); + + /// + /// 服务发现完成后 + /// + event RRQMMessageEventHandler ServiceDiscovered; + + /// + /// 发现服务 + /// + /// 是否触发初始化事件 + /// 已发现的服务 + MethodItem[] DiscoveryService(bool isTrigger = true); + } +} \ No newline at end of file diff --git a/RRQMSocket.RPC/RRQMRPC/Interface/IRRQMRpcParser.cs b/RRQMSocket.RPC/RRQMRPC/Interface/IRRQMRpcParser.cs new file mode 100644 index 000000000..273664229 --- /dev/null +++ b/RRQMSocket.RPC/RRQMRPC/Interface/IRRQMRpcParser.cs @@ -0,0 +1,97 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +using RRQMCore.ByteManager; +using System; +using System.Collections.Generic; +using System.Net; + +namespace RRQMSocket.RPC.RRQMRPC +{ + /// + /// RRQMRPC接口 + /// + public interface IRRQMRpcParser + { + /// + /// 内存池实例 + /// + BytePool BytePool { get; } + + /// + /// 获取生成的代理代码 + /// + CellCode[] Codes { get; } + + /// + /// 代理源文件命名空间 + /// + string NameSpace { get; } + + /// + /// 获取代理文件实例 + /// + RpcProxyInfo ProxyInfo { get; } + + /// + /// 函数库 + /// + MethodStore MethodStore { get; } + + /// + /// 代理令箭,当客户端获取代理文件时需验证令箭 + /// + string ProxyToken { get; } + + /// + /// RPC代理版本 + /// + Version RPCVersion { get; } + + /// + /// 序列化转换器 + /// + SerializeConverter SerializeConverter { get; } + + /// + /// 获取代理文件 + /// + /// 代理令箭 + /// 调用作用者,TCP模式下派生自,UDP模式下是 + /// + RpcProxyInfo GetProxyInfo(string proxyToken, object caller); + + /// + /// 执行函数 + /// + /// 函数内容 + /// 调用作用者,TCP模式下派生自,UDP模式下是 + void ExecuteContext(RpcContext context, object caller); + + /// + /// 获取注册函数 + /// + /// + /// 调用作用者,TCP模式下派生自,UDP模式下是 + /// + List GetRegisteredMethodItems(string proxyToken, object caller); + +#if NET45_OR_GREATER + + /// + /// 编译代理 + /// + /// 存放目标文件夹 + void CompilerProxy(string targetDic = ""); + +#endif + } +} \ No newline at end of file diff --git a/RRQMSocket.RPC/RRQMRPC/Interface/IRpcClient.cs b/RRQMSocket.RPC/RRQMRPC/Interface/IRpcClient.cs new file mode 100644 index 000000000..3818f6592 --- /dev/null +++ b/RRQMSocket.RPC/RRQMRPC/Interface/IRpcClient.cs @@ -0,0 +1,74 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +using RRQMCore.Exceptions; +using System; + +namespace RRQMSocket.RPC.RRQMRPC +{ + /// + /// 客户端RPC接口 + /// + public interface IRpcClient : IDisposable + { + /// + /// 函数式调用 + /// + /// 函数名 + /// 参数 + /// RPC调用设置 + /// + /// + /// + /// + void Invoke(string method, InvokeOption invokeOption, params object[] parameters); + + /// + /// 函数式调用 + /// + /// 方法名 + /// 参数 + /// RPC调用设置 + /// + /// + /// + /// + /// 服务器返回结果 + T Invoke(string method, InvokeOption invokeOption, params object[] parameters); + + /// + /// 函数式调用 + /// + /// 方法名 + /// 参数 + /// + /// RPC调用设置 + /// + /// + /// + /// + /// 服务器返回结果 + T Invoke(string method, InvokeOption invokeOption, ref object[] parameters, Type[] types); + + /// + /// 函数式调用 + /// + /// 方法名 + /// 参数 + /// + /// RPC调用设置 + /// + /// + /// + /// + void Invoke(string method, InvokeOption invokeOption, ref object[] parameters, Type[] types); + } +} \ No newline at end of file diff --git a/RRQMSocket.RPC/RRQMRPC/Serialization/BinarySerializeConverter.cs b/RRQMSocket.RPC/RRQMRPC/Serialization/BinarySerializeConverter.cs new file mode 100644 index 000000000..3aab3a98b --- /dev/null +++ b/RRQMSocket.RPC/RRQMRPC/Serialization/BinarySerializeConverter.cs @@ -0,0 +1,44 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +using RRQMCore.Serialization; +using System; + +namespace RRQMSocket.RPC.RRQMRPC +{ + /// + /// 二进制序列化器,默认最大可序列化1K byte的大小 + /// + public class BinarySerializeConverter : SerializeConverter + { +#pragma warning disable CS1591 // XML 注释跟随抽象类 + + public override object DeserializeParameter(byte[] parameterBytes, Type parameterType) + { + if (parameterBytes == null) + { + return null; + } + return SerializeConvert.RRQMBinaryDeserialize(parameterBytes, 0, parameterType); + } + + public override byte[] SerializeParameter(object parameter) + { + if (parameter == null) + { + return null; + } + return SerializeConvert.RRQMBinarySerialize(parameter, true); + } + +#pragma warning restore CS1591 + } +} \ No newline at end of file diff --git a/RRQMSocket.RPC/RRQMRPC/Serialization/JsonSerializeConverter.cs b/RRQMSocket.RPC/RRQMRPC/Serialization/JsonSerializeConverter.cs new file mode 100644 index 000000000..ad47d5156 --- /dev/null +++ b/RRQMSocket.RPC/RRQMRPC/Serialization/JsonSerializeConverter.cs @@ -0,0 +1,55 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +using RRQMCore.XREF.Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace RRQMSocket.RPC.RRQMRPC +{ + /// + /// Json序列化转化器 + /// + public class JsonSerializeConverter : RRQMSocket.RPC.RRQMRPC.SerializeConverter + { + /// + /// 反序列化 + /// + /// + /// + /// + public override object DeserializeParameter(byte[] parameterBytes, Type parameterType) + { + if (parameterBytes == null) + { + return null; + } + return JsonConvert.DeserializeObject(Encoding.UTF8.GetString(parameterBytes), parameterType); + } + + /// + /// 序列化 + /// + /// + /// + public override byte[] SerializeParameter(object parameter) + { + if (parameter == null) + { + return null; + } + return Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(parameter)); + } + } +} diff --git a/RRQMSocket.RPC/RRQMRPC/Serialization/SerializeConverter.cs b/RRQMSocket.RPC/RRQMRPC/Serialization/SerializeConverter.cs new file mode 100644 index 000000000..cd175ae6e --- /dev/null +++ b/RRQMSocket.RPC/RRQMRPC/Serialization/SerializeConverter.cs @@ -0,0 +1,36 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +using System; + +namespace RRQMSocket.RPC.RRQMRPC +{ + /// + /// 序列化转换器 + /// + public abstract class SerializeConverter + { + /// + /// 序列化RPC方法返回值参数 + /// + /// + /// + public abstract byte[] SerializeParameter(object parameter); + + /// + /// 反序列化传输对象 + /// + /// + /// + /// + public abstract object DeserializeParameter(byte[] parameterBytes, Type parameterType); + } +} \ No newline at end of file diff --git a/RRQMSocket.RPC/RRQMRPC/Serialization/XmlSerializeConverter.cs b/RRQMSocket.RPC/RRQMRPC/Serialization/XmlSerializeConverter.cs new file mode 100644 index 000000000..8eff4a7ac --- /dev/null +++ b/RRQMSocket.RPC/RRQMRPC/Serialization/XmlSerializeConverter.cs @@ -0,0 +1,44 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +using RRQMCore.Serialization; +using System; + +namespace RRQMSocket.RPC.RRQMRPC +{ + /// + /// Xml序列化 + /// + public class XmlSerializeConverter : SerializeConverter + { +#pragma warning disable CS1591 // XML 注释 + + public override object DeserializeParameter(byte[] parameterBytes, Type parameterType) + { + if (parameterBytes == null) + { + return null; + } + return SerializeConvert.XmlDeserializeFromBytes(parameterBytes, parameterType); + } + + public override byte[] SerializeParameter(object parameter) + { + if (parameter == null) + { + return null; + } + return SerializeConvert.XmlSerializeToBytes(parameter); + } + +#pragma warning restore CS1591 + } +} \ No newline at end of file diff --git a/RRQMSocket.RPC/RRQMRPC/Socket/RpcSocketClient .cs b/RRQMSocket.RPC/RRQMRPC/Socket/RpcSocketClient .cs new file mode 100644 index 000000000..f37969dea --- /dev/null +++ b/RRQMSocket.RPC/RRQMRPC/Socket/RpcSocketClient .cs @@ -0,0 +1,474 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +using RRQMCore.ByteManager; +using RRQMCore.Exceptions; +using RRQMCore.Log; +using RRQMCore.Run; +using RRQMCore.Serialization; +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading.Tasks; + +namespace RRQMSocket.RPC.RRQMRPC +{ + /// + /// RPC服务器辅助类 + /// + public class RpcSocketClient : ProtocolSocketClient + { + static RpcSocketClient() + { + AddUsedProtocol(100, "请求RPC代理文件"); + AddUsedProtocol(101, "RPC调用"); + AddUsedProtocol(102, "获取注册服务"); + AddUsedProtocol(103, "ID调用客户端"); + AddUsedProtocol(104, "RPC回调"); + for (short i = 105; i < 110; i++) + { + AddUsedProtocol(i, "保留协议"); + } + } + + internal Func IDAction; + + internal RRQMReceivedProcotolEventHandler Received; + + internal SerializeConverter serializeConverter; + + internal RRQMWaitHandle waitHandle; + + /// + /// 构造函数 + /// + public RpcSocketClient() + { + waitHandle = new RRQMWaitHandle(); + } + + /// + /// 回调RPC + /// + /// + /// + /// + /// + /// + public T CallBack(int methodToken, InvokeOption invokeOption = null, params object[] parameters) + { + RpcContext context = new RpcContext(); + WaitData waitData = this.waitHandle.GetWaitData(context); + + context.MethodToken = methodToken; + + ByteBlock byteBlock = this.BytePool.GetByteBlock(this.BufferLength); + if (invokeOption == null) + { + invokeOption = InvokeOption.WaitInvoke; + } + try + { + context.Feedback = (byte)invokeOption.FeedbackType; + List datas = new List(); + foreach (object parameter in parameters) + { + datas.Add(this.serializeConverter.SerializeParameter(parameter)); + } + context.ParametersBytes = datas; + context.Serialize(byteBlock); + + this.InternalSend(104, byteBlock.Buffer, 0, byteBlock.Len); + } + catch (Exception e) + { + throw new RRQMException(e.Message); + } + finally + { + byteBlock.Dispose(); + } + + switch (invokeOption.FeedbackType) + { + case FeedbackType.OnlySend: + { + this.waitHandle.Destroy(waitData); + return default(T); + } + case FeedbackType.WaitSend: + { + waitData.Wait(invokeOption.Timeout); + + RpcContext resultContext = waitData.WaitResult; + this.waitHandle.Destroy(waitData); + if (resultContext.Status == 0) + { + throw new RRQMTimeoutException("等待结果超时"); + } + return default(T); + } + case FeedbackType.WaitInvoke: + { + waitData.Wait(invokeOption.Timeout); + + RpcContext resultContext = waitData.WaitResult; + this.waitHandle.Destroy(waitData); + if (resultContext.Status == 0) + { + throw new RRQMTimeoutException("等待结果超时"); + } + else if (resultContext.Status == 2) + { + throw new RRQMRPCInvokeException("未找到该公共方法,或该方法未标记RRQMRPCCallBackMethod"); + } + else if (resultContext.Status == 3) + { + throw new RRQMRPCException("客户端未开启反向RPC"); + } + else if (resultContext.Status == 4) + { + throw new RRQMRPCException($"调用异常,信息:{resultContext.Message}"); + } + + try + { + return (T)this.serializeConverter.DeserializeParameter(resultContext.ReturnParameterBytes, typeof(T)); + } + catch (Exception e) + { + throw new RRQMException(e.Message); + } + } + default: + return default(T); + } + } + + /// + /// 回调RPC + /// + /// + /// + /// + public void CallBack(int methodToken, InvokeOption invokeOption = null, params object[] parameters) + { + RpcContext context = new RpcContext(); + WaitData waitData = this.waitHandle.GetWaitData(context); + + context.MethodToken = methodToken; + + ByteBlock byteBlock = this.BytePool.GetByteBlock(this.BufferLength); + if (invokeOption == null) + { + invokeOption = InvokeOption.WaitInvoke; + } + try + { + context.Feedback = (byte)invokeOption.FeedbackType; + List datas = new List(); + foreach (object parameter in parameters) + { + datas.Add(this.serializeConverter.SerializeParameter(parameter)); + } + context.ParametersBytes = datas; + context.Serialize(byteBlock); + + this.InternalSend(104, byteBlock.Buffer, 0, byteBlock.Len); + } + catch (Exception e) + { + throw new RRQMException(e.Message); + } + finally + { + byteBlock.Dispose(); + } + switch (invokeOption.FeedbackType) + { + case FeedbackType.OnlySend: + { + this.waitHandle.Destroy(waitData); + return; + } + case FeedbackType.WaitSend: + { + waitData.Wait(invokeOption.Timeout); + + RpcContext resultContext = waitData.WaitResult; + this.waitHandle.Destroy(waitData); + if (resultContext.Status == 0) + { + throw new RRQMTimeoutException("等待结果超时"); + } + return; + } + case FeedbackType.WaitInvoke: + { + waitData.Wait(invokeOption.Timeout); + + RpcContext resultContext = waitData.WaitResult; + this.waitHandle.Destroy(waitData); + if (resultContext.Status == 0) + { + throw new RRQMTimeoutException("等待结果超时"); + } + else if (resultContext.Status == 2) + { + throw new RRQMRPCInvokeException("未找到该公共方法,或该方法未标记RRQMRPCCallBackMethod"); + } + else if (resultContext.Status == 3) + { + throw new RRQMRPCException("客户端未开启反向RPC"); + } + else if (resultContext.Status == 4) + { + throw new RRQMRPCException($"调用异常,信息:{resultContext.Message}"); + } + return; + } + default: + return; + } + } + + /// + /// 回调RPC + /// + /// + /// + /// + public byte[] CallBack(RpcContext invokeContext, int timeout) + { + RpcContext context = new RpcContext(); + WaitData waitData = this.waitHandle.GetWaitData(context); + context.MethodToken = invokeContext.MethodToken; + + ByteBlock byteBlock = this.BytePool.GetByteBlock(this.BufferLength); + + try + { + context.Feedback = invokeContext.Feedback; + + context.ParametersBytes = invokeContext.ParametersBytes; + context.Serialize(byteBlock); + + this.InternalSend(104, byteBlock.Buffer, 0, byteBlock.Len); + } + catch (Exception e) + { + throw new RRQMException(e.Message); + } + finally + { + byteBlock.Dispose(); + } + switch (invokeContext.Feedback) + { + case 0: + { + this.waitHandle.Destroy(waitData); + return null; + } + case 1: + { + waitData.Wait(timeout * 1000); + + RpcContext resultContext = waitData.WaitResult; + this.waitHandle.Destroy(waitData); + if (resultContext.Status == 0) + { + throw new RRQMTimeoutException("等待结果超时"); + } + return null; + } + case 2: + { + waitData.Wait(timeout * 1000); + + RpcContext resultContext = waitData.WaitResult; + this.waitHandle.Destroy(waitData); + if (resultContext.Status == 0) + { + throw new RRQMTimeoutException("等待结果超时"); + } + else if (resultContext.Status == 2) + { + throw new RRQMRPCInvokeException("未找到该公共方法,或该方法未标记RRQMRPCCallBackMethod"); + } + else if (resultContext.Status == 3) + { + throw new RRQMRPCException("客户端未开启反向RPC"); + } + else if (resultContext.Status == 4) + { + throw new RRQMRPCException($"调用异常,信息:{resultContext.Message}"); + } + + return resultContext.ReturnParameterBytes; + } + default: + return null; + } + } + + /// + /// 内部发送器 + /// + /// + /// + /// + /// + internal void InternalSend(short procotol, byte[] buffer, int offset, int length) + { + base.InternalSend(procotol, buffer, offset, length); + } + + /// + /// 处理协议数据 + /// + /// + /// + protected sealed override void HandleProtocolData(short? procotol, ByteBlock byteBlock) + { + byte[] buffer = byteBlock.Buffer; + int r = (int)byteBlock.Position; + switch (procotol) + { + case 100:/*100,请求RPC文件*/ + { + try + { + string proxyToken = Encoding.UTF8.GetString(buffer, 2, r - 2); + byte[] data = SerializeConvert.RRQMBinarySerialize(((IRRQMRpcParser)this.Service).GetProxyInfo(proxyToken, this), true); + this.InternalSend(100, data, 0, data.Length); + } + catch (Exception e) + { + Logger.Debug(LogType.Error, this, $"错误代码: 100, 错误详情:{e.Message}"); + } + break; + } + case 101:/*函数调用*/ + { + try + { + byteBlock.Pos = 2; + RpcContext content = RpcContext.Deserialize(byteBlock); + if (content.Feedback == 1) + { + List ps = content.ParametersBytes; + + ByteBlock returnByteBlock = this.BytePool.GetByteBlock(this.BufferLength); + try + { + content.ParametersBytes = null; + content.Status = 1; + content.Serialize(returnByteBlock); + this.InternalSend(101, returnByteBlock.Buffer, 0, (int)returnByteBlock.Length); + } + finally + { + content.ParametersBytes = ps; + returnByteBlock.Dispose(); + } + } + ((IRRQMRpcParser)this.Service).ExecuteContext(content, this); + } + catch (Exception e) + { + Logger.Debug(LogType.Error, this, $"错误代码: 101, 错误详情:{e.Message}"); + } + break; + } + case 102:/*获取注册服务*/ + { + try + { + string proxyToken = Encoding.UTF8.GetString(buffer, 2, r - 2); + byte[] data = SerializeConvert.RRQMBinarySerialize(((IRRQMRpcParser)this.Service).GetRegisteredMethodItems(proxyToken, this), true); + this.InternalSend(102, data, 0, data.Length); + } + catch (Exception e) + { + Logger.Debug(LogType.Error, this, $"错误代码: 102, 错误详情:{e.Message}"); + } + break; + } + case 103:/*ID调用客户端*/ + { + Task.Run(() => + { + try + { + byteBlock.Pos = 2; + RpcContext content = RpcContext.Deserialize(byteBlock); + content = this.IDAction(this, content); + + ByteBlock retuenByteBlock = this.BytePool.GetByteBlock(this.BufferLength); + try + { + content.Serialize(retuenByteBlock); + this.InternalSend(103, retuenByteBlock.Buffer, 0, (int)retuenByteBlock.Length); + } + finally + { + byteBlock.Dispose(); + } + } + catch (Exception e) + { + Logger.Debug(LogType.Error, this, $"错误代码: 103, 错误详情:{e.Message}"); + } + }); + break; + } + case 104:/*回调函数调用*/ + { + try + { + byteBlock.Pos = 2; + RpcContext content = RpcContext.Deserialize(byteBlock); + this.waitHandle.SetRun(content); + } + catch (Exception e) + { + Logger.Debug(LogType.Error, this, $"错误代码: 104, 错误详情:{e.Message}"); + } + break; + } + default: + RPCHandleDefaultData(procotol, byteBlock); + break; + } + } + + /// + /// RPC处理其余协议 + /// + /// + /// + protected virtual void RPCHandleDefaultData(short? procotol, ByteBlock byteBlock) + { + this.OnHandleDefaultData(procotol, byteBlock); + } + + /// + /// 处理其余协议的事件触发 + /// + /// + /// + protected void OnHandleDefaultData(short? procotol, ByteBlock byteBlock) + { + Received?.Invoke(this, procotol, byteBlock); + } + } +} \ No newline at end of file diff --git a/RRQMSocket.RPC/RRQMRPC/Socket/TcpParser.cs b/RRQMSocket.RPC/RRQMRPC/Socket/TcpParser.cs new file mode 100644 index 000000000..ad032c681 --- /dev/null +++ b/RRQMSocket.RPC/RRQMRPC/Socket/TcpParser.cs @@ -0,0 +1,374 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +using RRQMCore.ByteManager; +using RRQMCore.Log; +using System; +using System.Collections.Generic; +using System.IO; + +namespace RRQMSocket.RPC.RRQMRPC +{ + /// + /// TcpRPCParser泛型类型 + /// + /// + public class TcpParser : ProtocolService, IRPCParser, IRRQMRpcParser where TClient : RpcSocketClient, new() + { + /// + /// 构造函数 + /// + public TcpParser() + { + this.methodStore = new MethodStore(); + this.proxyInfo = new RpcProxyInfo(); + } + +#pragma warning disable + + public MethodMap MethodMap { get; private set; } + + public RPCService RPCService { get; private set; } + + public Action RRQMExecuteMethod { get; private set; } + + public CellCode[] Codes { get => this.proxyInfo == null ? null : this.proxyInfo.Codes.ToArray(); } + + public string NameSpace { get; private set; } + + public RpcProxyInfo ProxyInfo { get => proxyInfo; } + + public string ProxyToken { get; private set; } + + public Version RPCVersion { get; private set; } + + public SerializeConverter SerializeConverter { get; private set; } + + private MethodStore methodStore; + + private RpcProxyInfo proxyInfo; + + public MethodStore MethodStore { get => methodStore; } + + public void OnEndInvoke(MethodInvoker methodInvoker, MethodInstance methodInstance) + { + RpcContext context = (RpcContext)methodInvoker.Flag; + if (context.Feedback != 2) + { + return; + } + ByteBlock byteBlock = this.BytePool.GetByteBlock(this.BufferLength); + try + { + switch (methodInvoker.Status) + { + case InvokeStatus.Ready: + { + break; + } + + case InvokeStatus.UnFound: + { + context.Status = 2; + break; + } + case InvokeStatus.Success: + { + if (methodInstance.MethodToken > 50000000) + { + context.ReturnParameterBytes = this.SerializeConverter.SerializeParameter(methodInvoker.ReturnParameter); + } + else + { + context.ReturnParameterBytes = null; + } + + if (methodInstance.IsByRef) + { + context.ParametersBytes = new List(); + foreach (var item in methodInvoker.Parameters) + { + context.ParametersBytes.Add(this.SerializeConverter.SerializeParameter(item)); + } + } + else + { + context.ParametersBytes = null; + } + + context.Status = 1; + break; + } + case InvokeStatus.Abort: + { + context.Status = 4; + context.Message = methodInvoker.StatusMessage; + break; + } + case InvokeStatus.UnEnable: + { + context.Status = 3; + break; + } + case InvokeStatus.InvocationException: + { + context.Status = 5; + context.Message = methodInvoker.StatusMessage; + break; + } + case InvokeStatus.Exception: + { + context.Status = 6; + context.Message = methodInvoker.StatusMessage; + break; + } + default: + break; + } + + context.Serialize(byteBlock); + ((RpcSocketClient)methodInvoker.Caller).InternalSend(101, byteBlock.Buffer, 0, byteBlock.Len); + } + catch (Exception ex) + { + Logger.Debug(LogType.Error, this, ex.Message); + } + finally + { + byteBlock.Dispose(); + } + } + + public void OnRegisterServer(IServerProvider provider, MethodInstance[] methodInstances) + { + Tools.GetRPCMethod(methodInstances, this.NameSpace, ref this.methodStore, this.RPCVersion, ref this.proxyInfo); + } + + public void OnUnregisterServer(IServerProvider provider, MethodInstance[] methodInstances) + { + foreach (var item in methodInstances) + { + this.methodStore.RemoveMethodItem(item.MethodToken); + } + + CellCode cellCode = null; + foreach (var item in this.proxyInfo.Codes) + { + if (item.Name == provider.GetType().Name) + { + cellCode = item; + break; + } + } + if (cellCode != null) + { + this.proxyInfo.Codes.Remove(cellCode); + } + } + + public void SetExecuteMethod(Action executeMethod) + { + this.RRQMExecuteMethod = executeMethod; + } + + public void SetMethodMap(MethodMap methodMap) + { + this.MethodMap = methodMap; + } + + public void SetRPCService(RPCService service) + { + this.RPCService = service; + } + +#if NET45_OR_GREATER + + /// + /// 编译代理 + /// + /// 存放目标文件夹 + public void CompilerProxy(string targetDic = "") + { + string assemblyInfo = CodeGenerator.GetAssemblyInfo(this.proxyInfo.AssemblyName, this.proxyInfo.Version); + List codesString = new List(); + codesString.Add(assemblyInfo); + foreach (var item in this.proxyInfo.Codes) + { + codesString.Add(item.Code); + } + RpcCompiler.CompileCode(Path.Combine(targetDic, this.proxyInfo.AssemblyName), codesString.ToArray()); + } + +#endif + + protected override void OnCreateSocketCliect(TClient socketClient, CreateOption createOption) + { + if (createOption.NewCreate) + { + socketClient.IDAction = this.IDInvoke; + socketClient.Received = this.OnReceived; + socketClient.serializeConverter = this.SerializeConverter; + } + } + + private void OnReceived(object sender, short? procotol, ByteBlock byteBlock) + { + this.Received?.Invoke(sender, procotol, byteBlock); + } + + public virtual RpcProxyInfo GetProxyInfo(string proxyToken, object caller) + { + RpcProxyInfo proxyInfo = new RpcProxyInfo(); + if (this.ProxyToken == proxyToken) + { + proxyInfo.AssemblyData = this.ProxyInfo.AssemblyData; + proxyInfo.AssemblyName = this.ProxyInfo.AssemblyName; + proxyInfo.Codes = this.ProxyInfo.Codes; + proxyInfo.Version = this.ProxyInfo.Version; + proxyInfo.Status = 1; + } + else + { + proxyInfo.Status = 2; + proxyInfo.Message = "令箭不正确"; + } + + return proxyInfo; + } + + public virtual void ExecuteContext(RpcContext context, object caller) + { + MethodInvoker methodInvoker = new MethodInvoker(); + methodInvoker.Caller = caller; + methodInvoker.Flag = context; + if (this.MethodMap.TryGet(context.MethodToken, out MethodInstance methodInstance)) + { + try + { + if (methodInstance.IsEnable) + { + object[] ps = new object[methodInstance.ParameterTypes.Length]; + for (int i = 0; i < methodInstance.ParameterTypes.Length; i++) + { + ps[i] = this.SerializeConverter.DeserializeParameter(context.ParametersBytes[i], methodInstance.ParameterTypes[i]); + } + methodInvoker.Parameters = ps; + } + else + { + methodInvoker.Status = InvokeStatus.UnEnable; + } + } + catch (Exception ex) + { + methodInvoker.Status = InvokeStatus.Exception; + methodInvoker.StatusMessage = ex.Message; + } + + this.RRQMExecuteMethod.Invoke(this, methodInvoker, methodInstance); + } + else + { + methodInvoker.Status = InvokeStatus.UnFound; + this.RRQMExecuteMethod.Invoke(this, methodInvoker, null); + } + } + + protected override void LoadConfig(ServiceConfig ServiceConfig) + { + base.LoadConfig(ServiceConfig); + this.SerializeConverter = (SerializeConverter)ServiceConfig.GetValue(TcpRpcParserConfig.SerializeConverterProperty); + this.ProxyToken = (string)ServiceConfig.GetValue(TcpRpcParserConfig.ProxyTokenProperty); + this.NameSpace = (string)ServiceConfig.GetValue(TcpRpcParserConfig.NameSpaceProperty); + this.RPCVersion = (Version)ServiceConfig.GetValue(TcpRpcParserConfig.RPCVersionProperty); + } + + public virtual List GetRegisteredMethodItems(string proxyToken, object caller) + { + if (proxyToken == this.ProxyToken) + { + return this.methodStore.GetAllMethodItem(); + } + return null; + } + + private RpcContext IDInvoke(RpcSocketClient socketClient, RpcContext context) + { + if (this.TryGetSocketClient(context.ID, out TClient targetsocketClient)) + { + try + { + context.ReturnParameterBytes = targetsocketClient.CallBack(context, 5); + context.Status = 1; + } + catch (Exception ex) + { + context.Status = 3; + context.Message = ex.Message; + } + } + else + { + context.Status = 2; + } + + return context; + } + +#pragma warning disable + + /// + /// 收到协议数据 + /// + public event RRQMReceivedProcotolEventHandler Received; + + /// + /// 回调RPC + /// + /// 返回值 + /// ID + /// 函数唯一标识 + /// 调用设置 + /// 参数 + /// + public T CallBack(string id, int methodToken, InvokeOption invokeOption = null, params object[] parameters) + { + if (this.SocketClients.TryGetSocketClient(id, out TClient socketClient)) + { + return socketClient.CallBack(methodToken, invokeOption, parameters); + } + else + { + throw new RRQMRPCException("未找到该客户端"); + } + } + + /// + /// 回调RPC + /// + /// ID + /// 函数唯一标识 + /// 调用设置 + /// 参数 + public void CallBack(string id, int methodToken, InvokeOption invokeOption = null, params object[] parameters) + { + if (this.SocketClients.TryGetSocketClient(id, out TClient socketClient)) + { + socketClient.CallBack(methodToken, invokeOption, parameters); + } + else + { + throw new RRQMRPCException("未找到该客户端"); + } + } + + } +} \ No newline at end of file diff --git a/RRQMSocket.RPC/RRQMRPC/Socket/TcpRpcClient.cs b/RRQMSocket.RPC/RRQMRPC/Socket/TcpRpcClient.cs new file mode 100644 index 000000000..8c62e7bbf --- /dev/null +++ b/RRQMSocket.RPC/RRQMRPC/Socket/TcpRpcClient.cs @@ -0,0 +1,1201 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +using RRQMCore; +using RRQMCore.ByteManager; +using RRQMCore.Exceptions; +using RRQMCore.Log; +using RRQMCore.Run; +using RRQMCore.Serialization; +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Text; +using System.Threading.Tasks; + +namespace RRQMSocket.RPC.RRQMRPC +{ + /// + /// TcpRPCClient + /// + public class TcpRpcClient : ProtocolClient, IRRQMRpcClient + { + private MethodMap methodMap; + + private MethodStore methodStore; + + private RpcProxyInfo proxyFile; + + private ServerProviderCollection serverProviders; + + private WaitData singleWaitData; + + private RRQMWaitHandle waitHandle; + + static TcpRpcClient() + { + AddUsedProtocol(100, "请求RPC代理文件"); + AddUsedProtocol(101, "RPC调用"); + AddUsedProtocol(102, "获取注册服务"); + AddUsedProtocol(103, "ID调用客户端"); + AddUsedProtocol(104, "RPC回调"); + + for (short i = 105; i < 110; i++) + { + AddUsedProtocol(i, "保留协议"); + } + } + + /// + /// 构造函数 + /// + public TcpRpcClient() + { + this.methodMap = new MethodMap(); + this.serverProviders = new ServerProviderCollection(); + this.methodStore = new MethodStore(); + this.singleWaitData = new WaitData(); + this.waitHandle = new RRQMWaitHandle(); + } + + /// + /// 收到协议数据 + /// + public event RRQMReceivedProcotolEventHandler Received; + + /// + /// RPC初始化后 + /// + public event RRQMMessageEventHandler ServiceDiscovered; + + /// + /// 获取反向RPC映射图 + /// + public MethodMap MethodMap + { + get { return methodMap; } + } + + /// + /// 序列化生成器 + /// + public SerializeConverter SerializeConverter { get; private set; } + + /// + /// 获取反向RPC服务实例 + /// + public ServerProviderCollection ServerProviders + { + get { return serverProviders; } + } + + /// + /// 获取远程服务器RPC服务文件 + /// + /// + /// + public RpcProxyInfo GetProxyInfo() + { + string proxyToken = (string)this.ClientConfig.GetValue(TcpRpcClientConfig.ProxyTokenProperty); + byte[] data = Encoding.UTF8.GetBytes(string.IsNullOrEmpty(proxyToken) ? string.Empty : proxyToken); + this.InternalSend(100, data, 0, data.Length); + this.singleWaitData.Wait(1000 * 10); + + if (this.proxyFile == null) + { + throw new RRQMTimeoutException("获取引用文件超时"); + } + else if (this.proxyFile.Status == 2) + { + throw new RRQMRPCException(this.proxyFile.Message); + } + return this.proxyFile; + } + + /// + /// 发现服务 + /// + /// 是否触发初始化事件 + /// 已发现的服务 + public MethodItem[] DiscoveryService(bool isTrigger = true) + { + lock (locker) + { + if (!this.Online) + { + base.Connect(); + } + try + { + this.methodStore = null; + string proxyToken = (string)this.clientConfig.GetValue(TcpRpcClientConfig.ProxyTokenProperty); + byte[] data = new byte[0]; + if (!string.IsNullOrEmpty(proxyToken)) + { + data = Encoding.UTF8.GetBytes(proxyToken); + } + this.InternalSend(102, data, 0, data.Length); + } + catch (Exception ex) + { + throw ex; + } + this.singleWaitData.Wait(1000 * 10); + if (this.methodStore == null) + { + throw new RRQMRPCException("初始化超时"); + } + + if (isTrigger) + { + this.OnServiceDiscovered(new MesEventArgs("success")); + } + return this.methodStore.GetAllMethodItem().ToArray(); + } + } + + /// + /// RPC调用 + /// + /// 方法名 + /// 调用配置 + /// 参数 + /// + /// + /// + /// + /// + /// + /// + public T Invoke(string method, InvokeOption invokeOption, ref object[] parameters, Type[] types) + { + if (!this.methodStore.TryGetMethodItem(method, out MethodItem methodItem)) + { + throw new RRQMRPCNoRegisterException($"服务名为{method}的服务未找到注册信息"); + } + RpcContext context = new RpcContext(); + WaitData waitData = this.waitHandle.GetWaitData(context); + context.MethodToken = methodItem.MethodToken; + ByteBlock byteBlock = this.BytePool.GetByteBlock(this.BufferLength); + if (invokeOption == null) + { + invokeOption = InvokeOption.WaitInvoke; + } + try + { + context.Feedback = (byte)invokeOption.FeedbackType; + + List datas = new List(); + foreach (object parameter in parameters) + { + datas.Add(this.SerializeConverter.SerializeParameter(parameter)); + } + context.ParametersBytes = datas; + context.Serialize(byteBlock); + + switch (invokeOption.FeedbackType) + { + case FeedbackType.OnlySend: + { + this.InternalSendAsync(101, byteBlock.Buffer, 0, byteBlock.Len); + } + break; + case FeedbackType.WaitSend: + case FeedbackType.WaitInvoke: + { + this.InternalSend(101, byteBlock.Buffer, 0, byteBlock.Len); + } + break; + default: + break; + } + + } + catch (Exception ex) + { + throw ex; + } + finally + { + byteBlock.Dispose(); + } + + switch (invokeOption.FeedbackType) + { + case FeedbackType.OnlySend: + { + this.waitHandle.Destroy(waitData); + return default; + } + case FeedbackType.WaitSend: + { + waitData.Wait(invokeOption.Timeout); + RpcContext resultContext = (RpcContext)waitData.WaitResult; + this.waitHandle.Destroy(waitData); + + if (resultContext.Status == 0) + { + throw new RRQMTimeoutException("等待结果超时"); + } + else + { + return default; + } + } + case FeedbackType.WaitInvoke: + { + waitData.Wait(invokeOption.Timeout); + RpcContext resultContext = (RpcContext)waitData.WaitResult; + this.waitHandle.Destroy(waitData); + + if (resultContext.Status == 0) + { + throw new RRQMTimeoutException("等待结果超时"); + } + else if (resultContext.Status == 2) + { + throw new RRQMRPCInvokeException("未找到该公共方法,或该方法未标记RRQMRPCMethod"); + } + else if (resultContext.Status == 3) + { + throw new RRQMRPCException("该方法已被禁用"); + } + else if (resultContext.Status == 4) + { + throw new RRQMRPCException($"服务器已阻止本次行为,信息:{resultContext.Message}"); + } + else if (resultContext.Status == 5) + { + throw new RRQMRPCInvokeException("函数执行异常,详细信息:" + resultContext.Message); + } + else if (resultContext.Status == 6) + { + throw new RRQMRPCException($"函数异常,信息:{resultContext.Message}"); + } + if (methodItem.IsOutOrRef) + { + try + { + for (int i = 0; i < parameters.Length; i++) + { + parameters[i] = this.SerializeConverter.DeserializeParameter(resultContext.ParametersBytes[i], types[i]); + } + } + catch (Exception e) + { + throw new RRQMException(e.Message); + } + } + else + { + parameters = null; + } + try + { + return (T)this.SerializeConverter.DeserializeParameter(resultContext.ReturnParameterBytes, typeof(T)); + } + catch (Exception ex) + { + throw ex; + } + } + default: + return default; + } + } + + /// + /// RPC调用 + /// + /// 方法名 + /// 调用配置 + /// 参数 + /// + /// + /// + /// + /// + /// + public void Invoke(string method, InvokeOption invokeOption, ref object[] parameters, Type[] types) + { + if (!this.methodStore.TryGetMethodItem(method, out MethodItem methodItem)) + { + throw new RRQMRPCNoRegisterException($"服务名为{method}的服务未找到注册信息"); + } + RpcContext context = new RpcContext(); + WaitData waitData = this.waitHandle.GetWaitData(context); + context.MethodToken = methodItem.MethodToken; + ByteBlock byteBlock = this.BytePool.GetByteBlock(this.BufferLength); + if (invokeOption == null) + { + invokeOption = InvokeOption.WaitInvoke; + } + try + { + context.Feedback = (byte)invokeOption.FeedbackType; + List datas = new List(); + foreach (object parameter in parameters) + { + datas.Add(this.SerializeConverter.SerializeParameter(parameter)); + } + context.ParametersBytes = datas; + context.Serialize(byteBlock); + switch (invokeOption.FeedbackType) + { + case FeedbackType.OnlySend: + { + this.InternalSendAsync(101, byteBlock.Buffer, 0, byteBlock.Len); + } + break; + case FeedbackType.WaitSend: + case FeedbackType.WaitInvoke: + { + this.InternalSend(101, byteBlock.Buffer, 0, byteBlock.Len); + } + break; + default: + break; + } + } + catch (Exception ex) + { + throw ex; + } + finally + { + byteBlock.Dispose(); + } + switch (invokeOption.FeedbackType) + { + case FeedbackType.OnlySend: + { + this.waitHandle.Destroy(waitData); + break; + } + case FeedbackType.WaitSend: + { + waitData.Wait(invokeOption.Timeout); + RpcContext resultContext = (RpcContext)waitData.WaitResult; + this.waitHandle.Destroy(waitData); + + if (resultContext.Status == 0) + { + throw new RRQMTimeoutException("等待结果超时"); + } + break; + } + case FeedbackType.WaitInvoke: + { + waitData.Wait(invokeOption.Timeout); + RpcContext resultContext = (RpcContext)waitData.WaitResult; + this.waitHandle.Destroy(waitData); + + if (resultContext.Status == 0) + { + throw new RRQMTimeoutException("等待结果超时"); + } + else if (resultContext.Status == 2) + { + throw new RRQMRPCInvokeException("未找到该公共方法,或该方法未标记RRQMRPCMethod"); + } + else if (resultContext.Status == 3) + { + throw new RRQMRPCException("该方法已被禁用"); + } + else if (resultContext.Status == 4) + { + throw new RRQMRPCException($"服务器已阻止本次行为,信息:{resultContext.Message}"); + } + else if (resultContext.Status == 5) + { + throw new RRQMRPCInvokeException("函数执行异常,详细信息:" + resultContext.Message); + } + else if (resultContext.Status == 6) + { + throw new RRQMRPCException($"函数异常,信息:{resultContext.Message}"); + } + if (methodItem.IsOutOrRef) + { + try + { + for (int i = 0; i < parameters.Length; i++) + { + parameters[i] = this.SerializeConverter.DeserializeParameter(resultContext.ParametersBytes[i], types[i]); + } + } + catch (Exception ex) + { + throw ex; + } + } + else + { + parameters = null; + } + break; + } + } + } + + /// + /// RPC调用 + /// + /// 方法名 + /// 调用配置 + /// 参数 + /// + /// + /// + /// + /// + public void Invoke(string method, InvokeOption invokeOption, params object[] parameters) + { + if (!this.methodStore.TryGetMethodItem(method, out MethodItem methodItem)) + { + throw new RRQMRPCNoRegisterException($"服务名为{method}的服务未找到注册信息"); + } + RpcContext context = new RpcContext(); + WaitData waitData = this.waitHandle.GetWaitData(context); + context.MethodToken = methodItem.MethodToken; + ByteBlock byteBlock = this.BytePool.GetByteBlock(this.BufferLength); + if (invokeOption == null) + { + invokeOption = InvokeOption.WaitInvoke; + } + try + { + context.Feedback = (byte)invokeOption.FeedbackType; + List datas = new List(); + foreach (object parameter in parameters) + { + datas.Add(this.SerializeConverter.SerializeParameter(parameter)); + } + context.ParametersBytes = datas; + context.Serialize(byteBlock); + switch (invokeOption.FeedbackType) + { + case FeedbackType.OnlySend: + { + this.InternalSendAsync(101, byteBlock.Buffer, 0, byteBlock.Len); + } + break; + case FeedbackType.WaitSend: + case FeedbackType.WaitInvoke: + { + this.InternalSend(101, byteBlock.Buffer, 0, byteBlock.Len); + } + break; + default: + break; + } + } + catch (Exception ex) + { + throw ex; + } + finally + { + byteBlock.Dispose(); + } + switch (invokeOption.FeedbackType) + { + case FeedbackType.OnlySend: + { + this.waitHandle.Destroy(waitData); + break; + } + case FeedbackType.WaitSend: + { + waitData.Wait(invokeOption.Timeout); + RpcContext resultContext = (RpcContext)waitData.WaitResult; + this.waitHandle.Destroy(waitData); + + if (resultContext.Status == 0) + { + throw new RRQMTimeoutException("等待结果超时"); + } + break; + } + case FeedbackType.WaitInvoke: + { + waitData.Wait(invokeOption.Timeout); + RpcContext resultContext = (RpcContext)waitData.WaitResult; + this.waitHandle.Destroy(waitData); + + if (resultContext.Status == 0) + { + throw new RRQMTimeoutException("等待结果超时"); + } + else if (resultContext.Status == 2) + { + throw new RRQMRPCInvokeException("未找到该公共方法,或该方法未标记RRQMRPCMethod"); + } + else if (resultContext.Status == 3) + { + throw new RRQMRPCException("该方法已被禁用"); + } + else if (resultContext.Status == 4) + { + throw new RRQMRPCException($"服务器已阻止本次行为,信息:{resultContext.Message}"); + } + else if (resultContext.Status == 5) + { + throw new RRQMRPCInvokeException("函数执行异常,详细信息:" + resultContext.Message); + } + else if (resultContext.Status == 6) + { + throw new RRQMRPCException($"函数异常,信息:{resultContext.Message}"); + } + break; + } + default: + break; + } + } + + /// + /// RPC调用 + /// + /// 方法名 + /// 调用配置 + /// 参数 + /// + /// + /// + /// + /// + /// + public T Invoke(string method, InvokeOption invokeOption, params object[] parameters) + { + if (!this.methodStore.TryGetMethodItem(method, out MethodItem methodItem)) + { + throw new RRQMRPCNoRegisterException($"服务名为{method}的服务未找到注册信息"); + } + RpcContext context = new RpcContext(); + WaitData waitData = this.waitHandle.GetWaitData(context); + context.MethodToken = methodItem.MethodToken; + ByteBlock byteBlock = this.BytePool.GetByteBlock(this.BufferLength); + if (invokeOption == null) + { + invokeOption = InvokeOption.WaitInvoke; + } + try + { + context.Feedback = (byte)invokeOption.FeedbackType; + List datas = new List(); + foreach (object parameter in parameters) + { + datas.Add(this.SerializeConverter.SerializeParameter(parameter)); + } + context.ParametersBytes = datas; + context.Serialize(byteBlock); + switch (invokeOption.FeedbackType) + { + case FeedbackType.OnlySend: + { + this.InternalSendAsync(101, byteBlock.Buffer, 0, byteBlock.Len); + } + break; + case FeedbackType.WaitSend: + case FeedbackType.WaitInvoke: + { + this.InternalSend(101, byteBlock.Buffer, 0, byteBlock.Len); + } + break; + default: + break; + } + } + catch (Exception ex) + { + throw ex; + } + finally + { + byteBlock.Dispose(); + } + switch (invokeOption.FeedbackType) + { + case FeedbackType.OnlySend: + { + this.waitHandle.Destroy(waitData); + return default; + } + case FeedbackType.WaitSend: + { + waitData.Wait(invokeOption.Timeout); + RpcContext resultContext = (RpcContext)waitData.WaitResult; + this.waitHandle.Destroy(waitData); + + if (resultContext.Status == 0) + { + throw new RRQMTimeoutException("等待结果超时"); + } + else + { + return default; + } + } + case FeedbackType.WaitInvoke: + { + waitData.Wait(invokeOption.Timeout); + RpcContext resultContext = (RpcContext)waitData.WaitResult; + this.waitHandle.Destroy(waitData); + + if (resultContext.Status == 0) + { + throw new RRQMTimeoutException("等待结果超时"); + } + else if (resultContext.Status == 2) + { + throw new RRQMRPCInvokeException("未找到该公共方法,或该方法未标记RRQMRPCMethod"); + } + else if (resultContext.Status == 3) + { + throw new RRQMRPCException("该方法已被禁用"); + } + else if (resultContext.Status == 4) + { + throw new RRQMRPCException($"服务器已阻止本次行为,信息:{resultContext.Message}"); + } + else if (resultContext.Status == 5) + { + throw new RRQMRPCInvokeException("函数执行异常,详细信息:" + resultContext.Message); + } + else if (resultContext.Status == 6) + { + throw new RRQMRPCException($"函数异常,信息:{resultContext.Message}"); + } + + try + { + return (T)this.SerializeConverter.DeserializeParameter(resultContext.ReturnParameterBytes, typeof(T)); + } + catch (Exception ex) + { + throw ex; + } + } + default: + return default; + } + } + + /// + /// RPC调用 + /// + /// 客户端ID + /// 方法名 + /// 调用配置 + /// 参数 + /// + /// + /// + /// + public void Invoke(string id, int methodToken, InvokeOption invokeOption, params object[] parameters) + { + if (string.IsNullOrEmpty(id)) + { + throw new RRQMRPCException("目标ID不能为null或empty"); + } + RpcContext context = new RpcContext(); + WaitData waitData = this.waitHandle.GetWaitData(context); + context.MethodToken = methodToken; + context.ID = id; + ByteBlock byteBlock = this.BytePool.GetByteBlock(this.BufferLength); + if (invokeOption == null) + { + invokeOption = InvokeOption.WaitInvoke; + } + try + { + context.Feedback = (byte)invokeOption.FeedbackType; + List datas = new List(); + foreach (object parameter in parameters) + { + datas.Add(this.SerializeConverter.SerializeParameter(parameter)); + } + context.ParametersBytes = datas; + context.Serialize(byteBlock); + this.InternalSend(103, byteBlock.Buffer, 0, byteBlock.Len); + } + catch (Exception ex) + { + throw ex; + } + finally + { + byteBlock.Dispose(); + } + + switch (invokeOption.FeedbackType) + { + case FeedbackType.OnlySend: + { + this.waitHandle.Destroy(waitData); + } + break; + + case FeedbackType.WaitSend: + case FeedbackType.WaitInvoke: + { + waitData.Wait(invokeOption.Timeout); + RpcContext resultContext = (RpcContext)waitData.WaitResult; + this.waitHandle.Destroy(waitData); + + if (resultContext.Status == 0) + { + throw new RRQMTimeoutException("等待结果超时"); + } + else if (resultContext.Status == 2) + { + throw new RRQMRPCInvokeException("未找到该客户端ID"); + } + else if (resultContext.Status == 3) + { + throw new RRQMRPCException(resultContext.Message); + } + } + break; + + default: + break; + } + } + + /// + /// RPC调用 + /// + /// 客户端ID + /// 方法名 + /// 调用配置 + /// 参数 + /// + /// + /// + /// + /// + public T Invoke(string id, int methodToken, InvokeOption invokeOption, params object[] parameters) + { + if (string.IsNullOrEmpty(id)) + { + throw new RRQMRPCException("目标ID不能为null或empty"); + } + RpcContext context = new RpcContext(); + WaitData waitData = this.waitHandle.GetWaitData(context); + context.MethodToken = methodToken; + context.ID = id; + ByteBlock byteBlock = this.BytePool.GetByteBlock(this.BufferLength); + if (invokeOption == null) + { + invokeOption = InvokeOption.WaitInvoke; + } + try + { + context.Feedback = (byte)invokeOption.FeedbackType; + List datas = new List(); + foreach (object parameter in parameters) + { + datas.Add(this.SerializeConverter.SerializeParameter(parameter)); + } + context.ParametersBytes = datas; + context.Serialize(byteBlock); + this.InternalSend(103, byteBlock.Buffer, 0, byteBlock.Len); + } + catch (Exception ex) + { + throw ex; + } + finally + { + byteBlock.Dispose(); + } + + switch (invokeOption.FeedbackType) + { + case FeedbackType.OnlySend: + { + this.waitHandle.Destroy(waitData); + return default; + } + case FeedbackType.WaitSend: + case FeedbackType.WaitInvoke: + { + waitData.Wait(invokeOption.Timeout); + RpcContext resultContext = (RpcContext)waitData.WaitResult; + this.waitHandle.Destroy(waitData); + + if (resultContext.Status == 0) + { + throw new RRQMTimeoutException("等待结果超时"); + } + else if (resultContext.Status == 2) + { + throw new RRQMRPCInvokeException("未找到该客户端ID"); + } + else if (resultContext.Status == 3) + { + throw new RRQMRPCException(resultContext.Message); + } + + try + { + return (T)this.SerializeConverter.DeserializeParameter(resultContext.ReturnParameterBytes, typeof(T)); + } + catch (Exception ex) + { + throw ex; + } + } + default: + return default; + } + } + + /// + /// 注册服务 + /// + /// + /// 返回T实例 + public ServerProvider RegisterServer() where T : ServerProvider + { + ServerProvider serverProvider = (ServerProvider)Activator.CreateInstance(typeof(T)); + this.RegisterServer(serverProvider); + return serverProvider; + } + + /// + /// 注册服务 + /// + /// + /// + public ServerProvider RegisterServer(Type providerType) + { + if (!typeof(ServerProvider).IsAssignableFrom(providerType)) + { + throw new RRQMRPCException("类型不相符"); + } + ServerProvider serverProvider = (ServerProvider)Activator.CreateInstance(providerType); + this.RegisterServer(serverProvider); + return serverProvider; + } + + /// + /// 注册服务 + /// + /// + public void RegisterServer(ServerProvider serverProvider) + { + this.ServerProviders.Add(serverProvider); + MethodInfo[] methodInfos = serverProvider.GetType().GetMethods(); + foreach (MethodInfo method in methodInfos) + { + if (method.IsGenericMethod) + { + continue; + } + RRQMRPCCallBackMethodAttribute attribute = method.GetCustomAttribute(); + + if (attribute != null) + { + MethodInstance methodInstance = new MethodInstance(); + methodInstance.MethodToken = attribute.MethodToken; + methodInstance.Provider = serverProvider; + methodInstance.Method = method; + methodInstance.RPCAttributes = new RPCAttribute[] { attribute }; + methodInstance.IsEnable = true; + methodInstance.Parameters = method.GetParameters(); + List names = new List(); + foreach (var parameterInfo in methodInstance.Parameters) + { + names.Add(parameterInfo.Name); + } + methodInstance.ParameterNames = names.ToArray(); + if (typeof(Task).IsAssignableFrom(method.ReturnType)) + { + methodInstance.Async = true; + } + + ParameterInfo[] parameters = method.GetParameters(); + List types = new List(); + foreach (var parameter in parameters) + { + if (parameter.ParameterType.IsByRef) + { + throw new RRQMRPCException("反向RPC方法不支持out或ref"); + } + types.Add(parameter.ParameterType); + } + methodInstance.ParameterTypes = types.ToArray(); + + if (method.ReturnType == typeof(void)) + { + methodInstance.ReturnType = null; + } + else + { + if (methodInstance.Async) + { + methodInstance.ReturnType = method.ReturnType.GetGenericArguments()[0]; + } + else + { + methodInstance.ReturnType = method.ReturnType; + } + } + + try + { + this.MethodMap.Add(methodInstance); + } + catch + { + throw new RRQMRPCKeyException("MethodToken必须唯一"); + } + } + } + } + + /// + /// 移除注册服务 + /// + /// + /// + public int UnregisterServer(ServerProvider provider) + { + return this.UnregisterServer(provider.GetType()); + } + + /// + /// 移除注册服务 + /// + /// + /// + public int UnregisterServer(Type providerType) + { + if (!typeof(ServerProvider).IsAssignableFrom(providerType)) + { + throw new RRQMRPCException("类型不相符"); + } + this.ServerProviders.Remove(providerType); + if (this.MethodMap.RemoveServer(providerType, out IServerProvider serverProvider, out MethodInstance[] instances)) + { + return instances.Length; + } + return 0; + } + + /// + /// 移除注册服务 + /// + /// + /// + public int UnregisterServer() where T : ServerProvider + { + return this.UnregisterServer(typeof(T)); + } + + /// + /// 协议数据 + /// + /// + /// + protected sealed override void HandleProtocolData(short? procotol, ByteBlock byteBlock) + { + byte[] buffer = byteBlock.Buffer; + int r = byteBlock.Len; + switch (procotol) + { + case 100:/* 100表示获取RPC引用文件上传状态返回*/ + { + try + { + proxyFile = SerializeConvert.RRQMBinaryDeserialize(buffer, 2); + this.singleWaitData.Set(); + } + catch + { + proxyFile = null; + } + + break; + } + + case 101:/*函数调用*/ + { + try + { + byteBlock.Pos = 2; + RpcContext result = RpcContext.Deserialize(byteBlock); + this.waitHandle.SetRun(result.Sign, result); + } + catch (Exception e) + { + Logger.Debug(LogType.Error, this, $"错误代码: 101, 错误详情:{e.Message}"); + } + break; + } + case 102:/*获取服务*/ + { + try + { + List methodItems = SerializeConvert.RRQMBinaryDeserialize>(buffer, 2); + this.methodStore = new MethodStore(); + if (methodItems != null) + { + foreach (var item in methodItems) + { + this.methodStore.AddMethodItem(item); + } + } + this.singleWaitData.Set(); + } + catch (Exception e) + { + Logger.Debug(LogType.Error, this, $"错误代码: 102, 错误详情:{e.Message}"); + } + break; + } + case 103:/*ID函数调用*/ + { + try + { + byteBlock.Pos = 2; + RpcContext result = RpcContext.Deserialize(byteBlock); + this.waitHandle.SetRun(result.Sign, result); + } + catch (Exception e) + { + Logger.Debug(LogType.Error, this, $"错误代码: 103, 错误详情:{e.Message}"); + } + break; + } + case 104:/*反向函数调用*/ + { + Task.Run(() => + { + byteBlock.Pos = 2; + RpcContext rpcContext = RpcContext.Deserialize(byteBlock); + ByteBlock block = this.BytePool.GetByteBlock(this.BufferLength); + try + { + rpcContext = this.OnExecuteCallBack(rpcContext); + rpcContext.Serialize(block); + this.InternalSend(104, block.Buffer, 0, (int)block.Length); + } + catch (Exception e) + { + Logger.Debug(LogType.Error, this, $"错误代码: 104, 错误详情:{e.Message}"); + } + finally + { + block.Dispose(); + } + }); + + break; + } + default: + { + RPCHandleDefaultData(procotol, byteBlock); + break; + } + } + } + + /// + /// 加载配置 + /// + /// + protected override void LoadConfig(TcpClientConfig clientConfig) + { + base.LoadConfig(clientConfig); + this.SetDataHandlingAdapter(new FixedHeaderDataHandlingAdapter()); + this.SerializeConverter = (SerializeConverter)clientConfig.GetValue(TcpRpcClientConfig.SerializeConverterProperty); + } + + /// + /// 处理其余协议的事件触发 + /// + /// + /// + protected void OnHandleDefaultData(short? procotol, ByteBlock byteBlock) + { + Received?.Invoke(this, procotol, byteBlock); + } + + /// + /// RPC完成初始化后 + /// + /// + protected virtual void OnServiceDiscovered(MesEventArgs args) + { + try + { + this.ServiceDiscovered?.Invoke(this, args); + } + catch (Exception ex) + { + this.Logger.Debug(LogType.Error, this, $"在事件{nameof(ServiceDiscovered)}中发生异常", ex); + } + } + + /// + /// RPC处理其余协议 + /// + /// + /// + protected virtual void RPCHandleDefaultData(short? procotol, ByteBlock byteBlock) + { + OnHandleDefaultData(procotol, byteBlock); + } + + private RpcContext OnExecuteCallBack(RpcContext rpcContext) + { + if (this.methodMap != null) + { + if (this.methodMap.TryGet(rpcContext.MethodToken, out MethodInstance methodInstance)) + { + try + { + object[] ps = new object[rpcContext.ParametersBytes.Count]; + for (int i = 0; i < rpcContext.ParametersBytes.Count; i++) + { + ps[i] = this.SerializeConverter.DeserializeParameter(rpcContext.ParametersBytes[i], methodInstance.ParameterTypes[i]); + } + object result = methodInstance.Method.Invoke(methodInstance.Provider, ps); + if (result != null) + { + rpcContext.ReturnParameterBytes = this.SerializeConverter.SerializeParameter(result); + } + rpcContext.Status = 1; + } + catch (Exception ex) + { + rpcContext.Status = 4; + rpcContext.Message = ex.Message; + } + } + else + { + rpcContext.Status = 2; + } + } + else + { + rpcContext.Status = 3; + } + + rpcContext.ParametersBytes = null; + return rpcContext; + } + } +} \ No newline at end of file diff --git a/RRQMSocket.RPC/RRQMRPC/Socket/TcpRpcParser.cs b/RRQMSocket.RPC/RRQMRPC/Socket/TcpRpcParser.cs new file mode 100644 index 000000000..e42e027ae --- /dev/null +++ b/RRQMSocket.RPC/RRQMRPC/Socket/TcpRpcParser.cs @@ -0,0 +1,21 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ + +namespace RRQMSocket.RPC.RRQMRPC +{ + /// + /// TCP RPC解释器 + /// + public class TcpRpcParser : TcpParser + { + } +} \ No newline at end of file diff --git a/RRQMSocket.RPC/RRQMRPC/Socket/UdpRpcClient.cs b/RRQMSocket.RPC/RRQMRPC/Socket/UdpRpcClient.cs new file mode 100644 index 000000000..2d9fd0263 --- /dev/null +++ b/RRQMSocket.RPC/RRQMRPC/Socket/UdpRpcClient.cs @@ -0,0 +1,712 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +using RRQMCore.ByteManager; +using RRQMCore.Exceptions; +using RRQMCore.Log; +using RRQMCore.Run; +using RRQMCore.Serialization; +using System; +using System.Collections.Generic; +using System.Net; +using System.Text; + +namespace RRQMSocket.RPC.RRQMRPC +{ + /// + /// UDP协议客户端 + /// + public class UdpRpcClient : UdpSession, IRRQMRpcClient + { + private MethodStore methodStore; + private RpcProxyInfo proxyFile; + private WaitData singleWaitData; + private RRQMWaitHandle waitHandle; + private WaitResult waitResult; + + /// + /// 发现服务后 + /// + public event RRQMMessageEventHandler ServiceDiscovered; + + /// + /// 构造函数 + /// + public UdpRpcClient() + { + this.waitHandle = new RRQMWaitHandle(); + this.SerializeConverter = new BinarySerializeConverter(); + this.waitResult = new WaitResult(); + this.singleWaitData = new WaitData(); + } + + /// + /// 返回ID + /// + public string ID => null; + + /// + /// 序列化生成器 + /// + public SerializeConverter SerializeConverter { get; set; } + + /// + /// 获取远程服务器RPC服务文件 + /// + /// + /// + /// + public RpcProxyInfo GetProxyInfo() + { + int count = 0; + while (count < 3) + { + lock (this) + { + byte[] datas; + string proxyToken = (string)this.ServiceConfig.GetValue(UdpRpcClientConfig.ProxyTokenProperty); + if (proxyToken == null) + { + datas = new byte[0]; + } + else + { + datas = Encoding.UTF8.GetBytes(proxyToken); + } + this.UDPSend(100, datas, 0, datas.Length); + this.singleWaitData.Wait(5000); + if (this.proxyFile != null) + { + return this.proxyFile; + } + } + count++; + } + + return null; + } + + /// + /// 发现服务 + /// + /// 是否触发初始化事件 + /// 已发现的服务 + public MethodItem[] DiscoveryService(bool isTrigger = true) + { + if (this.ServerState != ServerState.Running) + { + throw new RRQMRPCException("UDP端需要先绑定本地监听端口"); + } + + int count = 0; + while (count < 3) + { + lock (this) + { + try + { + this.methodStore = null; + + string proxyToken = (string)this.ServiceConfig.GetValue(UdpRpcClientConfig.ProxyTokenProperty); + byte[] data = new byte[0]; + if (!string.IsNullOrEmpty(proxyToken)) + { + data = Encoding.UTF8.GetBytes(proxyToken); + } + + this.UDPSend(102, data, 0, data.Length); + this.singleWaitData.Wait(1000 * 5); + if (this.methodStore != null) + { + if (isTrigger) + { + this.OnServiceDiscovered(new MesEventArgs("success")); + } + return this.methodStore.GetAllMethodItem().ToArray(); + } + } + catch (Exception e) + { + throw new RRQMRPCException(e.Message); + } + } + count++; + } + throw new RRQMTimeoutException("初始化超时"); + } + + /// + /// RPC调用 + /// + /// 方法名 + /// 调用配置 + /// 参数 + /// + /// + /// + /// + /// + /// + public T Invoke(string method, InvokeOption invokeOption, ref object[] parameters, Type[] types) + { + if (!this.methodStore.TryGetMethodItem(method, out MethodItem methodItem)) + { + throw new RRQMException($"服务名为{method}的服务未找到注册信息"); + } + RpcContext context = new RpcContext(); + WaitData waitData = this.waitHandle.GetWaitData(context); + context.MethodToken = methodItem.MethodToken; + ByteBlock byteBlock = this.BytePool.GetByteBlock(this.BufferLength); + if (invokeOption == null) + { + invokeOption = InvokeOption.WaitInvoke; + } + try + { + context.Feedback = (byte)invokeOption.FeedbackType; + + List datas = new List(); + foreach (object parameter in parameters) + { + datas.Add(this.SerializeConverter.SerializeParameter(parameter)); + } + context.ParametersBytes = datas; + context.Serialize(byteBlock); + this.UDPSend(101, byteBlock.Buffer, 0, byteBlock.Len); + } + catch (Exception e) + { + throw new RRQMException(e.Message); + } + finally + { + byteBlock.Dispose(); + } + + switch (invokeOption.FeedbackType) + { + case FeedbackType.OnlySend: + { + this.waitHandle.Destroy(waitData); + return default; + } + case FeedbackType.WaitSend: + { + waitData.Wait(invokeOption.Timeout); + RpcContext resultContext = waitData.WaitResult; + this.waitHandle.Destroy(waitData); + + if (resultContext.Status == 0) + { + throw new RRQMTimeoutException("等待结果超时"); + } + else + { + return default; + } + } + case FeedbackType.WaitInvoke: + { + waitData.Wait(invokeOption.Timeout); + RpcContext resultContext = waitData.WaitResult; + this.waitHandle.Destroy(waitData); + + if (resultContext.Status == 0) + { + throw new RRQMTimeoutException("等待结果超时"); + } + else if (resultContext.Status == 2) + { + throw new RRQMRPCInvokeException("未找到该公共方法,或该方法未标记RRQMRPCMethod"); + } + else if (resultContext.Status == 3) + { + throw new RRQMRPCException("该方法已被禁用"); + } + else if (resultContext.Status == 4) + { + throw new RRQMRPCException($"服务器已阻止本次行为,信息:{resultContext.Message}"); + } + else if (resultContext.Status == 5) + { + throw new RRQMRPCInvokeException("函数执行异常,详细信息:" + resultContext.Message); + } + else if (resultContext.Status == 6) + { + throw new RRQMRPCException($"函数异常,信息:{resultContext.Message}"); + } + if (methodItem.IsOutOrRef) + { + try + { + for (int i = 0; i < parameters.Length; i++) + { + parameters[i] = this.SerializeConverter.DeserializeParameter(resultContext.ParametersBytes[i], types[i]); + } + } + catch (Exception e) + { + throw new RRQMException(e.Message); + } + } + else + { + parameters = null; + } + try + { + return (T)this.SerializeConverter.DeserializeParameter(resultContext.ReturnParameterBytes, typeof(T)); + } + catch (Exception e) + { + throw new RRQMException(e.Message); + } + } + default: + return default; + } + } + + /// + /// RPC调用 + /// + /// 方法名 + /// 调用配置 + /// 参数 + /// + /// + /// + /// + /// + public void Invoke(string method, InvokeOption invokeOption, ref object[] parameters, Type[] types) + { + if (!this.methodStore.TryGetMethodItem(method, out MethodItem methodItem)) + { + throw new RRQMException($"服务名为{method}的服务未找到注册信息"); + } + RpcContext context = new RpcContext(); + WaitData waitData = this.waitHandle.GetWaitData(context); + context.MethodToken = methodItem.MethodToken; + ByteBlock byteBlock = this.BytePool.GetByteBlock(this.BufferLength); + if (invokeOption == null) + { + invokeOption = InvokeOption.WaitInvoke; + } + try + { + context.Feedback = (byte)invokeOption.FeedbackType; + List datas = new List(); + foreach (object parameter in parameters) + { + datas.Add(this.SerializeConverter.SerializeParameter(parameter)); + } + context.ParametersBytes = datas; + context.Serialize(byteBlock); + this.UDPSend(101, byteBlock.Buffer, 0, byteBlock.Len); + } + catch (Exception e) + { + throw new RRQMException(e.Message); + } + finally + { + byteBlock.Dispose(); + } + switch (invokeOption.FeedbackType) + { + case FeedbackType.OnlySend: + { + this.waitHandle.Destroy(waitData); + break; + } + case FeedbackType.WaitSend: + { + waitData.Wait(invokeOption.Timeout); + RpcContext resultContext = waitData.WaitResult; + this.waitHandle.Destroy(waitData); + + if (resultContext.Status == 0) + { + throw new RRQMTimeoutException("等待结果超时"); + } + break; + } + case FeedbackType.WaitInvoke: + { + waitData.Wait(invokeOption.Timeout); + RpcContext resultContext = waitData.WaitResult; + this.waitHandle.Destroy(waitData); + + if (resultContext.Status == 0) + { + throw new RRQMTimeoutException("等待结果超时"); + } + else if (resultContext.Status == 2) + { + throw new RRQMRPCInvokeException("未找到该公共方法,或该方法未标记RRQMRPCMethod"); + } + else if (resultContext.Status == 3) + { + throw new RRQMRPCException("该方法已被禁用"); + } + else if (resultContext.Status == 4) + { + throw new RRQMRPCException($"服务器已阻止本次行为,信息:{resultContext.Message}"); + } + else if (resultContext.Status == 5) + { + throw new RRQMRPCInvokeException("函数执行异常,详细信息:" + resultContext.Message); + } + else if (resultContext.Status == 6) + { + throw new RRQMRPCException($"函数异常,信息:{resultContext.Message}"); + } + if (methodItem.IsOutOrRef) + { + try + { + for (int i = 0; i < parameters.Length; i++) + { + parameters[i] = this.SerializeConverter.DeserializeParameter(resultContext.ParametersBytes[i], types[i]); + } + } + catch (Exception e) + { + throw new RRQMException(e.Message); + } + } + else + { + parameters = null; + } + break; + } + } + } + + /// + /// RPC调用 + /// + /// 方法名 + /// 调用配置 + /// 参数 + /// + /// + /// + /// + public void Invoke(string method, InvokeOption invokeOption, params object[] parameters) + { + if (!this.methodStore.TryGetMethodItem(method, out MethodItem methodItem)) + { + throw new RRQMException($"服务名为{method}的服务未找到注册信息"); + } + RpcContext context = new RpcContext(); + WaitData waitData = this.waitHandle.GetWaitData(context); + context.MethodToken = methodItem.MethodToken; + ByteBlock byteBlock = this.BytePool.GetByteBlock(this.BufferLength); + if (invokeOption == null) + { + invokeOption = InvokeOption.WaitInvoke; + } + try + { + context.Feedback = (byte)invokeOption.FeedbackType; + List datas = new List(); + foreach (object parameter in parameters) + { + datas.Add(this.SerializeConverter.SerializeParameter(parameter)); + } + context.ParametersBytes = datas; + context.Serialize(byteBlock); + this.UDPSend(101, byteBlock.Buffer, 0, byteBlock.Len); + } + catch (Exception e) + { + throw new RRQMException(e.Message); + } + finally + { + byteBlock.Dispose(); + } + switch (invokeOption.FeedbackType) + { + case FeedbackType.OnlySend: + { + this.waitHandle.Destroy(waitData); + break; + } + case FeedbackType.WaitSend: + { + waitData.Wait(invokeOption.Timeout); + RpcContext resultContext = waitData.WaitResult; + this.waitHandle.Destroy(waitData); + + if (resultContext.Status == 0) + { + throw new RRQMTimeoutException("等待结果超时"); + } + break; + } + case FeedbackType.WaitInvoke: + { + waitData.Wait(invokeOption.Timeout); + RpcContext resultContext = waitData.WaitResult; + this.waitHandle.Destroy(waitData); + + if (resultContext.Status == 0) + { + throw new RRQMTimeoutException("等待结果超时"); + } + else if (resultContext.Status == 2) + { + throw new RRQMRPCInvokeException("未找到该公共方法,或该方法未标记RRQMRPCMethod"); + } + else if (resultContext.Status == 3) + { + throw new RRQMRPCException("该方法已被禁用"); + } + else if (resultContext.Status == 4) + { + throw new RRQMRPCException($"服务器已阻止本次行为,信息:{resultContext.Message}"); + } + else if (resultContext.Status == 5) + { + throw new RRQMRPCInvokeException("函数执行异常,详细信息:" + resultContext.Message); + } + else if (resultContext.Status == 6) + { + throw new RRQMRPCException($"函数异常,信息:{resultContext.Message}"); + } + break; + } + default: + break; + } + } + + /// + /// RPC调用 + /// + /// 方法名 + /// 调用配置 + /// 参数 + /// + /// + /// + /// + /// + public T Invoke(string method, InvokeOption invokeOption, params object[] parameters) + { + if (!this.methodStore.TryGetMethodItem(method, out MethodItem methodItem)) + { + throw new RRQMException($"服务名为{method}的服务未找到注册信息"); + } + RpcContext context = new RpcContext(); + WaitData waitData = this.waitHandle.GetWaitData(context); + context.MethodToken = methodItem.MethodToken; + ByteBlock byteBlock = this.BytePool.GetByteBlock(this.BufferLength); + if (invokeOption == null) + { + invokeOption = InvokeOption.WaitInvoke; + } + try + { + context.Feedback = (byte)invokeOption.FeedbackType; + List datas = new List(); + foreach (object parameter in parameters) + { + datas.Add(this.SerializeConverter.SerializeParameter(parameter)); + } + context.ParametersBytes = datas; + context.Serialize(byteBlock); + this.UDPSend(101, byteBlock.Buffer, 0, byteBlock.Len); + } + catch (Exception e) + { + throw new RRQMException(e.Message); + } + finally + { + byteBlock.Dispose(); + } + switch (invokeOption.FeedbackType) + { + case FeedbackType.OnlySend: + { + this.waitHandle.Destroy(waitData); + return default; + } + case FeedbackType.WaitSend: + { + waitData.Wait(invokeOption.Timeout); + RpcContext resultContext = waitData.WaitResult; + this.waitHandle.Destroy(waitData); + + if (resultContext.Status == 0) + { + throw new RRQMTimeoutException("等待结果超时"); + } + else + { + return default; + } + } + case FeedbackType.WaitInvoke: + { + waitData.Wait(invokeOption.Timeout); + RpcContext resultContext = waitData.WaitResult; + this.waitHandle.Destroy(waitData); + + if (resultContext.Status == 0) + { + throw new RRQMTimeoutException("等待结果超时"); + } + else if (resultContext.Status == 2) + { + throw new RRQMRPCInvokeException("未找到该公共方法,或该方法未标记RRQMRPCMethod"); + } + else if (resultContext.Status == 3) + { + throw new RRQMRPCException("该方法已被禁用"); + } + else if (resultContext.Status == 4) + { + throw new RRQMRPCException($"服务器已阻止本次行为,信息:{resultContext.Message}"); + } + else if (resultContext.Status == 5) + { + throw new RRQMRPCInvokeException("函数执行异常,详细信息:" + resultContext.Message); + } + else if (resultContext.Status == 6) + { + throw new RRQMRPCException($"函数异常,信息:{resultContext.Message}"); + } + + try + { + return (T)this.SerializeConverter.DeserializeParameter(resultContext.ReturnParameterBytes, typeof(T)); + } + catch (Exception e) + { + throw new RRQMException(e.Message); + } + } + default: + return default; + } + } + + + /// + /// 密封数据 + /// + /// + /// + protected override void HandleReceivedData(EndPoint remoteEndPoint, ByteBlock byteBlock) + { + byte[] buffer = byteBlock.Buffer; + int r = (int)byteBlock.Position; + int procotol = BitConverter.ToInt16(buffer, 0); + switch (procotol) + { + case 100:/* 100表示获取RPC引用文件上传状态返回*/ + { + try + { + proxyFile = SerializeConvert.RRQMBinaryDeserialize(buffer, 2); + this.singleWaitData.Set(); + } + catch + { + proxyFile = null; + } + + break; + } + + case 101:/*函数调用返回数据对象*/ + { + try + { + byteBlock.Pos = 2; + RpcContext result = RpcContext.Deserialize(byteBlock); + this.waitHandle.SetRun(result.Sign, result); + } + catch (Exception e) + { + Logger.Debug(LogType.Error, this, $"错误代码: 101, 错误详情:{e.Message}"); + } + break; + } + + case 102:/*连接初始化返回数据对象*/ + { + try + { + List methodItems = SerializeConvert.RRQMBinaryDeserialize>(buffer, 2); + this.methodStore = new MethodStore(); + foreach (var item in methodItems) + { + this.methodStore.AddMethodItem(item); + } + + this.singleWaitData.Set(); + } + catch (Exception e) + { + Logger.Debug(LogType.Error, this, $"错误代码: 102, 错误详情:{e.Message}"); + } + break; + } + } + } + + private void UDPSend(short procotol, byte[] buffer, int offset, int length) + { + ByteBlock byteBlock = this.BytePool.GetByteBlock(length + 2); + try + { + byteBlock.Write(BitConverter.GetBytes(procotol)); + byteBlock.Write(buffer, offset, length); + this.Send(byteBlock.Buffer, 0, byteBlock.Len); + } + catch (Exception ex) + { + this.Logger.Debug(LogType.Error, this, ex.Message); + } + finally + { + byteBlock.Dispose(); + } + } + + private void UDPSend(short procotol) + { + this.UDPSend(procotol, new byte[0], 0, 0); + } + + /// + /// RPC完成初始化后 + /// + /// + protected virtual void OnServiceDiscovered(MesEventArgs args) + { + try + { + this.ServiceDiscovered?.Invoke(this, args); + } + catch (Exception ex) + { + this.Logger.Debug(LogType.Error, this, $"在事件{nameof(ServiceDiscovered)}中发生异常", ex); + } + } + } +} \ No newline at end of file diff --git a/RRQMSocket.RPC/RRQMRPC/Socket/UdpRpcParser.cs b/RRQMSocket.RPC/RRQMRPC/Socket/UdpRpcParser.cs new file mode 100644 index 000000000..6e0e87bbd --- /dev/null +++ b/RRQMSocket.RPC/RRQMRPC/Socket/UdpRpcParser.cs @@ -0,0 +1,382 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +using RRQMCore.ByteManager; +using RRQMCore.Log; +using RRQMCore.Serialization; +using System; +using System.Collections.Generic; +using System.IO; +using System.Net; +using System.Text; + +namespace RRQMSocket.RPC.RRQMRPC +{ + /// + /// UDP RPC解释器 + /// + public class UdpRpcParser : UdpSession, IRPCParser, IRRQMRpcParser + { + /// + /// 构造函数 + /// + public UdpRpcParser() + { + this.methodStore = new MethodStore(); + this.proxyInfo = new RpcProxyInfo(); + } + +#pragma warning disable + public MethodMap MethodMap { get; private set; } + + public RPCService RPCService { get; private set; } + + public Action RRQMExecuteMethod { get; private set; } + + public CellCode[] Codes { get => this.proxyInfo == null ? null : this.proxyInfo.Codes.ToArray(); } + + public string NameSpace { get; private set; } + + public RpcProxyInfo ProxyInfo { get => proxyInfo; } + + public string ProxyToken { get; private set; } + + public Version RPCVersion { get; private set; } + + public SerializeConverter SerializeConverter { get; private set; } + + private MethodStore methodStore; + + private RpcProxyInfo proxyInfo; + + public MethodStore MethodStore => this.methodStore; + + public void SetExecuteMethod(Action executeMethod) + { + this.RRQMExecuteMethod = executeMethod; + } + + public void SetMethodMap(MethodMap methodMap) + { + this.MethodMap = methodMap; + } + + public void SetRPCService(RPCService service) + { + this.RPCService = service; + } + + public virtual RpcProxyInfo GetProxyInfo(string proxyToken, object caller) + { + RpcProxyInfo proxyInfo = new RpcProxyInfo(); + if (this.ProxyToken == proxyToken) + { + proxyInfo.AssemblyData = this.ProxyInfo.AssemblyData; + proxyInfo.AssemblyName = this.ProxyInfo.AssemblyName; + proxyInfo.Codes = this.ProxyInfo.Codes; + proxyInfo.Version = this.ProxyInfo.Version; + proxyInfo.Status = 1; + } + else + { + proxyInfo.Status = 2; + proxyInfo.Message = "令箭不正确"; + } + + return proxyInfo; + } + + public virtual void ExecuteContext(RpcContext context, object caller) + { + MethodInvoker methodInvoker = new MethodInvoker(); + methodInvoker.Caller = caller; + methodInvoker.Flag = context; + if (this.MethodMap.TryGet(context.MethodToken, out MethodInstance methodInstance)) + { + try + { + if (methodInstance.IsEnable) + { + object[] ps = new object[methodInstance.ParameterTypes.Length]; + for (int i = 0; i < methodInstance.ParameterTypes.Length; i++) + { + ps[i] = this.SerializeConverter.DeserializeParameter(context.ParametersBytes[i], methodInstance.ParameterTypes[i]); + } + methodInvoker.Parameters = ps; + } + else + { + methodInvoker.Status = InvokeStatus.UnEnable; + } + } + catch (Exception ex) + { + methodInvoker.Status = InvokeStatus.Exception; + methodInvoker.StatusMessage = ex.Message; + } + + this.RRQMExecuteMethod.Invoke(this, methodInvoker, methodInstance); + } + else + { + methodInvoker.Status = InvokeStatus.UnFound; + this.RRQMExecuteMethod.Invoke(this, methodInvoker, null); + } + } + + protected override void LoadConfig(ServiceConfig ServiceConfig) + { + base.LoadConfig(ServiceConfig); + this.SerializeConverter = (SerializeConverter)ServiceConfig.GetValue(UdpRpcParserConfig.SerializeConverterProperty); + this.ProxyToken = (string)ServiceConfig.GetValue(UdpRpcParserConfig.ProxyTokenProperty); + this.NameSpace = (string)ServiceConfig.GetValue(UdpRpcParserConfig.NameSpaceProperty); + this.RPCVersion = (Version)ServiceConfig.GetValue(UdpRpcParserConfig.RPCVersionProperty); + } + + public virtual List GetRegisteredMethodItems(string proxyToken, object caller) + { + return this.methodStore.GetAllMethodItem(); + } + + public void OnRegisterServer(IServerProvider provider, MethodInstance[] methodInstances) + { + Tools.GetRPCMethod(methodInstances, this.NameSpace, ref this.methodStore, this.RPCVersion, ref this.proxyInfo); + } + + public void OnUnregisterServer(IServerProvider provider, MethodInstance[] methodInstances) + { + foreach (var item in methodInstances) + { + this.methodStore.RemoveMethodItem(item.MethodToken); + } + + CellCode cellCode = null; + foreach (var item in this.proxyInfo.Codes) + { + if (item.Name == provider.GetType().Name) + { + cellCode = item; + break; + } + } + if (cellCode != null) + { + this.proxyInfo.Codes.Remove(cellCode); + } + } + + public void OnEndInvoke(MethodInvoker methodInvoker, MethodInstance methodInstance) + { + RpcContext context = (RpcContext)methodInvoker.Flag; + if (context.Feedback != 2) + { + return; + } + ByteBlock byteBlock = this.BytePool.GetByteBlock(this.BufferLength); + try + { + switch (methodInvoker.Status) + { + case InvokeStatus.Ready: + { + break; + } + + case InvokeStatus.UnFound: + { + context.Status = 2; + break; + } + case InvokeStatus.Success: + { + if (methodInstance.MethodToken > 50000000) + { + context.ReturnParameterBytes = this.SerializeConverter.SerializeParameter(methodInvoker.ReturnParameter); + } + else + { + context.ReturnParameterBytes = null; + } + + if (methodInstance.IsByRef) + { + context.ParametersBytes = new List(); + foreach (var item in methodInvoker.Parameters) + { + context.ParametersBytes.Add(this.SerializeConverter.SerializeParameter(item)); + } + } + else + { + context.ParametersBytes = null; + } + + context.Status = 1; + break; + } + case InvokeStatus.Abort: + { + context.Status = 4; + context.Message = methodInvoker.StatusMessage; + break; + } + case InvokeStatus.UnEnable: + { + context.Status = 3; + break; + } + case InvokeStatus.InvocationException: + { + context.Status = 5; + context.Message = methodInvoker.StatusMessage; + break; + } + case InvokeStatus.Exception: + { + context.Status = 6; + context.Message = methodInvoker.StatusMessage; + break; + } + default: + break; + } + + context.Serialize(byteBlock); + this.UDPSend(101, (EndPoint)methodInvoker.Caller, byteBlock.Buffer, 0, byteBlock.Len); + } + catch (Exception ex) + { + Logger.Debug(LogType.Error, this, ex.Message); + } + finally + { + byteBlock.Dispose(); + } + } + +#pragma warning restore + + private void UDPSend(short procotol, EndPoint endPoint, byte[] buffer, int offset, int length) + { + ByteBlock byteBlock = this.BytePool.GetByteBlock(length + 2); + try + { + byteBlock.Write(BitConverter.GetBytes(procotol)); + byteBlock.Write(buffer, offset, length); + this.SendTo(byteBlock.Buffer, 0, byteBlock.Len, endPoint); + } + finally + { + byteBlock.Dispose(); + } + } + + private void UDPSend(short procotol, EndPoint endPoint, byte[] buffer) + { + this.UDPSend(procotol, endPoint, buffer, 0, buffer.Length); + } + + /// + /// 密封处理 + /// + /// + /// + protected sealed override void HandleReceivedData(EndPoint remoteEndPoint, ByteBlock byteBlock) + { + byte[] buffer = byteBlock.Buffer; + int r = byteBlock.Len; + short procotol = BitConverter.ToInt16(buffer, 0); + + switch (procotol) + { + case 100:/*100,请求RPC文件*/ + { + try + { + string proxyToken = Encoding.UTF8.GetString(buffer, 2, r - 2); + this.UDPSend(100, remoteEndPoint, SerializeConvert.RRQMBinarySerialize(this.GetProxyInfo(proxyToken, remoteEndPoint), true)); + } + catch (Exception e) + { + Logger.Debug(LogType.Error, this, $"UDP错误代码: 100, 错误详情:{e.Message}"); + } + break; + } + + case 101:/*函数式调用*/ + { + try + { + byteBlock.Pos = 2; + RpcContext content = RpcContext.Deserialize(byteBlock); + if (content.Feedback == 1) + { + List ps = content.ParametersBytes; + + ByteBlock returnByteBlock = this.BytePool.GetByteBlock(this.BufferLength); + try + { + content.ParametersBytes = null; + content.Status = 1; + content.Serialize(returnByteBlock); + this.UDPSend(101, remoteEndPoint, returnByteBlock.Buffer, 0, (int)returnByteBlock.Length); + } + finally + { + content.ParametersBytes = ps; + returnByteBlock.Dispose(); + } + } + this.ExecuteContext(content, remoteEndPoint); + } + catch (Exception e) + { + Logger.Debug(LogType.Error, this, $"错误代码: 101, 错误详情:{e.Message}"); + } + break; + } + case 102:/*连接初始化*/ + { + try + { + string proxyToken = Encoding.UTF8.GetString(buffer, 2, r - 2); + UDPSend(102, remoteEndPoint, SerializeConvert.RRQMBinarySerialize(this.GetRegisteredMethodItems(proxyToken, remoteEndPoint), true)); + } + catch (Exception e) + { + Logger.Debug(LogType.Error, this, $"错误代码: 102, 错误详情:{e.Message}"); + } + break; + } + } + } + +#if NET45_OR_GREATER + + /// + /// 编译代理 + /// + /// 存放目标文件夹 + public void CompilerProxy(string targetDic = "") + { + string assemblyInfo = CodeGenerator.GetAssemblyInfo(this.proxyInfo.AssemblyName, this.proxyInfo.Version); + List codesString = new List(); + codesString.Add(assemblyInfo); + foreach (var item in this.proxyInfo.Codes) + { + codesString.Add(item.Code); + } + RpcCompiler.CompileCode(Path.Combine(targetDic, this.proxyInfo.AssemblyName), codesString.ToArray()); + } + +#endif + } +} \ No newline at end of file diff --git a/RRQMSocket.RPC/RRQMSocket.RPC.csproj b/RRQMSocket.RPC/RRQMSocket.RPC.csproj new file mode 100644 index 000000000..522903a7f --- /dev/null +++ b/RRQMSocket.RPC/RRQMSocket.RPC.csproj @@ -0,0 +1,72 @@ + + + net45;netcoreapp3.1;netstandard2.0 + RRQM.ico + true + RRQM.pfx + 5.5.0 + 若汝棋茗 + Copyright © 2021 若汝棋茗 + 介绍:这是一个高性能的RPC微服务框架,支持异步调用、权限管理、错误状态返回、服务回调等。在空载函数执行时,10万次调用仅3.8秒,在不返回状态时,仅0.9秒。 + +更新说明: +修改:服务类ServerProvider改为IServerProvider接口。 +修改:RRQMRPC下多数含有“RPC”的类改为“Rpc”驼峰拼写。 +增加:序列化方式中增加JsonSerializeConverter。 + +Demo:https://gitee.com/RRQM_OS/RRQMBox +API:https://gitee.com/RRQM_OS/RRQM/wikis/pages + https://gitee.com/dotnetchina/RRQMSocket + + true + RRQM.png + 若汝棋茗 + true + LICENSE + RPC,TCP,UDP,IOCP + + + + bin\Debug\netstandard2.0\RRQMSocket.RPC.xml + + + + + bin\Release\netstandard2.0\RRQMSocket.RPC.xml + + + + + bin\Debug\net45\RRQMSocket.RPC.xml + + + + + bin\Release\net45\RRQMSocket.RPC.xml + + + + + bin\Debug\netcoreapp3.1\RRQMSocket.RPC.xml + + + + + bin\Release\netcoreapp3.1\RRQMSocket.RPC.xml + + + + + True + + + + True + + + + + + + + diff --git a/RRQMSocket.sln b/RRQMSocket.sln new file mode 100644 index 000000000..d07992e15 --- /dev/null +++ b/RRQMSocket.sln @@ -0,0 +1,61 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.30804.86 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RRQMSocket", "RRQMSocket\RRQMSocket.csproj", "{2869A094-BBB1-4F15-A54D-581BC927E92E}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RRQMSocket.FileTransfer", "RRQMSocket.FileTransfer\RRQMSocket.FileTransfer.csproj", "{717FB0F1-3DA3-4A70-9502-8BAA8A949672}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RRQMSocket.Http", "RRQMSocket.Http\RRQMSocket.Http.csproj", "{94F85A89-73E3-4DB2-A87F-CDE9B71CF6F3}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RRQMSocket.RPC", "RRQMSocket.RPC\RRQMSocket.RPC.csproj", "{E88189B5-188B-4798-B5A5-FB8AF9C2A3E3}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RRQMSocket.RPC.JsonRpc", "RRQMSocket.RPC.JsonRpc\RRQMSocket.RPC.JsonRpc.csproj", "{CDED55FF-86BF-47CC-926C-069B1AE6E680}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RRQMSocket.RPC.WebApi", "RRQMSocket.RPC.WebApi\RRQMSocket.RPC.WebApi.csproj", "{313AC178-2193-4B2A-8D1D-748EDE6E55BE}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RRQMSocket.RPC.XmlRpc", "RRQMSocket.RPC.XmlRpc\RRQMSocket.RPC.XmlRpc.csproj", "{61179122-C51F-47AC-8E2A-D124AF4E3DF5}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {2869A094-BBB1-4F15-A54D-581BC927E92E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2869A094-BBB1-4F15-A54D-581BC927E92E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2869A094-BBB1-4F15-A54D-581BC927E92E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2869A094-BBB1-4F15-A54D-581BC927E92E}.Release|Any CPU.Build.0 = Release|Any CPU + {717FB0F1-3DA3-4A70-9502-8BAA8A949672}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {717FB0F1-3DA3-4A70-9502-8BAA8A949672}.Debug|Any CPU.Build.0 = Debug|Any CPU + {717FB0F1-3DA3-4A70-9502-8BAA8A949672}.Release|Any CPU.ActiveCfg = Release|Any CPU + {717FB0F1-3DA3-4A70-9502-8BAA8A949672}.Release|Any CPU.Build.0 = Release|Any CPU + {94F85A89-73E3-4DB2-A87F-CDE9B71CF6F3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {94F85A89-73E3-4DB2-A87F-CDE9B71CF6F3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {94F85A89-73E3-4DB2-A87F-CDE9B71CF6F3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {94F85A89-73E3-4DB2-A87F-CDE9B71CF6F3}.Release|Any CPU.Build.0 = Release|Any CPU + {E88189B5-188B-4798-B5A5-FB8AF9C2A3E3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E88189B5-188B-4798-B5A5-FB8AF9C2A3E3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E88189B5-188B-4798-B5A5-FB8AF9C2A3E3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E88189B5-188B-4798-B5A5-FB8AF9C2A3E3}.Release|Any CPU.Build.0 = Release|Any CPU + {CDED55FF-86BF-47CC-926C-069B1AE6E680}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CDED55FF-86BF-47CC-926C-069B1AE6E680}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CDED55FF-86BF-47CC-926C-069B1AE6E680}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CDED55FF-86BF-47CC-926C-069B1AE6E680}.Release|Any CPU.Build.0 = Release|Any CPU + {313AC178-2193-4B2A-8D1D-748EDE6E55BE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {313AC178-2193-4B2A-8D1D-748EDE6E55BE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {313AC178-2193-4B2A-8D1D-748EDE6E55BE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {313AC178-2193-4B2A-8D1D-748EDE6E55BE}.Release|Any CPU.Build.0 = Release|Any CPU + {61179122-C51F-47AC-8E2A-D124AF4E3DF5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {61179122-C51F-47AC-8E2A-D124AF4E3DF5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {61179122-C51F-47AC-8E2A-D124AF4E3DF5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {61179122-C51F-47AC-8E2A-D124AF4E3DF5}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {DB787235-A13A-4A3D-B5A8-5DFEB6511EEE} + EndGlobalSection +EndGlobal diff --git a/RRQMSocket/BaseSocket.cs b/RRQMSocket/BaseSocket.cs new file mode 100644 index 000000000..e94f424c4 --- /dev/null +++ b/RRQMSocket/BaseSocket.cs @@ -0,0 +1,59 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +using RRQMCore.Log; +using System; + +namespace RRQMSocket +{ + /// + /// 通讯基类 + /// + public abstract class BaseSocket : ISocket, IDisposable + { + internal int bufferLength; + internal ILog logger; + + /// + /// 判断是否已释放资源 + /// + protected bool disposable = false; + + /// + /// 锁 + /// + protected object locker = new object(); + + /// + /// 数据交互缓存池限制 + /// + public int BufferLength + { + get { return bufferLength; } + } + + /// + /// 日志记录器 + /// + public ILog Logger + { + get { return logger; } + } + + /// + /// 释放资源 + /// + public virtual void Dispose() + { + this.disposable = true; + } + } +} \ No newline at end of file diff --git a/RRQMSocket/ClassDiagram.cd b/RRQMSocket/ClassDiagram.cd new file mode 100644 index 000000000..16d3c4384 --- /dev/null +++ b/RRQMSocket/ClassDiagram.cd @@ -0,0 +1,516 @@ + + + + + + AAAAAAAAAAAAAAABAAABEAAAAQAAAAAAAAAAAAAAAIA= + + + + + + AAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAA= + Common\AsyncResult.cs + + + + + + AAAAQAAAACiACAAiAAABAAAAAEAAACAAAAVAAAAAIFA= + Common\AsyncSender.cs + + + + + + + AAAAAAAAAAAAgAAAAAAAAAAAAAAAAAACAAAAAAAAAAA= + Common\CreateOption.cs + + + + + + AAAAAAAAAAAAQAAEAAAAAAAAKAAAAAAAAAAAAAAAAAE= + Common\IPHost.cs + + + + + + AAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAACAAABAAAAA= + Common\ProcotolHelper.cs + + + + + + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA= + Config\ClientConfig.cs + + + + + + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA= + Config\ProtocolClientConfig.cs + + + + + + AAAAAAAAAAAAAAAAAAAAAAAAAAAEIAAAAAAAAAAAAAA= + Config\ProtocolServerConfig.cs + + + + + + AAAIAAAAAAIAAAAAAgQgAAAAAAAAAAAAAIABAAQAAAA= + Config\RRQMConfig.cs + + + + + + AAIAAAAAAAAAABCAQAAAAAAAAAAAAAAAQAAgAAAAAAA= + Config\ServerConfig.cs + + + + + + AACAAAAAAAAAAQAAgQAEAAAAAAQAEBAgAAAABACgAAA= + Config\TcpClientConfig.cs + + + + + + AQAIAAAAAAAAQAAAAAgQAAAAIAAAAAAAAAAAQCAAAAA= + Config\TcpServerConfig.cs + + + + + + wAAAgAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAA= + Config\TokenClientConfig.cs + + + + + + wAAAgAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAA= + Config\TokenServerConfig.cs + + + + + + BAAAAAAAAgAAAAAACAAAAAAAAAAAAAACAAAAAAAAAAA= + Config\UdpSessionConfig.cs + + + + + + IAAAKAABAAIAAAAAAEAAAAAAAAIAAAAIAAAAAAIgAAA= + DataAdapter\DataHandlingAdapter.cs + + + + + + gEgAJEAAAAQAAAABAAAAAAAAAAIAACAEAAAAAABAAAA= + DataAdapter\FixedHeaderDataHandlingAdapter.cs + + + + + + gACAIEAAAAAAAAABAAAAAAAAAAIAAAAEAAAAAAAAAAA= + DataAdapter\FixedSizeDataHandlingAdapter.cs + + + + + + gAAAIAAAAAAAAAAAAAAABAAAAAIAAAAAAAAAgAAAAAA= + DataAdapter\JsonStringDataHandlingAdapter.cs + + + + + + AAAAIAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAAAAAAAA= + DataAdapter\NormalDataHandlingAdapter.cs + + + + + + gEAAIEAAAAAAAAAAAAAAAAAAgAIAAAAAAgAAAAEAAAI= + DataAdapter\TerminatorDataHandlingAdapter.cs + + + + + + AAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA= + EventArgs\BytesEventArgs.cs + + + + + + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAA= + EventArgs\MesEventArgs.cs + + + + + + AAAAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAAAA= + EventArgs\ReturnBytesEventArgs.cs + + + + + + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA= + Exceptions\RRQMNotConnectedException.cs + + + + + + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA= + Exceptions\RRQMOverlengthException.cs + + + + + + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA= + Exceptions\RRQMTimeoutException.cs + + + + + + AABAAAABAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAA= + InternalClass\BufferQueue.cs + + + + + + AAAAAAAAASAAAAAAAAAAAAAAAAAAACAAACEAIAAAAAA= + InternalClass\BufferQueueGroup.cs + + + + + + + AAAAAQAAACABQAAACAAAAASAQAAAAAAAAAAEACAAAQA= + InternalClass\SocketCliectCollection.cs + + + + + + + AAAAAAAAAAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAA= + Logger\Log.cs + + + + + + + AYAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAAAAAAABAAA= + ParameterClass\VerifyOption.cs + + + + + + AABAAAAAACBAAAAAEELAAACAEAAAACAAAAAAABQBAAA= + TCP\Client\ProtocolClient.cs + + + + + + AAAAAAABAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAA= + TCP\Client\SimpleProtocolClient.cs + + + + + + AAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQQAAAA= + TCP\Client\SimpleTcpClient.cs + + + + + + AAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAA= + TCP\Client\SimpleTokenClient.cs + + + + + + QAzBAAAAiCgACgQAAEIEUYWAKBgICAAEGCBBBEQhADA= + TCP\Client\TcpClient.cs + + + + + + + AAAEgAAAAABAgAAAAAAEAICAAAAAAACAAAAAAAAAAgA= + TCP\Client\TokenClient.cs + + + + + + BAAAAAAAAABAAAAAAAAAAACAAAAAAAAAAAAAAAAAAAA= + TCP\Service\ProtocolService.cs + + + + + + AAAAAAABAAAQQIAAAAAAAAAAAAAAAAAAAAAAAAAAAAA= + TCP\Service\SimpleProtocolService.cs + + + + + + AAAAAAABAAAQQIAAAAAAAAAAAAAAAAAAAAAAAAAAAAA= + TCP\Service\SimpleTcpService.cs + + + + + + AAAAAAABAAAQQIAAAAAAAAAAAAAAAAAAAAAAAAAAAAA= + TCP\Service\SimpleTokenService.cs + + + + + + AUJgAAEACKBgQBCBQkCUABCAUAAEAhIILIAEKAEgKCA= + TCP\Service\TcpService.cs + + + + + + + AAAEgAAAAAAAAAABAAAEAACAAAAAAACAAAAAgAAAAAA= + TCP\Service\TokenService.cs + + + + + + BABgAAAAAABAAAAAEEDAAAAAEAAAAAAAAAAAABQAAAA= + TCP\SocketClient\ProtocolSocketClient.cs + + + + + + AAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAQAAAAA= + TCP\SocketClient\SimpleProtocolSocketClient.cs + + + + + + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQQAAAA= + TCP\SocketClient\SimpleSocketClient.cs + + + + + + BghAAAAAAChggAQAAEIEUQSAKABIQEAKCAADACQgAhA= + TCP\SocketClient\SocketClient.cs + + + + + + + AAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAA= + UDP\SimpleUdpSession.cs + + + + + + AERgAAAASCAAAACACEIAgBiAOACMABIEKAABAAQkIBA= + UDP\UdpSession.cs + + + + + + + BAAAAAAAACIAIAAAAAAABAAAAEAAAAAAAAAAAAQAIAA= + BaseSocket.cs + + + + + + + AACAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAABAAAAAAAA= + Common\AsyncByte.cs + + + + + + BAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAABAAAAAA= + InternalClass\ClientBuffer.cs + + + + + + AAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA= + Interface\_ITcpService.cs + + + + + + AABAAAAAAAAAAAAAAEAAAAAAKAAAAAAACAAAAAAgAAA= + Interface\IClient.cs + + + + + + AAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA= + Interface\IHandleBuffer.cs + + + + + + AAAAAAAACCAAAACAAAAAAAAAAAAAABAAIAAAAAAgIAA= + Interface\IService.cs + + + + + + AAAAAAAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAA= + Interface\ISocket.cs + + + + + + AAAAAAAAAAAAgAAAAAAAAACAAAAAQAAAAAAAAAAAAAA= + Interface\ISocketClient.cs + + + + + + AAAAAAAAAAAAAAAAAAAEEQQAAAAAAAAAAAAAAAAAAAA= + Interface\ITcpClient.cs + + + + + + AQJAAAAAAAAAAAAAAEAQAAAAQAAAAAAABAAEIAAAAAA= + Interface\ITcpService.cs + + + + + + AAAAAAAACAAAAAAAAAAAAIAAAAgAAAAAAAAAAAAAAAA= + Interface\IUserClient.cs + + + + + + AASAAAAAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA= + Interface\IUserTcpClient.cs + + + + + + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA= + Pool\Interface\IClientGroup.cs + + + + + + AAAAAAAAAAIAEAAAIEAAEAACAAAAAAAAAQAAAAAgAAA= + Pool\Interface\IConnectionPool.cs + + + + + + AAAAAAAAAAIAAAAAAAAAAAAAAAAAAAAIAAAAAAECAAA= + Enum\ServerState.cs + + + + + + AAAAAAAAAAAAAAAAAAEAAAAQAAAAAAAAAAAAAAAAAAA= + DelegateCollection.cs + + + + + + AAAAAAAQAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAA= + DelegateCollection.cs + + + + + + AAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAAAA= + DelegateCollection.cs + + + + + + AAABAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAA= + DelegateCollection.cs + + + + + + AAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAACAAA= + DelegateCollection.cs + + + + \ No newline at end of file diff --git a/RRQMSocket/Common/AsyncByte.cs b/RRQMSocket/Common/AsyncByte.cs new file mode 100644 index 000000000..59d58aba3 --- /dev/null +++ b/RRQMSocket/Common/AsyncByte.cs @@ -0,0 +1,23 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +namespace RRQMSocket +{ + /// + /// 异步字节 + /// + internal struct AsyncByte + { + internal int offset; + internal int length; + internal byte[] buffer; + } +} \ No newline at end of file diff --git a/RRQMSocket/Common/AsyncResult.cs b/RRQMSocket/Common/AsyncResult.cs new file mode 100644 index 000000000..10f7fbad9 --- /dev/null +++ b/RRQMSocket/Common/AsyncResult.cs @@ -0,0 +1,29 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +namespace RRQMSocket +{ + /// + /// 异步执行结果 + /// + public class AsyncResult + { + /// + /// 异步状态 + /// + public bool Status { get; set; } + + /// + /// 消息 + /// + public string Message { get; set; } + } +} \ No newline at end of file diff --git a/RRQMSocket/Common/AsyncSender.cs b/RRQMSocket/Common/AsyncSender.cs new file mode 100644 index 000000000..5131ad597 --- /dev/null +++ b/RRQMSocket/Common/AsyncSender.cs @@ -0,0 +1,199 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +using RRQMCore.Log; +using System; +using System.Collections.Concurrent; +using System.Net; +using System.Net.Sockets; +using System.Threading; + +namespace RRQMSocket +{ + internal class AsyncSender : IDisposable + { + private readonly ConcurrentQueue asyncBytes; + + private readonly SocketAsyncEventArgs sendEventArgs; + + private readonly Thread sendThread; + + private readonly EventWaitHandle waitHandle; + + private byte[] buffer = new byte[1024 * 1024]; + + private bool dispose; + + private ILog logger; + + private bool sending; + + private Socket socket; + + internal AsyncSender(Socket socket, EndPoint endPoint, ILog logger) + { + sendEventArgs = new SocketAsyncEventArgs(); + sendEventArgs.Completed += this.SendEventArgs_Completed; + this.socket = socket; + this.sendEventArgs.RemoteEndPoint = endPoint; + this.logger = logger; + asyncBytes = new ConcurrentQueue(); + waitHandle = new AutoResetEvent(false); + this.sendThread = new Thread(this.BeginSend); + this.sendThread.IsBackground = true; + this.sendThread.Name = "AsyncSendThread"; + this.sendThread.Start(); + } + + public void AsyncSend(byte[] buffer, int offset, int length) + { + AsyncByte asyncByte = new AsyncByte(); + asyncByte.buffer = buffer; + asyncByte.offset = offset; + asyncByte.length = length; + this.asyncBytes.Enqueue(asyncByte); + if (!this.sending) + { + this.sending = true; + this.waitHandle.Set(); + } + } + + public void Dispose() + { + this.dispose = true; + this.waitHandle.Set(); + this.waitHandle.Dispose(); + this.sendEventArgs.Dispose(); + } + + internal void SetBufferLength(int len) + { + this.buffer = new byte[len]; + } + + private void BeginSend() + { + while (!this.dispose) + { + try + { + if (this.tryGet(out AsyncByte asyncByte)) + { + this.sendEventArgs.SetBuffer(asyncByte.buffer, asyncByte.offset, asyncByte.length); + + if (!this.socket.SendAsync(this.sendEventArgs)) + { + // 同步发送时处理发送完成事件 + this.ProcessSend(sendEventArgs); + } + else + { + this.waitHandle.WaitOne(); + } + } + else + { + this.sending = false; + this.waitHandle.WaitOne(); + } + } + catch (Exception ex) + { + this.logger.Debug(LogType.Error, this, "异步发送错误。", ex); + } + } + } + + /// + /// 发送完成时处理函数 + /// + /// 与发送完成操作相关联的SocketAsyncEventArg对象 + private void ProcessSend(SocketAsyncEventArgs e) + { + try + { + if (e.SocketError != SocketError.Success) + { + this.logger.Debug(LogType.Error, this, "异步发送错误。"); + } + } + catch + { + } + } + + private void SendEventArgs_Completed(object sender, SocketAsyncEventArgs e) + { + if (e.LastOperation == SocketAsyncOperation.Send) + { + ProcessSend(e); + if (!dispose) + { + this.waitHandle.Set(); + } + } + } + + private bool tryGet(out AsyncByte asyncByteDe) + { + int len = 0; + int surLen = buffer.Length; + while (true) + { + if (this.asyncBytes.TryPeek(out AsyncByte asyncB)) + { + if (surLen > asyncB.length) + { + if (this.asyncBytes.TryDequeue(out AsyncByte asyncByte)) + { + Array.Copy(asyncByte.buffer, asyncByte.offset, buffer, len, asyncByte.length); + len += asyncByte.length; + surLen -= asyncByte.length; + } + } + else if (asyncB.length > buffer.Length) + { + if (len > 0) + { + break; + } + else + { + asyncByteDe = asyncB; + return true; + } + } + else + { + break; + } + } + else + { + if (len > 0) + { + break; + } + else + { + asyncByteDe = default; + return false; + } + } + } + asyncByteDe = new AsyncByte(); + asyncByteDe.buffer = buffer; + asyncByteDe.length = len; + return true; + } + } +} \ No newline at end of file diff --git a/RRQMSocket/Common/CreateOption.cs b/RRQMSocket/Common/CreateOption.cs new file mode 100644 index 000000000..55616f7ef --- /dev/null +++ b/RRQMSocket/Common/CreateOption.cs @@ -0,0 +1,31 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ + +namespace RRQMSocket +{ + /// + /// 创建设置 + /// + public class CreateOption + { + /// + /// 判断该T是否为新建对象, + /// true:首次创建。false:从对象池获得 + /// + public bool NewCreate { get; internal set; } + + /// + /// 获取或设置该T的ID + /// + public string ID { get; set; } + } +} \ No newline at end of file diff --git a/RRQMSocket/Common/IPHost.cs b/RRQMSocket/Common/IPHost.cs new file mode 100644 index 000000000..60ea6748b --- /dev/null +++ b/RRQMSocket/Common/IPHost.cs @@ -0,0 +1,96 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +using RRQMCore.Exceptions; +using System; +using System.Net; +using System.Net.Sockets; + +namespace RRQMSocket +{ + /// + /// IP解析映射 + /// + public class IPHost + { + /// + /// 从字符串获取ip和port + /// + public IPHost(string ipHost) + { + try + { + int r = ipHost.LastIndexOf(":"); + this.IP = ipHost.Substring(0, r); + this.Port = Convert.ToInt32(ipHost.Substring(r + 1, ipHost.Length - (r + 1))); + this.EndPoint = new IPEndPoint(IPAddress.Parse(this.IP), this.Port); + if (this.IP.Contains(":")) + { + this.AddressFamily = AddressFamily.InterNetworkV6; + } + else + { + this.AddressFamily = AddressFamily.InterNetwork; + } + } + catch + { + throw new RRQMException("IPHost不合法"); + } + } + + /// + /// 从IPAddress和端口号 + /// + /// + /// + public IPHost(IPAddress iPAddress, int port) : this($"{iPAddress}:{port}") + { + } + + /// + /// 从端口号创建 + /// + /// + public IPHost(int port) : this($"0.0.0.0:{port}") + { + } + + /// + /// IP + /// + public string IP { get; private set; } + + /// + /// 端口号 + /// + public int Port { get; private set; } + + /// + /// 寻址方案 + /// + public AddressFamily AddressFamily { get; private set; } + + /// + /// 终结点 + /// + public EndPoint EndPoint { get; private set; } + + /// + /// 返回对象字符串 + /// + /// + public override string ToString() + { + return EndPoint == null ? null : EndPoint.ToString(); + } + } +} \ No newline at end of file diff --git a/RRQMSocket/Common/ProcotolHelper.cs b/RRQMSocket/Common/ProcotolHelper.cs new file mode 100644 index 000000000..b2d5c29d9 --- /dev/null +++ b/RRQMSocket/Common/ProcotolHelper.cs @@ -0,0 +1,200 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +using RRQMCore.ByteManager; +using System; +using System.Net.Sockets; + +namespace RRQMSocket +{ + /// + /// RRQM协议助手 + /// + public class ProcotolHelper + { + /// + /// 构造函数 + /// + /// + /// + public ProcotolHelper(ITcpClient client, bool separateThread) + { + this.mainSocket = client.MainSocket; + this.bytePool = client.BytePool; + this.separateThread = separateThread; + + if (separateThread) + { + this.asyncSender = new AsyncSender(client.MainSocket, client.MainSocket.RemoteEndPoint, client.Logger); + } + } + + private Socket mainSocket; + private BytePool bytePool; + private AsyncSender asyncSender; + private bool separateThread; + + #region 同步方法 + + /// + /// 发送简单协议 + /// + /// + public void SocketSend(short procotol) + { + this.SocketSend(procotol, new byte[0], 0, 0); + } + + /// + /// 发送字节 + /// + /// + /// + public void SocketSend(short procotol, byte[] dataBuffer) + { + this.SocketSend(procotol, dataBuffer, 0, dataBuffer.Length); + } + + /// + /// 发送协议流 + /// + /// + /// + public void SocketSend(short procotol, ByteBlock dataByteBlock) + { + this.SocketSend(procotol, dataByteBlock.Buffer, 0, (int)dataByteBlock.Length); + } + + /// + /// 发送字节 + /// + /// + /// + /// + /// + /// + public void SocketSend(short procotol, byte[] dataBuffer, int offset, int length, bool reserved = false) + { + int dataLen; + if (reserved) + { + dataLen = length - 4; + byte[] lenBytes1 = BitConverter.GetBytes(dataLen); + byte[] agreementBytes1 = BitConverter.GetBytes(procotol); + Array.Copy(lenBytes1, 0, dataBuffer, offset, 4); + Array.Copy(agreementBytes1, 0, dataBuffer, 4 + offset, 2); + this.mainSocket.Send(dataBuffer, 0, length, SocketFlags.None); + return; + } + dataLen = length + 2; + ByteBlock byteBlock = this.bytePool.GetByteBlock(dataLen + 4); + byte[] lenBytes = BitConverter.GetBytes(dataLen); + byte[] agreementBytes = BitConverter.GetBytes(procotol); + + byteBlock.Write(lenBytes); + byteBlock.Write(agreementBytes); + if (length > 0) + { + byteBlock.Write(dataBuffer, offset, length); + } + try + { + this.mainSocket.Send(byteBlock.Buffer, 0, byteBlock.Len, SocketFlags.None); + } + catch (Exception ex) + { + throw ex; + } + finally + { + byteBlock.Dispose(); + } + } + + #endregion 同步方法 + + #region 异步方法 + + /// + /// 发送简单协议 + /// + /// + public void SocketSendAsync(short procotol) + { + this.SocketSendAsync(procotol, new byte[0], 0, 0); + } + + /// + /// 发送字节 + /// + /// + /// + public void SocketSendAsync(short procotol, byte[] dataBuffer) + { + this.SocketSendAsync(procotol, dataBuffer, 0, dataBuffer.Length); + } + + /// + /// 发送协议流 + /// + /// + /// + public void SocketSendAsync(short procotol, ByteBlock dataByteBlock) + { + this.SocketSendAsync(procotol, dataByteBlock.Buffer, 0, dataByteBlock.Len); + } + + /// + /// 发送字节 + /// + /// + /// + /// + /// + public void SocketSendAsync(short procotol, byte[] dataBuffer, int offset, int length) + { + int dataLen; + dataLen = length - offset + 2; + ByteBlock byteBlock = this.bytePool.GetByteBlock(dataLen + 4); + byte[] lenBytes = BitConverter.GetBytes(dataLen); + byte[] agreementBytes = BitConverter.GetBytes(procotol); + + byteBlock.Write(lenBytes); + byteBlock.Write(agreementBytes); + if (length > 0) + { + byteBlock.Write(dataBuffer, offset, length); + } + try + { + byte[] data = byteBlock.ToArray(); + if (this.separateThread) + { + this.asyncSender.AsyncSend(data, 0, data.Length); + } + else + { + this.mainSocket.BeginSend(data, 0, data.Length, SocketFlags.None, null, null); + } + } + catch (Exception ex) + { + throw ex; + } + finally + { + byteBlock.Dispose(); + } + } + + #endregion 异步方法 + } +} \ No newline at end of file diff --git a/RRQMSocket/Config/ClientConfig.cs b/RRQMSocket/Config/ClientConfig.cs new file mode 100644 index 000000000..443941a1a --- /dev/null +++ b/RRQMSocket/Config/ClientConfig.cs @@ -0,0 +1,20 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +namespace RRQMSocket +{ + /// + /// 客户端配置 + /// + public class ClientConfig : RRQMConfig + { + } +} \ No newline at end of file diff --git a/RRQMSocket/Config/ProtocolClientConfig.cs b/RRQMSocket/Config/ProtocolClientConfig.cs new file mode 100644 index 000000000..c9c482d17 --- /dev/null +++ b/RRQMSocket/Config/ProtocolClientConfig.cs @@ -0,0 +1,21 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ + +namespace RRQMSocket +{ + /// + /// 协议客户端配置 + /// + public class ProtocolClientConfig : TokenClientConfig + { + } +} \ No newline at end of file diff --git a/RRQMSocket/Config/ProtocolServiceConfig.cs b/RRQMSocket/Config/ProtocolServiceConfig.cs new file mode 100644 index 000000000..dcd3ae872 --- /dev/null +++ b/RRQMSocket/Config/ProtocolServiceConfig.cs @@ -0,0 +1,36 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +using RRQMCore.Dependency; + +namespace RRQMSocket +{ + /// + /// 协议服务配置 + /// + public class ProtocolServiceConfig : TokenServiceConfig + { + /// + /// 是否能重新设置ID + /// + public bool CanResetID + { + get { return (bool)GetValue(CanResetIDProperty); } + set { SetValue(CanResetIDProperty, value); } + } + + /// + /// 是否能重新设置ID,所需类型 + /// + public static readonly DependencyProperty CanResetIDProperty = + DependencyProperty.Register("CanResetID", typeof(bool), typeof(ProtocolServiceConfig), true); + } +} \ No newline at end of file diff --git a/RRQMSocket/Config/RRQMConfig.cs b/RRQMSocket/Config/RRQMConfig.cs new file mode 100644 index 000000000..0ab687df0 --- /dev/null +++ b/RRQMSocket/Config/RRQMConfig.cs @@ -0,0 +1,84 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +using RRQMCore; +using RRQMCore.Dependency; +using RRQMCore.Log; + +namespace RRQMSocket +{ + /// + /// 配置文件基类 + /// + public class RRQMConfig : RRQMDependencyObject + { + /// + /// 日志记录器 + /// + public ILog Logger + { + get { return (ILog)GetValue(LoggerProperty); } + set { SetValue(LoggerProperty, value); } + } + + /// + /// 日志记录器依赖属性,所需类型 + /// + public static readonly DependencyProperty LoggerProperty = + DependencyProperty.Register("Logger", typeof(ILog), typeof(RRQMConfig), new Log()); + + /// + /// 内存池最大尺寸 + /// + public long BytePoolMaxSize + { + get { return (long)GetValue(BytePoolMaxSizeProperty); } + set { SetValue(BytePoolMaxSizeProperty, value); } + } + + /// + /// 内存池最大尺寸依赖属性,所需类型 + /// + public static readonly DependencyProperty BytePoolMaxSizeProperty = + DependencyProperty.Register("BytePoolMaxSize", typeof(long), typeof(RRQMConfig), 1024 * 1024 * 512L); + + /// + /// 内存池块最大尺寸 + /// + public int BytePoolMaxBlockSize + { + get { return (int)GetValue(BytePoolMaxBlockSizeProperty); } + set { SetValue(BytePoolMaxBlockSizeProperty, value); } + } + + /// + /// 内存池块最大尺寸,所需类型 + /// + public static readonly DependencyProperty BytePoolMaxBlockSizeProperty = + DependencyProperty.Register("BytePoolMaxBlockSize", typeof(int), typeof(RRQMConfig), 1024 * 1024 * 20); + + /// + /// 缓存池容量 + /// + public int BufferLength + { + get { return (int)GetValue(BufferLengthProperty); } + set { SetValue(BufferLengthProperty, value); } + } + + /// + /// 缓存池容量,所需类型 + /// + public static readonly DependencyProperty BufferLengthProperty = + DependencyProperty.Register("BufferLength", typeof(int), typeof(RRQMConfig), 1024); + + } +} \ No newline at end of file diff --git a/RRQMSocket/Config/ServiceConfig.cs b/RRQMSocket/Config/ServiceConfig.cs new file mode 100644 index 000000000..2eb3e9e3e --- /dev/null +++ b/RRQMSocket/Config/ServiceConfig.cs @@ -0,0 +1,82 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +using RRQMCore.Dependency; + +namespace RRQMSocket +{ + /// + /// 服务器配置 + /// + public class ServiceConfig : RRQMConfig + { + /// + /// 多线程数量 + /// + public int ThreadCount + { + get { return (int)GetValue(ThreadCountProperty); } + set { SetValue(ThreadCountProperty, value); } + } + + /// + /// 多线程数量依赖属性,所需类型 + /// + public static readonly DependencyProperty ThreadCountProperty = + DependencyProperty.Register("ThreadCount", typeof(int), typeof(ServiceConfig), 1); + + /// + /// 监听IP和端口号组 + /// + public IPHost[] ListenIPHosts + { + get { return (IPHost[])GetValue(ListenIPHostsProperty); } + set { SetValue(ListenIPHostsProperty, value); } + } + + /// + /// IP和端口号依赖属性,所需类型数组 + /// + public static readonly DependencyProperty ListenIPHostsProperty = + DependencyProperty.Register("ListenIPHosts", typeof(IPHost[]), typeof(ServiceConfig), null); + + /// + /// 名称 + /// + public string ServerName + { + get { return (string)GetValue(ServerNameProperty); } + set { SetValue(ServerNameProperty, value); } + } + + /// + /// 名称,所需类型 + /// + public static readonly DependencyProperty ServerNameProperty = + DependencyProperty.Register("ServerName", typeof(string), typeof(ServiceConfig), "RRQMServer"); + + /// + /// 独立线程接收 + /// + public bool SeparateThreadReceive + { + get { return (bool)GetValue(SeparateThreadReceiveProperty); } + set { SetValue(SeparateThreadReceiveProperty, value); } + } + + /// + /// 独立线程接收, + /// 所需类型 + /// + public static readonly DependencyProperty SeparateThreadReceiveProperty = + DependencyProperty.Register("SeparateThreadReceive", typeof(bool), typeof(ServiceConfig), false); + } +} \ No newline at end of file diff --git a/RRQMSocket/Config/TcpClientConfig.cs b/RRQMSocket/Config/TcpClientConfig.cs new file mode 100644 index 000000000..454f76c25 --- /dev/null +++ b/RRQMSocket/Config/TcpClientConfig.cs @@ -0,0 +1,130 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +using RRQMCore.ByteManager; +using RRQMCore.Dependency; + +namespace RRQMSocket +{ + /// + /// TcpClient配置 + /// + public class TcpClientConfig : ClientConfig + { + /// + /// 数据处理适配器 + /// + public DataHandlingAdapter DataHandlingAdapter + { + get { return (DataHandlingAdapter)GetValue(DataHandlingAdapterProperty); } + set { SetValue(DataHandlingAdapterProperty, value); } + } + + /// + /// 数据处理适配器,所需类型 + /// + public static readonly DependencyProperty DataHandlingAdapterProperty = + DependencyProperty.Register("DataHandlingAdapter", typeof(DataHandlingAdapter), typeof(TcpClientConfig), new NormalDataHandlingAdapter()); + + /// + /// 远程IPHost + /// + public IPHost RemoteIPHost + { + get { return (IPHost)GetValue(RemoteIPHostProperty); } + set { SetValue(RemoteIPHostProperty, value); } + } + + /// + /// 远程IPHost,所需类型 + /// + public static readonly DependencyProperty RemoteIPHostProperty = + DependencyProperty.Register("RemoteIPHost", typeof(IPHost), typeof(TcpClientConfig), null); + + /// + /// 内存池实例 + /// + public BytePool BytePool + { + get { return (BytePool)GetValue(BytePoolProperty); } + set { SetValue(BytePoolProperty, value); } + } + + /// + /// 内存池实例,所需类型 + /// + public static readonly DependencyProperty BytePoolProperty = + DependencyProperty.Register("BytePool", typeof(BytePool), typeof(TcpClientConfig), new BytePool()); + + /// + /// 仅发送,即不开启接收线程, + /// 同时不会感知断开操作。 + /// + public bool OnlySend + { + get { return (bool)GetValue(OnlySendProperty); } + set { SetValue(OnlySendProperty, value); } + } + + /// + /// 仅发送,即不开启接收线程, + /// 同时不会感知断开操作,所需类型 + /// + public static readonly DependencyProperty OnlySendProperty = + DependencyProperty.Register("OnlySend", typeof(bool), typeof(TcpClientConfig), false); + + /// + /// 在异步发送时,使用独立线程发送 + /// + public bool SeparateThreadSend + { + get { return (bool)GetValue(SeparateThreadSendProperty); } + set { SetValue(SeparateThreadSendProperty, value); } + } + + /// + /// 在异步发送时,使用独立线程发送,所需类型 + /// + public static readonly DependencyProperty SeparateThreadSendProperty = + DependencyProperty.Register("SeparateThreadSend", typeof(bool), typeof(TcpClientConfig), false); + + /// + /// 独立线程接收 + /// + public bool SeparateThreadReceive + { + get { return (bool)GetValue(SeparateThreadReceiveProperty); } + set { SetValue(SeparateThreadReceiveProperty, value); } + } + + /// + /// 独立线程接收, + /// 所需类型 + /// + public static readonly DependencyProperty SeparateThreadReceiveProperty = + DependencyProperty.Register("SeparateThreadReceive", typeof(bool), typeof(TcpClientConfig), false); + + /// + /// 独立线程发送缓存区 + /// + public int SeparateThreadSendBufferLength + { + get { return (int)GetValue(SeparateThreadSendBufferLengthProperty); } + set { SetValue(SeparateThreadSendBufferLengthProperty, value); } + } + + /// + /// 独立线程发送缓存区,所需类型 + /// + public static readonly DependencyProperty SeparateThreadSendBufferLengthProperty = + DependencyProperty.Register("SeparateThreadSendBufferLength", typeof(int), typeof(TcpClientConfig), 1024); + } +} \ No newline at end of file diff --git a/RRQMSocket/Config/TcpServiceConfig.cs b/RRQMSocket/Config/TcpServiceConfig.cs new file mode 100644 index 000000000..e7b1eb442 --- /dev/null +++ b/RRQMSocket/Config/TcpServiceConfig.cs @@ -0,0 +1,83 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +using RRQMCore.Dependency; + +namespace RRQMSocket +{ + /// + /// Tcp服务配置 + /// + public class TcpServiceConfig : ServiceConfig + { + /// + /// 挂起连接队列的最大长度。默认为100 + /// + public int Backlog + { + get { return (int)GetValue(BacklogProperty); } + set { SetValue(BacklogProperty, value); } + } + + /// + /// 挂起连接队列的最大长度,所需类型 + /// + public static readonly DependencyProperty BacklogProperty = + DependencyProperty.Register("Backlog", typeof(int), typeof(TcpServiceConfig), 100); + + /// + /// 最大可连接数,默认为10000 + /// + public int MaxCount + { + get { return (int)GetValue(MaxCountProperty); } + set { SetValue(MaxCountProperty, value); } + } + + /// + /// 最大可连接数,默认为10000,所需类型 + /// + public static readonly DependencyProperty MaxCountProperty = + DependencyProperty.Register("MaxCount", typeof(int), typeof(TcpServiceConfig), 10000); + + /// + /// 获取或设置清理无数据交互的SocketClient,默认60*1000 ms。如果不想清除,可使用-1。 + /// + public int ClearInterval + { + get { return (int)GetValue(ClearIntervalProperty); } + set { SetValue(ClearIntervalProperty, value); } + } + + /// + /// 获取或设置清理无数据交互的SocketClient,默认60*1000 ms。如果不想清除,可使用-1。 + /// 所需类型 + /// + public static readonly DependencyProperty ClearIntervalProperty = + DependencyProperty.Register("ClearInterval", typeof(int), typeof(TcpServiceConfig), 60*1000); + + /// + /// 统计类型,可叠加位域 + /// + public ClearType ClearType + { + get { return (ClearType)GetValue(ClearTypeProperty); } + set { SetValue(ClearTypeProperty, value); } + } + + /// + /// 统计类型,可叠加位域 + /// 所需类型 + /// + public static readonly DependencyProperty ClearTypeProperty = + DependencyProperty.Register("ClearType", typeof(ClearType), typeof(TcpServiceConfig), ClearType.Send | ClearType.Receive); + } +} \ No newline at end of file diff --git a/RRQMSocket/Config/TokenClientConfig.cs b/RRQMSocket/Config/TokenClientConfig.cs new file mode 100644 index 000000000..791951464 --- /dev/null +++ b/RRQMSocket/Config/TokenClientConfig.cs @@ -0,0 +1,57 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +using RRQMCore.Dependency; + +namespace RRQMSocket +{ + /// + /// TokenClient配置 + /// + public class TokenClientConfig : TcpClientConfig + { + /// + /// 连接令箭,当为null或空时,重置为默认值“rrqm” + /// + public string VerifyToken + { + get { return (string)GetValue(VerifyTokenProperty); } + set + { + SetValue(VerifyTokenProperty, value); + } + } + + /// + /// 连接令箭,当为null或空时,重置为默认值“rrqm”, 所需类型 + /// + public static readonly DependencyProperty VerifyTokenProperty = + DependencyProperty.Register("VerifyToken", typeof(string), typeof(TokenClientConfig), "rrqm"); + + /// + /// 验证超时时间,默认为3秒; + /// + public int VerifyTimeout + { + get { return (int)GetValue(VerifyTimeoutProperty); } + set + { + SetValue(VerifyTimeoutProperty, value); + } + } + + /// + /// 验证超时时间,默认为3000ms, 所需类型 + /// + public static readonly DependencyProperty VerifyTimeoutProperty = + DependencyProperty.Register("VerifyTimeout", typeof(int), typeof(TokenClientConfig), 3000); + } +} \ No newline at end of file diff --git a/RRQMSocket/Config/TokenServiceConfig.cs b/RRQMSocket/Config/TokenServiceConfig.cs new file mode 100644 index 000000000..1b03130af --- /dev/null +++ b/RRQMSocket/Config/TokenServiceConfig.cs @@ -0,0 +1,57 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +using RRQMCore.Dependency; + +namespace RRQMSocket +{ + /// + /// TokenTcp服务配置 + /// + public class TokenServiceConfig : TcpServiceConfig + { + /// + /// 连接令箭,当为null或空时,重置为默认值“rrqm” + /// + public string VerifyToken + { + get { return (string)GetValue(VerifyTokenProperty); } + set + { + SetValue(VerifyTokenProperty, value); + } + } + + /// + /// 连接令箭,当为null或空时,重置为默认值“rrqm”, 所需类型 + /// + public static readonly DependencyProperty VerifyTokenProperty = + DependencyProperty.Register("VerifyToken", typeof(string), typeof(TokenServiceConfig), "rrqm"); + + /// + /// 验证超时时间,默认为3000ms; + /// + public int VerifyTimeout + { + get { return (int)GetValue(VerifyTimeoutProperty); } + set + { + SetValue(VerifyTimeoutProperty, value); + } + } + + /// + /// 验证超时时间,默认为3000ms, 所需类型 + /// + public static readonly DependencyProperty VerifyTimeoutProperty = + DependencyProperty.Register("VerifyTimeout", typeof(int), typeof(TokenServiceConfig), 3000); + } +} \ No newline at end of file diff --git a/RRQMSocket/Config/UdpSessionConfig.cs b/RRQMSocket/Config/UdpSessionConfig.cs new file mode 100644 index 000000000..53a7bb277 --- /dev/null +++ b/RRQMSocket/Config/UdpSessionConfig.cs @@ -0,0 +1,52 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +using RRQMCore.Dependency; +using System.Net; + +namespace RRQMSocket +{ + /// + /// UDP服务器配置 + /// + public class UdpSessionConfig : ServiceConfig + { + /// + /// 默认远程节点 + /// + public EndPoint DefaultRemotePoint + { + get { return (EndPoint)GetValue(DefaultRemotePointProperty); } + set { SetValue(DefaultRemotePointProperty, value); } + } + + /// + /// 默认远程节点, 所需类型 + /// + public static readonly DependencyProperty DefaultRemotePointProperty = + DependencyProperty.Register("DefaultRemotePoint", typeof(EndPoint), typeof(UdpSessionConfig), null); + + /// + /// 使用绑定 + /// + public bool UseBind + { + get { return (bool)GetValue(UseBindProperty); } + set { SetValue(UseBindProperty, value); } + } + + /// + /// 使用绑定, 所需类型 + /// + public static readonly DependencyProperty UseBindProperty = + DependencyProperty.Register("UseBind", typeof(bool), typeof(UdpSessionConfig), false); + } +} \ No newline at end of file diff --git a/RRQMSocket/DataAdapter/DataAdapterTester.cs b/RRQMSocket/DataAdapter/DataAdapterTester.cs new file mode 100644 index 000000000..6277feb1a --- /dev/null +++ b/RRQMSocket/DataAdapter/DataAdapterTester.cs @@ -0,0 +1,201 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +using RRQMCore.ByteManager; +using RRQMCore.Log; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Threading; + +namespace RRQMSocket +{ + /// + /// 数据处理适配器测试 + /// + public class DataAdapterTester : IDisposable + { + private readonly ConcurrentQueue asyncBytes; + + private readonly Thread sendThread; + + private readonly EventWaitHandle waitHandle; + + private DataHandlingAdapter adapter; + + private bool sending; + + private bool dispose; + + private int bufferLength; + + private DataAdapterTester() + { + asyncBytes = new ConcurrentQueue(); + waitHandle = new AutoResetEvent(false); + this.sendThread = new Thread(this.BeginSend); + this.sendThread.IsBackground = true; + this.sendThread.Name = "DataAdapterTesterThread"; + this.sendThread.Start(); + } + + /// + /// 获取测试器 + /// + /// + /// + /// + /// + /// + public static DataAdapterTester CreateTester(DataHandlingAdapter adapter, Action receivedCallBack, ILog logger, int bufferLength = 1024) + { + DataAdapterTester tester = new DataAdapterTester(); + tester.adapter = adapter; + tester.bufferLength = bufferLength; + + adapter.Logger = logger; + adapter.SendCallBack = tester.SendCallback; + adapter.BytePool = new BytePool(); + adapter.ReceivedCallBack = receivedCallBack; + + return tester; + } + + /// + /// 模拟发送 + /// + /// + /// + /// + public void SimSend(byte[] buffer, int offset, int length) + { + adapter.Send(buffer, offset, length, false); + } + + /// + /// 模拟发送 + /// + /// + public void SimSend(byte[] buffer) + { + this.SimSend(buffer, 0, buffer.Length); + } + + private void BeginSend() + { + while (!this.dispose) + { + if (this.tryGet(out List byteBlocks)) + { + this.sending = true; + + foreach (var block in byteBlocks) + { + try + { + this.adapter.Received(block); + } + finally + { + block.Dispose(); + } + } + } + else + { + this.sending = false; + this.waitHandle.WaitOne(); + } + } + } + + private bool tryGet(out List byteBlocks) + { + byteBlocks = new List(); + + ByteBlock block = null; + + while (true) + { + if (this.asyncBytes.TryDequeue(out AsyncByte asyncByte)) + { + if (block == null) + { + block = this.adapter.BytePool.GetByteBlock(bufferLength); + byteBlocks.Add(block); + } + + int surLen = block.Capacity - (int)block.Position; + if (surLen >= asyncByte.length) + { + block.Write(asyncByte.buffer, asyncByte.offset, asyncByte.length); + } + else + { + block.Write(asyncByte.buffer, asyncByte.offset, surLen); + + int surDataLen = asyncByte.length - surLen; + int offset = asyncByte.offset + surLen; + + while (surDataLen > 0) + { + block = this.adapter.BytePool.GetByteBlock(bufferLength); + byteBlocks.Add(block); + int len = Math.Min(surDataLen, bufferLength); + block.Write(asyncByte.buffer, offset, len); + surDataLen -= len; + offset += len; + } + + break; + } + } + else + { + if (byteBlocks.Count > 0) + { + break; + } + else + { + return false; + } + } + } + return true; + } + + private void SendCallback(byte[] buffer, int offset, int length, bool isAsync) + { + AsyncByte asyncByte = new AsyncByte(); + asyncByte.buffer = new byte[length]; + + Array.Copy(buffer, offset, asyncByte.buffer, 0, length); + asyncByte.offset = 0; + asyncByte.length = length; + this.asyncBytes.Enqueue(asyncByte); + if (!this.sending) + { + this.waitHandle.Set(); + } + } + + /// + /// 释放 + /// + public void Dispose() + { + this.dispose = true; + this.waitHandle.Set(); + this.waitHandle.Dispose(); + } + } +} \ No newline at end of file diff --git a/RRQMSocket/DataAdapter/DataHandlingAdapter.cs b/RRQMSocket/DataAdapter/DataHandlingAdapter.cs new file mode 100644 index 000000000..6623712a6 --- /dev/null +++ b/RRQMSocket/DataAdapter/DataHandlingAdapter.cs @@ -0,0 +1,97 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +using RRQMCore.ByteManager; +using RRQMCore.Log; +using System; + +namespace RRQMSocket +{ + /// + /// 数据处理适配器 + /// + public abstract class DataHandlingAdapter + { + /// + /// 内存池 + /// + protected internal BytePool BytePool { get; internal set; } + + /// + /// 日志记录器 + /// + protected internal ILog Logger { get; internal set; } + + /// + /// 当接收数据处理完成后,回调该函数执行接收 + /// + internal Action ReceivedCallBack { get; set; } + + /// + /// 当接收数据处理完成后,回调该函数执行发送 + /// + internal Action SendCallBack { get; set; } + + /// + /// 当接收到数据后预先处理数据,然后调用处理数据 + /// + /// 数据流 + protected abstract void PreviewReceived(ByteBlock byteBlock); + + /// + /// 处理已经经过预先处理后的数据 + /// + /// + /// + protected void GoReceived(ByteBlock byteBlock, object obj) + { + try + { + this.ReceivedCallBack.Invoke(byteBlock, obj); + } + catch (Exception ex) + { + Logger.Debug(LogType.Error, this, ex.Message, ex); + } + } + + /// + /// 当发送数据前预先处理数据 + /// + /// 数据 + /// 偏移 + /// 长度 + /// 是否使用IOCP发送 + protected abstract void PreviewSend(byte[] buffer, int offset, int length, bool isAsync); + + /// + /// 发送已经经过预先处理后的数据 + /// + /// + /// + /// + /// 是否使用IOCP发送 + protected void GoSend(byte[] buffer, int offset, int length, bool isAsync) + { + this.SendCallBack.Invoke(buffer, offset, length, isAsync); + } + + internal void Received(ByteBlock byteBlock) + { + this.PreviewReceived(byteBlock); + } + + internal void Send(byte[] buffer, int offset, int length, bool isAsync) + { + this.PreviewSend(buffer, offset, length, isAsync); + } + } +} \ No newline at end of file diff --git a/RRQMSocket/DataAdapter/FixedHeaderDataHandlingAdapter.cs b/RRQMSocket/DataAdapter/FixedHeaderDataHandlingAdapter.cs new file mode 100644 index 000000000..6e40f1ab2 --- /dev/null +++ b/RRQMSocket/DataAdapter/FixedHeaderDataHandlingAdapter.cs @@ -0,0 +1,279 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +using RRQMCore.ByteManager; +using RRQMCore.Exceptions; +using RRQMCore.Log; +using System; + +namespace RRQMSocket +{ + /// + /// 固定包头数据处理器 + /// + public class FixedHeaderDataHandlingAdapter : DataHandlingAdapter + { + private int maxSizeHeader = 1024 * 1024 * 10; + + /// + /// 获取或设置包头的最大值(默认为10Mb) + /// + public int MaxSizeHeader + { + get { return maxSizeHeader; } + set { maxSizeHeader = value; } + } + + private int minSizeHeader = 0; + + /// + /// 获取或设置包头的最小值(默认为0) + /// + public int MinSizeHeader + { + get { return minSizeHeader; } + set { minSizeHeader = value; } + } + + private FixedHeaderType fixedHeaderType = FixedHeaderType.Int; + + /// + /// 设置包头类型,默认为int + /// + public FixedHeaderType FixedHeaderType + { + get { return fixedHeaderType; } + set { fixedHeaderType = value; } + } + + /// + /// 临时包 + /// + private ByteBlock tempByteBlock; + + /// + /// 包剩余长度 + /// + private int surPlusLength = 0; + + /// + /// 协议临时包 + /// + private byte[] agreementTempBytes; + + /// + /// 当接收到数据时处理数据 + /// + /// 数据流 + protected override void PreviewReceived(ByteBlock byteBlock) + { + byte[] buffer = byteBlock.Buffer; + int r = byteBlock.Len; + + if (agreementTempBytes != null) + { + SeamPackage(buffer, r); + } + else if (this.tempByteBlock == null) + { + SplitPackage(buffer, 0, r); + } + else + { + if (surPlusLength == r) + { + this.tempByteBlock.Write(buffer, 0, surPlusLength); + PreviewHandle(this.tempByteBlock); + this.tempByteBlock = null; + surPlusLength = 0; + } + else if (surPlusLength < r) + { + this.tempByteBlock.Write(buffer, 0, surPlusLength); + PreviewHandle(this.tempByteBlock); + this.tempByteBlock = null; + SplitPackage(buffer, surPlusLength, r); + } + else + { + this.tempByteBlock.Write(buffer, 0, r); + surPlusLength -= r; + } + } + } + + /// + /// 缝合包 + /// + /// + /// + private void SeamPackage(byte[] buffer, int r) + { + ByteBlock byteBlock = this.BytePool.GetByteBlock(r + agreementTempBytes.Length); + byteBlock.Write(agreementTempBytes); + byteBlock.Write(buffer, 0, r); + r += agreementTempBytes.Length; + agreementTempBytes = null; + SplitPackage(byteBlock.Buffer, 0, r); + byteBlock.Dispose(); + } + + /// + /// 分解包 + /// + /// + /// + /// + private void SplitPackage(byte[] dataBuffer, int index, int r) + { + while (index < r) + { + if (r - index <= (byte)this.fixedHeaderType) + { + agreementTempBytes = new byte[r - index]; + Array.Copy(dataBuffer, index, agreementTempBytes, 0, agreementTempBytes.Length); + return; + } + int length = 0; + + switch (this.fixedHeaderType) + { + case FixedHeaderType.Byte: + length = dataBuffer[index]; + break; + + case FixedHeaderType.Ushort: + length = BitConverter.ToUInt16(dataBuffer, index); + break; + + case FixedHeaderType.Int: + length = BitConverter.ToInt32(dataBuffer, index); + break; + } + + if (length < 0) + { + Logger.Debug(LogType.Error, this, "接收数据长度错误,已放弃接收"); + return; + } + else if (length < this.minSizeHeader) + { + Logger.Debug(LogType.Error, this, "接收数据长度小于设定值,已放弃接收"); + return; + } + else if (length > this.maxSizeHeader) + { + Logger.Debug(LogType.Error, this, "接收数据长度大于设定值,已放弃接收"); + return; + } + + int recedSurPlusLength = r - index - (byte)this.fixedHeaderType; + if (recedSurPlusLength >= length) + { + ByteBlock byteBlock = this.BytePool.GetByteBlock(length); + byteBlock.Write(dataBuffer, index + (byte)this.fixedHeaderType, length); + PreviewHandle(byteBlock); + surPlusLength = 0; + } + else//半包 + { + this.tempByteBlock = this.BytePool.GetByteBlock(length); + surPlusLength = length - recedSurPlusLength; + this.tempByteBlock.Write(dataBuffer, index + (byte)this.fixedHeaderType, recedSurPlusLength); + } + index += (length + (byte)this.fixedHeaderType); + } + } + + private void PreviewHandle(ByteBlock byteBlock) + { + try + { + this.GoReceived(byteBlock, null); + } + finally + { + byteBlock.Dispose(); + } + } + + /// + /// 当发送数据前处理数据 + /// + /// + /// + /// + /// + protected override void PreviewSend(byte[] buffer, int offset, int length, bool isAsync) + { + if (length < this.MinSizeHeader) + { + throw new RRQMException("发送数据小于设定值,相同解析器可能无法收到有效数据,已终止发送"); + } + + if (length > this.MaxSizeHeader) + { + throw new RRQMException("发送数据大于设定值,相同解析器可能无法收到有效数据,已终止发送"); + } + + ByteBlock byteBlock = null; + byte[] lenBytes = null; + + switch (this.fixedHeaderType) + { + case FixedHeaderType.Byte: + { + byte dataLen = (byte)(length - offset); + byteBlock = this.BytePool.GetByteBlock(dataLen + 1); + lenBytes = new byte[] { dataLen }; + break; + } + case FixedHeaderType.Ushort: + { + ushort dataLen = (ushort)(length - offset); + byteBlock = this.BytePool.GetByteBlock(dataLen + 2); + lenBytes = BitConverter.GetBytes(dataLen); + break; + } + case FixedHeaderType.Int: + { + int dataLen = length - offset; + byteBlock = this.BytePool.GetByteBlock(dataLen + 4); + lenBytes = BitConverter.GetBytes(dataLen); + break; + } + } + + try + { + byteBlock.Write(lenBytes); + byteBlock.Write(buffer, offset, length); + if (isAsync) + { + byte[] data = byteBlock.ToArray(); + this.GoSend(data, 0, data.Length, isAsync);//使用ByteBlock时不能异步发送 + } + else + { + this.GoSend(byteBlock.Buffer, 0, byteBlock.Len, isAsync); + } + } + catch (Exception ex) + { + throw ex; + } + finally + { + byteBlock.Dispose(); + } + } + } +} \ No newline at end of file diff --git a/RRQMSocket/DataAdapter/FixedSizeDataHandlingAdapter.cs b/RRQMSocket/DataAdapter/FixedSizeDataHandlingAdapter.cs new file mode 100644 index 000000000..934e2db8b --- /dev/null +++ b/RRQMSocket/DataAdapter/FixedSizeDataHandlingAdapter.cs @@ -0,0 +1,159 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +using RRQMCore.ByteManager; +using System; + +namespace RRQMSocket +{ + /// + /// 固定长度数据处理器 + /// + public class FixedSizeDataHandlingAdapter : DataHandlingAdapter + { + /// + /// 构造函数 + /// + /// 数据包的长度 + public FixedSizeDataHandlingAdapter(int fixedSize) + { + this.FixedSize = fixedSize; + } + + /// + /// 获取已设置的数据包的长度 + /// + public int FixedSize { get; private set; } + + /// + /// 临时包 + /// + private ByteBlock tempByteBlock; + + /// + /// 包剩余长度 + /// + private int surPlusLength = 0; + + /// + /// 预处理 + /// + /// + protected override void PreviewReceived(ByteBlock byteBlock) + { + byte[] buffer = byteBlock.Buffer; + int r = byteBlock.Len; + if (this.tempByteBlock == null) + { + SplitPackage(buffer, 0, r); + } + else + { + if (surPlusLength == r) + { + this.tempByteBlock.Write(buffer, 0, surPlusLength); + PreviewHandle(this.tempByteBlock); + this.tempByteBlock = null; + surPlusLength = 0; + } + else if (surPlusLength < r) + { + this.tempByteBlock.Write(buffer, 0, surPlusLength); + PreviewHandle(this.tempByteBlock); + this.tempByteBlock = null; + SplitPackage(buffer, surPlusLength, r); + } + else + { + this.tempByteBlock.Write(buffer, 0, r); + surPlusLength -= r; + } + } + } + + private void SplitPackage(byte[] dataBuffer, int index, int r) + { + while (index < r) + { + if (r - index >= this.FixedSize) + { + ByteBlock byteBlock = this.BytePool.GetByteBlock(this.FixedSize); + byteBlock.Write(dataBuffer, index, this.FixedSize); + PreviewHandle(byteBlock); + surPlusLength = 0; + } + else//半包 + { + this.tempByteBlock = this.BytePool.GetByteBlock(this.FixedSize); + surPlusLength = this.FixedSize - (r - index); + this.tempByteBlock.Write(dataBuffer, index, r - index); + } + index += this.FixedSize; + } + } + + private void PreviewHandle(ByteBlock byteBlock) + { + try + { + this.GoReceived(byteBlock, null); + } + finally + { + byteBlock.Dispose(); + } + } + + /// + /// 预处理 + /// + /// + /// + /// + /// + protected override void PreviewSend(byte[] buffer, int offset, int length, bool isAsync) + { + int dataLen = length - offset; + if (dataLen > this.FixedSize) + { + throw new RRQMOverlengthException("发送的数据包长度大于FixedSize"); + } + ByteBlock byteBlock = this.BytePool.GetByteBlock(this.FixedSize); + + byteBlock.Write(buffer, offset, length); + for (int i = (int)byteBlock.Position; i < this.FixedSize; i++) + { + byteBlock.Buffer[i] = 0; + } + byteBlock.SetLength(this.FixedSize); + try + { + if (isAsync) + { + byte[] data = byteBlock.ToArray(); + this.GoSend(data, 0, data.Length, true);//使用ByteBlock时不能异步发送 + } + else + { + this.GoSend(byteBlock.Buffer, 0, byteBlock.Len, false); + } + } + catch (Exception ex) + { + throw ex; + } + finally + { + byteBlock.Dispose(); + } + } + } +} \ No newline at end of file diff --git a/RRQMSocket/DataAdapter/JsonStringDataHandlingAdapter.cs b/RRQMSocket/DataAdapter/JsonStringDataHandlingAdapter.cs new file mode 100644 index 000000000..1e11ea0d5 --- /dev/null +++ b/RRQMSocket/DataAdapter/JsonStringDataHandlingAdapter.cs @@ -0,0 +1,135 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +using RRQMCore.ByteManager; +using System.Text; +using System.Text.RegularExpressions; + +namespace RRQMSocket +{ + /// + /// Json字符串数据处理解析器(该解析器由网友"明月"提供) + /// + public class JsonStringDataHandlingAdapter : DataHandlingAdapter + { + private ByteBlock Temp; + + /// + /// 预解析 + /// + /// + protected override void PreviewReceived(ByteBlock byteBlock) + { + byte[] buffer = byteBlock.Buffer; + int length = byteBlock.Len; + + //Console.WriteLine("----------------接收的新数据-------------------"); + if (Temp != null) + { + Temp.Write(byteBlock.Buffer, 0, length); + buffer = Temp.Buffer; + length = (int)Temp.Length; + } + + string msg = Encoding.UTF8.GetString(buffer, 0, length); + if (msg.Contains("}{")) + { + //Console.WriteLine("----------------发生粘包-------------------"); + string[] mes = Regex.Split(msg, "}{"); + for (int i = 0; i < mes.Length; i++) + { + string str = mes[i]; + if (i == 0) + { + str += "}"; + } + else if (i == mes.Length - 1) + { + str = "{" + str; + int start = StringCount(str, "{"); + int end = StringCount(str, "}"); + if (start == end) + { + if (Temp != null) + { + Temp = null; + } + } + else + { + byte[] surPlus = Encoding.UTF8.GetBytes(str); + + if (Temp != null) + { + Temp = null; + } + //Temp = BytePool.GetByteBlock(1024*1024*10); + Temp = BytePool.GetByteBlock(length); + + Temp.Write(surPlus); + //Console.WriteLine("----------------数据不完整-------------------"); + break; + } + } + else + { + str = "{" + str + "}"; + } + //Console.WriteLine(str); + PreviewHandle(str); + } + } + else if (msg[0] == '{' && msg[1] == '}') + { + Temp = null; + PreviewHandle(msg); + } + else + { + if (Temp == null) + { + Temp = BytePool.GetByteBlock(length); + Temp.Write(byteBlock.Buffer, 0, length); + } + + Temp.Write(byteBlock.Buffer, 0, length); + } + } + + /// + /// 预发送封装 + /// + /// + /// + /// + /// + protected override void PreviewSend(byte[] buffer, int offset, int length, bool isAsync) + { + this.GoSend(buffer, offset, length, isAsync); + } + + private int StringCount(string source, string match) + { + int count = 0; + if (source.Contains(match)) + { + string temp = source.Replace(match, ""); + count = (source.Length - temp.Length) / match.Length; + } + return count; + } + + private void PreviewHandle(string msg) + { + GoReceived(null, msg); + } + } +} \ No newline at end of file diff --git a/RRQMSocket/DataAdapter/NormalDataHandlingAdapter.cs b/RRQMSocket/DataAdapter/NormalDataHandlingAdapter.cs new file mode 100644 index 000000000..d5bf9d0d5 --- /dev/null +++ b/RRQMSocket/DataAdapter/NormalDataHandlingAdapter.cs @@ -0,0 +1,42 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +using RRQMCore.ByteManager; + +namespace RRQMSocket +{ + /// + /// 普通数据处理器 + /// + public class NormalDataHandlingAdapter : DataHandlingAdapter + { + /// + /// 当接收到数据时处理数据 + /// + /// 数据流 + protected override void PreviewReceived(ByteBlock byteBlock) + { + this.GoReceived(byteBlock, null); + } + + /// + /// 当发送数据前处理数据 + /// + /// 数据 + /// 偏移 + /// 长度 + /// + protected override void PreviewSend(byte[] buffer, int offset, int length, bool isAsync) + { + this.GoSend(buffer, offset, length, isAsync); + } + } +} \ No newline at end of file diff --git a/RRQMSocket/DataAdapter/TerminatorDataHandlingAdapter.cs b/RRQMSocket/DataAdapter/TerminatorDataHandlingAdapter.cs new file mode 100644 index 000000000..7e724173d --- /dev/null +++ b/RRQMSocket/DataAdapter/TerminatorDataHandlingAdapter.cs @@ -0,0 +1,220 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +using RRQMCore.ByteManager; +using RRQMCore.Exceptions; +using RRQMCore.Helper; +using RRQMCore.Log; +using System; +using System.Collections.Generic; +using System.Text; + +namespace RRQMSocket +{ + /// + /// 终止字符处理器 + /// + public class TerminatorDataHandlingAdapter : DataHandlingAdapter + { + /// + /// 构造函数 + /// + /// + /// + public TerminatorDataHandlingAdapter(int maxSize, string terminator) : this(maxSize, 0, Encoding.UTF8.GetBytes(terminator)) + { + } + + /// + /// 构造函数 + /// + /// + /// + /// + public TerminatorDataHandlingAdapter(int maxSize, string terminator, Encoding encoding) + : this(maxSize, 0, encoding.GetBytes(terminator)) + { + } + + /// + /// 构造函数 + /// + /// + /// + /// + public TerminatorDataHandlingAdapter(int maxSize, int minSize, byte[] terminatorCode) + { + this.maxSize = maxSize; + this.minSize = minSize; + this.terminatorCode = terminatorCode; + } + + private byte[] terminatorCode; + + private int maxSize = 1024; + + /// + /// 在未找到终止因子时,允许的最大长度,默认1024 + /// + public int MaxSize + { + get { return maxSize; } + set { maxSize = value; } + } + + private int minSize = 0; + + /// + /// 即使找到了终止因子,也不会结束,默认0 + /// + public int MinSize + { + get { return minSize; } + set { minSize = value; } + } + + private bool reserveTerminatorCode; + + /// + /// 保留终止因子 + /// + public bool ReserveTerminatorCode + { + get { return reserveTerminatorCode; } + set { reserveTerminatorCode = value; } + } + + private ByteBlock tempByteBlock; + + /// + /// 预处理 + /// + /// + protected override void PreviewReceived(ByteBlock byteBlock) + { + byte[] buffer = byteBlock.Buffer; + int r = byteBlock.Len; + if (this.tempByteBlock != null) + { + this.tempByteBlock.Write(buffer, 0, r); + buffer = this.tempByteBlock.Buffer; + r = (int)this.tempByteBlock.Position; + } + + List indexes = buffer.IndexOfInclude(0,r, this.terminatorCode); + if (indexes.Count == 0) + { + if (r > this.MaxSize) + { + if (this.tempByteBlock != null) + { + this.tempByteBlock.Dispose(); + this.tempByteBlock = null; + } + + Logger.Debug(LogType.Error, this, "在已接收数据大于设定值的情况下未找到终止因子,已放弃接收"); + return; + } + else if (this.tempByteBlock == null) + { + this.tempByteBlock = this.BytePool.GetByteBlock(r * 2); + this.tempByteBlock.Write(buffer, 0, r); + } + } + else + { + int startIndex = 0; + foreach (int lastIndex in indexes) + { + int length; + if (this.reserveTerminatorCode) + { + length = lastIndex - startIndex + 1; + } + else + { + length = lastIndex - startIndex - this.terminatorCode.Length + 1; + } + + ByteBlock packageByteBlock = this.BytePool.GetByteBlock(length); + packageByteBlock.Write(buffer, startIndex, length); + + string mes = Encoding.UTF8.GetString(packageByteBlock.Buffer, 0, (int)packageByteBlock.Position); + + this.PreviewHandle(packageByteBlock); + startIndex = lastIndex + 1; + } + if (this.tempByteBlock != null) + { + this.tempByteBlock.Dispose(); + this.tempByteBlock = null; + } + if (startIndex < r) + { + this.tempByteBlock = this.BytePool.GetByteBlock((r - startIndex) * 2); + this.tempByteBlock.Write(buffer, startIndex, r - startIndex); + } + } + } + + private void PreviewHandle(ByteBlock byteBlock) + { + try + { + this.GoReceived(byteBlock, null); + } + finally + { + byteBlock.Dispose(); + } + } + + /// + /// 预处理 + /// + /// + /// + /// + /// + protected override void PreviewSend(byte[] buffer, int offset, int length, bool isAsync) + { + if (length>this.maxSize) + { + throw new RRQMException("发送的数据长度大于适配器设定的最大值,接收方可能会抛弃。"); + } + int dataLen = length - offset + this.terminatorCode.Length; + ByteBlock byteBlock = this.BytePool.GetByteBlock(dataLen); + byteBlock.Write(buffer, offset, length); + byteBlock.Write(this.terminatorCode); + + try + { + if (isAsync) + { + byte[] data = byteBlock.ToArray(); + this.GoSend(data, 0, data.Length, isAsync);//使用ByteBlock时不能异步发送 + } + else + { + this.GoSend(byteBlock.Buffer, 0, byteBlock.Len, isAsync); + } + } + catch (Exception ex) + { + throw ex; + } + finally + { + byteBlock.Dispose(); + } + } + } +} \ No newline at end of file diff --git a/RRQMSocket/DelegateCollection.cs b/RRQMSocket/DelegateCollection.cs new file mode 100644 index 000000000..626d4265f --- /dev/null +++ b/RRQMSocket/DelegateCollection.cs @@ -0,0 +1,49 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +using RRQMCore.ByteManager; +using RRQMSocket; +using System.Net; + +/// +/// 显示信息 +/// +/// +/// +public delegate void RRQMMessageEventHandler(object sender, MesEventArgs e); + +/// +/// ByteBlock +/// +/// +/// +public delegate void RRQMByteBlockEventHandler(object sender, ByteBlock e); + +/// +/// UDP接收 +/// +/// +/// +public delegate void RRQMUDPByteBlockEventHandler(EndPoint endpoint, ByteBlock e); + +/// +/// 字节数据 +/// +/// +/// +public delegate void RRQMBytesEventHandler(object sender, BytesEventArgs e); + +/// +/// 字节数据 +/// +/// +/// +public delegate void RRQMReturnBytesEventHandler(object sender, ReturnBytesEventArgs e); \ No newline at end of file diff --git a/RRQMSocket/Enum/ClearType.cs b/RRQMSocket/Enum/ClearType.cs new file mode 100644 index 000000000..d82632120 --- /dev/null +++ b/RRQMSocket/Enum/ClearType.cs @@ -0,0 +1,32 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +using System; + +namespace RRQMSocket +{ + /// + /// 清理统计类型 + /// + [Flags] + public enum ClearType + { + /// + /// 从发送统计 + /// + Send = 1, + + /// + /// 从接收统计 + /// + Receive = 2 + } +} \ No newline at end of file diff --git a/RRQMSocket/Enum/FixedHeaderType.cs b/RRQMSocket/Enum/FixedHeaderType.cs new file mode 100644 index 000000000..e4876356f --- /dev/null +++ b/RRQMSocket/Enum/FixedHeaderType.cs @@ -0,0 +1,35 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ + +namespace RRQMSocket +{ + /// + /// 固定包头类型 + /// + public enum FixedHeaderType : byte + { + /// + /// 以1Byte标识长度,最长接收255 + /// + Byte = 1, + + /// + /// 以2Byte标识长度,最长接收65535 + /// + Ushort = 2, + + /// + /// 以4Byte标识长度,最长接收2147483647 + /// + Int = 4 + } +} \ No newline at end of file diff --git a/RRQMSocket/Enum/ServerState.cs b/RRQMSocket/Enum/ServerState.cs new file mode 100644 index 000000000..5e0265ced --- /dev/null +++ b/RRQMSocket/Enum/ServerState.cs @@ -0,0 +1,39 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +namespace RRQMSocket +{ + /// + /// 服务器状态 + /// + public enum ServerState + { + /// + /// 无状态,指示为初建 + /// + None, + + /// + /// 正在运行 + /// + Running, + + /// + /// 已停止 + /// + Stopped, + + /// + /// 已释放 + /// + Disposed + } +} \ No newline at end of file diff --git a/RRQMSocket/EventArgs/BytesEventArgs.cs b/RRQMSocket/EventArgs/BytesEventArgs.cs new file mode 100644 index 000000000..c8f529324 --- /dev/null +++ b/RRQMSocket/EventArgs/BytesEventArgs.cs @@ -0,0 +1,35 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +using RRQMCore.Event; + +namespace RRQMSocket +{ + /// + /// 字节事件 + /// + public class BytesEventArgs : RRQMEventArgs + { + /// + /// 构造函数 + /// + /// + public BytesEventArgs(byte[] data) + { + this.ReceivedDataBytes = data; + } + + /// + /// 字节数组 + /// + public byte[] ReceivedDataBytes { get; private set; } + } +} \ No newline at end of file diff --git a/RRQMSocket/EventArgs/MesEventArgs.cs b/RRQMSocket/EventArgs/MesEventArgs.cs new file mode 100644 index 000000000..7f05d54a6 --- /dev/null +++ b/RRQMSocket/EventArgs/MesEventArgs.cs @@ -0,0 +1,42 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +using RRQMCore.Event; + +namespace RRQMSocket +{ + /// + /// 消息事件 + /// + public class MesEventArgs : RRQMEventArgs + { + /// + /// 构造函数 + /// + /// + public MesEventArgs(string mes) + { + this.Message = mes; + } + + /// + /// 直接构造函数 + /// + public MesEventArgs() + { + } + + /// + /// 消息 + /// + public string Message { get; set; } + } +} \ No newline at end of file diff --git a/RRQMSocket/EventArgs/ReturnBytesEventArgs.cs b/RRQMSocket/EventArgs/ReturnBytesEventArgs.cs new file mode 100644 index 000000000..c63832ad9 --- /dev/null +++ b/RRQMSocket/EventArgs/ReturnBytesEventArgs.cs @@ -0,0 +1,32 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +namespace RRQMSocket +{ + /// + /// 允许返回的字节 + /// + public class ReturnBytesEventArgs : BytesEventArgs + { + /// + /// 构造函数 + /// + /// + public ReturnBytesEventArgs(byte[] receivedData) : base(receivedData) + { + } + + /// + /// 返回字节 + /// + public byte[] ReturnDataBytes { get; set; } + } +} \ No newline at end of file diff --git a/RRQMSocket/Exceptions/RRQMNotConnectedException.cs b/RRQMSocket/Exceptions/RRQMNotConnectedException.cs new file mode 100644 index 000000000..e0da099c8 --- /dev/null +++ b/RRQMSocket/Exceptions/RRQMNotConnectedException.cs @@ -0,0 +1,52 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +using RRQMCore.Exceptions; + +namespace RRQMSocket +{ + /* + 若汝棋茗 + */ + + /// + /// 未连接异常 + /// + + public class RRQMNotConnectedException : RRQMException + { + /// + /// + /// + public RRQMNotConnectedException() : base() { } + + /// + /// + /// + /// + public RRQMNotConnectedException(string message) : base(message) { } + + /// + /// + /// + /// + /// + public RRQMNotConnectedException(string message, System.Exception inner) : base(message, inner) { } + + /// + /// + /// + /// + /// + protected RRQMNotConnectedException(System.Runtime.Serialization.SerializationInfo info, + System.Runtime.Serialization.StreamingContext context) : base(info, context) { } + } +} \ No newline at end of file diff --git a/RRQMSocket/Exceptions/RRQMOverlengthException.cs b/RRQMSocket/Exceptions/RRQMOverlengthException.cs new file mode 100644 index 000000000..892c78c20 --- /dev/null +++ b/RRQMSocket/Exceptions/RRQMOverlengthException.cs @@ -0,0 +1,47 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +using RRQMCore.Exceptions; + +namespace RRQMSocket +{ + /// + /// 超长异常 + /// + public class RRQMOverlengthException : RRQMException + { + /// + /// + /// + public RRQMOverlengthException() : base() { } + + /// + /// + /// + /// + public RRQMOverlengthException(string message) : base(message) { } + + /// + /// + /// + /// + /// + public RRQMOverlengthException(string message, System.Exception inner) : base(message, inner) { } + + /// + /// + /// + /// + /// + protected RRQMOverlengthException(System.Runtime.Serialization.SerializationInfo info, + System.Runtime.Serialization.StreamingContext context) : base(info, context) { } + } +} \ No newline at end of file diff --git a/RRQMSocket/Exceptions/RRQMTimeoutException.cs b/RRQMSocket/Exceptions/RRQMTimeoutException.cs new file mode 100644 index 000000000..74ad7bbbd --- /dev/null +++ b/RRQMSocket/Exceptions/RRQMTimeoutException.cs @@ -0,0 +1,48 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +using RRQMCore.Exceptions; + +namespace RRQMSocket +{ + /// + /// 超时异常 + /// + + public class RRQMTimeoutException : RRQMException + { + /// + /// + /// + public RRQMTimeoutException() : base() { } + + /// + /// + /// + /// + public RRQMTimeoutException(string message) : base(message) { } + + /// + /// + /// + /// + /// + public RRQMTimeoutException(string message, System.Exception inner) : base(message, inner) { } + + /// + /// + /// + /// + /// + protected RRQMTimeoutException(System.Runtime.Serialization.SerializationInfo info, + System.Runtime.Serialization.StreamingContext context) : base(info, context) { } + } +} \ No newline at end of file diff --git a/RRQMSocket/Exceptions/RRQMTokenVerifyException.cs b/RRQMSocket/Exceptions/RRQMTokenVerifyException.cs new file mode 100644 index 000000000..42e291a7d --- /dev/null +++ b/RRQMSocket/Exceptions/RRQMTokenVerifyException.cs @@ -0,0 +1,47 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +using RRQMCore.Exceptions; + +namespace RRQMSocket +{ + /// + /// 验证令箭异常 + /// + public class RRQMTokenVerifyException : RRQMException + { + /// + /// + /// + public RRQMTokenVerifyException() : base() { } + + /// + /// + /// + /// + public RRQMTokenVerifyException(string message) : base(message) { } + + /// + /// + /// + /// + /// + public RRQMTokenVerifyException(string message, System.Exception inner) : base(message, inner) { } + + /// + /// + /// + /// + /// + protected RRQMTokenVerifyException(System.Runtime.Serialization.SerializationInfo info, + System.Runtime.Serialization.StreamingContext context) : base(info, context) { } + } +} \ No newline at end of file diff --git a/RRQMSocket/Interface/IClient.cs b/RRQMSocket/Interface/IClient.cs new file mode 100644 index 000000000..2bd694993 --- /dev/null +++ b/RRQMSocket/Interface/IClient.cs @@ -0,0 +1,108 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +using RRQMCore.ByteManager; +using RRQMCore.Exceptions; +using RRQMCore.Log; +using System; +using System.Net.Sockets; + +namespace RRQMSocket +{ + /// + /// 终端接口 + /// + public interface IClient : IDisposable + { + /// + /// IP地址 + /// + string IP { get; } + + /// + /// 端口号 + /// + int Port { get; } + + /// + /// 主通信器 + /// + Socket MainSocket { get; } + + /// + /// 内存池实例 + /// + BytePool BytePool { get; } + + /// + /// 日志记录器 + /// + ILog Logger { get; } + + /// + /// 发送字节流 + /// + /// + /// + /// + /// + /// + /// + void Send(byte[] buffer, int offset, int length); + + /// + /// 发送字节流 + /// + /// + /// + /// + /// + void Send(byte[] buffer); + + /// + /// 发送流中的有效数据 + /// + /// + /// + /// + /// + void Send(ByteBlock byteBlock); + + /// + /// IOCP发送 + /// + /// + /// + /// + /// + /// + /// + void SendAsync(byte[] buffer, int offset, int length); + + /// + /// IOCP发送 + /// + /// + /// + /// + /// + void SendAsync(byte[] buffer); + + /// + /// IOCP发送流中的有效数据 + /// + /// + /// + /// + /// + void SendAsync(ByteBlock byteBlock); + } +} \ No newline at end of file diff --git a/RRQMSocket/Interface/IHandleBuffer.cs b/RRQMSocket/Interface/IHandleBuffer.cs new file mode 100644 index 000000000..dc711cc32 --- /dev/null +++ b/RRQMSocket/Interface/IHandleBuffer.cs @@ -0,0 +1,28 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ + +using RRQMCore.ByteManager; + +namespace RRQMSocket +{ + /// + /// 处理数据 + /// + public interface IHandleBuffer + { + /// + /// 处理数据 + /// + /// + void HandleBuffer(ByteBlock byteBlock); + } +} \ No newline at end of file diff --git a/RRQMSocket/Interface/IService.cs b/RRQMSocket/Interface/IService.cs new file mode 100644 index 000000000..37503ab90 --- /dev/null +++ b/RRQMSocket/Interface/IService.cs @@ -0,0 +1,71 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +using RRQMCore.ByteManager; +using RRQMCore.Exceptions; +using System; + +namespace RRQMSocket +{ + /// + /// 服务器接口 + /// + public interface IService : IDisposable + { + /// + /// 获取默认内存池实例 + /// + BytePool BytePool { get; } + + /// + /// 服务器状态 + /// + ServerState ServerState { get; } + + /// + /// 获取服务器配置 + /// + ServiceConfig ServiceConfig { get; } + + /// + /// 名称 + /// + string ServerName { get; } + + /// + /// 配置服务器 + /// + /// 配置 + /// + void Setup(ServiceConfig serverConfig); + + /// + /// 配置服务器 + /// + /// + /// + void Setup(int port); + + /// + /// 启动 + /// + /// + /// + /// + void Start(); + + /// + /// 停止 + /// + /// + void Stop(); + } +} \ No newline at end of file diff --git a/RRQMSocket/Interface/ISocket.cs b/RRQMSocket/Interface/ISocket.cs new file mode 100644 index 000000000..bfebfdceb --- /dev/null +++ b/RRQMSocket/Interface/ISocket.cs @@ -0,0 +1,32 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +using RRQMCore.Log; +using System; + +namespace RRQMSocket +{ + /// + /// Socket基接口 + /// + public interface ISocket : IDisposable + { + /// + /// 数据交互缓存池限制 + /// + int BufferLength { get; } + + /// + /// 日志记录器 + /// + ILog Logger { get; } + } +} \ No newline at end of file diff --git a/RRQMSocket/Interface/ISocketClient.cs b/RRQMSocket/Interface/ISocketClient.cs new file mode 100644 index 000000000..e4882a062 --- /dev/null +++ b/RRQMSocket/Interface/ISocketClient.cs @@ -0,0 +1,34 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +namespace RRQMSocket +{ + /// + /// 服务器辅助类接口 + /// + public interface ISocketClient : ITcpClient + { + /// + /// 用于索引的ID + /// + string ID { get; } + + /// + /// 标记 + /// + object Flag { get; set; } + + /// + /// 包含此辅助类的主服务器类 + /// + _ITcpService Service { get; } + } +} \ No newline at end of file diff --git a/RRQMSocket/Interface/ITcpClient.cs b/RRQMSocket/Interface/ITcpClient.cs new file mode 100644 index 000000000..98b62b3e6 --- /dev/null +++ b/RRQMSocket/Interface/ITcpClient.cs @@ -0,0 +1,42 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +using System.Net.Sockets; + +namespace RRQMSocket +{ + /// + /// TCP客户端接口 + /// + public interface ITcpClient : IClient + { + /// + /// 判断是否在线 + /// + bool Online { get; } + + /// + /// IP及端口号 + /// + string Name { get; } + + /// + /// 数据处理适配器 + /// + DataHandlingAdapter DataHandlingAdapter { get; } + + /// + /// 禁用发送或接收 + /// + /// + void Shutdown(SocketShutdown how); + } +} \ No newline at end of file diff --git a/RRQMSocket/Interface/ITcpService.cs b/RRQMSocket/Interface/ITcpService.cs new file mode 100644 index 000000000..ba0e9a1fb --- /dev/null +++ b/RRQMSocket/Interface/ITcpService.cs @@ -0,0 +1,134 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ + +using RRQMCore.ByteManager; +using RRQMCore.Exceptions; +using System.Collections.Generic; + +namespace RRQMSocket +{ + /// + /// TCP系列服务器接口 + /// + public interface ITcpService : IService where TClient : ISocketClient + { + /// + /// 获取最大可连接数 + /// + int MaxCount { get; } + + /// + /// 获取当前连接的所有客户端 + /// + SocketCliectCollection SocketClients { get; } + + /// + /// 获取清理无数据交互的SocketClient,默认60。如果不想清除,可使用-1。 + /// + int ClearInterval { get; } + + /// + /// 客户端成功连接时 + /// + event RRQMMessageEventHandler ClientConnected; + + /// + /// 有用户断开连接的时候 + /// + event RRQMMessageEventHandler ClientDisconnected; + + /// + /// 根据ID判断SocketClient是否存在 + /// + /// + /// + bool SocketClientExist(string id); + + /// + /// 尝试获取TClient + /// + /// ID + /// TClient + /// + bool TryGetSocketClient(string id, out TClient socketClient); + + /// + /// 发送字节流 + /// + /// 用于检索TcpSocketClient + /// + /// + /// + /// + /// + void Send(string id, byte[] buffer); + + /// + /// 发送字节流 + /// + /// 用于检索TcpSocketClient + /// + /// + /// + /// + /// + /// + /// + void Send(string id, byte[] buffer, int offset, int length); + + /// + /// 发送流中的有效数据 + /// + /// 用于检索TcpSocketClient + /// + /// + /// + /// + /// + void Send(string id, ByteBlock byteBlock); + + /// + /// 发送字节流 + /// + /// 用于检索TcpSocketClient + /// + /// + /// + /// + /// + void SendAsync(string id, byte[] buffer); + + /// + /// 发送字节流 + /// + /// 用于检索TcpSocketClient + /// + /// + /// + /// + /// + /// + /// + void SendAsync(string id, byte[] buffer, int offset, int length); + + /// + /// 发送流中的有效数据 + /// + /// 用于检索TcpSocketClient + /// + /// + /// + /// + /// + void SendAsync(string id, ByteBlock byteBlock); + } +} \ No newline at end of file diff --git a/RRQMSocket/Interface/IUserClient.cs b/RRQMSocket/Interface/IUserClient.cs new file mode 100644 index 000000000..c5437963d --- /dev/null +++ b/RRQMSocket/Interface/IUserClient.cs @@ -0,0 +1,42 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +using RRQMCore.Exceptions; +using System; + +namespace RRQMSocket +{ + /// + /// 用户终端接口 + /// + public interface IUserClient + { + /// + /// 连接服务器 + /// + /// + void Connect(); + + /// + /// 异步连接服务器 + /// + /// + /// + void ConnectAsync(Action callback = null); + + /// + /// 配置服务器 + /// + /// + /// + void Setup(TcpClientConfig clientConfig); + } +} \ No newline at end of file diff --git a/RRQMSocket/Interface/IUserTcpClient.cs b/RRQMSocket/Interface/IUserTcpClient.cs new file mode 100644 index 000000000..e37908c26 --- /dev/null +++ b/RRQMSocket/Interface/IUserTcpClient.cs @@ -0,0 +1,35 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ + +namespace RRQMSocket +{ + /// + /// TCP客户端终端接口 + /// + public interface IUserTcpClient : ITcpClient, IUserClient + { + /// + /// 仅发送,即不会开启接收线程。 + /// + bool OnlySend { get; } + + /// + /// 客户端配置 + /// + TcpClientConfig ClientConfig { get; } + + /// + /// 断开连接 + /// + void Disconnect(); + } +} \ No newline at end of file diff --git a/RRQMSocket/Interface/_ITcpService.cs b/RRQMSocket/Interface/_ITcpService.cs new file mode 100644 index 000000000..c6bca54b8 --- /dev/null +++ b/RRQMSocket/Interface/_ITcpService.cs @@ -0,0 +1,27 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ + +namespace RRQMSocket +{ + /// + /// TCP服务器辅助接口 + /// + public interface _ITcpService : IService + { + /// + /// 重新设置ID + /// + /// + /// + void ResetID(string oldID, string newID); + } +} \ No newline at end of file diff --git a/RRQMSocket/InternalClass/BufferQueue.cs b/RRQMSocket/InternalClass/BufferQueue.cs new file mode 100644 index 000000000..315dc0872 --- /dev/null +++ b/RRQMSocket/InternalClass/BufferQueue.cs @@ -0,0 +1,38 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +using System.Collections.Concurrent; + +namespace RRQMSocket +{ + /// + /// buffer队列 + /// + internal class BufferQueue + { + internal BufferQueue() + { + queue = new ConcurrentQueue(); + } + + private ConcurrentQueue queue; + + internal void Enqueue(ClientBuffer item) + { + this.queue.Enqueue(item); + } + + internal bool TryDequeue(out ClientBuffer result) + { + return this.queue.TryDequeue(out result); + } + } +} \ No newline at end of file diff --git a/RRQMSocket/InternalClass/BufferQueueGroup.cs b/RRQMSocket/InternalClass/BufferQueueGroup.cs new file mode 100644 index 000000000..e94855527 --- /dev/null +++ b/RRQMSocket/InternalClass/BufferQueueGroup.cs @@ -0,0 +1,41 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +using RRQMCore.ByteManager; +using System; +using System.Threading; + +namespace RRQMSocket +{ + internal class BufferQueueGroup : IDisposable + { + internal Thread Thread; + internal BufferQueue bufferAndClient; + internal EventWaitHandle waitHandleBuffer; + internal bool isWait; + internal BytePool bytePool; + + public void Dispose() + { + if (bufferAndClient != null) + { + while (bufferAndClient.TryDequeue(out _)) + { + } + } + + if (waitHandleBuffer != null) + { + waitHandleBuffer.Set(); + } + } + } +} \ No newline at end of file diff --git a/RRQMSocket/InternalClass/ClientBuffer.cs b/RRQMSocket/InternalClass/ClientBuffer.cs new file mode 100644 index 000000000..a3cf7f420 --- /dev/null +++ b/RRQMSocket/InternalClass/ClientBuffer.cs @@ -0,0 +1,26 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +using RRQMCore.ByteManager; +using System.Net; + +namespace RRQMSocket +{ + /// + /// 处理 + /// + public struct ClientBuffer + { + internal IHandleBuffer client; + internal ByteBlock byteBlock; + internal EndPoint endPoint; + } +} \ No newline at end of file diff --git a/RRQMSocket/InternalClass/SocketCliectCollection.cs b/RRQMSocket/InternalClass/SocketCliectCollection.cs new file mode 100644 index 000000000..7f2cf12b1 --- /dev/null +++ b/RRQMSocket/InternalClass/SocketCliectCollection.cs @@ -0,0 +1,113 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Diagnostics; + +namespace RRQMSocket +{ + /// + /// 客户端集合 + /// + [DebuggerDisplay("Count={Count}")] + public class SocketCliectCollection : IDisposable where T : ISocketClient + { + private RRQMCore.SnowflakeIDGenerator iDGenerator = new RRQMCore.SnowflakeIDGenerator(4); + + /// + /// 数量 + /// + public int Count { get { return this.tokenDic.Count; } } + + private ConcurrentDictionary tokenDic = new ConcurrentDictionary(); + + internal bool TryAdd(T socketClient) + { + return this.tokenDic.TryAdd(socketClient.ID, socketClient); + } + + internal string GetDefaultID() + { + return iDGenerator.NextID().ToString(); + } + + /// + /// 获取ID集合 + /// + /// + public IEnumerable GetIDs() + { + return this.tokenDic.Keys; + } + + internal bool TryRemove(string id) + { + return this.tokenDic.TryRemove(id, out _); + } + + /// + /// 尝试获取实例 + /// + /// + /// + /// + public bool TryGetSocketClient(string id, out T socketClient) + { + return this.tokenDic.TryGetValue(id, out socketClient); + } + + /// + /// 根据ID判断SocketClient是否存在 + /// + /// + /// + public bool SocketClientExist(string id) + { + if (tokenDic.ContainsKey(id)) + { + return true; + } + return false; + } + + /// + /// 获取SocketClient + /// + /// + /// + public T this[string id] + { + get + { + T t; + this.TryGetSocketClient(id, out t); + return t; + } + } + + /// + /// 释放客户端 + /// + public void Dispose() + { + foreach (var item in this.tokenDic.Keys) + { + if (this.tokenDic.TryGetValue(item, out T value)) + { + value.Dispose(); + } + } + this.tokenDic.Clear(); + } + } +} \ No newline at end of file diff --git a/RRQMSocket/LICENSE b/RRQMSocket/LICENSE new file mode 100644 index 000000000..5a9bb6217 --- /dev/null +++ b/RRQMSocket/LICENSE @@ -0,0 +1,201 @@ +Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license procotol you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/RRQMSocket/Logger/Log.cs b/RRQMSocket/Logger/Log.cs new file mode 100644 index 000000000..4fd0191fa --- /dev/null +++ b/RRQMSocket/Logger/Log.cs @@ -0,0 +1,45 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +using RRQMCore.Log; +using System; + +namespace RRQMSocket +{ + /// + /// 日志记录器 + /// + public class Log : ILog + { + /// + /// 记录日志 + /// + /// + /// + /// + public void Debug(LogType logType, object source, string message) + { + Console.WriteLine($"类型:{logType},消息:{message}"); + } + + /// + /// 记录日志 + /// + /// + /// + /// + /// + public void Debug(LogType logType, object source, string message, Exception exception) + { + Console.WriteLine($"类型:{logType},消息:{message},堆:{exception.StackTrace}"); + } + } +} \ No newline at end of file diff --git a/RRQMSocket/ParameterClass/VerifyOption.cs b/RRQMSocket/ParameterClass/VerifyOption.cs new file mode 100644 index 000000000..991a57b1e --- /dev/null +++ b/RRQMSocket/ParameterClass/VerifyOption.cs @@ -0,0 +1,40 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ + +namespace RRQMSocket +{ + /// + /// Token连接验证 + /// + public class VerifyOption + { + /// + /// 令箭 + /// + public string Token { get; internal set; } + + /// + /// 是否接受 + /// + public bool Accept { get; set; } + + /// + /// 不接受时,返回客户端信息 + /// + public string ErrorMessage { get; set; } + + /// + /// 标记,会同步至TcpSocketClient + /// + public object Flag { get; set; } + } +} \ No newline at end of file diff --git a/RRQMSocket/Pool/Interface/IClientGroup.cs b/RRQMSocket/Pool/Interface/IClientGroup.cs new file mode 100644 index 000000000..c0c05fe4e --- /dev/null +++ b/RRQMSocket/Pool/Interface/IClientGroup.cs @@ -0,0 +1,21 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ + +namespace RRQMSocket.Pool +{ + /// + /// 终端组 + /// + public interface IClientGroup + { + } +} \ No newline at end of file diff --git a/RRQMSocket/Pool/Interface/IConnectionPool.cs b/RRQMSocket/Pool/Interface/IConnectionPool.cs new file mode 100644 index 000000000..3c70ba85b --- /dev/null +++ b/RRQMSocket/Pool/Interface/IConnectionPool.cs @@ -0,0 +1,91 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +using RRQMCore.ByteManager; +using RRQMCore.Exceptions; +using RRQMCore.Log; +using RRQMCore.Pool; +using System.Collections.Generic; + +namespace RRQMSocket.Pool +{ + /// + /// 连接池接口 + /// + public interface IConnectionPool : IObjectPool where T : IUserTcpClient + { + /// + /// 当池中的客户都端发生错误时 + /// + event RRQMMessageEventHandler OnClientError; + + /// + /// 日志记录器 + /// + ILog Logger { get; set; } + + /// + /// 获取内存池实例 + /// + BytePool BytePool { get; } + + /// + /// 对象池容量 + /// + int Capacity { get; } + + /// + /// 发生错误的客户端列表 + /// + List ErrorClientList { get; } + + /// + /// 获取即将在下一次通信的客户端单体 + /// + /// + T GetNextClient(); + + /// + /// 补充成员 + /// + /// + void Replenish(T client); + + /// + /// 发送字节流 + /// + /// + /// + /// + /// + /// + /// + void Send(byte[] buffer, int offset, int length); + + /// + /// 发送字节流 + /// + /// + /// + /// + /// + void Send(byte[] buffer); + + /// + /// 发送流中的有效数据 + /// + /// + /// + /// + /// + void Send(ByteBlock byteBlock); + } +} \ No newline at end of file diff --git a/RRQMSocket/Pool/TcpConnectionPool.cs b/RRQMSocket/Pool/TcpConnectionPool.cs new file mode 100644 index 000000000..0ae8acc71 --- /dev/null +++ b/RRQMSocket/Pool/TcpConnectionPool.cs @@ -0,0 +1,248 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +//using RRQMCore.ByteManager; +//using RRQMCore.Exceptions; +//using RRQMCore.Log; +//using System; +//using System.Collections.Concurrent; +//using System.Collections.Generic; + +//namespace RRQMSocket.Pool +//{ +// /// +// /// 连接池 +// /// +// public class TcpConnectionPool : IConnectionPool where T : TcpClient +// { +// private TcpConnectionPool() +// { +// this.Logger = new Log(); +// this.ErrorClientList = new List(); +// this.queue = new ConcurrentQueue(); +// } + +// /// +// /// 创建连接池 +// /// +// /// 容量 +// public static TcpConnectionPool CreatConnectionPool(int capacity) +// { +// return CreatConnectionPool(capacity, new BytePool(1024 * 1024 * 1000, 1024 * 1024 * 20), null, null); +// } + +// /// +// /// 创建连接池 +// /// +// /// 容量 +// /// 当每个连接单元被初始化时回调 +// /// +// public static TcpConnectionPool CreatConnectionPool(int capacity, Action onClientIniCallback) +// { +// return CreatConnectionPool(capacity, new BytePool(1024 * 1024 * 1000, 1024 * 1024 * 20), onClientIniCallback, null); +// } + +// /// +// /// 创建连接池 +// /// +// /// 容量 +// /// 指定内存池实例 +// /// 当每个连接单元被初始化时回调 +// /// 创建单元时构造函数参数 +// /// +// public static TcpConnectionPool CreatConnectionPool(int capacity, BytePool bytePool, Action onClientIniCallback, params object[] args) +// { +// if (capacity < 1) +// { +// throw new RRQMException("容量不可小于1"); +// } + +// TcpConnectionPool connectionPool = new TcpConnectionPool(); +// connectionPool.BytePool = bytePool; +// connectionPool.Capacity = capacity; +// for (int i = 0; i < capacity; i++) +// { +// T client = (T)Activator.CreateInstance(typeof(T), args); +// connectionPool.queue.Enqueue(client); +// onClientIniCallback?.Invoke(client); +// } +// return connectionPool; +// } + +// /// +// /// 连接到服务器 +// /// +// /// +// /// 连接成功数 +// public int Connect(IPHost iPHost) +// { +// int count = 0; +// int successCount = 0; +// while (count < this.Capacity) +// { +// T client; +// if (this.queue.TryDequeue(out client)) +// { +// try +// { +// client.Connect(iPHost); +// successCount++; +// } +// finally +// { +// this.queue.Enqueue(client); +// } +// } +// count++; +// } +// return successCount; +// } + +// private ConcurrentQueue queue; + +// /// +// /// 当池中的客户端发生错误时 +// /// +// public event RRQMMessageEventHandler OnClientError; + +// /// +// /// 发生错误的客户端列表 +// /// +// public List ErrorClientList { get; private set; } + +// /// +// /// 对象池容量 +// /// +// public int Capacity { get; private set; } + +// /// +// /// 日志记录器 +// /// +// public ILog Logger { get; set; } + +// /// +// /// 可使用数量 +// /// +// public int FreeSize { get { return this.queue.Count; } } + +// /// +// /// 获取内存池实例 +// /// +// public BytePool BytePool { get; private set; } + +// /// +// /// 获取即将在下一次通信的客户端单体 +// /// +// /// +// public T GetNextClient() +// { +// T client; +// this.queue.TryPeek(out client); +// return client; +// } + +// /// +// /// 补充成员 +// /// +// /// +// public void Replenish(T client) +// { +// this.queue.Enqueue(client); +// this.Capacity++; +// } + +// /// +// /// 发送字节流 +// /// +// /// +// /// +// /// +// /// +// public void Send(byte[] buffer) +// { +// this.Send(buffer, 0, buffer.Length); +// } + +// /// +// /// 发送流中的有效数据 +// /// +// /// +// /// +// /// +// /// +// public void Send(ByteBlock byteBlock) +// { +// this.Send(byteBlock.Buffer, 0, byteBlock.Len); +// } + +// /// +// /// 发送字节流 +// /// +// /// +// /// +// /// +// /// +// /// +// /// +// public void Send(byte[] buffer, int offset, int length) +// { +// int count = 0; +// while (true) +// { +// T client; +// if (this.queue.TryDequeue(out client)) +// { +// try +// { +// client.Send(buffer, offset, length); +// this.queue.Enqueue(client); +// break; +// } +// catch (Exception ex) +// { +// this.ErrorClientList.Add(client); +// Logger.Debug(LogType.Warning, client, ex.Message); +// this.OnClientError?.Invoke(client, new MesEventArgs(ex.Message)); +// } +// } +// if (++count > this.FreeSize) +// { +// throw new RRQMException(); +// } +// } +// } + +// /// +// /// 清空池中对象 +// /// +// public void Clear() +// { +// T client; +// while (this.queue.TryDequeue(out client)) +// { +// client.Dispose(); +// } +// foreach (var item in this.ErrorClientList) +// { +// item.Dispose(); +// } +// this.ErrorClientList.Clear(); +// } + +// /// +// /// 释放资源 +// /// +// public void Dispose() +// { +// this.Clear(); +// } +// } +//} \ No newline at end of file diff --git a/RRQMSocket/RRQM.ico b/RRQMSocket/RRQM.ico new file mode 100644 index 000000000..6465fb16e Binary files /dev/null and b/RRQMSocket/RRQM.ico differ diff --git a/RRQMSocket/RRQM.png b/RRQMSocket/RRQM.png new file mode 100644 index 000000000..1fc567ad9 Binary files /dev/null and b/RRQMSocket/RRQM.png differ diff --git a/RRQMSocket/RRQMSocket.csproj b/RRQMSocket/RRQMSocket.csproj new file mode 100644 index 000000000..368b56b66 --- /dev/null +++ b/RRQMSocket/RRQMSocket.csproj @@ -0,0 +1,88 @@ + + + net45;netcoreapp3.1;netstandard2.0 + RRQM.ico + true + RRQM.pfx + 5.5.0 + 若汝棋茗 + Copyright © 2021 若汝棋茗 + IOCP,TCP,UDP,Socket + 介绍:RRQMSocket是一个整合性的、超轻量级的网络通信框架。它具有高并发、事件订阅、插件式扩展、自动活性检测、多线程处理等特点,让使用者能够更加简单的、快速的搭建网络框架。 + +更新说明: +优化:架构接口。 +修改:ClearInterval单位由“秒”调整为“毫秒”。 +修改:BufferLength默认值调整为64Kb。 + +API:https://gitee.com/RRQM_OS/RRQM/wikis/pages +Demo:https://gitee.com/RRQM_OS/RRQMBox + https://gitee.com/dotnetchina/RRQMSocket + + true + RRQM.png + 若汝棋茗 + true + LICENSE + + + + bin\Debug\netstandard2.0\RRQMSocket.xml + + + + + bin\Release\netstandard2.0\RRQMSocket.xml + + + + + bin\Debug\net45\RRQMSocket.xml + + + + + bin\Release\net45\RRQMSocket.xml + + + + + bin\Debug\net461\RRQMSocket.xml + + + + + bin\Release\net461\RRQMSocket.xml + + + + + bin\Debug\netcoreapp3.1\RRQMSocket.xml + + + + + bin\Release\netcoreapp3.1\RRQMSocket.xml + + + + + + + + + + + True + + + + True + + + + + + + + diff --git a/RRQMSocket/TCP/Client/ProtocolClient.cs b/RRQMSocket/TCP/Client/ProtocolClient.cs new file mode 100644 index 000000000..b530296e1 --- /dev/null +++ b/RRQMSocket/TCP/Client/ProtocolClient.cs @@ -0,0 +1,325 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +using RRQMCore.ByteManager; +using RRQMCore.Exceptions; +using RRQMCore.Log; +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading; + +namespace RRQMSocket +{ + /// + /// 协议客户端 + /// + public abstract class ProtocolClient : TokenClient + { + private static readonly Dictionary usedProtocol = new Dictionary(); + + private ProcotolHelper procotolHelper; + + private EventWaitHandle waitHandle; + + /// + /// 构造函数 + /// + public ProtocolClient() + { + waitHandle = new AutoResetEvent(false); + } + + /// + /// 释放资源 + /// + public override void Dispose() + { + base.Dispose(); + this.waitHandle.Dispose(); + } + + /// + /// 重新设置ID,并且同步到服务器 + /// + /// + public override void ResetID(string id) + { + this.procotolHelper.SocketSend(0, Encoding.UTF8.GetBytes(id)); + if (this.waitHandle.WaitOne(5000)) + { + return; + } + throw new RRQMException("同步ID超时"); + } + + /// + /// 发送字节流 + /// + /// + /// + /// + /// + /// + /// + public sealed override void Send(byte[] buffer, int offset, int length) + { + this.procotolHelper.SocketSend(-1, buffer, offset, length); + } + + /// + /// 发送字节 + /// + /// + /// + public void Send(short procotol, byte[] buffer) + { + this.Send(procotol, buffer, 0, buffer.Length); + } + + /// + /// 发送字节 + /// + /// + /// + /// + /// + public void Send(short procotol, byte[] buffer, int offset, int length) + { + if (!usedProtocol.ContainsKey(procotol)) + { + this.InternalSend(procotol, buffer, offset, length); + } + else + { + StringBuilder stringBuilder = new StringBuilder(); + foreach (var item in usedProtocol.Keys) + { + stringBuilder.AppendLine($"协议{item}已被使用,描述为:{usedProtocol[item]}"); + } + throw new RRQMException(stringBuilder.ToString()); + } + } + + /// + /// 发送协议流 + /// + /// + /// + public void Send(short procotol, ByteBlock dataByteBlock) + { + this.Send(procotol, dataByteBlock.Buffer, 0, (int)dataByteBlock.Length); + } + + /// + /// 发送协议状态 + /// + /// + public void Send(short procotol) + { + this.Send(procotol, new byte[0], 0, 0); + } + + /// + /// 发送字节流(仍然为同步发送) + /// + /// + /// + /// + public sealed override void SendAsync(byte[] buffer, int offset, int length) + { + this.procotolHelper.SocketSendAsync(-1, buffer, offset, length); + } + + /// + /// 发送字节 + /// + /// + /// + /// + /// + public void SendAsync(short procotol, byte[] buffer, int offset, int length) + { + if (!usedProtocol.ContainsKey(procotol)) + { + this.InternalSend(procotol, buffer, offset, length); + } + else + { + StringBuilder stringBuilder = new StringBuilder(); + foreach (var item in usedProtocol.Keys) + { + stringBuilder.AppendLine($"协议{item}已被使用,描述为:{usedProtocol[item]}"); + } + throw new RRQMException(stringBuilder.ToString()); + } + } + + /// + /// 添加已被使用的协议 + /// + /// + /// + protected static void AddUsedProtocol(short procotol, string describe) + { + usedProtocol.Add(procotol, describe); + } + + /// + /// 收到协议数据,由于性能考虑, + /// byteBlock数据源并未剔除协议数据, + /// 所以真实数据起点为2, + /// 长度为Length-2。 + /// + /// + /// + protected abstract void HandleProtocolData(short? procotol, ByteBlock byteBlock); + + /// + /// 密封方法 + /// + /// + /// + protected sealed override void HandleReceivedData(ByteBlock byteBlock, object obj) + { + short procotol = BitConverter.ToInt16(byteBlock.Buffer, 0); + switch (procotol) + { + case 0: + { + try + { + string id = Encoding.UTF8.GetString(byteBlock.Buffer, 2, byteBlock.Len - 2); + base.ResetID(id); + this.waitHandle.Set(); + } + catch (Exception ex) + { + this.Logger.Debug(LogType.Error, this, "重置ID错误", ex); + } + break; + } + case -1: + { + try + { + HandleProtocolData(null, byteBlock); + } + catch (Exception ex) + { + this.Logger.Debug(LogType.Error, this, "处理无协议数据异常", ex); + } + break; + } + default: + { + try + { + HandleProtocolData(procotol, byteBlock); + } + catch (Exception ex) + { + this.Logger.Debug(LogType.Error, this, "处理协议数据异常", ex); + } + break; + } + } + } + + /// + /// 内部发送,不会进行协议检测 + /// + /// + /// + /// + /// + /// + protected void InternalSend(short procotol, byte[] buffer, int offset, int length, bool reserved = false) + { + if (procotol > 0) + { + this.procotolHelper.SocketSend(procotol, buffer, offset, length, reserved); + } + else + { + throw new RRQMException("小等于0的协议为系统使用协议"); + } + } + + /// + /// 内部发送,不会进行协议检测 + /// + /// + /// + /// + protected void InternalSend(short procotol, ByteBlock byteBlock, bool reserved = false) + { + this.InternalSend(procotol, byteBlock.Buffer, 0, byteBlock.Len, reserved); + } + + /// + /// 内部发送,不会进行协议检测 + /// + /// + /// + /// + /// + protected void InternalSendAsync(short procotol, byte[] buffer, int offset, int length) + { + if (procotol > 0) + { + this.procotolHelper.SocketSendAsync(procotol, buffer, offset, length); + } + else + { + throw new RRQMException("小等于0的协议为系统使用协议"); + } + } + + /// + /// 内部发送,不会进行协议检测 + /// + /// + /// + protected void InternalSendAsync(short procotol, ByteBlock byteBlock) + { + this.InternalSendAsync(procotol, byteBlock.Buffer, 0, byteBlock.Len); + } + + /// + /// 载入配置,协议客户端数据处理适配器不可更改。 + /// + /// + protected override void LoadConfig(TcpClientConfig clientConfig) + { + base.LoadConfig(clientConfig); + if (clientConfig.DataHandlingAdapter is FixedHeaderDataHandlingAdapter adapter) + { + adapter.FixedHeaderType = FixedHeaderType.Int; + this.SetDataHandlingAdapter(adapter); + } + else + { + this.SetDataHandlingAdapter(new FixedHeaderDataHandlingAdapter()); + } + } + + /// + /// 连接到服务器时 + /// + /// + protected override void OnConnectedService(MesEventArgs e) + { + this.procotolHelper = new ProcotolHelper(this, this.SeparateThreadSend); + base.OnConnectedService(e); + } + } +} \ No newline at end of file diff --git a/RRQMSocket/TCP/Client/SimpleProtocolClient.cs b/RRQMSocket/TCP/Client/SimpleProtocolClient.cs new file mode 100644 index 000000000..6c09136b9 --- /dev/null +++ b/RRQMSocket/TCP/Client/SimpleProtocolClient.cs @@ -0,0 +1,37 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +using RRQMCore.ByteManager; +using System; + +namespace RRQMSocket +{ + /// + /// 协议客户端 + /// + public class SimpleProtocolClient : ProtocolClient + { + /// + /// 接收到数据 + /// + public event Action Received; + + /// + /// 处理协议数据 + /// + /// + /// + protected sealed override void HandleProtocolData(short? procotol, ByteBlock byteBlock) + { + this.Received?.Invoke(procotol, byteBlock); + } + } +} \ No newline at end of file diff --git a/RRQMSocket/TCP/Client/SimpleTcpClient.cs b/RRQMSocket/TCP/Client/SimpleTcpClient.cs new file mode 100644 index 000000000..470f6607f --- /dev/null +++ b/RRQMSocket/TCP/Client/SimpleTcpClient.cs @@ -0,0 +1,47 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +using RRQMCore.ByteManager; +using System; + +namespace RRQMSocket +{ + /// + /// 简单TCP客户端 + /// + public class SimpleTcpClient : TcpClient + { + /// + /// 接收到数据 + /// + public event Action Received; + + /// + /// 接收数据 + /// + /// + /// + protected sealed override void HandleReceivedData(ByteBlock byteBlock, object obj) + { + OnReceived(byteBlock, obj); + } + + /// + /// 接收到数据 + /// + /// + /// + protected virtual void OnReceived(ByteBlock byteBlock, object obj) + { + this.Received?.Invoke(byteBlock, obj); + } + } +} \ No newline at end of file diff --git a/RRQMSocket/TCP/Client/SimpleTokenClient.cs b/RRQMSocket/TCP/Client/SimpleTokenClient.cs new file mode 100644 index 000000000..885808065 --- /dev/null +++ b/RRQMSocket/TCP/Client/SimpleTokenClient.cs @@ -0,0 +1,37 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +using RRQMCore.ByteManager; +using System; + +namespace RRQMSocket +{ + /// + /// 简单Token客户端 + /// + public class SimpleTokenClient : TokenClient + { + /// + /// 接收到数据 + /// + public event Action Received; + + /// + /// 接收数据 + /// + /// + /// + protected sealed override void HandleReceivedData(ByteBlock byteBlock, object obj) + { + this.Received?.Invoke(byteBlock, obj); + } + } +} \ No newline at end of file diff --git a/RRQMSocket/TCP/Client/TcpClient.cs b/RRQMSocket/TCP/Client/TcpClient.cs new file mode 100644 index 000000000..5f7ebee4e --- /dev/null +++ b/RRQMSocket/TCP/Client/TcpClient.cs @@ -0,0 +1,636 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +using RRQMCore.ByteManager; +using RRQMCore.Exceptions; +using RRQMCore.Log; +using System; +using System.Net.Sockets; +using System.Threading; +using System.Threading.Tasks; + +namespace RRQMSocket +{ + /// + /// TCP客户端 + /// + public abstract class TcpClient : BaseSocket, IUserTcpClient, IClient, IHandleBuffer + { + /// + /// 客户端配置 + /// + protected TcpClientConfig clientConfig; + + private AsyncSender asyncSender; + + private BytePool bytePool; + + private DataHandlingAdapter dataHandlingAdapter; + + private Socket mainSocket; + private bool online; + + private bool onlySend; + + private BufferQueueGroup queueGroup; + + private SocketAsyncEventArgs receiveEventArgs; + + private bool separateThreadReceive; + private bool separateThreadSend; + /// + /// 成功连接到服务器 + /// + public event RRQMMessageEventHandler ConnectedService; + + /// + /// 断开连接 + /// + public event RRQMMessageEventHandler DisconnectedService; + + /// + /// 获取内存池实例 + /// + public BytePool BytePool + { + get { return bytePool; } + } + + /// + /// 客户端配置 + /// + public TcpClientConfig ClientConfig + { + get { return clientConfig; } + } + + /// + /// 数据处理适配器 + /// + public DataHandlingAdapter DataHandlingAdapter + { + get { return dataHandlingAdapter; } + } + + /// + /// IPv4地址 + /// + public string IP { get; private set; } + + /// + /// 主通信器 + /// + public Socket MainSocket + { + get { return mainSocket; } + internal set + { + mainSocket = value; + } + } + + /// + /// IP及端口 + /// + public string Name => $"{this.IP}:{this.Port}"; + /// + /// 判断是否已连接 + /// + public bool Online { get { return this.online; } } + + /// + /// 仅发送,即不会开启接收线程。 + /// + public bool OnlySend + { + get { return onlySend; } + } + + /// + /// 端口号 + /// + public int Port { get; private set; } + /// + /// 在异步发送时,使用独立线程发送 + /// + public bool SeparateThreadSend + { + get { return separateThreadSend; } + } + + /// + /// 连接到服务器 + /// + public virtual void Connect() + { + if (this.disposable) + { + throw new RRQMException("无法利用已释放对象"); + } + if (this.clientConfig == null) + { + throw new ArgumentNullException("配置文件不能为空。"); + } + IPHost iPHost = this.clientConfig.RemoteIPHost; + if (iPHost == null) + { + throw new ArgumentNullException("iPHost不能为空。"); + } + if (!this.Online) + { + Socket socket = new Socket(iPHost.AddressFamily, SocketType.Stream, ProtocolType.Tcp); + PreviewConnect(socket); + socket.Connect(iPHost.EndPoint); + this.mainSocket = socket; + Start(); + } + } + + /// + /// 异步连接服务器 + /// + /// + public async void ConnectAsync(Action callback = null) + { + await Task.Run(() => + { + try + { + this.Connect(); + if (callback != null) + { + AsyncResult result = new AsyncResult(); + result.Status = true; + callback.Invoke(result); + } + } + catch (Exception ex) + { + if (callback != null) + { + AsyncResult result = new AsyncResult(); + result.Status = false; + result.Message = ex.Message; + callback.Invoke(result); + } + } + }); + } + + /// + /// 断开连接 + /// + public virtual void Disconnect() + { + this.online = false; + if (this.mainSocket != null) + { + this.mainSocket.Dispose(); + } + if (this.queueGroup != null) + { + this.queueGroup.Dispose(); + } + if (this.asyncSender != null) + { + this.asyncSender.Dispose(); + this.asyncSender = null; + } + } + + /// + /// 断开链接并释放资源 + /// + public override void Dispose() + { + this.online = false; + if (this.mainSocket != null) + { + this.mainSocket.Dispose(); + } + if (this.queueGroup != null) + { + this.queueGroup.Dispose(); + } + if (this.asyncSender != null) + { + this.asyncSender.Dispose(); + this.asyncSender = null; + } + base.Dispose(); + } + + /// + /// 处理数据 + /// + public void HandleBuffer(ByteBlock byteBlock) + { + try + { + if (this.dataHandlingAdapter == null) + { + throw new RRQMException("数据处理适配器为空"); + } + this.dataHandlingAdapter.Received(byteBlock); + } + catch (Exception ex) + { + Logger.Debug(LogType.Error, this, "在处理数据时发生错误", ex); + } + finally + { + byteBlock.Dispose(); + } + } + + /// + /// 发送字节流 + /// + /// + /// + /// + /// + public virtual void Send(byte[] buffer) + { + this.Send(buffer, 0, buffer.Length); + } + + /// + /// 发送流中的有效数据 + /// + /// + /// + /// + /// + public virtual void Send(ByteBlock byteBlock) + { + this.Send(byteBlock.Buffer, 0, byteBlock.Len); + } + + /// + /// 发送字节流 + /// + /// + /// + /// + /// + /// + /// + public virtual void Send(byte[] buffer, int offset, int length) + { + this.dataHandlingAdapter.Send(buffer, offset, length, false); + } + + /// + /// IOCP发送 + /// + /// + /// + /// + /// + /// + /// + public virtual void SendAsync(byte[] buffer, int offset, int length) + { + this.dataHandlingAdapter.Send(buffer, offset, length, true); + } + + /// + /// IOCP发送 + /// + /// + /// + /// + /// + public virtual void SendAsync(byte[] buffer) + { + this.SendAsync(buffer, 0, buffer.Length); + } + + /// + /// IOCP发送流中的有效数据 + /// + /// + /// + /// + /// + public virtual void SendAsync(ByteBlock byteBlock) + { + this.SendAsync(byteBlock.Buffer, 0, byteBlock.Len); + } + + /// + /// 配置服务器 + /// + /// + /// + public void Setup(TcpClientConfig clientConfig) + { + this.clientConfig = clientConfig; + this.LoadConfig(this.clientConfig); + } + + /// + /// 禁用发送或接收 + /// + /// + public void Shutdown(SocketShutdown how) + { + if (this.mainSocket != null) + { + mainSocket.Shutdown(how); + } + } + + internal void Start() + { + this.ReadIpPort(); + this.OnConnectedService(new MesEventArgs()); + + if (!this.onlySend) + { + if (this.separateThreadReceive) + { + queueGroup = new BufferQueueGroup(); + queueGroup.Thread = new Thread(BeginHandleBuffer);//处理用户的消息 + queueGroup.waitHandleBuffer = new AutoResetEvent(false); + queueGroup.bufferAndClient = new BufferQueue(); + queueGroup.Thread.IsBackground = true; + queueGroup.Thread.Name = "客户端处理线程"; + queueGroup.Thread.Start(); + } + + this.receiveEventArgs = new SocketAsyncEventArgs(); + this.receiveEventArgs.Completed += EventArgs_Completed; + BeginReceive(); + } + if (this.separateThreadSend) + { + if (this.asyncSender != null) + { + this.asyncSender.Dispose(); + } + this.asyncSender = new AsyncSender(this.MainSocket, this.MainSocket.RemoteEndPoint, this.Logger); + this.asyncSender.SetBufferLength((int)this.clientConfig.GetValue(TcpClientConfig.SeparateThreadSendBufferLengthProperty)); + } + } + + /// + /// 处理已接收到的数据。 + /// 覆盖父类方法将不触发OnReceived事件。 + /// + /// + /// + protected abstract void HandleReceivedData(ByteBlock byteBlock, object obj); + + /// + /// 加载配置 + /// + /// + protected virtual void LoadConfig(TcpClientConfig clientConfig) + { + if (clientConfig == null) + { + throw new RRQMException("配置文件为空"); + } + if (clientConfig.BytePool != null) + { + clientConfig.BytePool.MaxSize = clientConfig.BytePoolMaxSize; + clientConfig.BytePool.MaxBlockSize = clientConfig.BytePoolMaxBlockSize; + this.bytePool = clientConfig.BytePool; + } + else + { + throw new ArgumentNullException("内存池不能为空"); + } + this.logger = (ILog)clientConfig.GetValue(RRQMConfig.LoggerProperty); + this.bufferLength = (int)clientConfig.GetValue(RRQMConfig.BufferLengthProperty); + this.SetDataHandlingAdapter(clientConfig.DataHandlingAdapter); + this.onlySend = clientConfig.OnlySend; + this.separateThreadSend = clientConfig.SeparateThreadSend; + this.separateThreadReceive = clientConfig.SeparateThreadReceive; + } + + /// + /// 连接到服务器 + /// + /// + protected virtual void OnConnectedService(MesEventArgs e) + { + this.online = true; + this.ConnectedService?.Invoke(this, e); + } + + /// + /// 断开连接 + /// + /// + protected virtual void OnDisconnectedService(MesEventArgs e) + { + this.online = false; + this.DisconnectedService?.Invoke(this, e); + } + + /// + /// 在Socket初始化对象后,Connect之前调用。 + /// 可用于设置Socket参数。 + /// 父类方法可覆盖。 + /// + /// + protected virtual void PreviewConnect(Socket socket) + { + } + + /// + /// 设置数据处理适配器 + /// + /// + protected virtual void SetDataHandlingAdapter(DataHandlingAdapter adapter) + { + if (adapter == null) + { + throw new RRQMException("数据处理适配器为空"); + } + adapter.BytePool = this.bytePool; + adapter.Logger = this.Logger; + adapter.ReceivedCallBack = this.HandleReceivedData; + adapter.SendCallBack = this.Sent; + this.dataHandlingAdapter = adapter; + } + + private void BeginHandleBuffer() + { + while (true) + { + if (disposable || !this.online) + { + break; + } + ClientBuffer clientBuffer; + if (queueGroup.bufferAndClient.TryDequeue(out clientBuffer)) + { + clientBuffer.client.HandleBuffer(clientBuffer.byteBlock); + } + else + { + queueGroup.isWait = true; + queueGroup.waitHandleBuffer.WaitOne(); + } + } + } + + /// + /// 启动消息接收 + /// + private void BeginReceive() + { + try + { + ByteBlock byteBlock = this.bytePool.GetByteBlock(this.BufferLength); + this.receiveEventArgs.UserToken = byteBlock; + this.receiveEventArgs.SetBuffer(byteBlock.Buffer, 0, byteBlock.Buffer.Length); + if (!this.MainSocket.ReceiveAsync(this.receiveEventArgs)) + { + ProcessReceived(this.receiveEventArgs); + } + } + catch (Exception ex) + { + this.OnDisconnectedService(new MesEventArgs(ex.Message)); + } + } + + private void EventArgs_Completed(object sender, SocketAsyncEventArgs e) + { + try + { + if (e.LastOperation == SocketAsyncOperation.Receive) + { + ProcessReceived(e); + } + else + { + this.OnDisconnectedService(new MesEventArgs("BreakOut")); + } + } + catch (Exception ex) + { + this.OnDisconnectedService(new MesEventArgs(ex.Message)); + } + } + + private void ProcessReceived(SocketAsyncEventArgs e) + { + if (e.SocketError == SocketError.Success && e.BytesTransferred > 0) + { + if (this.separateThreadReceive) + { + ClientBuffer clientBuffer = new ClientBuffer(); + clientBuffer.client = this; + clientBuffer.byteBlock = (ByteBlock)e.UserToken; + clientBuffer.byteBlock.SetLength(e.BytesTransferred); + queueGroup.bufferAndClient.Enqueue(clientBuffer); + if (queueGroup.isWait) + { + queueGroup.isWait = false; + queueGroup.waitHandleBuffer.Set(); + } + } + else + { + ByteBlock byteBlock = (ByteBlock)e.UserToken; + byteBlock.SetLength(e.BytesTransferred); + this.HandleBuffer(byteBlock); + } + + try + { + ByteBlock newByteBlock = this.bytePool.GetByteBlock(this.bufferLength); + e.UserToken = newByteBlock; + e.SetBuffer(newByteBlock.Buffer, 0, newByteBlock.Buffer.Length); + + if (!this.MainSocket.ReceiveAsync(e)) + { + ProcessReceived(e); + } + } + catch + { + } + } + else + { + this.OnDisconnectedService(new MesEventArgs("BreakOut")); + } + } + + private void ReadIpPort() + { + if (MainSocket == null) + { + this.IP = null; + this.Port = -1; + return; + } + + string ipport; + if (MainSocket.Connected && MainSocket.RemoteEndPoint != null) + { + ipport = MainSocket.RemoteEndPoint.ToString(); + } + else if (MainSocket.IsBound && MainSocket.LocalEndPoint != null) + { + ipport = MainSocket.LocalEndPoint.ToString(); + } + else + { + return; + } + + int r = ipport.LastIndexOf(":"); + this.IP = ipport.Substring(0, r); + this.Port = Convert.ToInt32(ipport.Substring(r + 1, ipport.Length - (r + 1))); + } + + private void Sent(byte[] buffer, int offset, int length, bool isAsync) + { + if (!this.Online) + { + throw new RRQMNotConnectedException("该实例已断开"); + } + + if (isAsync) + { + if (separateThreadSend) + { + this.asyncSender.AsyncSend(buffer, offset, length); + } + else + { + this.mainSocket.BeginSend(buffer, offset, length, SocketFlags.None, null, null); + } + } + else + { + while (length > 0) + { + int r = MainSocket.Send(buffer, offset, length, SocketFlags.None); + if (r == 0 && length > 0) + { + throw new RRQMException("发送数据不完全"); + } + offset += r; + length -= r; + } + } + } + } +} \ No newline at end of file diff --git a/RRQMSocket/TCP/Client/TokenClient.cs b/RRQMSocket/TCP/Client/TokenClient.cs new file mode 100644 index 000000000..3a2486c48 --- /dev/null +++ b/RRQMSocket/TCP/Client/TokenClient.cs @@ -0,0 +1,163 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +using RRQMCore.ByteManager; +using RRQMCore.Exceptions; +using System; +using System.Net.Sockets; +using System.Text; +using System.Threading; + +namespace RRQMSocket +{ + /// + /// 需要验证的TCP客户端 + /// + public abstract class TokenClient : TcpClient + { + private string verifyToken = "rrqm"; + + /// + /// 验证令箭 + /// + public string VerifyToken + { + get { return verifyToken; } + } + + private string id; + + /// + /// 获取服务器分配的ID + /// + public string ID + { + get { return id; } + } + + private int verifyTimeout; + + /// + /// 验证超时时间,默认为3000ms; + /// + public int VerifyTimeout + { + get { return verifyTimeout; } + } + + /// + /// 重新设置ID,但是不会同步到服务器 + /// + /// + public virtual void ResetID(string id) + { + this.id = id; + } + + /// + /// 连接到服务器 + /// + /// + /// + /// + public override void Connect() + { + if (this.clientConfig == null) + { + throw new ArgumentNullException("配置文件不能为空。"); + } + IPHost iPHost = this.clientConfig.RemoteIPHost; + if (iPHost == null) + { + throw new ArgumentNullException("iPHost不能为空。"); + } + + if (this.disposable) + { + throw new RRQMException("无法重新利用已释放对象"); + } + + if (this.Online) + { + return; + } + + try + { + Socket socket = new Socket(iPHost.AddressFamily, SocketType.Stream, ProtocolType.Tcp); + PreviewConnect(socket); + socket.Connect(iPHost.EndPoint); + this.MainSocket = socket; + this.MainSocket.Send(Encoding.UTF8.GetBytes(this.verifyToken == null ? string.Empty : this.verifyToken)); + } + catch (Exception e) + { + throw new RRQMException(e.Message); + } + + int waitCount = 0; + while (waitCount < this.verifyTimeout / 10) + { + if (this.MainSocket.Available > 0) + { + ByteBlock byteBlock = this.BytePool.GetByteBlock(this.BufferLength); + try + { + int r = this.MainSocket.Receive(byteBlock.Buffer); + if (r > 0) + { + if (byteBlock.Buffer[0] == 1) + { + this.id = Encoding.UTF8.GetString(byteBlock.Buffer, 1, r - 1); + Start(); + return; + } + else if (byteBlock.Buffer[0] == 2) + { + this.MainSocket.Dispose(); + throw new RRQMTokenVerifyException(Encoding.UTF8.GetString(byteBlock.Buffer, 1, r - 1)); + } + else if (byteBlock.Buffer[0] == 3) + { + this.MainSocket.Dispose(); + throw new RRQMException("连接数量已达到服务器设定最大值"); + } + } + } + finally + { + byteBlock.Dispose(); + } + } + waitCount++; + Thread.Sleep(10); + } + + this.MainSocket.Dispose(); + throw new RRQMTimeoutException("验证Token超时"); + } + + /// + /// 加载配置 + /// + /// + protected override void LoadConfig(TcpClientConfig clientConfig) + { + base.LoadConfig(clientConfig); + this.verifyToken = (string)clientConfig.GetValue(TokenClientConfig.VerifyTokenProperty); + this.verifyTimeout = (int)clientConfig.GetValue(TokenClientConfig.VerifyTimeoutProperty); + if (string.IsNullOrEmpty(this.verifyToken)) + { + this.verifyToken = "rrqm"; + } + } + } +} \ No newline at end of file diff --git a/RRQMSocket/TCP/Service/ProtocolService.cs b/RRQMSocket/TCP/Service/ProtocolService.cs new file mode 100644 index 000000000..4b3097fa2 --- /dev/null +++ b/RRQMSocket/TCP/Service/ProtocolService.cs @@ -0,0 +1,56 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +using RRQMCore.Exceptions; +using System.Text; + +namespace RRQMSocket +{ + /// + /// 协议服务器 + /// + public abstract class ProtocolService : TokenService where TClient : ProtocolSocketClient, new() + { + private bool canResetID; + + /// + /// 重置ID + /// + /// + /// + public override void ResetID(string oldID, string newID) + { + if (!canResetID) + { + throw new RRQMException("服务器不允许修改ID"); + } + base.ResetID(oldID, newID); + if (this.TryGetSocketClient(newID, out TClient client)) + { + client.procotolHelper.SocketSend(0, Encoding.UTF8.GetBytes(newID)); + } + else + { + throw new RRQMException("新ID不可用,请清理客户端重新修改ID"); + } + } + + /// + /// 加载配置 + /// + /// + protected override void LoadConfig(ServiceConfig serverConfig) + { + base.LoadConfig(serverConfig); + this.canResetID = (bool)serverConfig.GetValue(ProtocolServiceConfig.CanResetIDProperty); + } + } +} \ No newline at end of file diff --git a/RRQMSocket/TCP/Service/SimpleProtocolService.cs b/RRQMSocket/TCP/Service/SimpleProtocolService.cs new file mode 100644 index 000000000..27c0f66d5 --- /dev/null +++ b/RRQMSocket/TCP/Service/SimpleProtocolService.cs @@ -0,0 +1,54 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +using RRQMCore.ByteManager; +using System; + +namespace RRQMSocket +{ + /// + /// 简单协议服务器 + /// + public class SimpleProtocolService : ProtocolService + { + /// + /// 处理数据 + /// + public event Action Received; + + /// + /// 成功连接后创建(或从对象池中获得)辅助类, + /// 用户可以在该方法中再进行自定义设置, + /// 但是如果该对象是从对象池获得的话,为避免重复设定某些值, + /// 例如事件等,请先判断CreatOption.NewCreat值再做处理。 + /// + public event Action CreateSocketCliect; + + /// + /// 接收辅助类 + /// + /// + /// + protected override void OnCreateSocketCliect(SimpleProtocolSocketClient tcpSocketClient, CreateOption createOption) + { + this.CreateSocketCliect?.Invoke(tcpSocketClient, createOption); + if (createOption.NewCreate) + { + tcpSocketClient.OnReceived = this.OnReceive; + } + } + + private void OnReceive(SimpleProtocolSocketClient socketClient, short? procotol, ByteBlock byteBlock) + { + this.Received?.Invoke(socketClient, procotol, byteBlock); + } + } +} \ No newline at end of file diff --git a/RRQMSocket/TCP/Service/SimpleTcpService.cs b/RRQMSocket/TCP/Service/SimpleTcpService.cs new file mode 100644 index 000000000..1892caa68 --- /dev/null +++ b/RRQMSocket/TCP/Service/SimpleTcpService.cs @@ -0,0 +1,57 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +using RRQMCore.ByteManager; +using System; + +namespace RRQMSocket +{ + /// + /// 若汝棋茗内置TCP验证服务器 + /// + public class SimpleTcpService : TcpService + { + /// + /// 处理数据 + /// + public event Action Received; + + /// + /// 成功连接后创建(或从对象池中获得)辅助类, + /// 用户可以在该方法中再进行自定义设置, + /// 但是如果该对象是从对象池获得的话,为避免重复设定某些值, + /// 例如事件等,请先判断CreatOption.NewCreat值再做处理。 + /// + public event Action CreateSocketCliect; + + /// + /// 成功连接后创建(或从对象池中获得)辅助类, + /// 用户可以在该方法中再进行自定义设置, + /// 但是如果该对象是从对象池获得的话,为避免重复设定某些值, + /// 例如事件等,请先判断CreatOption.NewCreat值再做处理。 + /// + /// + /// + protected override void OnCreateSocketCliect(SimpleSocketClient tcpSocketClient, CreateOption creatOption) + { + this.CreateSocketCliect?.Invoke(tcpSocketClient, creatOption); + if (creatOption.NewCreate) + { + tcpSocketClient.OnReceived = this.OnReceive; + } + } + + private void OnReceive(SimpleSocketClient socketClient, ByteBlock byteBlock, object obj) + { + this.Received?.Invoke(socketClient, byteBlock, obj); + } + } +} \ No newline at end of file diff --git a/RRQMSocket/TCP/Service/SimpleTokenService.cs b/RRQMSocket/TCP/Service/SimpleTokenService.cs new file mode 100644 index 000000000..0451912e7 --- /dev/null +++ b/RRQMSocket/TCP/Service/SimpleTokenService.cs @@ -0,0 +1,57 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +using RRQMCore.ByteManager; +using System; + +namespace RRQMSocket +{ + /// + /// 简单Token服务器 + /// + public class SimpleTokenService : TokenService + { + /// + /// 处理数据 + /// + public event Action Received; + + /// + /// 成功连接后创建(或从对象池中获得)辅助类, + /// 用户可以在该方法中再进行自定义设置, + /// 但是如果该对象是从对象池获得的话,为避免重复设定某些值, + /// 例如事件等,请先判断CreatOption.NewCreat值再做处理。 + /// + public event Action CreateSocketCliect; + + /// + /// 成功连接后创建(或从对象池中获得)辅助类, + /// 用户可以在该方法中再进行自定义设置, + /// 但是如果该对象是从对象池获得的话,为避免重复设定某些值, + /// 例如事件等,请先判断CreatOption.NewCreat值再做处理。 + /// + /// + /// + protected override void OnCreateSocketCliect(SimpleSocketClient tcpSocketClient, CreateOption creatOption) + { + this.CreateSocketCliect?.Invoke(tcpSocketClient, creatOption); + if (creatOption.NewCreate) + { + tcpSocketClient.OnReceived = this.OnReceive; + } + } + + private void OnReceive(SimpleSocketClient socketClient, ByteBlock byteBlock, object obj) + { + this.Received?.Invoke(socketClient, byteBlock, obj); + } + } +} \ No newline at end of file diff --git a/RRQMSocket/TCP/Service/TcpService.cs b/RRQMSocket/TCP/Service/TcpService.cs new file mode 100644 index 000000000..4a99f5751 --- /dev/null +++ b/RRQMSocket/TCP/Service/TcpService.cs @@ -0,0 +1,655 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +using RRQMCore.ByteManager; +using RRQMCore.Exceptions; +using RRQMCore.Log; +using RRQMCore.Pool; +using System; +using System.Collections.Generic; +using System.Net.Sockets; +using System.Threading; + +namespace RRQMSocket +{ + /// + /// TCP服务器 + /// + public abstract class TcpService : BaseSocket, ITcpService, _ITcpService where TClient : SocketClient, new() + { + /// + /// 构造函数 + /// + public TcpService() + { + this.socketClients = new SocketCliectCollection(); + this.socketClientPool = new ObjectPool(); + } + + #region 属性 + + private int clearInterval; + private IPHost[] listenIPHosts; + private Socket[] listenSockets; + private int maxCount; + private string name; + private ServerState serverState; + private ServiceConfig serviceConfig; + private SocketCliectCollection socketClients; + + /// + /// 获取默认内存池 + /// + public BytePool BytePool { get { return BytePool.Default; } } + + /// + /// 获取清理无数据交互的SocketClient,默认60。如果不想清除,可使用-1。 + /// + public int ClearInterval + { + get { return clearInterval; } + } + + /// + /// 获取监听的地址组 + /// + public IPHost[] ListenIPHosts + { + get { return listenIPHosts; } + } + + /// + /// 获取正在监听的socket组 + /// + public Socket[] ListenSockets + { + get { return listenSockets; } + } + + /// + /// 最大可连接数 + /// + public int MaxCount + { + get { return maxCount; } + } + + /// + /// 服务器名称 + /// + public string ServerName + { + get { return name; } + } + + /// + /// 服务器状态 + /// + public ServerState ServerState + { + get { return serverState; } + } + + /// + /// 获取服务器配置 + /// + public ServiceConfig ServiceConfig { get { return serviceConfig; } } + + /// + /// 获取当前连接的所有客户端 + /// + public SocketCliectCollection SocketClients + { + get { return socketClients; } + } + + #endregion 属性 + + #region 变量 + + internal ClearType clearType; + internal bool separateThreadReceive; + internal ObjectPool socketClientPool; + private int backlog; + private BufferQueueGroup[] bufferQueueGroups; + private Thread threadClearClient; + + #endregion 变量 + + #region 事件 + + /// + /// 有用户连接的时候 + /// + public event RRQMMessageEventHandler ClientConnected; + + /// + /// 有用户断开连接的时候 + /// + public event RRQMMessageEventHandler ClientDisconnected; + + /// + /// 在客户端连接时 + /// + /// + /// + protected virtual void OnClientConnected(TClient client, MesEventArgs e) + { + this.ClientConnected?.Invoke(client, e); + } + + /// + /// 客户端断开连接 + /// + /// + /// + protected virtual void OnClientDisconnected(TClient client, MesEventArgs e) + { + this.ClientDisconnected?.Invoke(client, e); + } + + #endregion 事件 + + /// + /// 关闭服务器并释放服务器资源 + /// + public override void Dispose() + { + base.Dispose(); + if (this.listenSockets != null) + { + foreach (var item in this.listenSockets) + { + item.Dispose(); + } + } + this.listenSockets = null; + this.listenIPHosts = null; + + this.SocketClients.Dispose(); + if (bufferQueueGroups != null) + { + foreach (var item in bufferQueueGroups) + { + item.Dispose(); + } + } + + this.serverState = ServerState.Disposed; + } + + /// + /// 重新设置ID + /// + /// + /// + /// + public virtual void ResetID(string oldID, string newID) + { + if (!this.socketClients.TryGetSocketClient(oldID, out TClient client)) + { + throw new RRQMException("oldID不存在"); + } + if (this.socketClients.TryRemove(oldID)) + { + client.id = newID; + if (!this.socketClients.TryAdd(client)) + { + throw new RRQMException("ID重复"); + } + } + else + { + throw new RRQMException("oldID不存在"); + } + } + + /// + /// 发送字节流 + /// + /// 用于检索TcpSocketClient + /// + /// + /// + /// + /// + public virtual void Send(string id, byte[] buffer) + { + this.Send(id, buffer, 0, buffer.Length); + } + + /// + /// 发送字节流 + /// + /// 用于检索TcpSocketClient + /// + /// + /// + /// + /// + /// + /// + public virtual void Send(string id, byte[] buffer, int offset, int length) + { + this.SocketClients[id].Send(buffer, offset, length); + } + + /// + /// 发送流中的有效数据 + /// + /// 用于检索TcpSocketClient + /// + /// + /// + /// + /// + public virtual void Send(string id, ByteBlock byteBlock) + { + this.Send(id, byteBlock.Buffer, 0, byteBlock.Len); + } + + /// + /// 发送字节流 + /// + /// 用于检索TcpSocketClient + /// + /// + /// + /// + /// + public virtual void SendAsync(string id, byte[] buffer) + { + this.SendAsync(id, buffer, 0, buffer.Length); + } + + /// + /// 发送字节流 + /// + /// 用于检索TcpSocketClient + /// + /// + /// + /// + /// + /// + /// + public virtual void SendAsync(string id, byte[] buffer, int offset, int length) + { + this.SocketClients[id].SendAsync(buffer, offset, length); + } + + /// + /// 发送流中的有效数据 + /// + /// 用于检索TcpSocketClient + /// + /// + /// + /// + /// + public virtual void SendAsync(string id, ByteBlock byteBlock) + { + this.SendAsync(id, byteBlock.Buffer, 0, byteBlock.Len); + } + + /// + /// 配置服务器 + /// + /// + public virtual void Setup(ServiceConfig serviceConfig) + { + this.serviceConfig = serviceConfig; + this.LoadConfig(serviceConfig); + } + + /// + /// 配置服务器 + /// + /// + public virtual void Setup(int port) + { + TcpServiceConfig serviceConfig = new TcpServiceConfig(); + serviceConfig.ListenIPHosts = new IPHost[] { new IPHost(port) }; + this.Setup(serviceConfig); + } + + /// + /// 根据ID判断SocketClient是否存在 + /// + /// + /// + public bool SocketClientExist(string id) + { + return this.SocketClients.SocketClientExist(id); + } + + /// + /// 启动服务 + /// + /// + /// + /// + public virtual void Start() + { + IPHost[] iPHosts = (IPHost[])this.ServiceConfig.GetValue(ServiceConfig.ListenIPHostsProperty); + if (iPHosts == null) + { + throw new RRQMException("IPHosts为空,无法绑定"); + } + switch (this.serverState) + { + case ServerState.None: + { + this.BeginListen(iPHosts); + this.BeginClearAndHandle(); + break; + } + case ServerState.Running: + { + return; + } + case ServerState.Stopped: + { + this.BeginListen(iPHosts); + break; + } + case ServerState.Disposed: + { + throw new RRQMException("无法重新利用已释放对象"); + } + } + this.listenIPHosts = iPHosts; + this.serverState = ServerState.Running; + } + + /// + /// 停止服务器,可重新启动 + /// + public virtual void Stop() + { + if (listenSockets != null) + { + foreach (var item in listenSockets) + { + item.Dispose(); + } + } + this.listenSockets = null; + this.listenIPHosts = null; + + this.SocketClients.Dispose(); + + this.serverState = ServerState.Stopped; + } + + /// + /// 尝试获取TClient + /// + /// ID + /// TClient + /// + public bool TryGetSocketClient(string id, out TClient socketClient) + { + return this.socketClients.TryGetSocketClient(id, out socketClient); + } + + internal virtual void PreviewCreateSocketCliect(Socket socket, BufferQueueGroup queueGroup) + { + try + { + if (this.SocketClients.Count > this.maxCount) + { + this.Logger.Debug(LogType.Error, this, "连接客户端数量已达到设定最大值"); + socket.Close(); + socket.Dispose(); + return; + } + + TClient client = this.socketClientPool.GetObject(); + if (client.NewCreate) + { + client.queueGroup = queueGroup; + client.Service = this; + client.logger = this.Logger; + client.clearType = this.clearType; + client.separateThreadReceive = this.separateThreadReceive; + } + + client.MainSocket = socket; + client.ReadIpPort(); + client.bufferLength = this.BufferLength; + + CreateOption creatOption = new CreateOption(); + creatOption.NewCreate = client.NewCreate; + + creatOption.ID = this.SocketClients.GetDefaultID(); + + this.OnCreateSocketCliect(client, creatOption); + client.id = creatOption.ID; + + if (!this.socketClients.TryAdd(client)) + { + throw new RRQMException("ID重复"); + } + + client.BeginReceive(); + OnClientConnected(client, null); + } + catch (Exception ex) + { + Logger.Debug(LogType.Error, this, $"在接收客户端时发生错误,信息:{ex.Message}"); + } + } + + /// + /// 加载配置 + /// + /// + protected virtual void LoadConfig(ServiceConfig serviceConfig) + { + if (serviceConfig == null) + { + throw new RRQMException("配置文件为空"); + } + this.maxCount = (int)serviceConfig.GetValue(TcpServiceConfig.MaxCountProperty); + this.clearInterval = (int)serviceConfig.GetValue(TcpServiceConfig.ClearIntervalProperty); + this.backlog = (int)serviceConfig.GetValue(TcpServiceConfig.BacklogProperty); + this.logger = (ILog)serviceConfig.GetValue(RRQMConfig.LoggerProperty); + this.bufferLength = (int)serviceConfig.GetValue(RRQMConfig.BufferLengthProperty); + this.name = serviceConfig.ServerName; + this.clearType = (ClearType)serviceConfig.GetValue(TcpServiceConfig.ClearTypeProperty); + this.separateThreadReceive = serviceConfig.SeparateThreadReceive; + this.socketClientPool.Capacity = this.maxCount; + } + + /// + /// 成功连接后创建(或从对象池中获得)辅助类, + /// 用户可以在该方法中再进行自定义设置, + /// 但是如果该对象是从对象池获得的话,为避免重复设定某些值, + /// 例如事件等,请先判断CreatOption.NewCreate值再做处理。 + /// + /// + /// + protected abstract void OnCreateSocketCliect(TClient socketClient, CreateOption createOption); + + /// + /// 在Socket初始化对象后,Bind之前调用。 + /// 可用于设置Socket参数。 + /// 父类方法可覆盖。 + /// + /// + protected virtual void PreviewBind(Socket socket) + { + } + + private void Args_Completed(object sender, SocketAsyncEventArgs e) + { + if (e.LastOperation == SocketAsyncOperation.Accept) + { + ProcessAccept(e); + } + } + + private void BeginClearAndHandle() + { + threadClearClient = new Thread(ClearClient); + threadClearClient.IsBackground = true; + threadClearClient.Name = "ClearClient"; + threadClearClient.Start(); + + this.bufferQueueGroups = new BufferQueueGroup[this.ServiceConfig.ThreadCount]; + for (int i = 0; i < this.ServiceConfig.ThreadCount; i++) + { + BufferQueueGroup bufferQueueGroup = new BufferQueueGroup(); + bufferQueueGroup.bytePool = new BytePool(this.ServiceConfig.BytePoolMaxSize, this.ServiceConfig.BytePoolMaxBlockSize); + bufferQueueGroups[i] = bufferQueueGroup; + + if (this.separateThreadReceive) + { + bufferQueueGroup.Thread = new Thread(Handle);//处理用户的消息 + bufferQueueGroup.waitHandleBuffer = new AutoResetEvent(false); + bufferQueueGroup.bufferAndClient = new BufferQueue(); + bufferQueueGroup.Thread.IsBackground = true; + bufferQueueGroup.Thread.Name = i + "-Num Handler"; + bufferQueueGroup.Thread.Start(bufferQueueGroup); + } + } + } + + private void BeginListen(IPHost[] iPHosts) + { + try + { + this.listenSockets = new Socket[iPHosts.Length]; + int i = 0; + foreach (var iPHost in iPHosts) + { + Socket socket = new Socket(iPHost.AddressFamily, SocketType.Stream, ProtocolType.Tcp); + this.listenSockets[i++] = socket; + PreviewBind(socket); + socket.Bind(iPHost.EndPoint); + socket.Listen(this.backlog); + } + } + catch (Exception ex) + { + throw ex; + } + + foreach (var socket in this.listenSockets) + { + SocketAsyncEventArgs e = new SocketAsyncEventArgs(); + e.UserToken = socket; + e.Completed += this.Args_Completed; + if (!socket.AcceptAsync(e)) + { + ProcessAccept(e); + } + } + } + + private void ClearClient() + { + while (true) + { + Thread.Sleep(1000); + if (disposable) + { + break; + } + else + { + long tick = DateTime.Now.Ticks / 10000000; + IEnumerable collection = this.SocketClients.GetIDs(); + foreach (var token in collection) + { + if (this.SocketClients.TryGetSocketClient(token, out TClient client)) + { + if (this.clearInterval > 0) + { + client.GetTimeout(this.clearInterval / 1000, tick); + } + + if (client.breakOut) + { + try + { + client.Dispose(); + if (this.SocketClients.TryRemove(token)) + { + this.socketClientPool.DestroyObject(client); + this.OnClientDisconnected(client, new MesEventArgs("breakOut")); + } + } + catch (Exception ex) + { + Logger.Debug(LogType.Error, this, $"在检验客户端时发生错误,信息:{ex.Message}"); + } + } + } + } + } + } + } + + private void Handle(object o) + { + BufferQueueGroup queueGroup = (BufferQueueGroup)o; + while (true) + { + if (disposable) + { + break; + } + ClientBuffer clientBuffer; + if (queueGroup.bufferAndClient.TryDequeue(out clientBuffer)) + { + clientBuffer.client.HandleBuffer(clientBuffer.byteBlock); + } + else + { + queueGroup.isWait = true; + queueGroup.waitHandleBuffer.WaitOne(); + } + } + } + + private void ProcessAccept(SocketAsyncEventArgs e) + { + try + { + if (!this.disposable) + { + if (e.SocketError == SocketError.Success && e.AcceptSocket != null) + { + try + { + Socket newSocket = e.AcceptSocket; + PreviewCreateSocketCliect(newSocket, this.bufferQueueGroups[this.SocketClients.Count % this.bufferQueueGroups.Length]); + } + catch (Exception ex) + { + Logger.Debug(LogType.Error, this, "接收新连接错误", ex); + } + } + e.AcceptSocket = null; + if (!((Socket)e.UserToken).AcceptAsync(e)) + { + ProcessAccept(e); + } + } + } + catch + { + } + } + } +} \ No newline at end of file diff --git a/RRQMSocket/TCP/Service/TokenService.cs b/RRQMSocket/TCP/Service/TokenService.cs new file mode 100644 index 000000000..8c91a0213 --- /dev/null +++ b/RRQMSocket/TCP/Service/TokenService.cs @@ -0,0 +1,173 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +using RRQMCore.ByteManager; +using RRQMCore.Exceptions; +using RRQMCore.Log; +using System; +using System.Net.Sockets; +using System.Text; +using System.Threading.Tasks; + +namespace RRQMSocket +{ + /// + /// 需要验证的TCP服务器 + /// + public abstract class TokenService : TcpService where TClient : SocketClient, new() + { + private string verifyToken; + + /// + /// 连接令箭 + /// + public string VerifyToken + { + get { return verifyToken; } + } + + private int verifyTimeout; + + /// + /// 验证超时时间,默认为3000ms + /// + public int VerifyTimeout + { + get { return verifyTimeout; } + } + + /// + /// 载入配置 + /// + /// + protected override void LoadConfig(ServiceConfig ServiceConfig) + { + base.LoadConfig(ServiceConfig); + this.verifyTimeout = (int)ServiceConfig.GetValue(TokenServiceConfig.VerifyTimeoutProperty); + this.verifyToken = (string)ServiceConfig.GetValue(TokenServiceConfig.VerifyTokenProperty); + if (string.IsNullOrEmpty(this.verifyToken)) + { + this.verifyToken = "rrqm"; + } + } + + internal override void PreviewCreateSocketCliect(Socket socket, BufferQueueGroup queueGroup) + { + Task.Run(async () => + { + ByteBlock byteBlock = this.BytePool.GetByteBlock(this.BufferLength); + int waitCount = 0; + while (waitCount < this.verifyTimeout/ 10) + { + if (socket.Available > 0) + { + try + { + int r = socket.Receive(byteBlock.Buffer); + + VerifyOption verifyOption = new VerifyOption(); + verifyOption.Token = Encoding.UTF8.GetString(byteBlock.Buffer, 0, r); + this.OnVerifyToken(verifyOption); + + if (verifyOption.Accept) + { + if (this.SocketClients.Count > this.MaxCount) + { + byteBlock.Write((byte)3); + this.Logger.Debug(LogType.Error, this, "连接客户端数量已达到设定最大值"); + socket.Send(byteBlock.Buffer, 0, 1, SocketFlags.None); + socket.Dispose(); + return; + } + else + { + TClient client = this.socketClientPool.GetObject(); + client.Flag = verifyOption.Flag; + if (client.NewCreate) + { + client.queueGroup = queueGroup; + client.Service = this; + client.logger = this.Logger; + client.clearType = this.clearType; + client.separateThreadReceive = this.separateThreadReceive; + } + client.MainSocket = socket; + client.ReadIpPort(); + client.bufferLength = this.bufferLength; + + CreateOption creatOption = new CreateOption(); + creatOption.NewCreate = client.NewCreate; + + creatOption.ID = this.SocketClients.GetDefaultID(); + + this.OnCreateSocketCliect(client, creatOption); + client.id = creatOption.ID; + + if (!this.SocketClients.TryAdd(client)) + { + throw new RRQMException("ID重复"); + } + + client.BeginReceive(); + byteBlock.Write((byte)1); + byteBlock.Write(Encoding.UTF8.GetBytes(client.ID)); + socket.Send(byteBlock.Buffer, 0, byteBlock.Len, SocketFlags.None); + OnClientConnected(client, null); + + return; + } + } + else + { + byteBlock.Write((byte)2); + if (verifyOption.ErrorMessage != null) + { + byteBlock.Write(Encoding.UTF8.GetBytes(verifyOption.ErrorMessage)); + } + socket.Send(byteBlock.Buffer, 0, byteBlock.Len, SocketFlags.None); + socket.Dispose(); + return; + } + } + catch (Exception ex) + { + Logger.Debug(LogType.Error, this, $"在验证客户端连接时发生错误,信息:{ex.Message}"); + } + finally + { + byteBlock.Dispose(); + } + } + waitCount++; + await Task.Delay(10); + } + + socket.Dispose(); + }); + } + + /// + /// 当验证Token时 + /// + /// + protected virtual void OnVerifyToken(VerifyOption verifyOption) + { + if (verifyOption.Token == this.verifyToken) + { + verifyOption.Accept = true; + } + else + { + verifyOption.ErrorMessage = "Token不受理"; + } + } + } +} \ No newline at end of file diff --git a/RRQMSocket/TCP/SocketClient/ProtocolSocketClient.cs b/RRQMSocket/TCP/SocketClient/ProtocolSocketClient.cs new file mode 100644 index 000000000..1df22d1e7 --- /dev/null +++ b/RRQMSocket/TCP/SocketClient/ProtocolSocketClient.cs @@ -0,0 +1,270 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +using RRQMCore.ByteManager; +using RRQMCore.Exceptions; +using RRQMCore.Log; +using System; +using System.Collections.Generic; +using System.Text; + +namespace RRQMSocket +{ + /// + /// 协议辅助类 + /// + public abstract class ProtocolSocketClient : SocketClient + { + /// + /// 辅助发送器 + /// + internal ProcotolHelper procotolHelper; + + private static readonly Dictionary usedProtocol = new Dictionary(); + + /// + /// 发送字节 + /// + /// + /// + public void Send(short procotol, byte[] buffer) + { + this.Send(procotol, buffer, 0, buffer.Length); + } + + /// + /// 发送字节流 + /// + /// + /// + /// + /// + /// + /// + public sealed override void Send(byte[] buffer, int offset, int length) + { + this.procotolHelper.SocketSend(-1, buffer, offset, length); + } + + /// + /// 发送字节 + /// + /// + /// + /// + /// + public void Send(short procotol, byte[] buffer, int offset, int length) + { + if (!usedProtocol.ContainsKey(procotol)) + { + this.InternalSend(procotol, buffer, offset, length); + } + else + { + StringBuilder stringBuilder = new StringBuilder(); + foreach (var item in usedProtocol.Keys) + { + stringBuilder.AppendLine($"协议{item}已被使用,描述为:{usedProtocol[item]}"); + } + throw new RRQMException(stringBuilder.ToString()); + } + } + + /// + /// 发送协议流 + /// + /// + /// + public void Send(short procotol, ByteBlock dataByteBlock) + { + this.Send(procotol, dataByteBlock.Buffer, 0, (int)dataByteBlock.Length); + } + + /// + /// 发送协议状态 + /// + /// + public void Send(short procotol) + { + this.Send(procotol, new byte[0], 0, 0); + } + + /// + /// 发送字节流(仍然为同步发送) + /// + /// + /// + /// + public sealed override void SendAsync(byte[] buffer, int offset, int length) + { + this.procotolHelper.SocketSend(-1, buffer, offset, length); + } + + /// + /// 发送字节 + /// + /// + /// + /// + /// + public void SendAsync(short procotol, byte[] buffer, int offset, int length) + { + if (!usedProtocol.ContainsKey(procotol)) + { + this.InternalSend(procotol, buffer, offset, length); + } + else + { + StringBuilder stringBuilder = new StringBuilder(); + foreach (var item in usedProtocol.Keys) + { + stringBuilder.AppendLine($"协议{item}已被使用,描述为:{usedProtocol[item]}"); + } + throw new RRQMException(stringBuilder.ToString()); + } + } + + /// + /// 发送字节 + /// + /// + /// + public void SocketSend(short procotol, byte[] dataBuffer) + { + this.Send(procotol, dataBuffer, 0, dataBuffer.Length); + } + + /// + /// 添加已被使用的协议 + /// + /// + /// + protected static void AddUsedProtocol(short procotol, string describe) + { + usedProtocol.Add(procotol, describe); + } + + /// + /// 收到协议数据,由于性能考虑, + /// byteBlock数据源并未剔除协议数据, + /// 所以真实数据起点为2, + /// 长度为Length-2。 + /// + /// + /// + protected abstract void HandleProtocolData(short? procotol, ByteBlock byteBlock); + + /// + /// 密封方法 + /// + /// + /// + protected sealed override void HandleReceivedData(ByteBlock byteBlock, object obj) + { + short procotol = BitConverter.ToInt16(byteBlock.Buffer, 0); + switch (procotol) + { + case 0: + { + try + { + string id = Encoding.UTF8.GetString(byteBlock.Buffer, 2, byteBlock.Len - 2); + base.ResetID(id); + this.procotolHelper.SocketSend(0, Encoding.UTF8.GetBytes(this.id)); + } + catch (Exception ex) + { + this.Logger.Debug(LogType.Error, this, "重置ID错误", ex); + } + + break; + } + case -1: + { + try + { + byte[] data = new byte[byteBlock.Len - 2]; + byteBlock.Position = 2; + byteBlock.Read(data); + HandleProtocolData(null, byteBlock); + } + catch (Exception ex) + { + this.Logger.Debug(LogType.Error, this, "处理无协议数据异常", ex); + } + break; + } + default: + { + HandleProtocolData(procotol, byteBlock); + break; + } + } + } + + /// + /// 内部发送,不会进行协议检测 + /// + /// + /// + /// + /// + /// + protected void InternalSend(short procotol, byte[] buffer, int offset, int length, bool reserved = false) + { + if (procotol > 0) + { + this.procotolHelper.SocketSend(procotol, buffer, offset, length, reserved); + } + else + { + throw new RRQMException("小等于0的协议为系统使用协议"); + } + } + + /// + /// 内部发送,不会进行协议检测 + /// + /// + /// + /// + protected void InternalSend(short procotol, ByteBlock byteBlock, bool reserved = false) + { + this.InternalSend(procotol, byteBlock.Buffer, 0, byteBlock.Len, reserved); + } + + /// + /// 接收之前 + /// + protected override void OnBeforeReceive() + { + base.OnBeforeReceive(); + this.procotolHelper = new ProcotolHelper(this, false); + if (this.DataHandlingAdapter is FixedHeaderDataHandlingAdapter adapter) + { + adapter.FixedHeaderType = FixedHeaderType.Int; + } + else + { + this.SetDataHandlingAdapter(new FixedHeaderDataHandlingAdapter()); + } + } + + /// + /// 重新设置ID + /// + /// + protected override void ResetID(string id) + { + this.Service.ResetID(this.id, id); + } + } +} \ No newline at end of file diff --git a/RRQMSocket/TCP/SocketClient/SimpleProtocolSocketClient.cs b/RRQMSocket/TCP/SocketClient/SimpleProtocolSocketClient.cs new file mode 100644 index 000000000..52fc0bce8 --- /dev/null +++ b/RRQMSocket/TCP/SocketClient/SimpleProtocolSocketClient.cs @@ -0,0 +1,37 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +using RRQMCore.ByteManager; +using System; + +namespace RRQMSocket +{ + /// + /// SimpleProtocolSocketClient + /// + public class SimpleProtocolSocketClient : ProtocolSocketClient + { + /// + /// 收到消息 + /// + public Action OnReceived; + + /// + /// 处理协议数据 + /// + /// + /// + protected override void HandleProtocolData(short? procotol, ByteBlock byteBlock) + { + this.OnReceived.Invoke(this, procotol, byteBlock); + } + } +} \ No newline at end of file diff --git a/RRQMSocket/TCP/SocketClient/SimpleSocketClient.cs b/RRQMSocket/TCP/SocketClient/SimpleSocketClient.cs new file mode 100644 index 000000000..30e8d6335 --- /dev/null +++ b/RRQMSocket/TCP/SocketClient/SimpleSocketClient.cs @@ -0,0 +1,37 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +using RRQMCore.ByteManager; +using System; + +namespace RRQMSocket +{ + /// + /// 服务器辅助类 + /// + public class SimpleSocketClient : SocketClient + { + /// + /// 收到消息 + /// + public Action OnReceived; + + /// + /// 处理数据 + /// + /// + /// + protected sealed override void HandleReceivedData(ByteBlock byteBlock, object obj) + { + this.OnReceived?.Invoke(this, byteBlock, obj); + } + } +} \ No newline at end of file diff --git a/RRQMSocket/TCP/SocketClient/SocketClient.cs b/RRQMSocket/TCP/SocketClient/SocketClient.cs new file mode 100644 index 000000000..ea34a9994 --- /dev/null +++ b/RRQMSocket/TCP/SocketClient/SocketClient.cs @@ -0,0 +1,545 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +using RRQMCore.ByteManager; +using RRQMCore.Exceptions; +using RRQMCore.Log; +using RRQMCore.Pool; +using System; +using System.Net.Sockets; + +namespace RRQMSocket +{ + /// + /// 服务器辅助类 + /// + public abstract class SocketClient : BaseSocket, ISocketClient, IHandleBuffer, IPoolObject + { + internal bool breakOut; + internal ClearType clearType; + internal string id; + internal long lastTick; + internal BufferQueueGroup queueGroup; + internal bool separateThreadReceive; + private DataHandlingAdapter dataHandlingAdapter; + private SocketAsyncEventArgs eventArgs; + private Socket mainSocket; + + /// + /// 获取内存池实例 + /// + public BytePool BytePool { get { return this.queueGroup == null ? null : this.queueGroup.bytePool; } } + + /// + /// 数据处理适配器 + /// + public DataHandlingAdapter DataHandlingAdapter + { + get { return dataHandlingAdapter; } + } + + /// + /// 标记 + /// + public object Flag { get; set; } + + /// + /// 用于索引的ID + /// + public string ID + { + get { return id; } + } + + /// + /// IPv4地址 + /// + public string IP { get; protected set; } + + /// + /// 主通信器 + /// + public Socket MainSocket + { + get { return mainSocket; } + internal set + { + mainSocket = value; + } + } + + /// + /// IP及端口 + /// + public string Name => $"{this.IP}:{this.Port}"; + + /// + /// 是否为新建对象 + /// + public bool NewCreate { get; set; } + + /// + /// 判断该实例是否还在线 + /// + public bool Online { get { return !this.breakOut; } } + + /// + /// 端口号 + /// + public int Port { get; protected set; } + + /// + /// 包含此辅助类的主服务器类 + /// + public _ITcpService Service { get; internal set; } + + /// + /// 初次创建对象,效应相当于构造函数,父类方法可覆盖 + /// + public virtual void Create() + { + + } + + /// + /// 在断开连接时销毁对象,父类方法不可覆盖 + /// + public virtual void Destroy() + { + this.MainSocket = null; + this.dataHandlingAdapter = null; + } + + /// + /// 完全释放资源 + /// + public override void Dispose() + { + base.Dispose(); + if (this.mainSocket != null) + { + this.mainSocket.Dispose(); + } + if (this.eventArgs != null) + { + this.eventArgs.Dispose(); + this.eventArgs = null; + } + this.breakOut = true; + } + + /// + /// 处理数据 + /// + /// + public void HandleBuffer(ByteBlock byteBlock) + { + try + { + if (this.dataHandlingAdapter == null) + { + throw new RRQMException("数据处理适配器为空"); + } + this.dataHandlingAdapter.Received(byteBlock); + } + catch (Exception ex) + { + Logger.Debug(LogType.Error, this, "在处理数据时发生错误", ex); + } + finally + { + byteBlock.Dispose(); + } + } + + /// + /// 读取IP、Port + /// + public void ReadIpPort() + { + if (MainSocket == null) + { + this.IP = null; + this.Port = -1; + return; + } + + string ipport; + if (MainSocket.Connected && MainSocket.RemoteEndPoint != null) + { + ipport = MainSocket.RemoteEndPoint.ToString(); + } + else if (MainSocket.IsBound && MainSocket.LocalEndPoint != null) + { + ipport = MainSocket.LocalEndPoint.ToString(); + } + else + { + return; + } + + int r = ipport.LastIndexOf(":"); + this.IP = ipport.Substring(0, r); + this.Port = Convert.ToInt32(ipport.Substring(r + 1, ipport.Length - (r + 1))); + } + + /// + /// 重新获取,父类方法不可覆盖 + /// + public virtual void Recreate() + { + this.breakOut = false; + this.disposable = false; + } + + /// + /// 发送字节流 + /// + /// + /// + /// + /// + public virtual void Send(byte[] buffer) + { + this.Send(buffer, 0, buffer.Length); + } + + /// + /// 发送字节流 + /// + /// + /// + /// + /// + /// + /// + public virtual void Send(byte[] buffer, int offset, int length) + { + this.dataHandlingAdapter.Send(buffer, offset, length, false); + } + + /// + /// 发送流中的有效数据 + /// + /// + /// + /// + /// + public virtual void Send(ByteBlock byteBlock) + { + this.Send(byteBlock.Buffer, 0, byteBlock.Len); + } + + /// + /// IOCP发送 + /// + /// + /// + /// + /// + /// + /// + public virtual void SendAsync(byte[] buffer, int offset, int length) + { + this.dataHandlingAdapter.Send(buffer, offset, length, true); + } + + /// + /// IOCP发送 + /// + /// + /// + /// + /// + public virtual void SendAsync(byte[] buffer) + { + this.SendAsync(buffer, 0, buffer.Length); + } + + /// + /// IOCP发送流中的有效数据 + /// + /// + /// + /// + /// + public virtual void SendAsync(ByteBlock byteBlock) + { + this.SendAsync(byteBlock.Buffer, 0, byteBlock.Len); + } + + /// + /// 设置数据处理适配器 + /// + /// + public virtual void SetDataHandlingAdapter(DataHandlingAdapter adapter) + { + if (adapter == null) + { + throw new RRQMException("数据处理适配器为空"); + } + adapter.BytePool = this.BytePool; + adapter.Logger = this.Logger; + adapter.ReceivedCallBack = this.HandleReceivedData; + adapter.SendCallBack = this.Sent; + this.dataHandlingAdapter = adapter; + } + + /// + /// 禁用发送或接收 + /// + /// + public void Shutdown(SocketShutdown how) + { + this.breakOut = true; + if (this.MainSocket != null) + { + MainSocket.Shutdown(how); + } + } + + /// + /// 启动消息接收 + /// + internal void BeginReceive() + { + try + { + this.OnBeforeReceive(); + eventArgs = new SocketAsyncEventArgs(); + eventArgs.Completed += this.EventArgs_Completed; + ByteBlock byteBlock = this.BytePool.GetByteBlock(this.BufferLength); + eventArgs.UserToken = byteBlock; + eventArgs.SetBuffer(byteBlock.Buffer, 0, byteBlock.Buffer.Length); + if (!MainSocket.ReceiveAsync(eventArgs)) + { + ProcessReceived(eventArgs); + } + this.lastTick = DateTime.Now.Ticks; + } + catch + { + this.breakOut = true; + } + } + + /// + /// 测试是否在线 + /// + internal void GetTimeout(int time, long nowTick) + { + if (nowTick - this.lastTick / 10000000 > time) + { + this.breakOut = true; + } + + try + { + this.OnPerSecond(); + } + catch (Exception ex) + { + this.Logger.Debug(LogType.Error, this, $"在{nameof(OnPerSecond)}中发生异常", ex); + } + } + + /// + /// 处理已接收到的数据 + /// + /// + /// + protected abstract void HandleReceivedData(ByteBlock byteBlock, object obj); + + /// + /// 在接收之前 + /// + protected virtual void OnBeforeReceive() + { + if (this.dataHandlingAdapter == null) + { + this.SetDataHandlingAdapter(new NormalDataHandlingAdapter()); + } + } + + /// + /// 每一秒执行 + /// + protected virtual void OnPerSecond() + { + } + + /// + /// 重新设置ID + /// + /// + protected virtual void ResetID(string id) + { + this.Service.ResetID(this.id, id); + } + + /// + /// 等待接收 + /// + protected virtual void WaitReceive() + { + } + + private void EventArgs_Completed(object sender, SocketAsyncEventArgs e) + { + try + { + if (e.LastOperation == SocketAsyncOperation.Receive) + { + ProcessReceived(e); + } + else if (e.LastOperation == SocketAsyncOperation.Send) + { + ProcessSend(e); + } + else + { + this.breakOut = true; + } + } + catch + { + this.breakOut = true; + } + } + + private void ProcessReceived(SocketAsyncEventArgs e) + { + if (!this.disposable) + { + if (e.SocketError == SocketError.Success && e.BytesTransferred > 0) + { + if (this.clearType.HasFlag(ClearType.Receive)) + { + this.lastTick = DateTime.Now.Ticks; + } + + if (this.separateThreadReceive) + { + ClientBuffer clientBuffer = new ClientBuffer(); + clientBuffer.client = this; + clientBuffer.byteBlock = (ByteBlock)e.UserToken; + clientBuffer.byteBlock.SetLength(e.BytesTransferred); + queueGroup.bufferAndClient.Enqueue(clientBuffer); + if (queueGroup.isWait) + { + queueGroup.isWait = false; + queueGroup.waitHandleBuffer.Set(); + } + } + else + { + ByteBlock byteBlock = (ByteBlock)e.UserToken; + byteBlock.SetLength(e.BytesTransferred); + this.HandleBuffer(byteBlock); + } + + try + { + WaitReceive(); + } + catch (Exception ex) + { + this.Logger.Debug(LogType.Error, this, ex.Message); + } + try + { + ByteBlock newByteBlock = this.BytePool.GetByteBlock(this.BufferLength); + e.UserToken = newByteBlock; + e.SetBuffer(newByteBlock.Buffer, 0, newByteBlock.Buffer.Length); + + if (!MainSocket.ReceiveAsync(e)) + { + ProcessReceived(e); + } + } + catch + { + } + } + else + { + this.breakOut = true; + } + } + else + { + this.breakOut = true; + } + } + + /// + /// 发送完成时处理函数 + /// + /// 与发送完成操作相关联的SocketAsyncEventArg对象 + private void ProcessSend(SocketAsyncEventArgs e) + { + if (e.SocketError == SocketError.Success) + { + e.Dispose(); + } + else + { + this.Logger.Debug(LogType.Error, this, "异步发送错误。"); + } + } + + private void Sent(byte[] buffer, int offset, int length, bool isAsync) + { + if (!this.Online) + { + throw new RRQMNotConnectedException("该实例已断开"); + } + try + { + if (isAsync) + { + SocketAsyncEventArgs sendEventArgs = new SocketAsyncEventArgs(); + sendEventArgs.Completed += EventArgs_Completed; + sendEventArgs.SetBuffer(buffer, offset, length); + sendEventArgs.RemoteEndPoint = this.MainSocket.RemoteEndPoint; + if (!this.MainSocket.SendAsync(sendEventArgs)) + { + // 同步发送时处理发送完成事件 + this.ProcessSend(sendEventArgs); + } + } + else + { + int r = 0; + while (length > 0) + { + r = MainSocket.Send(buffer, offset, length, SocketFlags.None); + if (r == 0 && length > 0) + { + throw new RRQMException("发送数据不完全"); + } + offset += r; + length -= r; + } + } + + if (this.clearType.HasFlag(ClearType.Send)) + { + this.lastTick = DateTime.Now.Ticks; + } + } + catch (Exception e) + { + throw new RRQMException(e.Message); + } + } + } +} \ No newline at end of file diff --git a/RRQMSocket/UDP/SimpleUdpSession.cs b/RRQMSocket/UDP/SimpleUdpSession.cs new file mode 100644 index 000000000..d4888d500 --- /dev/null +++ b/RRQMSocket/UDP/SimpleUdpSession.cs @@ -0,0 +1,37 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +using RRQMCore.ByteManager; +using System.Net; + +namespace RRQMSocket +{ + /// + /// 若汝棋茗内置UDP会话 + /// + public class SimpleUdpSession : UdpSession + { + /// + /// 当收到数据时 + /// + public event RRQMUDPByteBlockEventHandler Received; + + /// + /// 处理数据 + /// + /// + /// + protected override void HandleReceivedData(EndPoint remoteEndPoint, ByteBlock byteBlock) + { + Received?.Invoke(remoteEndPoint, byteBlock); + } + } +} \ No newline at end of file diff --git a/RRQMSocket/UDP/UdpSession.cs b/RRQMSocket/UDP/UdpSession.cs new file mode 100644 index 000000000..d583cbd51 --- /dev/null +++ b/RRQMSocket/UDP/UdpSession.cs @@ -0,0 +1,496 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在RRQMCore.XREF命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +using RRQMCore.ByteManager; +using RRQMCore.Exceptions; +using RRQMCore.Log; +using System; +using System.Net; +using System.Net.Sockets; +using System.Threading; + +namespace RRQMSocket +{ + /// + /// TCP服务器 + /// + public abstract class UdpSession : BaseSocket, IService, IClient + { + internal bool separateThreadReceive; + + private BufferQueueGroup[] bufferQueueGroups; + + private EndPoint defaultRemotePoint; + + private Socket mainSocket; + + private string name; + + private long recivedCount; + + private SocketAsyncEventArgs recviveEventArg; + + private ServiceConfig serverConfig; + + private ServerState serverState; + + /// + /// 获取默认内存池 + /// + public BytePool BytePool { get { return BytePool.Default; } } + + /// + /// 默认远程节点 + /// + public EndPoint DefaultRemotePoint + { + get { return defaultRemotePoint; } + } + + /// + /// IPv4地址 + /// + public string IP { get; private set; } + + /// + /// 主通信器 + /// + public Socket MainSocket + { + get { return mainSocket; } + internal set + { + mainSocket = value; + } + } + + /// + /// IP及端口号 + /// + public string Name { get { return $"{this.IP}:{this.Port}"; } } + + /// + /// 端口号 + /// + public int Port { get; private set; } + + /// + /// 服务器名称 + /// + public string ServerName + { + get { return name; } + } + + /// + /// 获取服务器状态 + /// + public ServerState ServerState + { + get { return serverState; } + } + + /// + /// 获取配置 + /// + public ServiceConfig ServiceConfig + { + get { return serverConfig; } + } + + /// + /// 关闭服务器并释放服务器资源 + /// + public override void Dispose() + { + base.Dispose(); + this.Stop(); + if (this.bufferQueueGroups != null) + { + foreach (var item in bufferQueueGroups) + { + item.Dispose(); + } + bufferQueueGroups = null; + } + this.serverState = ServerState.Disposed; + } + + /// + /// 向默认终结点发送 + /// + /// + /// + /// + public void Send(byte[] buffer, int offset, int length) + { + if (this.DefaultRemotePoint == null) + { + throw new RRQMException("默认终结点为空"); + } + this.SendTo(buffer, offset, length, this.defaultRemotePoint); + } + + /// + /// 向默认终结点发送 + /// + /// + public void Send(byte[] buffer) + { + this.Send(buffer, 0, buffer.Length); + } + + /// + /// 向默认终结点发送 + /// + /// + public void Send(ByteBlock byteBlock) + { + this.Send(byteBlock.Buffer, 0, byteBlock.Len); + } + + /// + /// IOCP发送 + /// + /// + /// + /// + /// + /// + /// + public virtual void SendAsync(byte[] buffer, int offset, int length) + { + this.SendAsync(buffer, offset, length, this.defaultRemotePoint); + } + + /// + /// IOCP发送 + /// + /// + /// + /// + /// + /// + /// + /// + public virtual void SendAsync(byte[] buffer, int offset, int length, EndPoint remoteEP) + { + this.mainSocket.BeginSendTo(buffer, offset, length, SocketFlags.None, remoteEP, null, null); + } + + /// + /// IOCP发送 + /// + /// + /// + /// + /// + public virtual void SendAsync(byte[] buffer) + { + this.SendAsync(buffer, 0, buffer.Length); + } + + /// + /// IOCP发送流中的有效数据 + /// + /// + /// + /// + /// + public virtual void SendAsync(ByteBlock byteBlock) + { + this.SendAsync(byteBlock.Buffer, 0, byteBlock.Len); + } + + /// + /// 发送 + /// + /// + /// + /// + /// + public void SendTo(byte[] buffer, int offset, int length, EndPoint remoteEP) + { + this.mainSocket.SendTo(buffer, offset, length, SocketFlags.None, remoteEP); + } + + /// + /// 配置服务 + /// + /// + public void Setup(ServiceConfig serverConfig) + { + this.serverConfig = serverConfig; + this.LoadConfig(this.serverConfig); + } + + /// + /// 通过端口配置 + /// + /// + public void Setup(int port) + { + UdpSessionConfig serverConfig = new UdpSessionConfig(); + serverConfig.ListenIPHosts = new IPHost[] { new IPHost(port) }; + this.Setup(serverConfig); + } + + /// + /// 启动服务 + /// + public void Start() + { + if (this.serverState == ServerState.Disposed) + { + throw new RRQMException("无法重新利用已释放对象"); + } + + bool useBind = (bool)this.serverConfig.GetValue(UdpSessionConfig.UseBindProperty); + + if (useBind) + { + IPHost[] iPHosts = (IPHost[])this.serverConfig.GetValue(ServiceConfig.ListenIPHostsProperty); + + if (iPHosts == null || iPHosts.Length != 1) + { + throw new RRQMException("ListenIPHosts为空或不明确,无法绑定"); + } + switch (this.serverState) + { + case ServerState.None: + { + this.BeginReceive(iPHosts[0]); + BeginThread(); + break; + } + case ServerState.Running: + break; + + case ServerState.Stopped: + { + this.BeginReceive(iPHosts[0]); + break; + } + case ServerState.Disposed: + { + throw new RRQMException("无法再次利用已释放对象"); + } + } + } + else + { + this.mainSocket = new Socket(SocketType.Dgram, ProtocolType.Udp); + } + this.serverState = ServerState.Running; + } + + /// + /// 停止服务器 + /// + public void Stop() + { + if (this.mainSocket != null) + { + this.mainSocket.Dispose(); + } + + this.serverState = ServerState.Stopped; + } + + /// + /// 处理已接收到的数据 + /// + /// + /// + protected abstract void HandleReceivedData(EndPoint remoteEndPoint, ByteBlock byteBlock); + + /// + /// 加载配置 + /// + /// + protected virtual void LoadConfig(ServiceConfig serverConfig) + { + if (serverConfig == null) + { + throw new RRQMException("配置文件为空"); + } + this.logger = serverConfig.Logger; + this.defaultRemotePoint = (EndPoint)serverConfig.GetValue(UdpSessionConfig.DefaultRemotePointProperty); + this.bufferLength = serverConfig.BufferLength; + this.name = serverConfig.ServerName; + this.separateThreadReceive = (bool)serverConfig.GetValue(UdpSessionConfig.SeparateThreadReceiveProperty); + } + + /// + /// 在Socket初始化对象后,Bind之前调用。 + /// 可用于设置Socket参数。 + /// 父类方法可覆盖。 + /// + /// + protected virtual void PreviewBind(Socket socket) + { + } + + private void BeginReceive(IPHost iPHost) + { + Socket socket = new Socket(iPHost.AddressFamily, SocketType.Dgram, ProtocolType.Udp); + PreviewBind(socket); + socket.Bind(iPHost.EndPoint); + this.MainSocket = socket; + this.ReadIpPort(); + this.recviveEventArg = new SocketAsyncEventArgs(); + this.recviveEventArg.Completed += this.IO_Completed; + ByteBlock byteBlock = this.BytePool.GetByteBlock(this.BufferLength); + this.recviveEventArg.UserToken = byteBlock; + this.recviveEventArg.SetBuffer(byteBlock.Buffer, 0, byteBlock.Buffer.Length); + this.recviveEventArg.RemoteEndPoint = iPHost.EndPoint; + if (!this.MainSocket.ReceiveFromAsync(this.recviveEventArg)) + { + ProcessReceive(this.recviveEventArg); + } + } + + private void BeginThread() + { + bufferQueueGroups = new BufferQueueGroup[serverConfig.ThreadCount]; + for (int i = 0; i < serverConfig.ThreadCount; i++) + { + BufferQueueGroup bufferQueueGroup = new BufferQueueGroup(); + bufferQueueGroups[i] = bufferQueueGroup; + bufferQueueGroup.bytePool = new BytePool(this.serverConfig.BytePoolMaxSize, this.serverConfig.BytePoolMaxBlockSize); + bufferQueueGroup.waitHandleBuffer = new AutoResetEvent(false); + bufferQueueGroup.bufferAndClient = new BufferQueue(); + + if (this.separateThreadReceive) + { + bufferQueueGroup.Thread = new Thread(Handle);//处理用户的消息 + bufferQueueGroup.Thread.IsBackground = true; + bufferQueueGroup.Thread.Name = i + "-Num Handler"; + bufferQueueGroup.Thread.Start(bufferQueueGroup); + } + } + } + + private void Handle(object o) + { + BufferQueueGroup queueGroup = (BufferQueueGroup)o; + while (true) + { + if (disposable) + { + break; + } + ClientBuffer clientBuffer; + if (queueGroup.bufferAndClient.TryDequeue(out clientBuffer)) + { + this.HandleBuffer(clientBuffer.endPoint, clientBuffer.byteBlock); + } + else + { + queueGroup.isWait = true; + queueGroup.waitHandleBuffer.WaitOne(); + } + } + } + + private void HandleBuffer(EndPoint endPoint, ByteBlock byteBlock) + { + try + { + HandleReceivedData(endPoint, byteBlock); + } + catch (Exception e) + { + Logger.Debug(LogType.Error, this, $"在处理数据时发生错误,信息:{e.Message}"); + } + finally + { + byteBlock.Dispose(); + } + } + + private void IO_Completed(object sender, SocketAsyncEventArgs e) + { + if (e.LastOperation == SocketAsyncOperation.ReceiveFrom) + { + ProcessReceive(e); + } + } + + private void ProcessReceive(SocketAsyncEventArgs e) + { + if (!this.disposable) + { + if (e.SocketError == SocketError.Success) + { + ByteBlock byteBlock = (ByteBlock)e.UserToken; + byteBlock.SetLength(e.BytesTransferred); + + BufferQueueGroup queueGroup = this.bufferQueueGroups[++this.recivedCount % this.bufferQueueGroups.Length]; + + if (this.separateThreadReceive) + { + ClientBuffer clientBuffer = new ClientBuffer(); + clientBuffer.endPoint = e.RemoteEndPoint; + clientBuffer.byteBlock = byteBlock; + queueGroup.bufferAndClient.Enqueue(clientBuffer); + + if (queueGroup.isWait) + { + queueGroup.isWait = false; + queueGroup.waitHandleBuffer.Set(); + } + } + else + { + this.HandleBuffer(e.RemoteEndPoint, byteBlock); + } + + ByteBlock newByteBlock = queueGroup.bytePool.GetByteBlock(this.bufferLength); + e.UserToken = newByteBlock; + e.SetBuffer(newByteBlock.Buffer, 0, newByteBlock.Buffer.Length); + if (!this.mainSocket.ReceiveFromAsync(this.recviveEventArg)) + { + ProcessReceive(e); + } + } + } + } + + private void ReadIpPort() + { + if (MainSocket == null) + { + this.IP = null; + this.Port = -1; + return; + } + + string ipport; + if (MainSocket.Connected && MainSocket.RemoteEndPoint != null) + { + ipport = MainSocket.RemoteEndPoint.ToString(); + } + else if (MainSocket.IsBound && MainSocket.LocalEndPoint != null) + { + ipport = MainSocket.LocalEndPoint.ToString(); + } + else + { + return; + } + + int r = ipport.LastIndexOf(":"); + this.IP = ipport.Substring(0, r); + this.Port = Convert.ToInt32(ipport.Substring(r + 1, ipport.Length - (r + 1))); + } + } +} \ No newline at end of file