<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>尼记笔记</title><link>http://www.nasay.cn/</link><description>好记性不如烂笔头,读书千遍不如写一写记一记</description><generator>尼记笔记</generator><language>zh-CN</language><pubDate>Fri, 15 Dec 2023 08:37:50 +0800</pubDate><item><title>SQL Server下7种“数据分页”方案</title><author>532898411@qq.com (ningjian)</author><link>http://www.nasay.cn/view.asp?id=2053</link><pubDate>Fri, 15 Dec 2023 08:28:24 +0800</pubDate><guid>http://www.nasay.cn/view.asp?id=2053</guid><description><![CDATA[<p>数据分页往往有三种常用方案。</p>

<p>第一种，把数据库中存放的相关数据，全部读入PHP/Java/C#代码/内存，再由代码对其进行分页操作（速度慢，简易性高）。</p>

<p>第二种，直接在数据库中对相关数据进行分页操作，再把分页后的数据输出给代码程序（速度中，简易性中）。</p>

<p>第三种，先把数据库中的相关数据全部读入&ldquo;缓存&rdquo;或第三方工具，再由代码程序对&ldquo;缓存&rdquo;或第三方工具中的数据进行读取+分页操作（速度快，简易性差）。</p>

<p>&nbsp;</p>

<p>本文下面重点阐述上述【第二种】方案在SQL Server上的使用（其它种类数据库由于Sql语句略有差异，所以需要调整，但方案也类似）</p>

<p>&nbsp;</p>

<p>1、ROW_NUMBER() OVER()方式（SQL2012以下推荐使用）</p>

<p>示例：</p>

<pre class="prettyprint">
SELECT * FROM (SELECT ROW_NUMBER() OVER(ORDER BY menuId) AS RowId,* FROM sys_menu ) AS r WHERE RowId BETWEEN 1 AND 10</pre>

<p>&nbsp;</p>

<p>用子查询新增一列行号（ROW_NUMBER）RowId查询，比较高效的查询方式，只有在SQL Server2005或更高版本才支持。</p>

<p>BETWEEN 1 AND 10&nbsp;是指查询第1到第10条数据（闭区间），在这里面需要注意的是OVER的括号里面可以写多个排序字段。</p>

<p>通用用法</p>

<pre class="prettyprint">
--pageIndex 表示指定页

--pageSize 表示每页显示的条数

SELECT * FROM (SELECT ROW_NUMBER() OVER(ORDER BY 排序字段) AS RowId,* FROM 表名 ) AS r WHERE RowId BETWEEN ((pageIndex-1)*pageSize + 1) AND (pageIndex * PageSize)</pre>

<p>&nbsp;</p>

<p>2、offset fetch next方式（SQL2012及以上的版本才支持：推荐使用 ）</p>

<p>示例：</p>

<pre class="prettyprint">
--offset fetch next方式查询，最高效的查询方式，只有在SQL Server2012或更高版本才支持

SELECT * FROM sys_menu ORDER BY menuId offset 0 ROWS FETCH NEXT 10 ROWS ONLY

offset 是跳过多少行，

next是取接下来的多少行，</pre>

<p>&nbsp;</p>

<p>句式&nbsp;offset...rows fetch nect ..rows only&nbsp;，注意rows和末尾的only 不要写漏掉了，并且这种方式必须要接着Order by XX 使用，不然会报错。</p>

<p>通用用法</p>

<pre class="prettyprint">
--pageIndex 表示指定页

--pageSize 表示每页显示的条数

SELECT * FROM 表名 ORDER BY 排序字段 offset ((pageIndex - 1) * pageSize) ROWS FETCH NEXT pageSize ROWS ONLY</pre>

<p>3、top not in方式 （不推荐）</p>

<p>示例：</p>

<p>--查询第11-20条记录</p>

<pre class="prettyprint">
SELECT TOP 10 menuId, *F

ROM sys_menu

WHERE menuId NOT IN (SELECT TOP 10 menuId FROM sys_menu)</pre>

<p>这条语句的原理是先查询1-10条记录的ID，然后再查询ID不属于这1-10条记录的ID，并且只需要10条记录，因为每页大小就是10，</p>

<p>这就是获取到的第11-20条记录，这是非常简单的一种写法。</p>

<p>&nbsp;</p>

<p>另外IN语句与NOT IN语句类似，这是NOT IN的写法，但是这种写法数据量大的话效率太低。</p>

<p>通用用法</p>

<pre class="prettyprint">
--pageIndex 表示指定页

--pageSize 表示每页显示的条数

SELECT TOP pageSize menuId, *FROM sys_menu WHERE menuId NOT IN (SELECT TOP ((pageSize-1)*pageIndex) menuId FROM sys_menu)</pre>

<p>4、通过升序与降序方式进行查询分页（不推荐）</p>

<p>示例：</p>

<pre class="prettyprint">
--查询第11-20条记录

SELECT * FROM( SELECT TOP 10 * FROM( SELECT TOP 20 * FROM sys_menu ORDER BY menuId ASC) AS TEMP1 ORDER BY menuId DESC) AS TEMP2 ORDER BY menuId ASC</pre>

<p>这条语句首先查询前20条记录，然后在倒序查询前10条记录（即倒数10条记录），</p>

<p>&nbsp;</p>

<p>这个时候就已经获取到了11-20条记录，但是他们的顺序是倒序，所以最后又进行升序排序。</p>

<p>通用方法</p>

<pre class="prettyprint">
--pageIndex 表示指定页

--pageSize 表示每页显示的条数

SELECT * FROM( SELECT TOP pageSize * FROM( SELECT TOP ((pageIndex - 1) * pageSize +(pageSize*2)) * FROM sys_menu

ORDER BY menuId ASC) AS TEMP1 ORDER BY menuId DESC) AS TEMP2 ORDER BY menuId ASC</pre>

<p>5、采用MAX(ID)或者MIN(ID)函数（不推荐）</p>

<p>示例：</p>

<p>--查询第11-20条记录</p>

<pre class="prettyprint">
SELECT TOP 10 * FROM sys_menu WHERE menuId&gt; (SELECT MAX(menuId) FROM(SELECT TOP 10 menuId FROM sys_menu ORDER BY menuId) AS TEMP1) --（第10条的id）</pre>

<p>这个理解起来也简单，先把第10条记录的id找出来（当然这里面是直接使用MAX()进行查找，MIN()函数的用法也是类似的），</p>

<p>然后再对比取比第10条记录的id大的前10条记录即为我们需要的结果。</p>

<p>&nbsp;</p>

<p>这里要注意开始时的边界值调整。</p>

<p>通用用法</p>

<pre class="prettyprint">
--pageIndex 表示指定页

--pageSize 表示每页显示的条数

SELECT TOP pageSize * FROM sys_menu WHERE menuId&gt; (SELECT MAX(menuId) FROM(SELECT TOP ((PageIndex-1)*PageSize) menuId FROM sys_menu ORDER BY menuId) AS TEMP1) --（第10条的id）</pre>

<p>&nbsp;</p>

<p>上述1~5方案，再配合存储过程，你就能制造出适合你自己的&ldquo;分页&rdquo;轮子，日后反复使用。</p>

<p>但它们有一定自身局限性：方案1、2、5都需要依赖一个排序Id（这个Id要么是个排序列，要么是个主键）。方案3、4则效率太低，完全不推荐。</p>

<p>7、不依赖排序/排序Id的终极方案</p>

<p>此方案在DeveloperSharp框架中有提供（基于.Net/.Net Core/.Net Framework），方案被广东省的多个公司/项目采用，得到了实战验证+稳定性。</p>

<p>【第一步】：从NuGet引用DeveloperSharp包。</p>

<p>【第二步】：创建一个用来与数据库进行通信的&ldquo;数据源类&rdquo;（文本示例为：TestData.cs），内容如下：</p>

<pre class="prettyprint">
using DeveloperSharp.Structure.Model;

using DeveloperSharp.Framework.QueryEngine;

