一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務器之家 - 編程語言 - C# - C# 常用協議實現模版及FixedSizeReceiveFilter示例(SuperSocket入門)

C# 常用協議實現模版及FixedSizeReceiveFilter示例(SuperSocket入門)

2021-12-21 13:56黃昏前黎明后 C#

本文主要介紹了常用協議實現模版及FixedSizeReceiveFilter示例。具有很好的參考價值,下面跟著小編一起來看下吧

socket里面的協議解析是socket通訊程序設計中最復雜的地方,如果你的應用層協議設計或實現不佳,socket通訊中常見的粘包,分包就難以避免。supersocket內置了命令行格式的協議commandlineprotocol,如果你使用了其它格式的協議,就必須自行實現自定義協議customprotocol。看了一篇文檔之后, 你可能會覺得用 supersocket 來實現你的自定義協議并不簡單。 為了讓這件事變得更容易一些, supersocket 提供了一些通用的協議解析工具, 你可以用他們簡單而且快速的實現你自己的通信協議:

  • terminatorreceivefilter (supersocket.socketbase.protocol.terminatorreceivefilter, supersocket.socketbase) ---結束符協議
  • countspliterreceivefilter (supersocket.facility.protocol.countspliterreceivefilter, supersocket.facility)---固定數量分隔符協議
  • fixedsizereceivefilter (supersocket.facility.protocol.fixedsizereceivefilter, supersocket.facility)---固定請求大小協議
  • beginendmarkreceivefilter (supersocket.facility.protocol.beginendmarkreceivefilter, supersocket.facility)---帶起止符協議
  • fixedheaderreceivefilter (supersocket.facility.protocol.fixedheaderreceivefilter, supersocket.facility)---頭部格式固定并包含內容長度協議

1、terminatorreceivefilter結束符協議

結束符協議和命令行協議類似,一些協議用結束符來確定一個請求.例如, 一個協議使用兩個字符 "##" 作為結束符, 于是你可以使用類 "terminatorreceivefilterfactory":

結束符協議terminatorprotocolserver :

?
1
2
3
4
5
6
7
public class terminatorprotocolserver : appserver
{
 public terminatorprotocolserver()
  : base(new terminatorreceivefilterfactory("##"))
 {
 }
}

基于terminatorreceivefilter實現你的接收過濾器(receivefilter):

?
1
2
3
4
public class yourreceivefilter : terminatorreceivefilter<yourrequestinfo>
{
 //more code
}

實現你的接收過濾器工廠(receivefilterfactory)用于創建接受過濾器實例:

?
1
2
3
4
public class yourreceivefilterfactory : ireceivefilterfactory<yourrequestinfo>
{
 //more code
}

2、countspliterreceivefilter 固定數量分隔符協議

有些協議定義了像這樣格式的請求 "#part1#part2#part3#part4#part5#part6#part7#". 每個請求有7個由 '#' 分隔的部分. 這種協議的實現非常簡單:

?
1
2
3
4
5
6
7
8
9
10
/// <summary>
/// 請求格式:#part1#part2#part3#part4#part5#part6#part7#
/// </summary>
public class countspliterappserver : appserver
{
 public countspliterappserver()
  : base(new countspliterreceivefilterfactory((byte)'#', 8)) //8個分隔符,7個參數。除使用默認的過濾工廠,還可以參照上一個實例定制協議
 {
 }
}

3、fixedsizereceivefilter 固定請求大小協議

在這種協議之中, 所有請求的大小都是相同的。如果你的每個請求都是有8個字符組成的字符串,如"huang li", 你應該做的事就是想如下代碼這樣實現一個接收過濾器(receivefilter):

?
1
2
3
4
5
6
7
8
9
10
11
class myreceivefilter : fixedsizereceivefilter<stringrequestinfo>
{
 public myreceivefilter()
  : base(8) //傳入固定的請求大小
 {
 }
 protected override stringrequestinfo processmatchedrequest(byte[] buffer, int offset, int length, bool tobecopied)
 {
  //todo: 通過解析到的數據來構造請求實例,并返回
 }
}

然后在你的 appserver 類中使用這個接受過濾器 (receivefilter):

?
1
2
3
4
5
6
7
public class myappserver : appserver
{
 public myappserver()
  : base(new defaultreceivefilterfactory<myreceivefilter, stringrequestinfo>()) //使用默認的接受過濾器工廠 (defaultreceivefilterfactory)
 {
 }
}

