问题:使用 MySqlConnection (.NET) 到 SSL 连接到 MySql-server 从某些客户端失败

这确实是一个关于 SSL 的问题,下面是 Socket-traces,所以即使您以前从未使用过 MySql,也不要害羞继续阅读。无论如何,我们正在尝试使用 MySqlConnection(.NET 2.0 和 4.0,版本无关紧要)通过 SSL(带有客户端 .PFX 证书)连接到 MySql 服务器,如下所示:

using (MySqlConnection con = new MySqlConnection(@"server=xxx.yyy.zzz.uuu;database=whopper;user=Username;password=Secret;Certificate Store Location=CurrentUser;Pooling=false;SSL Mode=Required;Certificate Thumbprint=NNNN;Keepalive=60")) {
    con.Open();
}

证书安装在个人存储中,整个过程在_一些_机器上运行;但不在其他人身上。堆栈跟踪:

System.IO.IOException: Received an unexpected EOF or 0 bytes from the transport stream.
   at System.Net.FixedSizeReader.ReadPacket(Byte[] buffer, Int32 offset, Int32 count)
   at System.Net.Security.SslState.StartReadFrame(Byte[] buffer, Int32 readBytes, AsyncProtocolRequest asyncRequest)
   at System.Net.Security.SslState.StartReceiveBlob(Byte[] buffer, AsyncProtocolRequest asyncRequest)
   at System.Net.Security.SslState.CheckCompletionBeforeNextReceive(ProtocolToken message, AsyncProtocolRequest asyncRequest)
   at System.Net.Security.SslState.StartSendBlob(Byte[] incoming, Int32 count, AsyncProtocolRequest asyncRequest)
   at System.Net.Security.SslState.ProcessReceivedBlob(Byte[] buffer, Int32 count, AsyncProtocolRequest asyncRequest)
   at System.Net.Security.SslState.StartReadFrame(Byte[] buffer, Int32 readBytes, AsyncProtocolRequest asyncRequest)
   at System.Net.Security.SslState.StartReceiveBlob(Byte[] buffer, AsyncProtocolRequest asyncRequest)
   at System.Net.Security.SslState.CheckCompletionBeforeNextReceive(ProtocolToken message, AsyncProtocolRequest asyncRequest)
   at System.Net.Security.SslState.StartSendBlob(Byte[] incoming, Int32 count, AsyncProtocolRequest asyncRequest)
   at System.Net.Security.SslState.ProcessReceivedBlob(Byte[] buffer, Int32 count, AsyncProtocolRequest asyncRequest)
   at System.Net.Security.SslState.StartReadFrame(Byte[] buffer, Int32 readBytes, AsyncProtocolRequest asyncRequest)
   at System.Net.Security.SslState.StartReceiveBlob(Byte[] buffer, AsyncProtocolRequest asyncRequest)
   at System.Net.Security.SslState.CheckCompletionBeforeNextReceive(ProtocolToken message, AsyncProtocolRequest asyncRequest)
   at System.Net.Security.SslState.StartSendBlob(Byte[] incoming, Int32 count, AsyncProtocolRequest asyncRequest)
   at System.Net.Security.SslState.ProcessReceivedBlob(Byte[] buffer, Int32 count, AsyncProtocolRequest asyncRequest)
   at System.Net.Security.SslState.StartReadFrame(Byte[] buffer, Int32 readBytes, AsyncProtocolRequest asyncRequest)
   at System.Net.Security.SslState.StartReceiveBlob(Byte[] buffer, AsyncProtocolRequest asyncRequest)
   at System.Net.Security.SslState.CheckCompletionBeforeNextReceive(ProtocolToken message, AsyncProtocolRequest asyncRequest)
   at System.Net.Security.SslState.StartSendBlob(Byte[] incoming, Int32 count, AsyncProtocolRequest asyncRequest)
   at System.Net.Security.SslState.ProcessReceivedBlob(Byte[] buffer, Int32 count, AsyncProtocolRequest asyncRequest)
   at System.Net.Security.SslState.StartReadFrame(Byte[] buffer, Int32 readBytes, AsyncProtocolRequest asyncRequest)
   at System.Net.Security.SslState.StartReceiveBlob(Byte[] buffer, AsyncProtocolRequest asyncRequest)
   at System.Net.Security.SslState.CheckCompletionBeforeNextReceive(ProtocolToken message, AsyncProtocolRequest asyncRequest)
   at System.Net.Security.SslState.StartSendBlob(Byte[] incoming, Int32 count, AsyncProtocolRequest asyncRequest)
   at System.Net.Security.SslState.ForceAuthentication(Boolean receiveFirst, Byte[] buffer, AsyncProtocolRequest asyncRequest)
   at System.Net.Security.SslState.ProcessAuthentication(LazyAsyncResult lazyResult)
   at System.Net.Security.SslStream.AuthenticateAsClient(String targetHost, X509CertificateCollection clientCertificates, SslProtocols enabledSslProtocols, Boolean checkCertificateRevocation)
   at MySql.Data.MySqlClient.NativeDriver.StartSSL() in D:\Downloads\mysql-connector-net-6.4.4-src\Source\MySql.Data\NativeDriver.cs:line 353
   at MySql.Data.MySqlClient.NativeDriver.Open() in D:\Downloads\mysql-connector-net-6.4.4-src\Source\MySql.Data\NativeDriver.cs:line 267
   at MySql.Data.MySqlClient.Driver.Open() in D:\Downloads\mysql-connector-net-6.4.4-src\Source\MySql.Data\Driver.cs:line 211
   at MySql.Data.MySqlClient.Driver.Create(MySqlConnectionStringBuilder settings) in D:\Downloads\mysql-connector-net-6.4.4-src\Source\MySql.Data\Driver.cs:line 199
   at MySql.Data.MySqlClient.MySqlConnection.Open() in D:\Downloads\mysql-connector-net-6.4.4-src\Source\MySql.Data\Connection.cs:line 489
   at ConsoleApplication1.Program.Main(String[] args) in D:\Downloads\mysql-connector-net-6.4.4-src\Source\ConsoleApplication1\Program.cs:line 14