namespace YZZ{

[DataSource(DatabaseType.SQLServer, &quot;Server=localhost;Database=Test;Uid=sa;Pwd=123&quot;)]

public class TestData : DeveloperSharp.Structure.Model.DataLayer { //类中没有任何代码 }}</pre>

<p>说 明 ：&ldquo;数据源类&rdquo;（文本示例为：TestData.cs）必 须 继 承 自 DeveloperSharp.Structure.Model.DataLayer 类 ， 并 且 在 其 上 设 置DataSource属 性 的 初 始 化 值 为&ldquo;数据库类型&rdquo;及其&ldquo;链接字符串&rdquo;。</p>

<p>&nbsp;</p>

<p>【第三步】：添加通过&ldquo;数据源类&rdquo;（TestData）调用其PagePartition方法进行数据分页的代码。注 意：核心代码就一行而已！！</p>

<p>代码如下：</p>

<pre class="prettyprint">
using DeveloperSharp.Extension;//Table扩展所在的命名空间----------------------------- 
class Program { 
               static void Main(string[] args) { TestData td = new TestData(); //分页 var pp = td.PagePartition(&quot;select top 5000 * from t_Order where Id&gt;10 order by Id desc&quot;, 20, 162); List&lt;Product&gt; Products = pp.Table.ToList&lt;Product&gt;(); foreach (var P in Products) { Console.WriteLine(P.Name); } Console.ReadLine(); }
 }</pre>

<p>Product类代码如下：</p>

<pre class="prettyprint">
public class Product { public string Id { get; set; } public string Name { get; set; } public int Quantity { get; set; } }</pre>

<p>&nbsp;</p>

<p>此处的PagePartition方法有两个重载方法，其详细功能说明如下：</p>

<p>PagePartition声明：public PagePiece PagePartition(string RecordSet, string Id, int PageSize, int PageIndex)用途：分页功能(有主键)参数：（1）string RecordSet --需要分页的记录集，可以是表、视图、或者SQL语句（2）string Id --主键（3）int PageSize --页面大小（4）int PageIndex --当前页码返回：PagePiece --页片实体 PagePartition声明：public PagePiece PagePartition(string RecordSet, int PageSize, int PageIndex)用途：分页功能(无主键)参数：（1）string RecordSet -- 需要分页的记录集，可以是表、视图、或者SQL语句 （2）int PageSize --页面大小（3）int PageIndex --当前页码返回：PagePiece --页片实体</p>

<p>注意：</p>

<p>（1）&nbsp;&nbsp;&nbsp;&nbsp; 当你需要分页的数据表有&ldquo;主键&rdquo;字段时，使用&ldquo;分页功能(有主键)&rdquo;。反之，使用&ldquo;分页功能(无主键)&rdquo;。</p>

<p>（2）&nbsp;&nbsp;&nbsp;&nbsp; RecordSet是你需要分页的&ldquo;数据总集&rdquo;的SQL语句。该SQL语句的形式丰富多样，可以带条件、排序、甚至还能是多表的联合查询、等。</p>

<p>（3）&nbsp; &nbsp; &nbsp;此方法符合最开始的【第二种】方案，是在SQL Server内部进行的分页操作。而且可以不依赖于排序/排序Id。</p>

<p>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;</p>
]]></description><category>MSSQL</category><comments>http://www.nasay.cn/view.asp?id=2053#comment</comments><wfw:commentRss>http://www.nasay.cn/feed.asp?cmt=2053</wfw:commentRss></item><item><title>C# 窗口最小化到托盘及右键图标显示菜单</title><author>532898411@qq.com (ningjian)</author><link>http://www.nasay.cn/view.asp?id=2052</link><pubDate>Thu, 15 Jun 2023 10:26:05 +0800</pubDate><guid>http://www.nasay.cn/view.asp?id=2052</guid><description><![CDATA[<p>1、需要在主界面添加2个控件</p>

<p>分别是NotifyIcon控件和ContextMenuStrip控件</p>

<p><img alt="" src="data:image/png;base64,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" /></p>

<p>2、NotifyIcon 设置ico图标</p>

<p>3、 在控件NotifyIcon中绑定上ContextMenuStrip控件</p>

<p><img alt="" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAdoAAAFKCAYAAAC6gp7sAAAgAElEQVR4nOy9fXwcZ3nv/R1JtpyYWBQQFGOrqyxTFy+QmoQHE4OcdaRYtA7s2fK0tA4uT7Ksq/QpFqdw5gRDmqYN7jzlUBmeopPtYpoXcSilyxZ8ipwV2UTg4NC4goQ1NYOixTZJwUlobBxb1sucP2Znd/Z9V9qVVtL1/Xz2Y++83rOamd99vdz3paxfv97Egdvt5tSpUxSio6Oj6DpBEARBWKmU0semBW6LIAiCIKwoRGgFQRAEoY6I0AqCIAhCHRGhFQRBEIQ60lLtDq997Wvr0Q5BEARBWJI8++yzJdeLRSsIgiAIdUSEVhAEQRDqiAitIAiCINQREVpBEARBqCMitIIgCIJQRxpcaHdyYHSU0dED7HQuPTDKaIHlQgOh7mVodIi96hz23XmA0aG9zGVXoQry/kYqe4esZ+uAPFiCUDMaSmjVvUOMlnk5q3uH2L+t0iMWFuq6sfNAqgNQqcDY7Wv8l9vOA6OM5jRQ3TuUJ4iFli0NMiKT/jTMddhtK3Bf1bBTou69i92n7qGrq4s7jhTfbueBUYbm1IMShJVJ1eNo68nVro3AaceSI9zRdaTANnD0ntIvg4VFZe/Q59m9sZp9dnJgdD/c04V1iSp7hw6w88gdVH9Z1vld99fvNznyyFH273GhAkbqnDu6NsLGLnao92JYC7natZHTow9jGAa7u+6tT2PqSGPdV9kcPXqK3Xft5eHd96b+BtWSc58Y92b9ja52beR08ukatVYQBJsaWrQp62xoL2rassvt+WZbcE5Lc+eB0ZSlupHdn7etp2yLNLMNbNs/yujQAQ7k9fRL9P4LtSFtpRVvW2XXdpR7um5j6DSVobroOD3EofRL3eDe3XMR2QXi6SSnN3axI33JV+PaeJSjRzfiutpepuLqOM3ow3OTAaEMjxxiiN3cJdakICwp6uA67uIuh2934+5bU4JlWXDZXt9t7J+XWzfJI6OngY102Qqg7qBrI3D0fu7Ne98XakM1bSt2bXMQSSPJqY27uTXv4q2OQpaIO9yDlnvd2RnYyYFRy5q2Oh+2GzG745A53k4OjA6xd+dehpwu66IdCLu9DzN62iGqO29g29FHOJQ8zbYbUheh7qBr4ymSBqn43wHH336IvXsr7IDlxQZKd9Cyr83hgi/5u1VDqU6Y9fc6sNPRGavyN668bQb33jUEuz9fIsxQrK0F7hPH38juxG7c/XlGR4cYKnEPljtn7t+jeOe11D0hCMuH2gvtRhi9rYuutHXXgUvFejEDnB7itq4uurq6uOcowDb27FU5cof9/TRDt3XRVcB/l9nGcvF17b6XIw+PchrY2LXDeqHu6GIjcPSRArJnt+GoFYfq6kqdp0zbyl7bnDjCHfcctV56WS8gg3vvP5q+HqvZ2zh6/70Y6l7u2n2Ke+y2d93BEY5wR6o99m9isJMDo3tI3mZvdxujXc6X80Z274G7urrosttwwyPWtrcNQboD4cTg4dGMqO68YRtHHzmC8fAopzsslzJXu9h49JEiHY6N7HY5z3FXyuPgcKFn/fY21vqOodsyf7N7SHeCnk6eZqOt/jtvoOP0aTpSf5SdN2yz3NgFf7fCWH8P54u/9Pkz+93AI13271/Fb1xF26w/w73cNXSabfsLCXKptha6TzLYz9bpodvo6trN7mL3YAXn3H2vUeHvVuyeEITG4tChQ3mfaqi90J4exfIcGiQdNXBVV0dq9cPph/XpZKV+1hIY93L/UWCji6vtuCFHKaSzdhtyRbjithW5tjlz5A66urq4baiD/U439pFHOJp20+7khm2p6zGSnGIb+8slW+28gW22C350lNGUJdOR7hWcZuiu1EvzyCMc5TRDtg/bSHKqSAciI6qWizj5NJali9XWnTdsKxHjc57jYUbtn3fnDWzLcqFb8eCsazk9xF1O98SRQwyd3sYNO1Nt2naDZZXd0MHoXfdzqmtHuo2jDxuV/26kRMgpGGXOn9kvVyAr/I2raJuNce9dDJ3exv5cs7bCtlZEsXswl0LnrLgtRe4JQWgwbr311pLfy7FgWcdGSpk2ZgJ66cSmU8n5xfSsF/M2bthru42LWVUW23LeOvVsWyUY9+6mq+sejm7bn7I6j3BoCLp2qKh797AtfT1HuKOri66uu+CuMtnNDus829KYT0OTnNro4mp1B104Ox0bcV290PFZh9Cf7sCl7uSGjlEeNp4mSRc7djrbWMXvVu35581c2ma5kE9v28+BGyo5x1zaWuwenC+1+t0EYWGxxbVakYWFHN5z5BEsb+z+tGvOCsXl9pSdyVDVHbujq4uNzl5yDsa99+e1YfTAziraVkPUvQzlXWPmJWQ8PApdt3JrF5nrUfeydydYMeHbGHLGTJ0ceYSjOfHfnQfmEAvPG2d5hEeObuOGW13gsP6PPHKUbTfciistbFWQ11aVvXu25a3PSgDaeSu7HUL/8Ch03bWHbaeSGPb3PV2ZNhb73SoZ61v2/PNkrm2zXcjbqvmtqqPgPZhL3t9vJ3v3qvX/3QRhEZiLyMKCDu85wh1d5CQdHeUeR0zqyKEh9mzbTVWjZFLHfuTofvZv2winh0o8yEe44zYXQ5/PPUf5ttUc417uSg4xOro/c8Z7ujIJXMa93H9qlP0dQxxyLEveOkp6l6P3pIYGWfHTz+8fZXTPELftvjd1nZltj97TxR01aPaRR46yf/82jj5yh3MhR/fvZ9vRR+ZwjiPccc8NjO6323qaoaGj0OVY3wUHRj/P6G572VHu6crEC42HR2H3bo7ef8TxvStjXRf73Sqyasuff17Mo23GvXcx1PV50s0q29ac++Su8m3LuwfzOJJzrx3lnq4jgFHf300QlhDK+vXrTecCt9vNqVOFA5AdHR1MTU0tSMOqRd07xOd3b+T00G3zd5E2CDsPjLInuXyuR1h6yD0oCOV59tln6ejoKKqdDTUz1Nyxk6CW0RhOdS97th3lfnnBCYuF3IOCUBMaamaoueCcxKLw2Nmlh31NR+/patwJLIRljdyDglA7lrzQpjk9xG2NOndelRy5Q15uwuIi96Ag1I4lL7TyQhAEQRAamWUSoxUEQRCExkSEVhAEQRDqiAitIAiCINSRqmO0zz77bD3aIQiCIAjLErFoBUEQBKGONGzWsd/7eN2OHYm/rWHOKQiCICxvGlZoAb75bzfV/Jg3vuWhhjunIAiCsHxZ8a7jjo6OxW5CTVlu1yMIgrDUaWiL1uaFF14ou80rXvGKRTn3a17zGr7xjW/Q3d09r/Mkk0ne/e53MzY2VnSb5ubmeZ2jGHfeeSdPPfUUzzzzTHrZ+vXredOb3sTdd99dl3MKgiCsFGoqtPfdd1/esj/8wz8surwaHnzwwaLrbrnllqqOVS2lzn3y5Ene8573MDIyMi+xPX78OLte+wxf+7NPFFynj/yC7373u3M+fjE+/OEPMzo6yoYNG7j++uvTyw3D4MiRI3z4wx/mb/7mb2p+XkEQhJVCXSzaW2+9lUOHDqW/VyuqpXjssccIh8MEAoEsYag3hQr+Hjp0iNbWtXzgAx+Yl9ja1uyf/Vkwb50tsl/+8pdxuVxzansx7rzzTkZHR9myZUvW8rNnz3L27FnA+r3vvPNOsWwFQRDmSM1jtLYgOYXpvvvuy/s4l1fDd77znax/nbzwwgtZn4Xg7rvvBMgS22qxrdlrCyyvl8gCPPXUU2zYsCFr2dmzZzGMTAmk9vZ2nnrqqZqfWxAEYaVQc4v20KFDFVu01Vq6586dI5FIoGkauq5z7tw51q1bl7XNb0VPA/Avvo1Vtjyfb33rW3g8nizX9OrVq/O203UdTdP4wAc+wLve9a6qzlHMmq23yAI888wzeV4BZ5xWVVXa29t57LHH6nJ+QRCElUBdXMdOkYXysdtKBffw4cOAJWz29z/4gz+YT1OL8q1vfYsdO3Zw++2387nPfQ6A22+/nXA4nLVd7rX+7Gc/qyoxq5A1e/z4cf788LPp2HAymQSom+A6ueaaa/j+97/P+vXraW9vr/v5BEEQljs1FdpqLddqLdpvfetb6djsY489xgMPPFBWaF944YU5ZST/7d/+Lb/zO7/D9ddfnxZa2/qzxTY3bpsruuUoZM0eP34c31/8CwAejydr+3plHedyzTXXLMh5BEEQVgILMrynVhbt4OBg+v/XX399ltuzUEx2PnHaBx54IOt8zvN+7nOfK+hCrpZC1uy1117L6ei1edvVOuv4Yx/7GKOjo0xPT9PSUvw2OHv2LOvXr6/ZeQVBEFYaCyK0tbBoyw3hec0novzsL3xll82F3OE9tRDZUpnGTuoRq7VFdvPmzSVFFuDMmTN0dXXx1a9+tSbnFgRBWGksCYv2Fa94RVnr9Nprr+X4J6Lp76/5RJRrr72Wf5lDewHe//7389hjj+UlC9UyMWhsbIz3v//H6e8PvPNKrg1mu5HrKbJr165NL8+1bM+ePcuZM2fYsmWLDO0RBEGYBwsyYUWtYrSFJo6wLd3jx49z7bUZl+u1117L8ePHqzq+kz/+4z9mx44dQCY2a4/hrQUul4vm5mZeeuklkskkN954IwR/L71+IUX2woULnDhxgqmpqfSy9evX09XVJSIrCIIwT+oy13GhyR1qwS233MItt9yStiqTyTOAZfH+7C98WcJ6/PhxfvYXvjlPzfjOd76Thx9+OJ0IBdRMZHM5fvw4v/f6F9Ox2sUQ2a6uLh5//PH056tf/aqIrCAIQg2ouevYOWGFnYVbiykYbffx7bffzvXXX08yeYaPf/y/p4U0LbYp9/F8RNbmne9857z2r4TcWG29xs8+9thjJUX2k5/8ZM3OJQiCIGRY1AkrqqGQyNrLc8XW/v9SwJl5XM9JKi5evCgiKwiCsAgsqQkr7Fipy7UhHa/NzUauh8DaLmqofqxs6eNmrNl6zwR1xRVXcOHCBdauXSsiKwiCsIAsqQkr6l2lpxi29bx69Wr6+vpqeuyxsTF8vjHe/va313W6xe3bt/Poo49y8eJFrrjiCnbs2CExWEEQhAVgSdSjhcVzBVd73mq2tzOPAb773e/WdYrFP//zP8/6/sgjj9TtXIIgCEKGumQdC4IgCIJg0dAW7Y1veWhFnFMQBEFYvjSs0Ebib1uQ85w6dWrBz1lPnNcjCIIgLD7iOhYEQRCEOlK1RdvW1laPdgiCIAjCskQsWkEQBEGoIwsao33xxRcX8nSCsORpa2uT50YQGpxynl6xaAVBEAShjojQCoIgCEIdEaEVBEEQhDoyrxjtE088UXab6667bj6nqDF+QkYvw2qQiEcjFu1mxNeDnqjBoWt9vFrT6O0TBEFYpszbolVVteinUjxaDCPkdy5Bixk5y1YSfkKGgZH6LIWfwR8yiGmexW6GIAhCw1FT17FhGNXv5NEY6B7BF4w4lu2imyRJby9105iETo86H+vO6gykRXDex7PxEzJ00OwOi4/xvlAFv0NOe+rWvsJEgj5GugcQrRUEQchm0WO0/n0BJgZ1nO9/z65uGOlnMO6ldwlYczXFo9KZDHMw3e9IoPcEiZTapyFIoA9OENi30v5ggiAIpamJ0Nouztz/l8eD2plk/GT2sl3dMHI4QWQ4jrdPw+NYZ1lpxVyr5dY7T6MRM5yWYvY+aTeoRyOWdyw/ISNKwAVe3cCIaXjKHM/IWxdD00L550sYTLgCFNerQu3MbU+MWMn2lTh/3jlS28UyfwePFit8XSfHSXaqiFErCIKQoSZC64zJVhef3YTbNYHhNGf9+wgwwuEEEBkm7upmV86b26v3MmzHgrU4Xj3btVpufT6Wu7Yz7Eu3v0dPAB60fdCfd6wIQdVHOAlxTUXtybbICx1P1UDPElsXAfewtc4XhoDtdo0Q1OKWQBq57fYTMvoY92XcyiPdUUL+3Pb00FOyfaXOn+u67odub2Y3j8ZAYAItHYt3WNsJgwmXm00lf2tBEISVxeK6jj0qnclxnAatv9dLcuRwShgiDMdddOcobVxzvNwjBwkns13M5dbn4e/FmwzTnxfATKAHdbAtON1bcPeKjpfXjiRh2z+cOMxI0rF/JIiqqvjCneiGIynM34sXF4GobU1aVmunOhcbssj5U23Pcl0PxjO7JQwm8KIbsQLx2JOMJzuZU3MEQRCWKTUV2moyjYECFpCfXi+4AtG0a1L3giuwr35JUaVIuY0H6E9bfsnye5Ug101emoTeg6pqxL16xv2dDOPLye7uWdDxOhGCtqU7kHItp4W1gIdCEARhhTNvoTWy4pDZn8pwWED+XrzEHW5J1RIasi1Sr+OLRxsg4IozHKl8fR6RYeJZcVE/muaBTW5cDsvUs6sbVyWXlDregNPkc7rES+HRiOUFlVMCnddO8IcqyUiugrxzeND6sl3Hmh+sJC0f4aQLt91T8qh01rItgiAIy4B5TVgx/8koIgzHdXo3AQnLbUxcy8mwtbbRe/0QsczBOL0Yhp5aH0dTs7Nyy60v1I6gTyUWNbB2i6OpEeAg4b4oUSMAQDIed1i0CQ6PJInqBkZfGF9/zvFUCBlRUrumjlkoVppDQqd/POZovxVntbQ+t53WumCh9vToJdpXzW+RJByOQ3emfca+zPmJa6j2j7vJjSs+vAQypAVBEBYOZf369aZzgdvt5tSpUwU37ujomFclkYL7ejRiA9BfMGEnb2O0WBT3oEqw4Nu83HphTvhDGH3j+Er+jTxo1h9SZp6qIVK9RxAan7a2Ntra2opq56KPoyWh0z/STXQpTH+0IvCgxXKGIunOBLXC+ENRukf6RWQFQRByWNB6tMVI6D1UmUYl1I0Eev84McPA9g4nw76yCVeRoCouY0EQhAIsvutYEISiiOtYEBqfcq7jBbVoy1WhFwQhH3luBGFps6BCKz1zQagOsWgFofEp1xle/GQoQRAEQVjGiNAKgiAIQh0RoRUEQRCEOiJCKwgrHrssYvZ0nv5QoRKPgiBUS0MIrUeLpSrUWPVkjUKVYfzZNVHrjT+UW6NVEJYHVj3hQtWXsreptFhVMaGuG/5QplayPKLCEmDxhdajMdA9gs8xZ2I8PkFgYOFEtRCRoI+R7gF5kIVlxyZ3bmkMuyJTZk5we5u4llNzeFFJdcQr7wEIQkOw6ELr3xdgYjBnDt3hg4TJqX6z4CTQBycI7BOnmdBIpKzHmIYnbdnlel9sCzPf9esPGSlLNVXXOOQn1yLNbANe3cCIhQjleZpKeJ8KtSE9xWrxtlV2bXE01Ud4fvUqBWFBWWSh9aB2FqrRmkDvD0MgStEpkFO1Yu2HMeQocRcyYmj+zHrrXVLZSylr3clxkp3qolrWglCYbgYcll2mZrOfkKGTbfN50efl1h1neCQJuOjelXoaPLvodgHxwQLzWxdqQzVtK3ZtCfSeRrGuBaFyFlloSxQKT+j0h5N49UIvCA/aPui3a9Zq8ZztXAT6Uuu1uNUr7x1OF28n66XUx7jPrn3rY6TbIe55hekFoUFwwYjPumct6y5V19nfawlZMowv9XxocQAvfZqHSND+niTsU1ELlLnKbJNyHffoRA6PkARc3bvwkKnNHC9U6NluQ1zL1JUORsq2rey1CcISZXGF1qPSmRwnz6BNkdD7CSe96HlmbQI9qIMWsyzRvJhNknB/yh0dGSZOkvDB1AshYTCR9VJKudAMA8OIEnBBZ/qpPsl4Uh5yoQFJjnA4AZDAmMgs9qidqdWZaksnx2vgZ03oDMYBl5tNeNhlmbMU0lm7DbkiXHHbilybICxVFldoy1qMlgs56dUJ9ToWp9zGA/SnrdQ5v0ocvWv7k6lUU8LiFoQGJJFSJpc781TZiU0T87yRI8NxwEuvZruNh0u6cb292R3kerZNEBqZRU+GKusWsl3IXofVusmNKxmmPyWIthuraiLDxF0BnPlO/pDDBe1R6ZzLcQVhsYgMY3lj9XTegeXwybU+nclQ1R27s7sbl9NLlENCH8xrgxHyV9E2QVheLLLQRhiOu3CXCYJaLmTnblZWcjT1sA64J+Zo0UYI+sJ06plkqN5hR7LFJjeuMr12QWgsIgRVjXjWsjiaY4hO5OBcPUARhuPgcrkc7t0ibSjoZSrfNkFYjix+PVqPRmwA+ntyhvgsOh40q2EFsioFYWFotOo9Hi1GNOAiGfY5QiyCsLIpV4928V3HCZ3+kW6i1biwFgB/KEr3SL+IrCCksZOgkowUN2cFQchhQevRFiOh96AudiNyiARVcWcJQgrnJBaFx84KglCMxbdoBUFYOiTDWdOlCoJQngW1aMtVoRcEIZ9GeG6++dHruM7+0tbG4rdIEJYOCyq0jZTUIQhLgUZLhhIEIZ9ynWFxHQuCIAhCHRGhFQRBEIQ6IkIrCIIgCHVkCQutn1C6vJbz/4uERyNWtDanIAiCsFJZdKH1h5xFoFNFnxe7UUXIa2teUevqjhUTVRaqpkTBdX+ooZ8fQVipNMSEFc7p3Pwhg2jIKFgnc7HJTGLhJ2T0MpwzR2uPqi9Ow4QVRzw+QWBA43DDTV0qCEIui27R5lKT2pmCsNwZtgprDIhXRBAangYTWmsu1ayC0anas7abtjIvrZ9Qlns3N35bar2fkBFD00Lp9RW5eD0asfRxLPdeyJ99ntJtz942c846tFVYBli1mglES9xX87l3it2PgiBUS0MIrSsQTT3QUQKEyZS59KDtg367KLsWx6uXS3ryEzJ0OsO+TDF3DfSsxKlS6wFcBNzD6aLyBAbmlOTk1XsZrqjt+W2yXOkL11ZhCWLXai54X83n3vETMvoY96X2U32MdJcSdEEQStEQQpt0vgwG3UTTL4MEelAHLWYJse4tcyTA34vXURQesOrXJr30+itYb7UoU9Q6cZiROXqz45ojhpt3jjJtruRaathWYWli1Wr2oueq4HzuHX8vXrswvN0BdkGnKj04QZgLDZEMlUVkmLjeh/VMa8SiAQj7UNWE5Z6Nuud44CTjJ4GiReZT65cES6mtQn2xXMjdUZ1QTkn1wlR47yTD+CTRShBqQkNYtFn4e/EygZEANrlxOXrlnl3duMrtHxkm7spJEvHvI8AIhxMVrK8hXof56tEGCLjiDBdKpk61aV96cz+a5lnQtgpLGNuF7HV4fOZz7+Tdj+APLfI4dUFYwjSEResKRDEC9rc4mj1sJnKQcF+UaGplMh6nvGc0QlCFkJF7TLt3Xm597YjTi2Ho6W9aznCgrDb7VGJRA2vzOJoaARIL1lZhaZPQ+wl3R0nfJvO6z3PvR4hrKsHaN1sQVgTK+vXrTecCt9vNqVOnCm7c0dExr0oiK6cKiQctFsU9qNKAw4GFJYRU7xGExqetrY22trai2tl4rmNBEARBWEaI0AqCIAhCHWmIGO3yI4Heoy52IwRBEIQGYEGFtlwVekEQ8pHnRhCWNgsqtJLUIQjVIclQgtD4lOsMS4xWEARBEOqICK0gCIIg1BERWkEQBEGoI0tYaP2EsiryLPIUcR6NmBGTyjmCIAhCFos+vMcfMsgqytPAk5nntdUmrqEerNFxZCopQRCEZcWiCy1YZfJ6UoUD/CGDaMhoSMGJBNXUXMV+QkYvwzlzF/eoeuEdqzxO9ciUj4IgCI1Kw7mOT45LQVVBEARh+dBgQuthV7eLuLOWnEcjZtgFqA1y61sXxk/IsY+RF78ttd5PyIihaaH0+lglgVePRix9HA9azCDkzz5PZW3Pb599fo8Wy2qrP2RghEKEUoW5vbqBEdOQMLEgCELj0BBC6wpEU6ISJUCYg2md9aDtg35VRVVVVC2OVy+X9OQnZOh0hn3WPqqKqoGelThVaj2Ai4B72FrnC0NgYE5JTl69l+Gq2m63r49xX2o/1cdId5SQHxJ6D1rcS5/mAX8IvTOMLxgkqPoIJ61SZmqDxrcFQRBWKg0htEmn6A26iaZFL4Ee1EGLWUJcMIMoB38vXkexeMCqa5v00uuvYL3VIsK22icOMzJHb3Zcc8Re885Rov24CESNTOfDBZ2qpfSRoMZEIIqhdxLuF1EVBEFodBoiGSqLyDBxvQ9LVzRi0QCEfahqwnLPRt1zPHCS8ZPApjLrG4EGzrwWBEEQqqMhLNos/L14mcBIAJvcuBzWp2dXN65y+0eGibsCDDh9vf59BBjhcKKC9TXE6zBfPdoAAVec4XJZwan27XNYvv6QMy5rub0tj7bEYwVBEBqdhrBoXYEoRsD+Fkezh7tEDhLuixJNrUzG45T34kYIqhAyco9pW4jl1teOOL0Yhp7+plU0jCdC0KcSixrYu8Y1lSCp8bedYXzBBAn6CXdH00OhDo8kieoGRp9Yw4IgCI2Esn79etO5wO12c+rUqYIbd3R0zKuSyMqpQiLjWoXaINV7BKHxaWtro62trah2Np7rWBAEQRCWESK0giAIglBHGiJGu/xIoPeoi90IQRAEoQFYUKEtV4VeEIR85LkRhKXNggqtJHUIQnVIMpQgND7lOsMSoxUEQRCEOiJCKwiCIAh1RIRWEARBEOrIEs46dhZNr1UB9Xng0YhFuxnx9aDPZ1qmksep/jrbMXEzU3KbDeYsG5it6HhuxaS9wm2XApMojJmV9TfPoXBCaS65zVmaGJf+qyAIDhZdaP0hg6yiPA08oX5eW23iGurB+R1HefwofC/ELV95miseP47nhcv0m9mC1qoc57pbHudqLvC7zuUmbGG68gbMB7P8JkuN7dVsPI/rP0Ez5xSl8PKc49qivnb6EhdSf9sxWpic++kFQVgkFl1owSqT15My3/whIz1/b6MRCaopSzLbsmzHZCszfEK9B4D35liIm5VZ1jne0O0ffC2riliZXwLYHWYD0JW70nwSjsErgNfX6qKEBWMzMwWFemuxTpIJ/PJC0eOdoYkzSsZ6zrXOcy3wMzRxRqxtQVhwGkJonZwcT8JcK+HVgA1kRNLpUnW6TNeZ/8zm13+Bm4BPV3LQRbQCz9LEuFL65XqGJkd3f0UAACAASURBVM6Y+ZZWIcZp5mwBq2ypss40LQGsgHaFmrrh58sGZtmQ4/XIs85L3HvOv+U5FE6kRNp5z4grXBDmT4MJrYdd3S7igw5r1mPVpLXL48W1Sibq9xMydNLe2bNR/u76DzMKbDFnaOU3+C//z9vo+OEPrPU/T6I8/dPSh8x6Yb1Q2eVUyE9o4Vlbu9Z2sHlnB9/5p//k9R/qodM+/YUn2X+oifc++H/x3Vs+xSgAXXzkwSC/uaED83UbszwDVbN8tLM6FHiIVTU9XjE2M5Pl2bCx7sns5esUa/uWlhampy2Lt9Yi7mYGt+O0NzlXFhBopwA7O2fHlJbMMhFlQcijIYQ2q0xeMowvLaQetH3Qr6pWzNYfwtBD+COZZKBXM8tW/oONXxxhp3mJ65UOdrztR1xxo8qqn2QG+t+e+lh8D77wvZq1/5jSAms7uOaNa/jx4wbfNH+Vd+x5H295+QT/8Jn/zT8rTdD1ER4MwsFb/gefxXbnFUhucgWI6W6ORILs/ezRnCpAfna8bTM/poVj+AkZffyrr4f3JlK/VSxKyJCKQY3KCQonUtlCVYi2tY4JKwqIeK5451roGxQzO4xhFhb7SmhnlnazgNCb+ZHjMVqYVFLibCppN/Y5lKK/gyAsVxpCaLMsMX8IwwilarcmGAo+xTt+///ja7/xS5TnnoD+x7idC9xh/gPtKfctfAPuhDcAmAYcM+bcljFeza9t3cilH3yfL/+yGXg179jTy0+/9UX+V9Le5nf4rPHb+QL5oJsvqEEiykZa93yUFwZV7lBWW+tHY/zdJ6K8yT9ATVKj/b14ceGNGgQci5OqBxoylUyoB3miVc5CzxHrLWSsaacIb1Fm08sLWdzl2MJ0vlXs+J4lvGZT+ru4qoXlSEMIbdPkNFuZxm3O0v5PB3jxuSvR1lzg0xdTSSJf7Mva3koEqjzL9gRreflv/gbT408S2fAePvTWX/DX93+HsVR8aow30x87CP096InfJvRgH+O+HgYSABtp3fNB+Lcvcyx9xAbpkTdwhrawNBhz3MtFLWuHONuJW07L2c5fqCb7fR0mW01r27TL2iHEdob2MbM5LcLjNHN2xcY4hKXMggptK9aDuNmcwa1Y4zu3fvDVQCrbFoDvwaOVH/OnNHNaaee1H3gL//6Fb/JFtvMnQ328+pt30fWFlAnqD2H0jePr+TGJf7+KzV//HP71PgbTVnQ/AUbw1VixvL1+iFjmq0cbIOCKo9XKrRsZJq7r7PPraVexPxSC4CKOJRaWPcfsV0Yhy9mhgc6kQltQtyozWd9LYWdopzOyHSI8RgvjShPjZhNjSrMlyiLAQgNTN6G9ypzlrdOTbJq5zJtmLtM5M8V65wNWoSfq/Gs28rLODsw3vJH/fO55Jn/LxVdvH+R/4uNTxrtT7tt3Edrfy5G/P8qjPM6jt7yOkBHD+Jh9lDiaalt+EYIqhAxHXDhrfe2I04th6I5zVCuCCQ6PJInqBkZfGF+P0yUeIehTiUUN7FPENZVgTVouCPPDmRiVZykrmU63bQW3KgpbmKbdLD/Byham2WK/P1L/2olax0xLeMeUFrF+hYZBWb9+fZbkud1uTp06VXDjjo6OopVENs1c5s3Tl3nTzCRvnr5M5+xUxY2YRGFMaU5nMo7RwjlFyXJrLS08OUlMgjA3Vmr1HjeztDPLVnM6nYHtNmermpXsDE2MKS2MmU0cU1okCUuoG21tbbS1tRXVzjlbtK8yZ9k2dZHrpy+xbeoiVxXKRizACZqteEsxt490QgVhxTOeSorKsoZT7wY7n8OtzLKZmaKuaGuc8WVuBjCtDv0xpYWHzBYeVVpkKJKwYFQltB1Tk1wzeZ6dl1/izTPlJ4N7srmVJ1tW86Pm1Uw0r+Jbv7yYWSmCKgjCHDhGC8ec7w/FsoDd5owlvMpMwUzpVky2m1NsZwpMa8KOR5UWHqIlE3sWhDpQ1nXcCtxsXmY3UyUzCp9TmnmyZTVPpcT1iZY1edusRBeYIMyHleo6rgVbmGGLOc0WxXJBl3I7n6GJIVYzpKyWxCqhasq5josKbSuw25ykj8tFb9AnVq0h3nIFD6+6gmeapEcoCELjsn52mm1Tl7h++iLbpi4VHBt8XmliqPUqHmy9ivNlpi4VBCdVC+36U09zj3mpYPbf8Oq1PNayhodXXVH1jSg9c0GoDrFo68d2c5rtyjQ3m1N5xsRZmtBp5Sv2hDOCUIKqk6He9ctf8F/Nl7J6e2dp4hCr+c6GTiZKVBMRBEFYKjyqtPAoLejKGm42L9PPZHrsbzuzfIqLbDBnGVDyw2CCUA1ZJulblFn+9IVn0iJ7DoW7WcM7lKsYVFp5oVncw4IgLC8mga8oq3mHchVBruSs47XYzyTvNS8vXuOEZUGW0AaaplmdGqZzgmZuVK7ikNIqxaYFQVgRPKSs4h3KVXzd4TLulzegME+yTNS3OGKyf6JcWXZmlSeeeKLsCa677ro5Nk2oCx6NWLSbEV8Pc62oJwjLmUngI1zBzViW7ELVFxaWL1kWrdNlspvK3CWqqhb9VI6fkGFg2J+Qv4p9FxIPWszAMGJonkLrU9cR0yi4umZk/14N+3MJDY7zPgqRfRvZ93qhdcsfp7tYhvsI8yVLaL9mZqYou9Wc5D5eqqo3ZxhzKE/nD1nzAWsOkR7uJVZYyarAelHMT4QKHyOZhO5d+e3zaH2ZYvN1wypqn/m9fIz32S/CCq45odOjijUr5OKl13nfeHbR7Vq0xiwa6zD5FBe5h8zkOpJ5LMyXLKH9u9kWHr/iqvT37eYU3zbPW9l3dXGf+AnpXmsyfOecwJFgpj5tAzIxMgKBfXkWwK5uCIfj9T25R6UzGeZg+vdKoPdIxZ4Vg0cjZntN/KGUxZnysKS/G6mOaiEPTIFlySRJUtWm7K12deMiSTKZ24Bsb0qmQ+zw5uS1w9HudC/QbkdjWMvtmPSbl/i2eT7Lmj1BMzqSdSzMj7yBsB99tYvBnHT295qX+bZ5nr/+eZLeyy/lzWtsP1S5/y+LvxdvlmgU3CjbrZz1YPoJGTE0LffB9hMyogRc4NWdrtzCLwmPFss6rj9kYIRCRY4BGDqDcS99Tqvbv48AIxzOu/RSL6ZCbSf1UnJcp/N7wmDCFWBf3tup0DXbFq7jJZh17Jz14opeQnQzoNv+ExeBgRgxPeNPcQUG0DxW9SdwZTwwtqUaH3R4NSaYsJQ2fV/s6nZBcoKJrHNa3hSn18YViObcL852gSuvQ9pY3GRO8Vku8q/mOfqZTBe+Bysx6n3KWkmFEuZNwRkndFr5LeVleeWt3nbxPPpLz/HtF8/wuQtnafrifWxgNismW318thTWg90Z9mXcyhroWWLrIuAettb5whAYQPNECKo+wkmrdJzao5PAT8joY9yXcbmOdFsviYTeg2YLpz+E3hnGFwwWOEaGyHDc8RLxoPV5iQ/mltorfs7ibS/3m0QIanFLTLN+h0LXbOHVexlW86+B3PWqiqrF8eqNYWUIJXBNMJi6b5IALhcTmnWPha0FuDdB4vAIScDVvQsPtqUK8WFn73ac4ZEkafdxSozjgwcZd57T32uJbFxL37NJoFN13LQuGPE529GJWt+EhapYh8nNKXE9aZ4jxEvcnDN85wTN/CFrCXKlxGeFmlB0aqcTNPM+1vI+ZW1WqrvNtqmLNN/5Ub5tnudfzF9yJ5doin2D9koLzVZCyuLtd7qRIwcJJ53xpCRh2yROHGYkz9XlOBYuAlHbcrOsP/slEQlqTASiGHon4f4KatOm2mGJ8z4CFLDMy5yz4rbnnTuIqqr4wp3oFSSPxbXSruWs9Xm/r9CQJMc5CZaHA4A4lnYmMJxmaEJnMA643GyyLdX0thlO6oPEsdzHlhjnb+NRO63/eHXrfo4GcAEu9yZHu0Y4nCjQjkWiFavaT795ift4iSfNc3w2Ja65UzCO0UKQK/kt5WU8mltDVxDmQdm76ViqssXdyhr2vHwt28+9kFe5ZzMzbDZnoO8P+VesCbqPKS00ffE+Ns1c5mRzkWSCyDBxvY9dHp1EVSHZJOMnq9ne3i2Mr4hVVz0J9ME4gb5daG4vyZGDhY9b9JybCmxcZQv0HlTdsvpD/ojUvhUKEhmOo3u99GrjdFrmbIGOV4ThuI7X28dApwvig0QArcDxkmFfgRyKCntmnSoeIMEm3HVItnIzmy4ksMWcZrNzGtkCNsAZmnhIWcVXWCX1aoW6UfFkxWdR+Md1r+L9V72GG9s28JdXvILZnncV3HYDs7zXvEzznR/ly+f/g+//5ym+fP4/+CwX6TcvsZXplOUb4WAYAtGc4TL+kBWvjAwTdwUYKBQLrVYtU8dyxjb9IWdc1nJRWx7cCofnpI4Z8MYZLJS8Veacpcm43Gx3n/VFI5Znwc6x45EiKwlGGyDgyrdmhCVMZJg40NltJTiFiyRFRIbjgAuXK9e1bJFIWb2uQLRIzkQJbMvbFSBqGBg5sd5qWYfJVqbpMyf5FBf5qnmBpPki30wlb+42J7NF1sEYLdzNGm5UruIdylXczRoRWaGuzMk/8pzSxD+2vox//NcEV7Vt4M3Tl7luZpI3z0xy3dSlgvtsmrmcseFMyyI+h8IJ/Xoe+KebCL7nT/jgHdfChg7MH/3/qMEEkCCoQsiIYgTsneNoaiVWqZUIEtUNjD7Lqgz6VGJRA0NPHUlTCWIlP1lx2QQJ+gl3R4mGDNRgJOcYh3POEeFguA+vu5CFYK0vds7STdfpD3cTjRoEgGQ8TtK5bjxmDYmyfxFNTSW25F5zbnsLE6fXcbw4mipZzMsLy1rVvS5Ihot3UiPDxHUv3gJuY/s41v0coHpjNEJQ68VIJUrFNR/jfVYopRTtmLhTHrN2Bbakir5nFQEoE60ao4VjSgtjpuVpk7irsNCUrUfrpKOjo6JKIm+eucybpyd508xlNk1fpnN2quqGjdHCWUXhhNnEOM2cVRTrX3lIaogHLRbFPaiK27lBqVX1Ho8WIxpwFXH7Li6tWAK6zjTZzAxuxaQdq4ZstZyliRNKc1pUpaC7sBBUXb2nFjzZvJonc+Ky101f4ld/eZ4NiskWptlszmSl0ueyhWkw4SZ7gWNTW3jP0MQZU+EsTYwrTda/lXvDBWGFYCdBJRmpOuYyfzYwywZmaTct67RdATczaWHNo8J8ymNKC+M0c8ZUGFOaOUFzxlqV/rjQQCxYd++JljW8qNhJVK2gWHGWzTluoQ3mbNnJMdzM4M59GHO+20OTxmnmrJlyUytWHCbrgRSEZYw/ZJAe1po1dnb+2JYokHbnrlNIi+cWc6ZgcfVqBibYz639HB9TWqwOtrNDLY+y0OAsql/lHIqV1ZxOpW9NPzRbmKHdnM1yJVUiwja222krDvdTzgNuW8J2W06Y1v8nsXrINsvX/ZRA76nVmGehoUmG8VUQH9hCRhydXif7GQRKP4dzGN1ni6n9DJ6hiTNKE2O0ZE8WIYIqLFHqEqOtN68yZ3HNXGadabJp5jIvM2f5jdkpVs+aeUOPas2Tza1cbso88U80t6b/f15p4t+bV6W/P9/UwkTTchVpoVGxnw+b183OsH420+H89ZnLWWGbYgmMteJk82rONzUx0bSK55Umnmlq4adNzfJ8CMuKBY/RFqNWIv0iZM9YQxOWIwtITR+5Gas37owDbVFmacVMx4rmwptnJnHuWs1LKtdSBtJxZicnaOackr1sEoUxGYKwrLFDKU5eceVafv1C9nOzWZnNEsr53M/zwekROpYqSGJbo1n362zqk2Y69ZHJDYXlQVtbW8n1y7Y7mR4Xp8BDrMpe6dAwW5CBrGEDGxQz7R6r1YusFbPyTMoqXHD2y60QZ2li3Cztcyu1fy7LLfO7kLgVo5L7wHnfFKJcEmAeFy7kL6vh5GtO5hVKWT63hCDUnGUrtJXiHKh+rNjLwrHcmQAC5GVO5r5o88b81YENqfj1vKjTy3vFsci/Y64oOgUT8jtVJRMDRTwFoSaseKGtlklyevSFLGYnOS8rewC+k1wr15l4YlN0KISwLLCHrDmZROGHrVdy6VImPJEbVqhoSJsIpiAsKiK0C8xZFM7m/Oy5VZIKUuJl6WaWdjr4/U/9Ja/72i18ajSzrhbuzqxzLYCFvpAUipsXI9c6LITT/Vpw/yrj7G1r2nhRQpmCsKRpEKG1ZijKTMe2lKcBtK8lSdjXU2DcYqqmZw0LHIzTxDhtbL9mG1c81sIx50oFKGVxV8tKt44W+fqzxsU6iWuoMr2XIDQkiy+0Ho1YNGDV0ky/J/yEYhona1ZpZ75UP1VhMgnduzzoOWWJPFrfvCZTr5bcmHIuRScVKEBututSwc6IrWjbEt6FRpjoJBJUUx1QPyGjl+El2yEVhJXDIgutB23AEtlsAYsQ7FmsNtWGiZEROgP78OvOF6GHXd0QDscJdBff13IFZ9yzheKz+XHcBFu7Xw1ApWVtq2bpaSyQM2lJOczq/bSF4quFhm3lJiLJlKGCsDJYXKH17KKbMP1lu+Qpd2vqW2ZidD8ho4/x8ASBgDdnXan9bAtVA93hxk1Z17YHO66pBCOOYzgqASWKHtui7fFPEpt5gP/6+xu44otPW6L4hvXsvf8OHh85R8vw8/wVF7gAuM3VtPNSdb9dnugt/kQiK5WCU4IWo8R2uYI9RgvKpRe5ZF7KmkIUyA4PpClwX/fDQNTNoG35ejRizu9l7mNBEObP4grtJjcu59QTWUJnx2lTYupTCSbAfpmEDNsKdhFwD6KqwdT+A2iHe9AT5fYDr96Lptpl6zxo+6BfVS13tT+EoYfwR4IE1ZMc/NRHed3XbuFLj87SwyY+86EWpt66geP/OQus5fVXbyRpvgg8BinL8m3x/wLAPfb1/fAr8EN4O8Az8Kb0hVdfpaQafqy08Byv4tff9jou/eD7nLoAsJaOa97I1IUn+Krxat6x53285eUTxD7zdX5Y5DiFJtJodFrN0q7zXLYqxRPHqh4DWyW5gr2VaXBkHGef+gvw+i9wE/DHaYFO8Kp79nL1m5qJvX4jd9HEVZ27ueLxSa7AJD/AXP4ZEQRh/ix+jNZJQqdH1bHjTwD4e/HiwpuqzWqTtKuiOwtZJw4zkuyucD+Ia0EmmGErJpvNf2Pyg8f5wNt/n/eaBvz0MyhvT/Bp80U+zWPwp5ZoXgfA9+Az33Mc9UUYr5FF2f4aTPfrufTsD/j+qSt4fdfVJH94nG//HOAN3Pyht3L2Hx5g4OfNQBcfefDd/PTjH+Hvk25uio3kxJEdsWUOYuhergLanb9H2MeADq17PsoLgyp7UzNrLRsUeLRWt3mRPoZz0pP0sgKiXK8ZnTIC/SLEvwpx2In1YSIMu63O3j3AuR/ezapbruZ2LnBt5xm6P/OPdO8I8sE7rgfAdNnPiFi1glArFldoI8PE9T52eXQSpZ7rohm6mwpsnEF5/K/5+C2f40LqhWZXFvkSa9PxzJtyd/rO31d3DQU4/5qNTM0+y4+eg3Ov3UJ39zr+7Rdv4epffIlPfvs5zmz/bzz47p/y8Y/8L77dqxP/03OZ63vuEjz3g9SR3kXo832M+3oYOAuwhc0f+iDjD3+ZYz8H+FVeeNu1PLO2hTFeln8tuRT9HT0FNhYqpdCQnbkM2codY73ONHnLmlXpcbTOuPw6cx2beaHqtq6buQTHTvB64PVPx+AzMYCsSLEKfJUWJhXLfT1pmta/9veqzyoIK5tFtmgjHAz3EY3GoOBQGFJirLPPr6ctNX8oBEErxtR69nnWX5hmtzlLO6f57Y/1c+WPf0nfD34P/gn+ynmstDFRnfV5ltVM/eZbWfPTx4k+18w58w3c/KEefvH9z6XHrLoHPsvF/g8TwYM25MhQfvaNhO7U8RJHU0NElNXwqs2Y17TyFC2cGT5O/LPFr69avL1+iFh7erQBAq44WgSg9O8oLD55Y6wVeLzYOFrlPVlZx1bCl4vf/9Rf8hv3/T7feNK62be+7HVc88ZfYXLse7x8snLr2a4HnUkkm8xyXR9TWvKq7Sy36TkFoVYsuus4ofegHtaI5bp4w76UAEQI+lQe/fQ3+PFvnaLp+8f5+d3vJ8ksnza/AG//AgDvAeA0fPd0xeeebVb47qxljRwzX8s79ryPLZ6NmBs28h/f+x4X925OJ414ev+GaMDF+1NW4cDDv0IsOsEDqWPFNTvWm4vVmfC6h4sImnV9saiBoZc7Vnni9GLYB8oaj1zb8wiNhTVbWWostdrKwFOpFReex3PDl4g+6GIKSMbjtP5qksPvvpvvmSabX/129rzvLbz8hz9AOX+OmeOP0TJVPg5tz2aW9qI4dhmjhbOKJcInaOaM0lT1RB2CsJxY0DJ5lexrT/K+xZzBrVh1LyueiD8Hu6dtD7WwZ+2Zyww9grAYtLW1LUppSjvubMeabbf1fGYGG6PFsnxNhTFaGFdyCrgLwhKlra2tccrk5WJPprDVnGazMstmcyZ7KsAKEjztYQ+2mI7RwjmlQEk58WgJQsXYHdG8WHPqOdrCDOtMky1M067YCVmlRXgL02xJP9OWK/osTZxQmhkzrULvY8riTwoiCLVmQYW2HZPt5hTblRk2mzPZGZdlRNW2RsdoYdxUOJP6f1b4Sp5PQVgQxmjOz+h2iHC7OWt5phSrslSx7Op2ZtluzrIdsMX3DE2MKS0cM5t5VGkRq1dY8tRdaLdNXeK6mUm2TV1kk3nZWlhGVI8pLVZsx7SsVRFUQVg6WCLcnKlqlXpetzJtia5iWcLFxiVbZR8vczOkhfdRZVVaeMXiFZYadRHa3ssv4Z2+iPfyS2Xn0T3ZvJonW1o52bSKJ1tWc7J5dd42a1IfQViJtLW1LXYTasIPU5+YY1nn7DSbpi+zaXaKN89Mct3Upbz9NjDLbnOS3QAmPLFqDcMtVzK8+krOF6mUJAiNRM2EdsfURW6YeokdUxe5qkQR8jFaeJRmjiktlqU6C1wGmAEupj6CIMDiJUMtFN9LfSxaQWlNJ0NuV6bZbk7nddavm7rEdVOX+PjFF3hIWcXXzVU8pKyS8b3ColGuMzxvoX3P5Qv80aUXWT9bODN4omkVR1et4XjLGv7lpcvyMAjCHPjJT35S0+P92q/9Wk2PV0tO0MwJpZkhVoMC281ptiuppMmcWO9N5hQ3McVZs4lBVjOktMo7Rmg45iy0pQR2omkVX1u9luHVV/JMU+YUk1ye6+kEYcVTK3GstWjXm0eVFivpSrHcyDeZU7yXqSzRbWeWO7lEn3lZBFdoOKoOcLzKnOWB8z/j7peezxLZ55Rm/ueaNnzr1uNb91oOrVmXJbKCINSWV+6pbD7iSrdbCpyhiUNKK7+lvIx3KFdxN2sYdwzlswX3m+Z5ttRgHmlBqAVVKaE6dYmBc89kx2DbX8NM8P+l7Q8+wG2trdyWs891111Xg2YKgjBXnr/fg9JyBeb0PPMf/CEM3Vt+O+wSk+DRYkQDE44ZymqHLbqHaOUmcwqNyfQwog3M8iXzAkGu5NFK5p0WhDpSsUXbCvz1z5JZIjvzkY+z6blLuD/5KdQ3vhFVVbM+leAPGcQ0mdReEKqiqYUXj/whLa+5rqLPK/7giTmfyh8yMAyDUKqgVlxTUVUf4SRWoQrnc6/FSx0IwzAKfkKaRswwMEL+ObXxIWUVNyovYz9XpIf/tGIS4qW8uK4gLDQVd/U0LvGKmSkAzitN3P6yV3Pojz7E5P/4bHobwzAqFlhBEObOK2/5Ps/fX/8Oqj9koHtTc2b7Qxhee3mUgAsgQNSwZylPEg5PpP5vlWi0tnGhGzFUXw8lXw+HIRbViWkn51x8fkhZzTFauM+8wAZmacWkn0mCXDmn4wlCLajYonXON3znla/kyQLjXQVBWEZ4NPq8kAwfzHL7enUDvTNJkpR16wuTBEiOcNiwt0qg96hYBm4cTS1SnctJQmcwDq7APuZm11qM08T7lLXp79vnOFe6INSKqlzHNj9typ5H2Hb/5P5/bvgJOVxKGbdy9nLDCDkeRj8hI4amhQrsJwgrg1fuSdQ08cm/L4CLOIM5ChnXVNSeQSawRNeIWttpubWOU0Kd+oIWK+w2Nhwu48jBMEm89M3z+T1LE5MOF7IgLCYVC+0JJSOu/+3Sf2YJrzMmW018Nh8/IUOnM+xLH8dyIeUvVzXQs8TWRcA9bK3zhSEwgGitIMwVP71eIDnOyZw1Xt3AMOwayyqqqhHHi24YWclSllADeNFDm9B77FhugfiuXSQ5YTABuNyb5tzyViBEZla6scWvBiqscCoW2kEyruLrpi7xwPn/QHn6x7Vtjb8XbzJMf66PqdDyyEHCSS+9aaVNEj5oP6yHGUnWtmmCsNjYFqvTai20bM2v/25B63ZOFu+EQe4eVjKUQ1yNPsZ9uclQHtROSCaTQJzweB8hv+WVimm2iHYzMI8EqEK0pxKgtptT6WXOd5cgLAaVW7Q0c+/LfzX9fdPMZVpuup5PcTFd2m7hE6GSjOd2twVhmfL8/R7M6UtZSVC/+PJ2pl84mbXs0o++zPP3e3j+fk9aXJ+/38MvvnJj9QlUnSqF94gQTFuzLgJRg2y9TGCMhBkcsb4Zeg8H1T68JBk5bD+0IwyGk+Dtm7f3qR2TO7nEt83zWSI7QCsPKavmd3BBmCdVTVgx1NbOnVe+Mmsi7/eal/m2eZ6n/e/ix1/7albcpWoiw8RdAfalH1g/muZJLx9wPo3+fQQY4fDyGYsvCCV55Z4EL3zxWl65J8F/ft0HwK/87qO8eNjPK/ckmJz4Rlpc7e1tYX3Ztr9i9qX/qMKijTAcB1xu8p24znyJjDUbzBkoG9F1h9vZw65uF8QHs5KiTh4eIYmL7l2pZ9uj0gkkK+xBb0hNUPFt8zy3mpNZ8VidNQwoUo5EWHyqDl788+q1PNnSyocu/oIdU5kB8E3/9CWa/ulLPNPUkpp+cS0TVc8M1kthPwAAE4lJREFUFSHoU4lFDQwdrGzFCJAgqELIiJIeSUAcTdXz3FqCsJyxhXLmF1ZH1imsv/zWR7KW2SJrf29138y5kWDF54oMx9G9VngmkiWiEYKqvSAlumDFXAeLHMyzC0tnI+C0kRM6g/EAevcuPHoCdnXjIkm4RA+6HTM9DeMW8jOKT9DMfuUKq1yfIDQAyvr167NS8txuN6dOnSq4cUdHR1YlkU0zl/mjSy9mCa6Tk82ria+6gqOrruDJ5tXLugqJINQDu3rPT37yk6y5jl+5J8HML39K88telxZUp7Wau8zpMs49VnHssbCOcbTznBnKGpdrbZMM+3LGy1pJj964lkmOSuFmlu3mFFuVGW5yuIadnKBZXMXCotDW1kZbW1tR7ZyX0Nq8eeYy7508X7JE3iQKjyotqeLNqxivfpplQVhxFBPa+VDdsXLEtiYtKERKZJNhfD06LzLLVnOa7coMW81p2ileevPrymq+braIwAqLxoIIrU0r4L38EjunLhS1cm3OoTCmtDBmNmVq05a5GEFYaTiFtpY0Wpm8rUyzxZxhi2LVoi0lrADHlBa+kqpDa0+5KAiLRTmhrekAs0lgePWVDK++kleZs2ybusj101aR5leZ2fONrsNkuznFdgDTktgTNKfF94zSxDEZ/yYIQOMJ43zYwgxuc4bNyqwlrs44a5G5Jc6h8KiyKuURa+EMTYi+CkuFuinZc0oT/7x6Lf+82poKrXN2mm1TF7l2+hJvnZ4s6GLezAybzRl2Q/qBm2haxTPNLTzVvJqTzav5aVMzJ2X6R2EF0dbWtthNmBOds9N0zkyxaeYyvz5zmc6ZaTpnHfHVMhM2PbFqDUeb1/DEqjV5U74uzV9EWKksmMk40dTC9y7NAquB1WxWLFHdosyyxZwuWmGjc3aKztkptuW4osdoYVKBY2Yz51A4oTRzhiarpysIywTbddyotGPiZga3OUs7s2xRZllnmgWzgUtxhqa0N+uE0mx5s6aBaRMmLwLzLPEnCHWkXGd40XyzJ2jmhNLMVyDtArLjNJuVWTaYsyUf1i1Mg2ntA2T1jk/QzDlF4ZhppfcfS9WjlDiwIFTPFmZoxWSzOcM6zNJiWsG0wuM0c0ZpYsxsYowWxpTmTJxV3MHCMqShgqDHaEmLov3AbWaGDeas5VZWZnGbs+nizsXYzEyOCGfLqy3E4zRz1rR602cUaxJyGXsnrCTcWJboOtNkMzO0KkpaQLcWq3pT4Rz9Z2liXGnimNmcfsay8i5EVIUVQkMJbSFsy/chUqn7DgFeh8lWc5p1SkaQN5TJVrT3zRJiyHt5jNPMWcU6mW0Z2y8LQBK1hIbGvrfbTTPdMd2szLIOMy2qBamy0E1W2MZUMt4kEVRBSLNk1eIE2W5hIP1A2730LeYMrW/Yxb6rnuQH//o0v9bUwrqZSxUd34o7Wf8vJciQLcrnUDhhZuLEzvZJDFmolqvMWd7guP9s9y3ABsVMdyxLiqfNHKvFZYVeTLOwmIIIqiAUYZGF1h4MnyTsK1QYOnsQe6XTLY7TxDjW+Fw2/9/8cd9b+O89Ook3aMSi3Tz+2zdy+EezbLn5Y3zU/Ef+/vAP2bz2dVzzxl9h9eP/RrNZ3RvJKcoANzlXmsWjwk4LGVKuNlPJ/q7kC7PEmpcGWwvEMHO9Lu0KWaEQpwUKQKlEqBqUWbVF1LZI7XuuaBhFxFQQqqYhLNpkErp3edAT2VLq0fqobMK3Ckno9Kg6Vi2FJtbf/EHGx/83dx9eAxeeh8efB9aBYo3ztS0EO6PSGb+qyIIowwaspK+SVBkPK4Tt+i6EbZ1Uwgmal9TkALZno6JtzdLb5gqik0pDFgWpQ01yZwfuBM2cM62Z2cYURwikkGel4J/W6gy7B/OLBgiCUBkNIbQTIyN0Bvbh151TvHnY1Q3hcJxA98K36RwZ19ixrBdQquR9zkvJab04X9q5L+gt5kxWhZFa0c4s7UVEu5BllUUdXvbLjkX4jSZReGpVK9PT1t/P8ni8hnfs6WX6yAMM/NwSzq6PPMjO6QPcOFCk4s3S6RsJwrKkIYQWQ2cwbtCneYjY/uNUGTyf4c4R2pQ7Of09dw7W3PVActz616MRi7oZVINgT27ujWIE4mi+cfpS6yIFjpOZAN3u4WugZ9za57U40YCrSJscOF56rZA1RCLXbehWzIJWVtFsUKFhyA0LpJc5QgN2IpFNIXdt29qccbTKRlr3fBD+7csc+7m1aP2vb6NnvIWC92Y/DETdDKoHUWNRukcyE/l7tBjR7hErLOPRiEUDpO9gTSUYcTwDuoHRZ4dwij0bgiAUojGEllRJLt22aj1ofV7ig0EShBxbWQ94Z9iHmhbkEIYRAjVIJLUeTcWu4uUPGeidBc5n1d2jbzz1kvBo9GWdx6qzGUyA/QILGRn3mVfvRVNVgmAJeGACTe2patL1SSiQnVnBxOgFLJQNFHZftpqUHo+szFZsYTsTcZYChcSu5LZmcdMvVxTz9q00ya3W1qVHo8+bZOSg1QmE/HvTIoE+GCfQZ5WjS6Tqw8YHdRJ40PZBv6paeRD+EIYewh8JElRP5riOyz8bgiBk0zBCS+Qg4b6oZdUa+wgQxhcB/I5t/L1WL93Ze07t1+uHCKn1jgc+MhxH76M6/L14ceGNGgQci5NqptRYXHNYrAmDCQLoRgy1YFJX/SkVd3u0Vn/m5e6CXDLX5yKQvjfzEwmz7k0nkWHieh+7PDoJdtHtijMYAUigBxN4tBiGwytTkJLPhli1glCIxhFaR49bc3tJjhys4rFNMn4S2FTD5hTNdPYU2NguhO1BixkYRbOoBaEWzPX+ijAc1+nb5eEw3RDutwQ55TYm7ENVE+kQS/HTVzcKQBBWOo01qDMyTNwVIOCNM1joLZJaP6A5xC4Vyz2cyKzfl7aCLRf0XNuxz2FN+0OhLOM6C4+G5gdIoPf4CCdduGsp+oJQIyIHw9C9j33dMHI49YxtcuNKhum3Y7e7utOx2vwDVPlsCILQSBYtQISD4T687uEisc4IVmg1ipH2W8XRVLt3HSGo9WLoBoYOkCQcjkPVWcsRgj6VWNQ+Tio5pNjmCR1jX2Zb4lo6RiwIDUXiMCNECUxoqRgr6fBLNPVQJeNxkpkdODySJOpIhqrq2RAEobaF38vRyFVIBKERafTqPYIglC/83liuY0EQBEFYZojQCoIgCEIdEaEVBEEQhDqyoMlQ5arQC4KQjzw3grC0WVChlaQOQagOSYYShManXGdYXMeCIAiCUEdEaAVBEAShjojQCoIgCEIdEaEVBEEQhDqyqFMw+u2asLnENVSpuSUIgiAsAxZVaCNB1VFkvZfhYsXSBUEQBGGJIq5jQRAEQagjjS20Ho2YYWCkPqGsWlx+Qul1MTQthBHTClaLFQRBEITFooGF1oO2D/pVFVVVUbU4Xt2ue+knZOigpdap/dA9h7qzgiAIglBnGlhoE+hBHbSYZbU6s6b8vXiTYQ5GHNsOxhejkYIgCIJQksYV2pTbeIB+y2r1hR3FqAVBEARhadC4QrvJjSsZpl9PAODZ1Y3LXhcZJu4KsC8ds/Wg9YnrWBAEQWg8GldoIwcJEyCaSngacE84LNoIQV+YTt1OhhqAEXEdC4IgCI2Hsn79etO5wO12c+rUqYIbd3R0zKuSSF2rkPhDGH3j+Hp0EvU7iyAsKFK9RxAan7a2Ntra2opqZ+NatCXxoMXsDGQAPyHdS3LksIisIAiC0FAs6sxQcyeB3j9OzDDQU0uSYR89usisIAiC0FgsUaEFEjo9ql5+O0EQBEFYRBZUaMtVoRcEIR95bgRhabN0LVpBWCFIMpQgNDblOsNLNBlKEARBEJYGLc8880zWArfbvUhNEQRBEITlh1i0giAIglBHRGgFYQnhsYtsVPCJaR57J2LO71nHiqFVWlvSH3Ic3zmOHfz/p707dm1b3cM4/ly4u/cMjXKCaEFr54JLDB48GHUNhYIwZGlLl3c4S5YzaDFpl4AwXAhdKzR4MNeh4p7ZqyEH0WP1L7l3kBzLjh2nN31j1/l+IJDIcuxFfvy+v1fvL5odK37/gf8L7Lh/7u3tbezFW92RTl8seeDPUz3/0H/w9wNsu3HYkBsWIZkEjpQauZ14zZP6uswDBcGZTL+hW28394yGSTDbV3xOrl7bLZ/vyfMkjYvnFFuN1xVmWXmuoyDJFMw9d81rAzvqQbdgXK2l7uilvj7/oPvF6zO9/fJZzr+ei5zGrpi75m4NwpnpBi5+lClc7LeR99RuFC0ok8BZevxM74sNYPxIWVivbAjjyQwTHZ67muZ78RqpjNtRXBxQFtaVmtk5wC5btwUjt/cAvxD/XSVkl41myyCeijuuVmVdMbO7ONL0ZI4cOU6iKHPViTsyzUxh8E5+2JGiRIEjqelLcSz5URnkxWi2uoVMPcyUzbZuYx9yPFpbXqNtqTsaaVT+fHn7TJL07O0XjUZdtaZndUcadbvqjj7r9b704nSk0Ze3era5Nw5YEXdcuW5bvVxSPbxZm10c7frRrF5a1lgXa7XzxgobbfXyXAeuL8mTeyAp/6a/5KtZl1LjXge835wOl1MZ1y16R5uik1Zqyr9dVy4hi0dsi6eOW+qO3ig/fqVPV9LitHCrO9Kb/Fiv/u5o9CbX8atPumLqGDto3TVXTN1WR6aeTNRSvxNqPB3h9tp6rzMlgdTrTRQEN/s3p8bVoLlkqnmVckTtLU5BrzydqWTspl936rj1Ui+0rxefR3pdOfz9t2eSrtT/cKqXo88a6bsujj/oalPvE3hIZf3zWt5TbyJJjoIzo/75oZLy8SPTVyPMNJFUP3yqp3KkvKd+dqigOmVcmW6em2pefK1bFzRNa7RFDTcQU8XA1PYGrSR9vyhHqgAkSXFHbqzZoqhJpuk6XzmBklALtduxBmmo+kFTTUmaZBrrDpvSXC9oauvbSRmc54dKkqFUCdvZaNZZqNEGSrJg/n/eZYU0sIO2t0bb/6o/91+r05odanWrddlT7V8c6/hCev0H9Vg8Hp4ZlrXYVOY6uFKZdk+5JNXDuTps3HHlNgaSUzwWNW///36ULV81HHfkmomCZFbnHYeNsg5b1o2VK88l5anSXEW4Tuu0hCweqe0NWvX14fhC+6ezxVAvvxY13FZ3pNP9C/3+6UpXn37XhV7rc7cl6Ur//s93FkNhZ/lRVo4gc/XaHcXlAiVJZetIo1SSEyTKIr/cPCJTloWql6HXGUjT+1wXF1DNbtVZUU+NO3Jdo0mQKBuaYuWyHynLEgUTI9d9r0tJ0jd9bLTVOwjZvAKP3pYshgKwyvw158uYvxQu3iP7I9OyfqQsPLhRo50sXax0e831xj20y86fTnMzdYwdtW4xFEELbDmuOWC7rQvaLZ46BgDg10fQAgBg0Xbf3gNAtVpt028BwD08aNBSawJ+TK1W47oBtty6L8NMHQMAYBFBCwCARQQtAAAWbXfQekbDdbvKzJ3jyQwzRf6qk31FWaSVDwMA8JNtNGj9KFO2JBU9M5xt7wagVHyRvNGDdvrDNQNspY3e3hMPUoVhU77iWWsueWodOUrPQ43HUsMNb/kPKvd3XXMOAAAbstmp43igVHU1q4Nar6UjJ9WALVGBBWOFjbITTtk8oOgDWx5rlI3es6zSUCCSXzlWmI6Mq2UUX1FldDykCwDw02y4RhtrkEr1StJ6rSM56aAY4XpGw8qHgWeGlamy8vjCOYX5D43ba7Z8uGAHHZzopL7+tIKvKAtVPd0JkluuGwA/YuOLoeKPPeX1ZhmU5bTxsuGsZ3QWTGbf3q+7hdxUD5saTM8zqerhsgVQvqLsRN/a7nU/zcsjPlywIxzpsn37dXLNbxYhO+0dW/a1PXD54gn8DBsPWo37uszL6ePbpo3HmSaqK7xDb8vUVD5c4o/q5QvT01L54VLpyZklChw+XLAj0vOiBd4deO5B8Us9nOtP6xw+tfb2gMdkC/Y6Hqt/mStp+vLcIznp+Ypv4LE6bqzr+pKTF/007/PS+fIem8BOOnDlSRrrqQ6dmw/nvbYad01nAHe2+RGtpHH/Unn9RGdHUu/jiokuz8j4UrEgpK1e7mjVF+65mq85U7BslBwPlDqB3lVGun7EPbbYQeNME0lyAiVZpmyhHjsOz5WqqMveWAMB4N62ImiL6WNHji7VX/WFehwqa1ameSdGnRWZnKp5/YGRBBOZpXWqWJ12TwfhbDFUc3CHehbwy4nVMen1X6lpq5cvPF7WZQH8fP/Y29v7b/XA4eHhyi7xT548uVcnEbqQAD+G7j3A9qvVaqrVaiuzcztGtAAA7CiCFgAAiwhaAAAsetDbe9Z1oQdwE9cN8Gv7v4KWxRkAABTWfRlm6hgAAIsIWgAALCJoAQCwiKAFAMAighYAAIsIWgAALCJoAQCwiKAFAMAighYAAIsIWgAALCJoAQCwiKAFAMAighYAAIsIWgAALCJoAQCwiKAFAMAighYAAIsIWgAALCJoAQCwiKAFAMAighYAAIsIWgAALCJoAQCwiKAFAMAighYAAIsIWgAALCJoAQCwiKAFAMAighYAAIsIWgAALCJoAQCwiKAFAMAighYAAIsIWgAALCJoAQCwiKAFAMAighYAAIsIWgAALCJoAQCwiKAFAMAighYAAIsIWgAALCJoAQCwiKAFAMCi/wHKTGaQBaoVdAAAAABJRU5ErkJggg==" /></p>

<p>当运行程序时，就可以看见图标显示在右下角了</p>

<p><img alt="" src="data:image/png;base64,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" /></p>

<p>以下代码根据实际需要添加</p>

<p>&nbsp;</p>

<pre class="prettyprint">
      
&nbsp;private void Form1_Load(object sender, EventArgs e)
&nbsp; &nbsp; &nbsp; &nbsp; {

&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ToolStripMenuItem itm2 = new ToolStripMenuItem();
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; itm2.Name = &quot;showthis&quot;;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; itm2.Text = &quot;显示窗体&quot;;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; itm2.Click += ShowthisDialog;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; myMenu.Items.Add(itm2);

｝

 private void notifyIcon1_MouseDoubleClick(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left)
            {
                if (this.WindowState == FormWindowState.Normal)
                {
                    this.WindowState = FormWindowState.Minimized;
                    this.Hide();

                }
                else if (this.WindowState == FormWindowState.Minimized)
                {
                    this.Show();
                    this.WindowState = FormWindowState.Normal;
                    this.Activate();
                }
            }
            else if (e.Button == MouseButtons.Right)
            {

                myMenu.Show();

            }
        }