4、beginendmarkreceivefilter 帶起止符協議

在這類協議的每個請求之中 都有固定的開始和結束標記。例如, 我有個協議,它的所有消息都遵循這種格式 "&xxxxxxxxxxxxxx#"。因此,在這種情況下, "&" 是開始標記, "#" 是結束標記,于是你的接受過濾器可以定義成這樣:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class myreceivefilter : beginendmarkreceivefilter<stringrequestinfo>
{
 //開始和結束標記也可以是兩個或兩個以上的字節
 private readonly static byte[] beginmark = new byte[] { (byte)'&' };
 private readonly static byte[] endmark = new byte[] { (byte)'#' };
 
 public myreceivefilter()
  : base(beginmark, endmark) //傳入開始標記和結束標記
 {
 }
 protected override stringrequestinfo processmatchedrequest(byte[] readbuffer, int offset, int length)
 {
  //todo: 通過解析到的數據來構造請求實例,并返回
 }
}

然后在你的 appserver 類中使用這個接受過濾器 (receivefilter):

?
1
2
3
4
5
6
7
public class myappserver : appserver
{
 public myappserver()
  : base(new defaultreceivefilterfactory<myreceivefilter, stringrequestinfo>()) //使用默認的接受過濾器工廠 (defaultreceivefilterfactory)
 {
 }
}

5、fixedheaderreceivefilter 頭部格式固定并包含內容長度協議

這種協議將一個請求定義為兩大部分, 第一部分定義了包含第二部分長度等等基礎信息. 我們通常稱第一部分為頭部.

例如, 我們有一個這樣的協議: 頭部包含 6 個字節, 前 4 個字節用于存儲請求的名字, 后兩個字節用于代表請求體的長度:

/// +-------+---+-------------------------------+
/// |request| l |                               |
/// | name  | e |    request body               |
/// |  (4)  | n |                               |
/// |       |(2)|                               |
/// +-------+---+-------------------------------+
使用 supersocket, 你可以非常方便的實現這種協議:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class myreceivefilter : fixedheaderreceivefilter<binaryrequestinfo>
{
 public myreceivefilter()
  : base(6)
 {
 }
 protected override int getbodylengthfromheader(byte[] header, int offset, int length)
 {
  return (int)header[offset + 4] * 256 + (int)header[offset + 5];
 }
 protected override binaryrequestinfo resolverequestinfo(arraysegment<byte> header, byte[] bodybuffer, int offset, int length)
 {
  return new binaryrequestinfo(encoding.utf8.getstring(header.array, header.offset, 4), bodybuffer.clonerange(offset, length));
 }
}

你需要基于類fixedheaderreceivefilter實現你自己的接收過濾器.

  • 傳入父類構造函數的 6 表示頭部的長度;
  • 方法"getbodylengthfromheader(...)" 應該根據接收到的頭部返回請求體的長度;
  • 方法 resolverequestinfo(....)" 應該根據你接收到的請求頭部和請求體返回你的請求類型的實例.

實際使用場景:

到這里五種協議的模板你都已經了解了一遍,并且知道了相關的格式處理。接下來看一個網絡示例:

通訊協議格式:

C# 常用協議實現模版及FixedSizeReceiveFilter示例(SuperSocket入門)

在看到上圖協議是在糾結客戶端發送16進制,服務器怎么接收,16進制的報文如下:

26 01 00 19 4e 4a 30 31 31 01 44 41 31 31 32 00 07 00 00 00 00 00 00 34 23

16進制也好,10進制也好,其他的進制也好,最終都是轉換成byte[],其實在處理數據時,發送過去的數據都是可以轉換成為byte[]的,所以服務的只要解析byte[]數組就行了。按照協議來解析就能得到想要的數據。下面使用fixedsizereceivefilter的例子,代碼如下:

根據上面的通訊協議,開始來實現解析:

