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 @@
+
+
+
+
+
+
+
+
+[](https://www.nuget.org/packages/RRQMSocket/)
+[](https://www.apache.org/licenses/LICENSE-2.0.html)
+[](https://www.nuget.org/packages/RRQMSocket/)
+[](https://gitee.com/dotnetchina/RRQMSocket/stargazers)
+[](https://gitee.com/dotnetchina/RRQMSocket/members)
+
+
+
+
+
+
+
+合抱之木,生于毫末;九层之台,起于垒土。
+
+
+
+
+
+
+## 💿描述
+| 名称 |描述|
+|---|---|
+|[](https://www.nuget.org/packages/RRQMSocket/)| **RRQMSocket**是一个整合性的、超轻量级的、可以免费商用使用的网络通信服务框架。 它具有 **高并发连接** 、 **高并发处理** 、 **事件订阅** 、 **插件式扩展** 、 **多线程处理** 、 **内存池** 、 **对象池** 等特点, 让使用者能够更加简单的、快速的搭建网络框架。|
+|[](https://www.nuget.org/packages/RRQMSocketFramework/)| **RRQMSocketFramework**是RRQMSocket系列的企业版, 两者在功能上几乎没有区别,但是RRQMSocketFramework无任何依赖, 且可以提供专属的定制功能。后续也会加入企业已定制的优秀功能,希望大家多多支持。|
+| [](https://www.nuget.org/packages/RRQMSocket.FileTransfer/) | RRQMSocket.FileTransfer是一个高性能的文件传输框架, 您可以用它传输**任意大小**的文件,它可以完美支持**上传下载混合式队列传输**、 **断点续传**、 **快速上传** 、**传输限速**、**获取文件信息**、**删除文件**等。 在实时测试中,它的传输速率可达500Mb/s。 |
+|[](https://www.nuget.org/packages/RRQMSocket.RPC/) |RPC是一个超轻量、高性能、可扩展的微服务管理平台框架, 目前已完成开发**RRQMRPC**、**XmlRpc**、**JsonRpc**、**WebApi**部分。 **RRQMRPC**部分使用RRQM专属协议,支持客户端**异步调用**, 服务端**异步触发**、以及**out**和**ref**关键字,**函数回调**等。 在调用效率上也是非常强悍,在调用空载函数,且返回状态时, **10w**次调用仅用时**3.8**秒,不返回状态用时**0.9**秒。 其他协议调用性能详看性能评测。|
+|[](https://www.nuget.org/packages/RRQMSocket.RPC.WebApi/)| WebApi是一个扩展于RRQMSocket.RPC的WebApi组件, 可以通过该组件创建WebApi服务解析器,让桌面端、Web端、移动端可以跨语言调用RPC函数。 功能支持路由、Get传参、Post传参等。|
+|[](https://www.nuget.org/packages/RRQMSocket.RPC.XmlRpc/)| XmlRpc是一个扩展于RRQMSocket.RPC的XmlRpc组件, 可以通过该组件创建XmlRpc服务解析器,完美支持XmlRpc数据类型,类型嵌套, Array等,也能与CookComputing.XmlRpcV2完美对接。不限Web,Android等平台。|
+| [](https://www.nuget.org/packages/RRQMSocket.RPC.JsonRpc/)| JsonRpc是一个扩展于RRQMSocket.RPC的JsonRpc组件, 可以通过该组件创建JsonRpc服务解析器,支持JsonRpc全部功能,可与Web,Android等平台无缝对接。|
+| [](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) | [](https://www.nuget.org/packages/RRQMCore/) | [](https://www.nuget.org/packages/RRQMCore/) | RRQMCore是为RRQM系提供基础服务功能的库,其中包含:**内存池**、**对象池**、**等待逻辑池**、**AppMessenger**、**3DES加密**、**Xml快速存储**、**运行时间测量器**、**文件快捷操作**、**高性能序列化器**、**规范日志接口**等。 |
+| [RRQMMVVM](https://gitee.com/RRQM_OS/RRQMMVVM) | [](https://www.nuget.org/packages/RRQMMVVM/) | [](https://www.nuget.org/packages/RRQMMVVM/) | RRQMMVVM是超轻简的MVVM框架,但是麻雀虽小,五脏俱全。|
+| [RRQMSkin](https://gitee.com/RRQM_OS/RRQMSkin) | [](https://www.nuget.org/packages/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。
+
+
+
+
+
+
+
+
+
+#### 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基本上没有释放,性能非常强悍,图二是下载文件,性能依旧非常强悍。
+
+
+
+
+
+## 致谢
+
+谢谢大家对我的支持,如果还有其他问题,请加群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();
+ }
+ }
+
+ ///