        private void exitPram_Click(object sender, EventArgs e)
        {
            DialogResult result = MessageBox.Show(&quot;您确认要关闭吗？关闭后将无法接收和处理数据&quot;, &quot;警告&quot;, MessageBoxButtons.YesNo, MessageBoxIcon.Warning);
            if (result == DialogResult.Yes)
            {
                Application.Exit();
                // this.notifyIcon1.Dispose();
            }
        }

#region 显示隐藏窗体
        private void ShowthisDialog(object sender, EventArgs e)
        {
            this.Show();

            if (myMenu.Items.ContainsKey(&quot;showthis&quot;))
            {
                myMenu.Items.RemoveByKey(&quot;showthis&quot;);
            }
            ToolStripMenuItem itm1 = new ToolStripMenuItem();
            itm1.Name = &quot;hidethis&quot;;
            itm1.Text = &quot;隐藏窗体&quot;;
            itm1.Click += HidethisDialog;
            myMenu.Items.Add(itm1);
            //this.Hide(); 
        }
        private void HidethisDialog(object sender, EventArgs e)
        {
            this.Hide();
            if (myMenu.Items.ContainsKey(&quot;hidethis&quot;))
            {
                myMenu.Items.RemoveByKey(&quot;hidethis&quot;);
            }
            ToolStripMenuItem itm2 = new ToolStripMenuItem();
            itm2.Name = &quot;showthis&quot;;
            itm2.Text = &quot;显示窗体&quot;;
            itm2.Click += ShowthisDialog;
            myMenu.Items.Add(itm2);
        }
        private void startFrm_Shown(object sender, EventArgs e)
        {
            // textBox1.Text += &quot;Form1_Shown&quot; + System.Environment.NewLine;
            this.Hide();
        }
        #endregion</pre>

<p>&nbsp;</p>

<p>&nbsp;</p>

<p>&nbsp;</p>
]]></description><category>C#笔记</category><comments>http://www.nasay.cn/view.asp?id=2052#comment</comments><wfw:commentRss>http://www.nasay.cn/feed.asp?cmt=2052</wfw:commentRss></item><item><title>C#异常：“集合已被修改，可能无法操作枚举类型”</title><author>532898411@qq.com (ningjian)</author><link>http://www.nasay.cn/view.asp?id=1052</link><pubDate>Mon, 08 May 2023 15:19:32 +0800</pubDate><guid>http://www.nasay.cn/view.asp?id=1052</guid><description><![CDATA[<p>异常原因<br />
一般产生于未安全处理集合一类的循环导致，常见于foreach循环的时候，表达式被改变导致。</p>