第一步、定義一個和協議合適的數據結構

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
using system;
using system.collections.generic;
using system.linq;
using system.text;
using system.threading.tasks;
/****************************************************************
* 作者:黃昏前黎明后
* clr版本:4.0.30319.42000
* 創建時間:2017-01-23 21:12:30
* 2017
* 描述說明:協議數據包
*
* 修改歷史:
*
*
*****************************************************************/
namespace supersocketdemo
{
 public class hldata
 {
  /// <summary>
  /// 開始符號
  /// </summary>
  public char head { get; set; }
  /// <summary>
  /// 協議包數據
  /// </summary>
  public byte ping { get; set; }
  /// <summary>
  /// 數據長度
  /// </summary>
  public ushort lenght { get; set; }
  /// <summary>
  /// 終端id
  /// </summary>
  public uint fid { get; set; }
  /// <summary>
  /// 目標類型
  /// </summary>
  public byte type { get; set; }
  /// <summary>
  /// 轉發終端id
  /// </summary>
  public uint sid { get; set; }
  /// <summary>
  /// 發送計數
  /// </summary>
  public ushort sendcount { get; set; }
  /// <summary>
  /// 保留字段
  /// </summary>
  public byte[] retain { get; set; }
  /// <summary>
  /// 異或校驗
  /// </summary>
  public byte check { get; set; }
  /// <summary>
  /// 結束符號
  /// </summary>
  public char end { get; set; }
  public override string tostring()
  {
   return string.format("開始符號:{0},包數據:{1},數據長度:{2},終端id:{3},目標類型:{4},轉發終端id:{5},發送包計數:{6},保留字段:{7},異或校驗:{8},結束符號:{9}",
    head, ping, lenght, fid, type, sid, sendcount, retain, check, end);
  }
 }
}
hldata

第二步、建立一個requestinfo來給server數據接收

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
using system;
using system.collections.generic;
using system.linq;
using system.text;
using system.threading.tasks;
using supersocket.socketbase.protocol;
/****************************************************************
* 作者:黃昏前黎明后
* clr版本:4.0.30319.42000
* 創建時間:2017-01-22 21:03:31
* 2017
* 描述說明:數據請求
*
* 修改歷史:
*
*
*****************************************************************/
namespace supersocketdemo
{
 public class hlprotocolrequestinfo : requestinfo<hldata>
 {
  public hlprotocolrequestinfo(hldata hldata)
  {
   //如果需要使用命令行協議的話,那么命令類名稱hldata相同
   initialize("hldata", hldata);
  }
 }
}
hlprotocolrequestinfo 類

第三步、fixedsize協議解析

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
using system;
using system.collections.generic;
using system.linq;
using system.text;
using system.threading.tasks;
using supersocket.socketbase.protocol;
using supersocket.facility.protocol;
using supersocket.common;
/****************************************************************
* 作者:黃昏前黎明后
* clr版本:4.0.30319.42000
* 創建時間:2017-01-22 21:06:01
* 2017
* 描述說明:協議解析類,固定請求大小的協議
*
* 修改歷史:
*
*
*****************************************************************/
namespace supersocketdemo
{
 /// <summary>
 /// 固定請求大小的協議,(幀格式為hlprotocolrequestinfo)
 /// </summary>
 public class hlprotocolreceivefilter : fixedsizereceivefilter<hlprotocolrequestinfo>
 {
  public hlprotocolreceivefilter() : base(25)//總的字節長度 1+1+2+5+1+5+2+6+1+1 = 25
  {
  }
  protected override hlprotocolrequestinfo processmatchedrequest(byte[] buffer, int offset, int length, bool tobecopied)
  {
   var hldata = new hldata();
   hldata.head = (char)buffer[offset];//開始標識的解析,1個字節
   hldata.ping = buffer[offset + 1];//數據,從第2位起,只有1個字節
   hldata.lenght = bitconverter.touint16(buffer, offset + 2);//數據長度,從第3位開始,2個字節
   hldata.fid = bitconverter.touint32(buffer, offset + 4);//本終端id,從第5位開始,5個字節
   hldata.type = buffer[offset + 9];//目標類型,從第10位開始,1個字節
   hldata.sid = bitconverter.touint32(buffer, offset + 10);//轉發終端id,從第11位開始,5個字節
   hldata.sendcount = bitconverter.touint16(buffer, offset + 15);//發送包計數,從第16位開始,2個字節
   hldata.retain = buffer.clonerange(offset + 17, 6);//保留字段,從18位開始,6個字節
   hldata.check = buffer[offset + 23];//異或校驗,從24位開始,1個字節
   hldata.end = (char)buffer[offset + 24];//結束符號,從第25位開始,一個字節
   return new hlprotocolrequestinfo(hldata);
  }
 }
}
hlprotocolreceivefilter類