现在,为了调试它,我下载了 MySqlConnection 的最新 src,所以我可以跟踪所有内容,直到调用 AuthenticateAsClient - MySqlConnection 调用 .NET 框架来设置 SSL 连接。接下来,我将以下内容添加到我的 app.config 中(以获取套接字跟踪并继续调查):

<?xml version="1.0"?>
<configuration>
    <system.diagnostics>
        <sources>
            <source name="System.Net.Sockets">
                <listeners>
                    <add name="Sockets"/>
                </listeners>
            </source>
        </sources>
        <switches>
            <add name="System.Net.Sockets" value="31"/>
        </switches>
        <sharedListeners>
            <add name="Sockets" type="System.Diagnostics.TextWriterTraceListener" initializeData="c:\socketdump.log"/>
        </sharedListeners>
        <trace autoflush="true"/>
    </system.diagnostics>
</configuration>

在连接实际成功的客户端计算机上,该文件以:

System.Net.Sockets Verbose: 0 : [5000] Socket#29190913::Socket(InterNetwork#2)
System.Net.Sockets Verbose: 0 : [5000] Exiting Socket#29190913::Socket() 
System.Net.Sockets Verbose: 0 : [5000] Socket#29190913::BeginConnect(142:3306#-1905378776)
System.Net.Sockets Verbose: 0 : [5000] Socket#29190913::BeginConnectEx()
System.Net.Sockets Verbose: 0 : [5000] Socket#29190913::InternalBind(0:0#0)
System.Net.Sockets Verbose: 0 : [5000] Exiting Socket#29190913::InternalBind() 
System.Net.Sockets Verbose: 0 : [5000] Exiting Socket#29190913::BeginConnectEx()    -> ConnectOverlappedAsyncResult#9029417
System.Net.Sockets Verbose: 0 : [5000] Socket#29190913::EndConnect(ConnectOverlappedAsyncResult#9029417)
System.Net.Sockets Verbose: 0 : [5000] Exiting Socket#29190913::EndConnect() 
System.Net.Sockets Verbose: 0 : [5000] Socket#29190913::Receive()
System.Net.Sockets Verbose: 0 : [5000] Data from Socket#29190913::Receive
System.Net.Sockets Verbose: 0 : [5000] 00000000 : 3D 00 00 00 0A 35 2E 31-2E 35 34 2D 31 75 62 75 : =....5.1.54-1ubu
System.Net.Sockets Verbose: 0 : [5000] 00000010 : 6E 74 75 34 00 5E 04 00-00 2F 32 4C 69 4D 26 73 : ntu4.^.../2LiM&s
System.Net.Sockets Verbose: 0 : [5000] 00000020 : 32 00 FF FF 08 02 00 00-00 00 00 00 00 00 00 00 : 2...............
System.Net.Sockets Verbose: 0 : [5000] 00000030 : 00 00 00 00 3E 23 31 6B-4B 75 2B 72 7D 3F 60 48 : ....>#1kKu+r}?`H
System.Net.Sockets Verbose: 0 : [5000] 00000040 : 00                                              : .
System.Net.Sockets Verbose: 0 : [5000] Exiting Socket#29190913::Receive()   -> 65#65
System.Net.Sockets Verbose: 0 : [5000] Socket#29190913::Send()
System.Net.Sockets Verbose: 0 : [5000] Data from Socket#29190913::Send
System.Net.Sockets Verbose: 0 : [5000] 00000000 : 04 00 00 01 8F AA 03 00-                        : ........
System.Net.Sockets Verbose: 0 : [5000] Exiting Socket#29190913::Send()  -> 8#8
System.Net.Sockets Verbose: 0 : [5000] Socket#29190913::Send()
System.Net.Sockets Verbose: 0 : [5000] Data from Socket#29190913::Send
System.Net.Sockets Verbose: 0 : [5000] 00000000 : 16 03 01 00 5A 01 00 00-56 03 01 4E 89 D2 2C 88 : ....Z...V..N..,.
System.Net.Sockets Verbose: 0 : [5000] 00000010 : 6E E9 57 C7 A0 A2 83 59-DC B1 1D 11 01 F6 32 F0 : n.W....Y......2.
System.Net.Sockets Verbose: 0 : [5000] 00000020 : E0 50 63 37 BD EE AD A2-07 3B CC 00 00 18 00 2F : .Pc7.....;...../
System.Net.Sockets Verbose: 0 : [5000] 00000030 : 00 35 00 05 00 0A C0 13-C0 14 C0 09 C0 0A 00 32 : .5.............2
System.Net.Sockets Verbose: 0 : [5000] 00000040 : 00 38 00 13 00 04 01 00-00 15 FF 01 00 01 00 00 : .8..............
System.Net.Sockets Verbose: 0 : [5000] 00000050 : 0A 00 06 00 04 00 17 00-18 00 0B 00 02 01 00    : ...............
System.Net.Sockets Verbose: 0 : [5000] Exiting Socket#29190913::Send()  -> 95#95
System.Net.Sockets Verbose: 0 : [5000] Socket#29190913::Receive()
System.Net.Sockets Verbose: 0 : [5000] Data from Socket#29190913::Receive
System.Net.Sockets Verbose: 0 : [5000] 00000000 : 16 03 01 00 4A                                  : ....J
System.Net.Sockets Verbose: 0 : [5000] Exiting Socket#29190913::Receive()   -> 5#5
System.Net.Sockets Verbose: 0 : [5000] Socket#29190913::Receive()
System.Net.Sockets Verbose: 0 : [5000] Data from Socket#29190913::Receive
System.Net.Sockets Verbose: 0 : [5000] 00000005 : 02 00 00 46 03 01 D3 49-C4 02 A3 F4 60 21 C1 F0 : ...F...I....`!..
System.Net.Sockets Verbose: 0 : [5000] 00000015 : 19 06 33 F1 F2 9B D1 8D-65 C6 A6 EF C8 C8 21 BC : ..3.....e.....!.
System.Net.Sockets Verbose: 0 : [5000] 00000025 : C4 80 AC 9D 0D 0C 20 D9-5D A0 92 8A F5 70 D8 B0 : ...... .]....p..
System.Net.Sockets Verbose: 0 : [5000] 00000035 : 9A EB 86 26 2E AD 0B EC-EF FB EE AB B7 4F 74 BC : ...&.........Ot.
System.Net.Sockets Verbose: 0 : [5000] 00000045 : F3 31 E0 2A 0F 9F EB 00-35 00                   : .1.*....5.
System.Net.Sockets Verbose: 0 : [5000] Exiting Socket#29190913::Receive()   -> 74#74
System.Net.Sockets Verbose: 0 : [5000] Socket#29190913::Receive()
System.Net.Sockets Verbose: 0 : [5000] Data from Socket#29190913::Receive
System.Net.Sockets Verbose: 0 : [5000] 00000000 : 16 03 01 03 AC                                  : .....
System.Net.Sockets Verbose: 0 : [5000] Exiting Socket#29190913::Receive()   -> 5#5
System.Net.Sockets Verbose: 0 : [5000] Socket#29190913::Receive()
System.Net.Sockets Verbose: 0 : [5000] Data from Socket#29190913::Receive
[server certificate received here. identical in both dumps]
System.Net.Sockets Verbose: 0 : [5000] Exiting Socket#29190913::Receive()   -> 940#940
System.Net.Sockets Verbose: 0 : [5000] Socket#29190913::Receive()
System.Net.Sockets Verbose: 0 : [5000] Data from Socket#29190913::Receive
System.Net.Sockets Verbose: 0 : [5000] 00000000 : 16 03 01 00 09                                  : .....
System.Net.Sockets Verbose: 0 : [5000] Exiting Socket#29190913::Receive()   -> 5#5
System.Net.Sockets Verbose: 0 : [5000] Socket#29190913::Receive()
System.Net.Sockets Verbose: 0 : [5000] Data from Socket#29190913::Receive
System.Net.Sockets Verbose: 0 : [5000] 00000005 : 0D 00 00 05 02 01 02 00-00                      : .........
System.Net.Sockets Verbose: 0 : [5000] Exiting Socket#29190913::Receive()   -> 9#9
System.Net.Sockets Verbose: 0 : [5000] Socket#29190913::Receive()
System.Net.Sockets Verbose: 0 : [5000] Data from Socket#29190913::Receive
System.Net.Sockets Verbose: 0 : [5000] 00000000 : 16 03 01 00 04                                  : .....
System.Net.Sockets Verbose: 0 : [5000] Exiting Socket#29190913::Receive()   -> 5#5
System.Net.Sockets Verbose: 0 : [5000] Socket#29190913::Receive()
System.Net.Sockets Verbose: 0 : [5000] Data from Socket#29190913::Receive
System.Net.Sockets Verbose: 0 : [5000] 00000005 : 0E 00 00 00                                     : ....
System.Net.Sockets Verbose: 0 : [5000] Exiting Socket#29190913::Receive()   -> 4#4
System.Net.Sockets Verbose: 0 : [5000] Socket#29190913::Send()
System.Net.Sockets Verbose: 0 : [5000] Data from Socket#29190913::Send
[everything seems encrypted here]

在建立 SSL 失败的机器上,该文件以(不要介意 BeginConnectEx(),因为这似乎是由 .NET 框架在从 ThreadPool 中的线程调用时完成的):

System.Net.Sockets Verbose: 0 : [3880] Socket#39785641::Socket(InterNetwork#2)
System.Net.Sockets Verbose: 0 : [3880] Exiting Socket#39785641::Socket() 
System.Net.Sockets Verbose: 0 : [3880] Socket#39785641::BeginConnect(142:3306#-1905378776)



System.Net.Sockets Verbose: 0 : [3880] Exiting Socket#39785641::BeginConnect()  -> ConnectAsyncResult#45523402
System.Net.Sockets Verbose: 0 : [3880] Socket#39785641::EndConnect(ConnectAsyncResult#45523402)
System.Net.Sockets Verbose: 0 : [3880] Exiting Socket#39785641::EndConnect() 
System.Net.Sockets Verbose: 0 : [3880] Socket#39785641::Receive()
System.Net.Sockets Verbose: 0 : [3880] Data from Socket#39785641::Receive
System.Net.Sockets Verbose: 0 : [3880] 00000000 : 3D 00 00 00 0A 35 2E 31-2E 35 34 2D 31 75 62 75 : =....5.1.54-1ubu
System.Net.Sockets Verbose: 0 : [3880] 00000010 : 6E 74 75 34 00 62 04 00-00 7A 27 51 43 3C 43 7B : ntu4.b...z'QC<C{
System.Net.Sockets Verbose: 0 : [3880] 00000020 : 64 00 FF FF 08 02 00 00-00 00 00 00 00 00 00 00 : d...............
System.Net.Sockets Verbose: 0 : [3880] 00000030 : 00 00 00 00 63 44 68 61-2E 5C 69 79 46 32 65 29 : ....cDha.\iyF2e)
System.Net.Sockets Verbose: 0 : [3880] 00000040 : 00                                              : .
System.Net.Sockets Verbose: 0 : [3880] Exiting Socket#39785641::Receive()   -> 65#65
System.Net.Sockets Verbose: 0 : [3880] Socket#39785641::Send()
System.Net.Sockets Verbose: 0 : [3880] Data from Socket#39785641::Send
System.Net.Sockets Verbose: 0 : [3880] 00000000 : 04 00 00 01 8F AA 03 00-                        : ........
System.Net.Sockets Verbose: 0 : [3880] Exiting Socket#39785641::Send()  -> 8#8
System.Net.Sockets Verbose: 0 : [3880] Socket#39785641::Send()
System.Net.Sockets Verbose: 0 : [3880] Data from Socket#39785641::Send
System.Net.Sockets Verbose: 0 : [3880] 00000000 : 16 03 01 00 5A 01 00 00-56 03 01 4E 89 D1 92 4C : ....Z...V..N...L
System.Net.Sockets Verbose: 0 : [3880] 00000010 : 61 0C 93 9D 9F 7C 45 39-71 8C 0A 6E 29 7C D9 32 : a....|E9q..n)|.2
System.Net.Sockets Verbose: 0 : [3880] 00000020 : 89 CF 7B D8 1E 33 4F 90-A1 2C 27 00 00 18 00 2F : ..{..3O..,'..../
System.Net.Sockets Verbose: 0 : [3880] 00000030 : 00 35 00 05 00 0A C0 13-C0 14 C0 09 C0 0A 00 32 : .5.............2
System.Net.Sockets Verbose: 0 : [3880] 00000040 : 00 38 00 13 00 04 01 00-00 15 FF 01 00 01 00 00 : .8..............
System.Net.Sockets Verbose: 0 : [3880] 00000050 : 0A 00 06 00 04 00 17 00-18 00 0B 00 02 01 00    : ...............
System.Net.Sockets Verbose: 0 : [3880] Exiting Socket#39785641::Send()  -> 95#95
System.Net.Sockets Verbose: 0 : [3880] Socket#39785641::Receive()
System.Net.Sockets Verbose: 0 : [3880] Data from Socket#39785641::Receive
System.Net.Sockets Verbose: 0 : [3880] 00000000 : 16 03 01 00 4A                                  : ....J
System.Net.Sockets Verbose: 0 : [3880] Exiting Socket#39785641::Receive()   -> 5#5
System.Net.Sockets Verbose: 0 : [3880] Socket#39785641::Receive()
System.Net.Sockets Verbose: 0 : [3880] Data from Socket#39785641::Receive
System.Net.Sockets Verbose: 0 : [3880] 00000005 : 02 00 00 46 03 01 81 9E-F1 4F F4 7A C6 21 B7 94 : ...F.....O.z.!..
System.Net.Sockets Verbose: 0 : [3880] 00000015 : 03 9B 37 C4 E0 EF 0D 1D-86 95 04 2B FE 31 6A A0 : ..7........+.1j.
System.Net.Sockets Verbose: 0 : [3880] 00000025 : DF 1A D8 32 82 9B 20 18-1A 7B C6 FC BB 89 0B DE : ...2.. ..{......
System.Net.Sockets Verbose: 0 : [3880] 00000035 : 97 71 EC 38 9E 14 29 4B-65 BF 23 17 6C 17 1C 3C : .q.8..)Ke.#.l..<
System.Net.Sockets Verbose: 0 : [3880] 00000045 : 6A 5E D9 13 BE CC F9 00-35 00                   : j^......5.
System.Net.Sockets Verbose: 0 : [3880] Exiting Socket#39785641::Receive()   -> 74#74
System.Net.Sockets Verbose: 0 : [3880] Socket#39785641::Receive()
System.Net.Sockets Verbose: 0 : [3880] Data from Socket#39785641::Receive
System.Net.Sockets Verbose: 0 : [3880] 00000000 : 16 03 01 03 AC                                  : .....
System.Net.Sockets Verbose: 0 : [3880] Exiting Socket#39785641::Receive()   -> 5#5
System.Net.Sockets Verbose: 0 : [3880] Socket#39785641::Receive()
System.Net.Sockets Verbose: 0 : [3880] Data from Socket#39785641::Receive
[server certificate received here. identical in both dumps]
System.Net.Sockets Verbose: 0 : [3880] Exiting Socket#39785641::Receive()   -> 940#940
System.Net.Sockets Verbose: 0 : [3880] Socket#39785641::Receive()
System.Net.Sockets Verbose: 0 : [3880] Data from Socket#39785641::Receive
System.Net.Sockets Verbose: 0 : [3880] 00000000 : 16 03 01 00 09                                  : .....
System.Net.Sockets Verbose: 0 : [3880] Exiting Socket#39785641::Receive()   -> 5#5
System.Net.Sockets Verbose: 0 : [3880] Socket#39785641::Receive()
System.Net.Sockets Verbose: 0 : [3880] Data from Socket#39785641::Receive
System.Net.Sockets Verbose: 0 : [3880] 00000005 : 0D 00 00 05 02 01 02 00-00                      : .........
System.Net.Sockets Verbose: 0 : [3880] Exiting Socket#39785641::Receive()   -> 9#9
System.Net.Sockets Verbose: 0 : [3880] Socket#39785641::Receive()
System.Net.Sockets Verbose: 0 : [3880] Data from Socket#39785641::Receive
System.Net.Sockets Verbose: 0 : [3880] 00000000 : 16 03 01 00 04                                  : .....
System.Net.Sockets Verbose: 0 : [3880] Exiting Socket#39785641::Receive()   -> 5#5
System.Net.Sockets Verbose: 0 : [3880] Socket#39785641::Receive()
System.Net.Sockets Verbose: 0 : [3880] Data from Socket#39785641::Receive
System.Net.Sockets Verbose: 0 : [3880] 00000005 : 0E 00 00 00                                     : ....
System.Net.Sockets Verbose: 0 : [3880] Exiting Socket#39785641::Receive()   -> 4#4
System.Net.Sockets Verbose: 0 : [3880] Socket#39785641::Send()
System.Net.Sockets Verbose: 0 : [3880] Data from Socket#39785641::Send
System.Net.Sockets Verbose: 0 : [3880] (printing 1024 out of 1540)
[my client certificate seems to be sent here]

(我已经删除了上面文件的证书部分)。无论如何,我的猜测是,我在某些机器上收到的错误源于 SSL 握手中选择的密码算法。但这真的是黑暗中的一击(?),此刻我正在撕裂我的头发。 SSL 握手似乎在某些机器上失败了。

如果有人知道剖析/解码/分析上述流量的好方法(我已经在 WireShark 中敲了几个小时,但仍然无法弄清楚如何将上述十六进制提供给 SSL 解析器)。或者,如果有人可以直接发现#2中的差异/失败之处,那将有很大帮助!

解答

我在剖析 TLS 协议时自己找到了解决方案。事实证明,在上面的示例中不起作用的客户端在握手时发送了我的客户端证书;而_做工作_的客户不这样做。显然,无论如何都建立了加密(我没有继续讨论 TLS 协议),并且可能会进一步进行证书交换/密钥交换。

要使连接正常工作,我所要做的就是更改连接字符串并删除所有 Certificate*u003d 密钥。特别是“证书存储位置 u003d CurrentUser”。我当前工作的 MySql SSL 连接字符串是:

server=xxx.yyy.zzz.uuu;database=whopper;user=Username;password=Secret;Pooling=false;SSL Mode=Required;Keepalive=60

另外,作为旁注,在剖析通信时,我发现 Tamos CommView 在 VPN 通信中的拦截和剖析方面做得比 WireShark 更好。可能是由于 WinPCaps 无法在 Windows 7 x64 下剖析 VPN 数据包。 CommView 中的 TLS 解析器也真正帮助我找到了握手问题。

另外,作为第二个旁注。 Windows 中的所有 SSL/TLS 通信都由一个名为 schannel.dll 的 DLL 处理。可以通过创建值为 7 的 DWORD HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\EventLogging 来启用对该 dll 的系统事件日志的完全登录。在此处阅读更多信息:http://support.microsoft .com/kb/260729。

让它发挥作用。删除东西。

Logo

华为、百度、京东云现已入驻,来创建你的专属开发者社区吧!

更多推荐