<p>简单的讲就是：被遍历的集合在遍历过程中，被其他线程或者地方改变</p>

<p>&nbsp;</p>

<p>public void ForeachDic()&nbsp; &nbsp; &nbsp;<br />
{&nbsp; &nbsp; &nbsp;<br />
&nbsp; &nbsp; &nbsp;Dictionary&lt;String, Int32&gt; dic = new Dictionary&lt;String, Int32&gt;();&nbsp; &nbsp;<br />
&nbsp; &nbsp; &nbsp;dic.Add(&quot;1&quot;, 10);&nbsp; &nbsp; &nbsp;<br />
&nbsp; &nbsp; &nbsp;dic.Add(&quot;2&quot;, 20);&nbsp; &nbsp; &nbsp;<br />
&nbsp; &nbsp; &nbsp;dic.Add(&quot;3&quot;, 30);&nbsp; &nbsp; &nbsp;<br />
&nbsp; &nbsp; &nbsp;foreach (KeyValuePair&lt;String, Int32&gt; kvp in dic)&nbsp; &nbsp; &nbsp;<br />
&nbsp; &nbsp; &nbsp;{&nbsp; &nbsp; &nbsp;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;Console.WriteLine(String.Format(&quot;Key:{0}; Value:{1}&quot;, kvp.Key, kvp.Value));&nbsp; &nbsp; &nbsp;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;dic[kvp.Key] = 100;//此操作会报错:集合已修改;可能无法执行枚举操作。&nbsp; &nbsp;&nbsp;<br />
&nbsp; &nbsp; &nbsp;}&nbsp; &nbsp; &nbsp;<br />
}<br />
&nbsp;&nbsp;</p>