第四步、建立協議工廠hlreceivefilterfactory

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
using system;
using system.collections.generic;
using system.linq;
using system.text;
using system.threading.tasks;
using supersocket.socketbase;
using supersocket.socketbase.protocol;
using system.net;
/****************************************************************
* 作者:黃昏前黎明后
* clr版本:4.0.30319.42000
* 創建時間:2017-01-23 :22:01:25
* 2017
* 描述說明:協議工廠
*
* 修改歷史:
*
*
*****************************************************************/
namespace supersocketdemo
{
 public class hlreceivefilterfactory: ireceivefilterfactory<hlprotocolrequestinfo>
 {
  public ireceivefilter<hlprotocolrequestinfo> createfilter(iappserver appserver, iappsession appsession, ipendpoint remoteendpoint)
  {
   return new hlbeginendmarkreceivefilter();
  }
 }
}
 
hlreceivefilterfactory類

第五步、自定義hlprotocolsession繼承appsession

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
using supersocket.socketbase;
using supersocket.socketbase.protocol;
using system;
/****************************************************************
* 作者:黃昏前黎明后
* clr版本:4.0.30319.42000
* 創建時間:2017-01-22 21:15:11
* 2017
* 描述說明:自定義hlprotocolsession
*
* 修改歷史:
*
*
*****************************************************************/
namespace supersocketdemo
{
 public class hlprotocolsession : appsession<hlprotocolsession, hlprotocolrequestinfo>
 {
  protected override void handleexception(exception e)
  {
 
  }
 
 }
}
 
hlprotocolsession類

第六步、自定義hlprotocolserver繼承appserver

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
using system;
using system.collections.generic;
using system.linq;
using system.text;
using system.threading.tasks;
using supersocket.socketbase;
using supersocket.socketbase.protocol;
/****************************************************************
*  作者:黃昏前黎明后
*  clr版本:4.0.30319.42000
*  創建時間:2017-01-22 21:16:57
*  2017
*  描述說明:自定義server
*
*  修改歷史:
*
*
*****************************************************************/
namespace supersocketdemo
{
 public class hlprotocolserver : appserver<hlprotocolsession, hlprotocolrequestinfo>
  {
    /// <summary>
    /// 使用自定義協議工廠
    /// </summary>
    public hlprotocolserver()
      : base(new hlreceivefilterfactory())
    {
    }
  }
}
 
hlprotocolserver類

第七步、加上起止符協議hlbeginendmarkreceivefilter

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
using system;
using system.collections.generic;
using system.linq;
using system.text;
using system.threading.tasks;
using supersocket.common;
using supersocket.facility.protocol;
/****************************************************************
*  作者:黃昏前黎明后
*  clr版本:4.0.30319.42000
*  創建時間:2017-01-23 22:07:03
*  2017
*  描述說明:帶起止符的協議, "&" 是開始標記, "#" 是結束標記,開始結束標記由自己定義
*
*  修改歷史:
*
*
*****************************************************************/
namespace supersocketdemo
{
  public class hlbeginendmarkreceivefilter : beginendmarkreceivefilter<hlprotocolrequestinfo>
  {
    private readonly static char strbegin = '&';
    private readonly static char strend = '#';
    //開始和結束標記也可以是兩個或兩個以上的字節
    private readonly static byte[] beginmark = new byte[] { (byte)strbegin };
    private readonly static byte[] endmark = new byte[] { (byte)strend };
 
    public hlbeginendmarkreceivefilter() : base(beginmark, endmark)
    {
    }
    /// <summary>
    /// 這里解析的到的數據是會把頭和尾部都給去掉的
    /// </summary>
    /// <param name="readbuffer"></param>
    /// <param name="offset"></param>
    /// <param name="length"></param>
    /// <returns></returns>
    protected override hlprotocolrequestinfo processmatchedrequest(byte[] readbuffer, int offset, int length)
    {
      var hldata = new hldata();
      hldata.head = strbegin;//自己定義開始符號
      hldata.ping = readbuffer[offset];//數據,從第1位起,只有1個字節
      hldata.lenght = bitconverter.touint16(readbuffer, offset + 1);//數據長度,從第2位開始,2個字節
      hldata.fid = bitconverter.touint32(readbuffer, offset + 3);//本終端id,從第4位開始,5個字節
      hldata.type = readbuffer[offset + 8];//目標類型,從第9位開始,1個字節
      hldata.sid = bitconverter.touint32(readbuffer, offset + 9);//轉發終端id,從第10位開始,5個字節
      hldata.sendcount = bitconverter.touint16(readbuffer, offset + 14);//發送包計數,從第15位開始,2個字節
      hldata.retain = readbuffer.clonerange(offset + 16, 6);//保留字段,從17位開始,6個字節
      hldata.check = readbuffer[offset + 22];//異或校驗,從23位開始,1個字節
      hldata.end = strend;//結束符號,自己定義
      return new hlprotocolrequestinfo(hldata);
    }
  }
}
 