<pre>

&nbsp;</pre>

<p>解决办法<br />
1、通常情况下，不要在枚举集合的操作，比如Foreach中去执行修改集合的操作，如果需要修改，应先使用ToArray()方法</p>

<p>&nbsp;</p>

<p>public void ForeachDic()&nbsp; &nbsp; &nbsp;<br />
{&nbsp; &nbsp; &nbsp;<br />
&nbsp; &nbsp; &nbsp;Dictionary&lt;String, Int32&gt; dic = new Dictionary&lt;String, Int32&gt;();&nbsp; &nbsp;<br />
&nbsp; &nbsp; &nbsp;dic.Add(&quot;1&quot;, 10);&nbsp; &nbsp; &nbsp;<br />
&nbsp; &nbsp; &nbsp;dic.Add(&quot;2&quot;, 20);&nbsp; &nbsp; &nbsp;<br />
&nbsp; &nbsp; &nbsp;dic.Add(&quot;3&quot;, 30);&nbsp; &nbsp; &nbsp;<br />
&nbsp; &nbsp; &nbsp;foreach (KeyValuePair&lt;String, Int32&gt; kvp in dic.ToArray())&nbsp; &nbsp; &nbsp;<br />
&nbsp; &nbsp; &nbsp;{&nbsp; &nbsp; &nbsp;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;Console.WriteLine(String.Format(&quot;Key:{0}; Value:{1}&quot;, kvp.Key, kvp.Value));&nbsp; &nbsp; &nbsp;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;dic[kvp.Key] = 100;<br />
&nbsp; &nbsp; &nbsp;}&nbsp; &nbsp; &nbsp;<br />
}</p>