hlbeginendmarkreceivefilter類

第八步、服務啟動和停止

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
using system;
using system.collections.generic;
using system.linq;
using system.text;
using system.threading.tasks;
using supersocket.socketbase;
using supersocket.socketbase.protocol;
using supersocket.socketengine;
/****************************************************************
*  作者:黃昏前黎明后
*  clr版本:4.0.30319.42000
*  創建時間:2017-01-19 00:02:17
*  2017
*  描述說明:服務啟動和停止入口
*
*  修改歷史: 2017 -01-19 調整自定義mysession和myserver
*       2017 -01-23 通訊協議解析,直接使用入口注冊事件
*
*****************************************************************/
namespace supersocketdemo
{
  class program
  {
    /// <summary>
    /// supersocket服務啟動或停止
    /// </summary>
    /// <param name="args"></param>
    static void main(string[] args)
    {
      console.writeline("請按任何鍵進行啟動supersocket服務!");
      console.readkey();
      console.writeline();
      var hlprotocolserver = new hlprotocolserver();
      // 設置端口號
      int port = 2017;
      //啟動應用服務端口
      if (!hlprotocolserver.setup(port)) //啟動時監聽端口2017
      {
        console.writeline("服務端口啟動失敗!");
        console.readkey();
        return;
      }
      console.writeline();
      //注冊連接事件
      hlprotocolserver.newsessionconnected += hlprotocolserver_newsessionconnected;
      //注冊請求事件
      hlprotocolserver.newrequestreceived += hlprotocolserver_newrequestreceived;
      //注冊session關閉事件
      hlprotocolserver.sessionclosed += hlprotocolserver_sessionclosed;
      //嘗試啟動應用服務
      if (!hlprotocolserver.start())
      {
        console.writeline("服務啟動失敗!");
        console.readkey();
        return;
      }
      console.writeline("服務器狀態:" + hlprotocolserver.state.tostring());
      console.writeline("服務啟動成功,請按'e'停止服務!");
      while (console.readkey().keychar != 'e')
      {
        console.writeline();
        continue;
      }
      //停止服務
      hlprotocolserver.stop();
      console.writeline("服務已停止!");
      console.readkey();
    }
    static void hlprotocolserver_sessionclosed(hlprotocolsession session, supersocket.socketbase.closereason value)
    {
      console.writeline(session.remoteendpoint.tostring() + "連接斷開. 斷開原因:" + value);
    }
    static void hlprotocolserver_newsessionconnected(hlprotocolsession session)
    {
      console.writeline(session.remoteendpoint.tostring() + " 已連接.");
    }
    /// <summary>
    /// 協議并沒有什么太多復雜邏輯,不需要用到命令模式,直接用這種方式就可以了
    /// </summary>
    /// <param name="session"></param>
    /// <param name="requestinfo"></param>
    private static void hlprotocolserver_newrequestreceived(hlprotocolsession session, hlprotocolrequestinfo requestinfo)
    {
      console.writeline();
      console.writeline("數據來源: " + session.remoteendpoint.tostring());
      console.writeline("接收數據內容:"+requestinfo.body);
    }
  }
}
 
program類

通訊協議需要使用小工具進行調試,本人使用的是tcp/udp端口調試工具sockettool v2.大家可以直接進行下載。使用hex模式進行發送16進制報文,服務器輸出結果:

C# 常用協議實現模版及FixedSizeReceiveFilter示例(SuperSocket入門)

C# 常用協議實現模版及FixedSizeReceiveFilter示例(SuperSocket入門)

本文參考官方文檔 內置的常用協議實現模版

以上就是本文的全部內容,希望本文的內容對大家的學習或者工作能帶來一定的幫助,同時也希望多多支持服務器之家!

原文鏈接:http://www.cnblogs.com/fly-bird/p/6345084.html

延伸 · 閱讀

精彩推薦
  • C#WPF 自定義雷達圖開發實例教程

    WPF 自定義雷達圖開發實例教程

    這篇文章主要介紹了WPF 自定義雷達圖開發實例教程,本文介紹的非常詳細,具有參考借鑒價值,需要的朋友可以參考下...

    WinterFish13112021-12-06
  • C#深入解析C#中的交錯數組與隱式類型的數組

    深入解析C#中的交錯數組與隱式類型的數組

    這篇文章主要介紹了深入解析C#中的交錯數組與隱式類型的數組,隱式類型的數組通常與匿名類型以及對象初始值設定項和集合初始值設定項一起使用,需要的...

    C#教程網6172021-11-09
  • C#C#設計模式之Visitor訪問者模式解決長隆歡樂世界問題實例

    C#設計模式之Visitor訪問者模式解決長隆歡樂世界問題實例

    這篇文章主要介紹了C#設計模式之Visitor訪問者模式解決長隆歡樂世界問題,簡單描述了訪問者模式的定義并結合具體實例形式分析了C#使用訪問者模式解決長...

    GhostRider9502022-01-21
  • C#C#裁剪,縮放,清晰度,水印處理操作示例

    C#裁剪,縮放,清晰度,水印處理操作示例

    這篇文章主要為大家詳細介紹了C#裁剪,縮放,清晰度,水印處理操作示例,具有一定的參考價值,感興趣的小伙伴們可以參考一下...

    吳 劍8332021-12-08
  • C#C# 實現對PPT文檔加密、解密及重置密碼的操作方法

    C# 實現對PPT文檔加密、解密及重置密碼的操作方法

    這篇文章主要介紹了C# 實現對PPT文檔加密、解密及重置密碼的操作方法,非常不錯,具有參考借鑒價值,需要的朋友可以參考下...

    E-iceblue5012022-02-12
  • C#Unity3D實現虛擬按鈕控制人物移動效果

    Unity3D實現虛擬按鈕控制人物移動效果

    這篇文章主要為大家詳細介紹了Unity3D實現虛擬按鈕控制人物移動效果,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一...

    shenqingyu060520232410972022-03-11
  • C#C#實現XML文件讀取

    C#實現XML文件讀取

    這篇文章主要為大家詳細介紹了C#實現XML文件讀取的相關代碼,具有一定的參考價值,感興趣的小伙伴們可以參考一下...

    Just_for_Myself6702022-02-22
  • C#C#通過KD樹進行距離最近點的查找

    C#通過KD樹進行距離最近點的查找

    這篇文章主要為大家詳細介紹了C#通過KD樹進行距離最近點的查找,具有一定的參考價值,感興趣的小伙伴們可以參考一下...

    帆帆帆6112022-01-22
主站蜘蛛池模板: 久久精品麻豆国产天美传媒果冻 | 久久精品视频uu | 亚洲国产自拍在线 | 国内精品久久久久影院网站 | 丝瓜秋葵番茄绿巨人在线观看 | 日本春菜花在线中文字幕 | 小鸟酱视频在线观看 | 国产三级自拍视频 | 97久久精品午夜一区二区 | 青青青国产成人久久111网站 | ssni-497新任美脚女教师 | xxxx成人 | 亚洲AV国产国产久青草 | 亚洲国产欧美在线成人aaaa | 热99精品只有里视频最新 | 欧美另类性xxoo | 美女脱一净二净不带胸罩 | 男人的天堂在线 | 成人影院免费看 | 91庥豆果冻天美精东蜜桃传媒 | 成年人在线观看视频 | 欧美同志网址 | 奇米色7777| 国产自拍视频一区 | 亚洲黄色天堂 | 毛片在线免费观看网站 | 手机在线免费观看视频 | 成人午夜在线视频 | bl双性受乖调教改造身体 | 阿v天堂2020| 欧美日韩一品道 | 日本天堂影院在线播放 | 桃乃木香奈作品在线观看 | 国产精品综合在线 | 亚洲天堂网在线观看视频 | 范冰冰上面好大下面好紧 | 免费观看欧美成人禁片 | 美国雪白人妖sarina | 西西人体大胆啪啪私拍色约约 | 黑人巨大vs北条麻妃在线 | 色里番52kkm全彩 |