<p>2、把foreach改为 for(int i =0) 后，问题解决</p>

<pre>
private void ForeachDic()     
{     
     Dictionary&lt;String, Int32&gt; dic = new Dictionary&lt;String, Int32&gt;();   
     dic.Add(&quot;1&quot;, 10);     
     dic.Add(&quot;2&quot;, 20);     
     dic.Add(&quot;3&quot;, 30);    
     String[] keyArr = dic.Keys.ToArray&lt;String&gt;();     
     for (int i = 0; i &lt; keyArr.Length; i++)     
     {     
         dic[keyStr[i]] = dic[keyStr[i]] + 1;     
      }     
} </pre>

<p>foreach是取只读的，在取的时候数据不能变（包括修改，删除，添加等）。要避免这个问题，就应该使用for循环</p>
]]></description><category>C#笔记</category><comments>http://www.nasay.cn/view.asp?id=1052#comment</comments><wfw:commentRss>http://www.nasay.cn/feed.asp?cmt=1052</wfw:commentRss></item><item><title>C# Byte[]使用总结</title><author>532898411@qq.com (ningjian)</author><link>http://www.nasay.cn/view.asp?id=55</link><pubDate>Mon, 03 Apr 2023 08:22:58 +0800</pubDate><guid>http://www.nasay.cn/view.asp?id=55</guid><description><![CDATA[<p><strong>C# Byte[] string转换</strong></p>

<p>01，C# string类型转成byte[]：</p>

<p>Byte[] byteArray = System.Text.Encoding.Default.GetBytes ( str );</p>

<p>02， C# byte[]转成string：</p>

<p>stringstr = System.Text.Encoding.Default.GetString ( byteArray );</p>

<p>03，C# string类型转成ASCIIbyte[]：</p>

<p>（&quot;01&quot;转成byte[] =newbyte[]{0x30,0x31}）</p>

<p>byte[] byteArray = System.Text.Encoding.ASCII.GetBytes ( str );</p>

<p>04，C# ASCIIbyte[]转成string：</p>

<p>（byte[] = new byte[]{ 0x30, 0x31} 转成&quot;01&quot;）</p>

<p>string str = System.Text.Encoding.ASCII.GetString ( byteArray );</p>

<p>05， C# byte[]转16进制格式string：</p>

<p>new byte[]{ 0x30, 0x31}转成&quot;3031&quot;:</p>

<p>public static string ToHexString ( byte[] bytes ) // 0xae00cf =&gt; &quot;AE00CF &quot;</p>

<p>{</p>

<p>string hexString = string.Empty;</p>

<p>if ( bytes != null )</p>

<p>{&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;</p>

<p>StringBuilder strB = new StringBuilder ();</p>

<p>for ( int i = 0; i &lt; bytes.Length; i++ )</p>

<p>{&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;</p>

<p>strB.Append ( bytes[i].ToString ( &quot;X2&quot; ) );&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;</p>

<p>}&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;</p>

<p>hexString = strB.ToString ();&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;</p>

<p>}return hexString;&nbsp; &nbsp; &nbsp; &nbsp;</p>

<p>}</p>

<p>&nbsp;</p>

<p>C# 常见的字节数组 byte[]截取</p>

<p>C# byte[]数组截取：</p>

<p>byte[] data= new byte[]{1,2,3,4,5,6,7,8,9};</p>

<p>截取2位：BitConverter.ToInt16(data,3)；//3表示从第三个位置开始</p>

<p>截取4位：BitConverter.ToInt32(data,3);</p>

<p>截取8位：BitConverter.ToInt64(data,3);</p>

<p>如果截取的数量不规则：data.Skip(5).Take(3).ToArray()；//表示从第五个位置截取三个字节</p>

<p>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;data.Skip(4).ToArray();&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; //表示从第四个位置截取到最后</p>

<p>C# byte[]数组复制</p>

<p>(1)</p>

<p>byte[] data = new byte[]{0,1,2,3,4,5,6,7,8,9};</p>

<p>byte[] data1 = new byte[data.length];</p>

<p>for(int i=0;i&lt;data1.length;i++){</p>

<p>&nbsp; &nbsp; &nbsp; &nbsp; data1[i] = data[i];</p>

<p>}</p>

<p>(2)全部复制</p>

<p>byte[] data = new byte[]{0,1,2,3,4,5,6,7,8,9};</p>

<p>byte[] data1 = new byte[data.length];</p>

<p>Array.Copy(data,data1,data.length);//源数据data，目标数据data1，复制长度data.length</p>

<p>(3)全部复制</p>

<p>byte[] data = new byte[]{0,1,2,3,4,5,6,7,8,9};</p>

<p>byte[] data1;</p>

<p>data1 = (byte[])data .Clone();</p>

<p>(4)深度复制</p>

<p>byte[] srcArray = new byte[] { 0x01, 0x02, 0x03, 0x04 };</p>

<p>byte[] dstArray = new byte[srcArray.Length];</p>

<p>Buffer.BlockCopy(srcArray, 0, dstArray, 0, srcArray.Length);//源数据srcArray ， 起始位置0，目标数组dstArray ，开始位置0，&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 多少长度 srcArray.Length</p>

<p>&nbsp;</p>

<p>C# byte[]数组删除</p>

<p>byte[] data = new byte[]{0,1,2,3,4,5,6,7,8,9};</p>

<p>(1)删除指定位置数量的数据</p>

<p>Array.Clear(data,0,5); //data 从起始位置0， 删除5个位置</p>

<p>(2)删除指定的数据--返回byte[] 空间减少</p>

<p>///</p>

<p>/// 去掉byte[] 中特定的byte</p>

<p>///</p>

<p>/// 需要处理的byte[]</p>

<p>/// byte[] 中需要除去的特定 byte (此处: byte cut = 0x00 ;)</p>

<p>///</p>

<p>public static byte[] ByteCut(byte[] b, byte cut)</p>

<p>{</p>

<p>var list = new List();</p>

<p>list.AddRange(b);</p>

<p>for (var i = list.Count - 1; i &gt;= 0; i--)</p>

<p>{</p>

<p>if (list[i] == cut)</p>

<p>list.RemoveAt(i);</p>

<p>}</p>

<p>var lastbyte = new byte[list.Count];</p>

<p>for (var i = 0; i &lt; list.Count; i++)</p>

<p>{</p>

<p>lastbyte[i] = list[i];</p>

<p>}</p>

<p>return lastbyte;</p>

<p>}</p>

<p>(3)清空byte[]</p>

<p>byte[] data = new byte[50];</p>

<p>data = new byte[50];//新new 出来则原来空间数据清除</p>
]]></description><category>C#笔记</category><comments>http://www.nasay.cn/view.asp?id=55#comment</comments><wfw:commentRss>http://www.nasay.cn/feed.asp?cmt=55</wfw:commentRss></item><item><title>如何使用nginx配置wss且ws能同时使用</title><author>532898411@qq.com (ningjian)</author><link>http://www.nasay.cn/view.asp?id=54</link><pubDate>Thu, 16 Mar 2023 21:16:54 +0800</pubDate><guid>http://www.nasay.cn/view.asp?id=54</guid><description><![CDATA[<p>如何使用nginx配置wss且ws能同时使用，这两天由于项目的需要必须要用到wss，即pc端用ws， 手机端需要使用wss，我用的是C# 写的ws服务，软件启动时就会开启ws服务，那么要如何启用wss呢？</p>

<p>配置文件如下,也是在网上找了很久的资料</p>

<p>&nbsp;</p>

<p>打开nginx配置文件，nginx.conf</p>

<p>在http中加入以下代码：</p>

<div>&nbsp; &nbsp;map $http_upgrade $connection_upgrade{</div>

<div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;default upgrade;</div>

<div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &#39;&#39; close;</div>

<div>&nbsp; &nbsp;}</div>

<div>&nbsp;</div>

<div>
<div>&nbsp; &nbsp;</div>

<div>&nbsp; &nbsp;upstream websocket1{</div>

<div>&nbsp; &nbsp; &nbsp; &nbsp;server IP:原ws端口号;</div>

<div>&nbsp; &nbsp;}</div>

<div>&nbsp;</div>

<div>server {</div>
</div>

<div>listen 将要使用的端口号 ssl;</div>

<div>server_name XXX.XXX.com;</div>

<div>ssl_certificate ../cert/对应的nginx SSL证书.pem;</div>

<div>ssl_certificate_key ../cert/对应的nginx SSL证书.key;</div>

<div>ssl_session_timeout 5m;</div>

<div>ssl_protocols TLSv1 TLSv1.1 TLSv1.2;</div>

<div>ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5:!RC4:!DHE;</div>

<div>ssl_prefer_server_ciphers on;</div>

<div>&nbsp;</div>

<div>location / {</div>

<div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;proxy_pass http://websocket1;</div>

<div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;proxy_http_version 1.1;</div>

<div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;proxy_set_header Upgrade $http_upgrade;</div>

<div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;proxy_set_header Connection &quot;Upgrade&quot;;</div>

<div>&nbsp; &nbsp; &nbsp;}&nbsp;</div>

<div>&nbsp; &nbsp;}</div>

<div>&nbsp;</div>

<div>需要修改和注意的地方已经用中文标识准确的地方了，之前虽然看到代码 ，并不知道准确的含义</div>

<div>至于SSL证书，大家可以去阿里云或腾讯云上申请免费的ssl证书，一年有效期，不过生产环境中建议至少买最便宜的证书，有保障，当然免费的也可以用，但是不知道它是否是不是随时失去有效性</div>

<div>访问地址：</div>

<div>ws://域名：原ws端口号</div>

<div>wss://域名：ssl端口号</div>

<div>希望能帮助到大家尽快解决问题</div>
]]></description><category>通讯协议</category><comments>http://www.nasay.cn/view.asp?id=54#comment</comments><wfw:commentRss>http://www.nasay.cn/feed.asp?cmt=54</wfw:commentRss></item><item><title>c#中多线程间的同步</title><author>532898411@qq.com (ningjian)</author><link>http://www.nasay.cn/view.asp?id=53</link><pubDate>Wed, 15 Mar 2023 21:04:46 +0800</pubDate><guid>http://www.nasay.cn/view.asp?id=53</guid><description><![CDATA[<pre class="prettyprint">
 public class Sample
    {   //先执行的线程设置为 true
        ManualResetEvent even = new ManualResetEvent(true);
        ManualResetEvent odd = new ManualResetEvent(false);        
        
        public void Sum()
        {           
            var ta = Task.Run(() =&gt; PrintEven(even, odd));
            var tb = Task.Run(() =&gt; PrintOdd (even,odd));                      
        }
				//等待自己的信号，控制另一个线程的信号
        public void PrintEven(EventWaitHandle evenHandle, EventWaitHandle oddHandle)
        {            
            string design =  &quot;偶数&quot;;
            for (int i = 0; i &lt;= 20; i++)
            {               
                evenHandle.WaitOne();
                if ((i &amp; 1) == 0)
                {
                    Console.WriteLine($&quot;{design}：{i}&quot;);
                    evenHandle.Reset();
                    oddHandle.Set();
                }
            }
        }
        public void PrintOdd(EventWaitHandle evenHandle, EventWaitHandle oddHandle)
        {
            string design = &quot;奇数&quot;;
            for (int i = 0; i &lt;= 20; i++)
            {               
                oddHandle.WaitOne();
                if ((i &amp; 1) == 1)
                {
                    Console.WriteLine($&quot;{design}：{i}&quot;);
                    oddHandle.Reset();
                    evenHandle.Set();
                }
            }
        }
    }</pre>

<p>&nbsp;</p>

<p>&nbsp;</p>

<p><strong>Event</strong></p>

<p>与信号量一样，事件也是一个系统范围内的资源同步方法。又分为ManualResetEvent，AutoResetEvent，CountdownEvent以及ManualResetEventSlim，在构建对象实例时若传入了name参数，代表这是一个可以跨进程的系统级同步事件。</p>

<p>以&nbsp;ManualResetEvent&nbsp;为例，该类有&nbsp;signaled&nbsp;和&nbsp;nonsignaled&nbsp;两种状态，这两种状态通过实例化对象时的 布尔类型 参数决定，TRUE&nbsp;就是signaled,&nbsp;False相反</p>

<p>文档中常见的翻译是发出信号的状态和未发出信号的状态，微软官网的机翻是终止状态和非终止状态，还有一些释放线程之类的描述，直观上难以理解。其实就是改变状态而已，还不如英文的好理解。</p>

<p>ManualResetEvent 的基类 EventWaitHandle 中提供了Set() 和Reset() 方法，用于改变状态，Set 将事件修改为&nbsp;signaled，Reset重置为&nbsp;nonsignaled</p>

<p>这里说一下Set和Reset，这两个方法是改变了事件的状态，并不是一个瞬时性的动作，也就意味着在调用Set后，调用Reset之前，事件都处于&nbsp;signaled&nbsp;状态（AutoResetEvent会自动调用Reset重置事件状态）</p>

<p>WaitHandle 类中提供了众多等待信号的方法，EventWaitHandle 继承自WaitHandle， ManualResetEvent中也可以调用WaitOne等方法。</p>

<p>回头再看一下信号量 Semaphore 的示例，也调用 WaitOne 等待信号，因为它也继承自 Waithandler。</p>

<p>&nbsp;</p>
]]></description><category>C#笔记</category><comments>http://www.nasay.cn/view.asp?id=53#comment</comments><wfw:commentRss>http://www.nasay.cn/feed.asp?cmt=53</wfw:commentRss></item><item><title>优化C# socket服务端和多客户端连接</title><author>532898411@qq.com (ningjian)</author><link>http://www.nasay.cn/view.asp?id=52</link><pubDate>Sat, 11 Mar 2023 17:01:31 +0800</pubDate><guid>http://www.nasay.cn/view.asp?id=52</guid><description><![CDATA[<p>物联网系统需要，项目使用C# + rabbitmq + sql server 实现 设备的数据实时查看和存储</p>

<p>放到互联网一段时间试运行，会发现有人使用软件经常扫描我们的socket端口，并且造成了程序的卡死 ，今天通过优化，基本上解决了问题，但是我不断断开连接，导致rabbitmq崩掉了，无法连接，这个倒是没想到，周一在做进一步优化了，找找原因看到底是哪里的问题。</p>
]]></description><category>C#笔记</category><comments>http://www.nasay.cn/view.asp?id=52#comment</comments><wfw:commentRss>http://www.nasay.cn/feed.asp?cmt=52</wfw:commentRss></item><item><title>遇到rabbitmq不能及时发送消息的问题</title><author>532898411@qq.com (ningjian)</author><link>http://www.nasay.cn/view.asp?id=51</link><pubDate>Fri, 10 Mar 2023 21:58:52 +0800</pubDate><guid>http://www.nasay.cn/view.asp?id=51</guid><description><![CDATA[<p>今天遇到一个奇葩的问题，就是之前项目中运行rabbitmq一直没有遇到问题，今天在本地测试遇到问题，需要操作两次，rabbitmq才能发送出去消息。</p>

<p>我这个项目主要是别人通过网页接口ajax请求启停设备，接口会将请求数据发送到rabbitmq，然后桌面应该程序接收到rabbitmq消息之后，通过4G向设备发送指令，今天所有指令需要发送两次，调试程序，也没发现问题，后来登录rabbitmq管理后台，发现多了一个连接，是之前服务器上安装的程序产生的长连接。之前已经关闭了应用程序，但是它却依然在连着rabbitmq，通过进程结束原程序，在本地测试，发送指令后，rabbitmq也及时的发送接收了。</p>

<p>但是这个问题很奇怪，可能队列之间有冲突吧，否则多个连接也不影响什么。</p>
]]></description><category>随心笔记</category><comments>http://www.nasay.cn/view.asp?id=51#comment</comments><wfw:commentRss>http://www.nasay.cn/feed.asp?cmt=51</wfw:commentRss></item><item><title>解决SQL Server数据库不同版本不能兼容使用的问题</title><author>532898411@qq.com (ningjian)</author><link>http://www.nasay.cn/view.asp?id=50</link><pubDate>Fri, 10 Mar 2023 13:50:24 +0800</pubDate><guid>http://www.nasay.cn/view.asp?id=50</guid><description><![CDATA[<p>&nbsp;</p>

<p>服务器上安装的版本是2019的SQL Server，我自己开发的电脑上安装的是2012的，将服务器上的分离之后拷贝到我的电脑上并不能附加到数据库中，发现是数据库版本不兼容，高版本的不能附加到低版本上面。就只能生成相应的脚本，建库建表，然后在导入数据进去。</p>

<p>步骤：</p>

<p>选择数据库，右击，&ldquo;任务&rdquo; 中选择 &ldquo;生成脚本&rdquo; ，</p>

<p>&ldquo;下一步&rdquo; ，到选择对象，第一个是数据库整体，第二个可自由选择数据库中的表，勾选其一，</p>

<p>然后 &ldquo;下一步&rdquo;，在&ldquo;高级&rdquo;中，将服务器版脚本更改到2012版的，然后在&ldquo;要编写脚本的数据的类型&rdquo;，选择 &ldquo;架构和数据&rdquo;。然后退回来，存储路径随意，然后在我的电脑上执行脚本就能使用了。</p>

<p>表的框架是搞定了，但是数据还要重新导入</p>

<p>在我的电脑上，点击数据库，选择任务-》导入数据，选择源数据 输入服务器地址 用户名和帐号，选择好数据库，设置本地可以直接用windows验证，选择数据库 ，下一步选择所有表，后面都直接下一步即可。 注意的是 如果表中有种子标识的，需要先将标识去掉，等数据全部导入完成后，在设置回来。</p>

<p>&nbsp;</p>

<p>相反 如果低版本到高版本的，可以直接附加上去。</p>
]]></description><category>MSSQL</category><comments>http://www.nasay.cn/view.asp?id=50#comment</comments><wfw:commentRss>http://www.nasay.cn/feed.asp?cmt=50</wfw:commentRss></item><item><title>RabbitMQ 原理初探</title><author>532898411@qq.com (ningjian)</author><link>http://www.nasay.cn/view.asp?id=49</link><pubDate>Thu, 09 Mar 2023 20:59:11 +0800</pubDate><guid>http://www.nasay.cn/view.asp?id=49</guid><description><![CDATA[<p>RabbitMQ 2007 年发布，是使用 Erlang 语言开发的开源消息队列系统，基于 AMQP 协议来实现。</p>

<p><strong>2.1 基本概念</strong></p>

<p>提到RabbitMQ，就不得不提AMQP协议。AMQP协议是具有现代特征的二进制协议。是一个提供统一消息服务的应用层标准高级消息队列协议，是应用层协议的一个开放标准，为面向消息的中间件设计。</p>

<p>先了解一下AMQP协议中间的几个重要概念：</p>

<p>Server：接收客户端的连接，实现AMQP实体服务。<br />
Connection：连接，应用程序与Server的网络连接，TCP连接。<br />
Channel：信道，消息读写等操作在信道中进行。客户端可以建立多个信道，每个信道代表一个会话任务。<br />
Message：消息，应用程序和服务器之间传送的数据，消息可以非常简单，也可以很复杂。由Properties和Body组成。Properties为外包装，可以对消息进行修饰，比如消息的优先级、延迟等高级特性；Body就是消息体内容。<br />
Virtual Host：虚拟主机，用于逻辑隔离。一个虚拟主机里面可以有若干个Exchange和Queue，同一个虚拟主机里面不能有相同名称的Exchange或Queue。<br />
Exchange：交换器，接收消息，按照路由规则将消息路由到一个或者多个队列。如果路由不到，或者返回给生产者，或者直接丢弃。RabbitMQ常用的交换器常用类型有direct、topic、fanout、headers四种，后面详细介绍。<br />
Binding：绑定，交换器和消息队列之间的虚拟连接，绑定中可以包含一个或者多个RoutingKey。<br />
RoutingKey：路由键，生产者将消息发送给交换器的时候，会发送一个RoutingKey，用来指定路由规则，这样交换器就知道把消息发送到哪个队列。路由键通常为一个&ldquo;.&rdquo;分割的字符串，例如&ldquo;com.rabbitmq&rdquo;。<br />
Queue：消息队列，用来保存消息，供消费者消费。</p>

<p><strong>2.2 工作原理</strong></p>

<p>AMQP 协议模型由三部分组成：生产者、消费者和服务端，执行流程如下：</p>

<p>生产者是连接到 Server，建立一个连接，开启一个信道。<br />
生产者声明交换器和队列，设置相关属性，并通过路由键将交换器和队列进行绑定。<br />
消费者也需要进行建立连接，开启信道等操作，便于接收消息。<br />
生产者发送消息，发送到服务端中的虚拟主机。<br />
虚拟主机中的交换器根据路由键选择路由规则，发送到不同的消息队列中。<br />
订阅了消息队列的消费者就可以获取到消息，进行消费。</p>

<p><strong>2.3 常用交换器</strong></p>

<p>RabbitMQ常用的交换器类型有direct、topic、fanout、headers四种：</p>

<p>Direct Exchange：见文知意，直连交换机意思是此交换机需要绑定一个队列，要求该消息与一个特定的路由键完全匹配。简单点说就是一对一的，点对点的发送。</p>

<p>Fanout Exchange：这种类型的交换机需要将队列绑定到交换机上。一个发送到交换机的消息都会被转发到与该交换机绑定的所有队列上。很像子网广播，每台子网内的主机都获得了一份复制的消息。简单点说就是发布订阅。</p>

<p>Topic Exchange：直接翻译的话叫做主题交换机，如果从用法上面翻译可能叫通配符交换机会更加贴切。这种交换机是使用通配符去匹配，路由到对应的队列。通配符有两种：&quot;*&quot; 、 &quot;#&quot;。需要注意的是通配符前面必须要加上&quot;.&quot;符号。</p>

<p>*符号：有且只匹配一个词。比如 a.*可以匹配到&quot;a.b&quot;、&quot;a.c&quot;，但是匹配不了&quot;a.b.c&quot;。</p>

<p>#符号：匹配一个或多个词。比如&quot;rabbit.#&quot;既可以匹配到&quot;rabbit.a.b&quot;、&quot;rabbit.a&quot;，也可以匹配到&quot;rabbit.a.b.c&quot;。</p>

<p>Headers Exchange：这种交换机用的相对没这么多。它跟上面三种有点区别，它的路由不是用routingKey进行路由匹配，而是在匹配请求头中所带的键值进行路由。创建队列需要设置绑定的头部信息，有两种模式：全部匹配和部分匹配。如上图所示，交换机会根据生产者发送过来的头部信息携带的键值去匹配队列绑定的键值，路由到对应的队列。</p>

<p><strong>2.4 消费原理</strong></p>

<p>我们先看几个基本概念：</p>

<p>broker：每个节点运行的服务程序，功能为维护该节点的队列的增删以及转发队列操作请求。<br />
master queue：每个队列都分为一个主队列和若干个镜像队列。<br />
mirror queue：镜像队列，作为master queue的备份。在master queue所在节点挂掉之后，系统把mirror queue提升为master queue，负责处理客户端队列操作请求。注意，mirror queue只做镜像，设计目的不是为了承担客户端读写压力。</p>

<p>集群中有两个节点，每个节点上有一个broker，每个broker负责本机上队列的维护，并且borker之间可以互相通信。集群中有两个队列A和B，每个队列都分为master queue和mirror queue（备份）。那么队列上的生产消费怎么实现的呢？</p>

<p>对于消费队列，有两个consumer消费队列A，这两个consumer连在了集群的不同机器上。RabbitMQ集群中的任何一个节点都拥有集群上所有队列的元信息，所以连接到集群中的任何一个节点都可以，主要区别在于有的consumer连在master queue所在节点，有的连在非master queue节点上。</p>

<p>因为mirror queue要和master queue保持一致，故需要同步机制，正因为一致性的限制，导致所有的读写操作都必须都操作在master queue上（想想，为啥读也要从master queue中读？和数据库读写分离是不一样的），然后由master节点同步操作到mirror queue所在的节点。即使consumer连接到了非master queue节点，该consumer的操作也会被路由到master queue所在的节点上，这样才能进行消费。</p>

<p>对于生成队列，原理和消费一样，如果连接到非 master queue 节点，则路由过去。</p>

<p>所以，到这里小伙伴们就可以看到 RabbitMQ的不足：由于master queue单节点，导致性能瓶颈，吞吐量受限。虽然为了提高性能，内部使用了Erlang这个语言实现，但是终究摆脱不了架构设计上的致命缺陷。</p>

<p><strong>2.5 高级特性</strong><br />
2.5.1 过期时间</p>

<p>Time To Live，也就是生存时间，是一条消息在队列中的最大存活时间，单位是毫秒，下面看看RabbitMQ过期时间特性：</p>

<p>RabbitMQ可以对消息和队列设置TTL。<br />
RabbitMQ支持设置消息的过期时间，在消息发送的时候可以进行指定，每条消息的过期时间可以不同。<br />
RabbitMQ支持设置队列的过期时间，从消息入队列开始计算，直到超过了队列的超时时间配置，那么消息会变成死信，自动清除。<br />
如果两种方式一起使用，则过期时间以两者中较小的那个数值为准。<br />
当然也可以不设置TTL，不设置表示消息不会过期；如果设置为0，则表示除非此时可以直接将消息投递到消费者，否则该消息将被立即丢弃。</p>

<p>2.5.2 消息确认</p>

<p>为了保证消息从队列可靠地到达消费者，RabbitMQ提供了消息确认机制。</p>

<p>消费者订阅队列的时候，可以指定autoAck参数，当autoAck为true的时候，RabbitMQ采用自动确认模式，RabbitMQ自动把发送出去的消息设置为确认，然后从内存或者硬盘中删除，而不管消费者是否真正消费到了这些消息。</p>

<p>当autoAck为false的时候，RabbitMQ会等待消费者回复的确认信号，收到确认信号之后才从内存或者磁盘中删除消息。</p>

<p>消息确认机制是RabbitMQ消息可靠性投递的基础，只要设置autoAck参数为false，消费者就有足够的时间处理消息，不用担心处理消息的过程中消费者进程挂掉后消息丢失的问题。</p>

<p>2.5.3 持久化</p>

<p>消息的可靠性是RabbitMQ的一大特色，那么RabbitMQ是如何保证消息可靠性的呢？答案就是消息持久化。持久化可以防止在异常情况下丢失数据。RabbitMQ的持久化分为三个部分：交换器持久化、队列持久化和消息的持久化。</p>

<p>交换器持久化可以通过在声明队列时将durable参数设置为true。如果交换器不设置持久化，那么在RabbitMQ服务重启之后，相关的交换器元数据会丢失，不过消息不会丢失，只是不能将消息发送到这个交换器了。</p>

<p>队列的持久化能保证其本身的元数据不会因异常情况而丢失，但是不能保证内部所存储的消息不会丢失。要确保消息不会丢失，需要将其设置为持久化。队列的持久化可以通过在声明队列时将durable参数设置为true。</p>

<p>设置了队列和消息的持久化，当RabbitMQ服务重启之后，消息依然存在。如果只设置队列持久化或者消息持久化，重启之后消息都会消失。</p>

<p>当然，也可以将所有的消息都设置为持久化，但是这样做会影响RabbitMQ的性能，因为磁盘的写入速度比内存的写入要慢得多。</p>

<p>对于可靠性不是那么高的消息可以不采用持久化处理以提高整体的吞吐量。鱼和熊掌不可兼得，关键在于选择和取舍。在实际中，需要根据实际情况在可靠性和吞吐量之间做一个权衡。</p>

<p>2.5.4 死信队列</p>

<p>当消息在一个队列中变成死信之后，他能被重新发送到另一个交换器中，这个交换器成为死信交换器，与该交换器绑定的队列称为死信队列。</p>

<p>消息变成死信有下面几种情况：</p>

<p>消息被拒绝。</p>

<p>消息过期</p>

<p>队列达到最大长度</p>

<p>DLX也是一个正常的交换器，和一般的交换器没有区别，他能在任何的队列上面被指定，实际上就是设置某个队列的属性。当这个队列中有死信的时候，RabbitMQ会自动将这个消息重新发送到设置的交换器上，进而被路由到另一个队列，我们可以监听这个队列中消息做相应的处理。</p>

<p>死信队列有什么用？当发生异常的时候，消息不能够被消费者正常消费，被加入到了死信队列中。后续的程序可以根据死信队列中的内容分析当时发生的异常，进而改善和优化系统。</p>

<p>2.5.5 延迟队列</p>

<p>一般的队列，消息一旦进入队列就会被消费者立即消费。延迟队列就是进入该队列的消息会被消费者延迟消费，延迟队列中存储的对象是的延迟消息，&ldquo;延迟消息&rdquo;是指当消息被发送以后，等待特定的时间后，消费者才能拿到这个消息进行消费。</p>

<p>延迟队列用于需要延迟工作的场景。最常见的使用场景：淘宝或者天猫我们都使用过，用户在下单之后通常有30分钟的时间进行支付，如果这30分钟之内没有支付成功，那么订单就会自动取消。</p>

<p>除了延迟消费，延迟队列的典型应用场景还有延迟重试。比如消费者从队列里面消费消息失败了，可以延迟一段时间以后进行重试。</p>

<p><strong>2.6 特性分析</strong></p>

<p>这里才是内容的重点，不仅需要知道Rabbit的特性，还需要知道支持这些特性的原因：</p>

<p>消息路由（支持）：RabbitMQ可以通过不同的交换器支持不同种类的消息路由；<br />
消息有序（不支持）：当消费消息时，如果消费失败，消息会被放回队列，然后重新消费，这样会导致消息无序；<br />
消息时序（非常好）：通过延时队列，可以指定消息的延时时间，过期时间TTL等；<br />
容错处理（非常好）：通过交付重试和死信交换器（DLX）来处理消息处理故障；<br />
伸缩（一般）：伸缩其实没有非常智能，因为即使伸缩了，master queue还是只有一个，负载还是只有这一个master queue去抗，所以我理解RabbitMQ的伸缩很弱（个人理解）。<br />
持久化（不太好）：没有消费的消息，可以支持持久化，这个是为了保证机器宕机时消息可以恢复，但是消费过的消息，就会被马上删除，因为RabbitMQ设计时，就不是为了去存储历史数据的。<br />
消息回溯（不支持）：因为消息不支持永久保存，所以自然就不支持回溯。<br />
高吞吐（中等）：因为所有的请求的执行，最后都是在master queue，它的这个设计，导致单机性能达不到十万级的标准。</p>
]]></description><category>随心笔记</category><comments>http://www.nasay.cn/view.asp?id=49#comment</comments><wfw:commentRss>http://www.nasay.cn/feed.asp?cmt=49</wfw:commentRss></item></channel></rss>
