Web服务软件工厂(WSSF)演练之三:创建服务契约和实现方法
Web Services Software Factory
Web服务软件工厂(WSSF)演练之三:创建服务契约和实现方法
关键字:Web Services Software Factory, Service Contracts, Service Implementation
您应该已经完成了本演练的第1部分和第2部分,然后再开始这一部分的演练。
今天的教程将为我们的WCF服务创建服务契约和实际实现方法。 首先,在您的解决方案资源管理器中,右键点击MyCryptographyService.model后Add–>New Model。
具体的命名请参见下图所示。
如果您的开发环境没有出现属性窗口,右击空白设计区域,然后单击属性。跟我们创建数据契约一样, Project Mapping Table选择我们对应的项目 “Implementation Technology”下拉列表中选择 WCF Extension。设置“Serializer Type”为DataContractSerializer 。
通过第二部分的练习,你应该熟悉如何操作我们的设计区域 ,工具箱的内容将显示与创建服务契约相关的工具。
拖动一个Service并命名为CryptService,一个Service Contract并命名为CryptService,一个Operation并命名为EncryptString,和两个messages分别命名为 EncryptStringRequest和EncryptStringResponse。到我们的空白设计区域,如图所示排列他们。
使用“Connector”连接工具连接Service Contract Service对象(也可以在Service的属性窗口中设置Service Contract的属性)。下面将是设定Operation和Service Contract及messages之间关系的操作,但这部分使用。“Connector”连接工具有些繁琐,可以直接定义Operation的Service Contract的属性为CryptService,“Request”属性为EncryptStringRequest,“Response”的属性为EncryptStringResponse即可。设置好以后应该如下图所示。
按照上述步骤,从工具箱中在拖动2个Operations到您的设计区域,并分别命名为DecryptString和HashString 。工具箱中在拖动4个Messages到设计区域,并分别命名为DecryptStringRequest 、DecryptStringResponse 、 HashStringRequest 和HashStringResponse 。Operations连接各对象间的连接关系参照EncryptString 的各项属性设置,完成后如下图所示 。
继续努力,我们已经接近成功。下面需要给messages对象添加一些Data Contract Message Part。右键单击EncryptStringRequest选择Add–>Data Contract Message Part。将其命名为EncryptionObject 。单击我们刚刚建立的EncryptionObject,在属性窗口中设置Type。单击(...)按钮,将弹出如下图所示的此对话框。选择逐层展开后选择EncryptionObject单击确定。
继续为DecryptStringRequest对象添加一个Data Contract Message Part 命名为EncryptionObject,为HashStringRequest添加命名为HashObject。并对应设置好Type属性。继续分别为3个Response messages对象添加一个Primitive Message Parts,分别命名为“ReturnValue”。需要注意的是,每个名称输入时要精确,名称字符串结尾不能留有空格,在您采用Copy / Paste名称时尤其要注意这一点,否则检查起来很麻烦。
现在,在设计区域的某个白色地方点击右键,选择Validate All(全部验证)如果返回一些错误,请逐一认真检查。如果全部验证已经通过,右键单击,并选择Generate Code(生成代码)。随后,将会有更多的生成代码出现在MyCryptographyService.ServiceImplementation和MyCryptographyService.ServiceContracts 解决方案的类库项目中。
下一步,右键点击MyCryptographyService.BusinessLogic项目,并选择添加一个Class(类)。命名为CryptographyServiceBusinessLogic.cs 。为了节省时间,我已经准备好了如下的代码,请直接使用如下的代码。
using MyCryptographyService.BusinessEntities;
using MyCryptographyService.DataAccess;
namespace MyCryptographyService.BusinessLogic
{
public class CryptographyServiceBusinessLogic
{
private CryptManager manager = new CryptManager();
public string EncryptString(EncryptionObject encryptionObject)
{
return manager.EncryptString(encryptionObject);
}
public string DecryptString(EncryptionObject encryptionObject)
{
return manager.DecryptString(encryptionObject);
}
public string HashString(HashObject hashObject)
{
return manager.HashString(hashObject);
}
}
}
using MyCryptographyService.BusinessEntities;
using MyCryptographyService.DataAccess;
namespace MyCryptographyService.BusinessLogic
{
///
/// This class isn't going to do much in this tutorial.
/// However, in here, you could apply business logic to your service,
/// and maybe make some decisions about which methods in the Data Access Layer's
/// "manager" that you wanted to call. For this example, it is just an
/// "enterprisey" pass-through to show the concept.
///
public class CryptographyServiceBusinessLogic
{
private CryptManager manager = new CryptManager();
public string EncryptString(EncryptionObject encryptionObject)
{
return manager.EncryptString(encryptionObject);
}
public string DecryptString(EncryptionObject encryptionObject)
{
return manager.DecryptString(encryptionObject);
}
public string HashString(HashObject hashObject)
{
return manager.HashString(hashObject);
}
}
}
Next, right click on the MyCryptographyService.DataAccess project and add a new class called CryptographyServiceManager. The code for it is below.
下一步,右键点击MyCryptographyService.DataAccess项目,并添加一个新类称为CryptographyServiceManager 。它的代码如下。
using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;
using MyCryptographyService.BusinessEntities;
namespace MyCryptographyService.DataAccess
{
/// <SUMMARY>
///
/// </SUMMARY>
public class CryptManager
{
private const string KEY = "$0ftw@r3";
private const string IV = "@p3t30n&";
private DESCryptoServiceProvider des = new DESCryptoServiceProvider();
public CryptManager()
{
des.Key = ASCIIEncoding.ASCII.GetBytes(KEY);
des.IV = ASCIIEncoding.ASCII.GetBytes(IV);
}
public string HashString(HashObject hashObject)
{
switch (hashObject.HashType)
{
case HashType.MD5:
return getMd5Sum(hashObject.StringToHash);
case HashType.SHA256:
return getSha256Hash(hashObject.StringToHash);
default:
throw new NotImplementedException("That Hashing Algorithm is not supported");
}
}
public string EncryptString(EncryptionObject encryptionObject)
{
string cryptText = string.Empty;
switch (encryptionObject.EncryptionAlgorithm)
{
case EncryptionAlgorithm.DES:
cryptText = encryptDes(encryptionObject.Text);
break;
case EncryptionAlgorithm.Rijndael:
cryptText = encryptRijndael(encryptionObject.Text);
break;
default:
throw new NotImplementedException("You provided an algorithm that is not implemented.");
}
return cryptText;
}
public string DecryptString(EncryptionObject encryptionObject)
{
string plainText = string.Empty;
switch (encryptionObject.EncryptionAlgorithm)
{
case EncryptionAlgorithm.DES:
plainText = decryptDes(encryptionObject.Text);
break;
case EncryptionAlgorithm.Rijndael:
plainText = decryptRijndael(encryptionObject.Text);
break;
default:
throw new NotImplementedException("You provided an algorithm that is not implemented.");
}
return plainText;
}
private string encryptDes(string plainText)
{
MemoryStream memoryStream = new MemoryStream();
CryptoStream cryptoStream = new CryptoStream(memoryStream, des.CreateEncryptor(), CryptoStreamMode.Write);
StreamWriter writer = new StreamWriter(cryptoStream);
writer.Write(plainText);
writer.Flush();
cryptoStream.FlushFinalBlock();
writer.Flush();
return Convert.ToBase64String(memoryStream.GetBuffer(), 0, (int)memoryStream.Length);
}
private string decryptDes(string cryptText)
{
MemoryStream memoryStream = new MemoryStream(Convert.FromBase64String(cryptText));
CryptoStream cryptoStream = new CryptoStream(memoryStream, des.CreateDecryptor(), CryptoStreamMode.Read);
StreamReader reader = new StreamReader(cryptoStream);
return reader.ReadToEnd();
}
private string encryptRijndael(string plainText)
{
RijndaelManaged rijndaelCipher = new RijndaelManaged();
byte[] plainBytes = Encoding.Unicode.GetBytes(plainText);
byte[] salt = Encoding.ASCII.GetBytes(KEY.Length.ToString());
//This class uses an extension of the PBKDF1 algorithm defined in the PKCS#5 v2.0
//standard to derive bytes suitable for use as key material from a password.
//The standard is documented in IETF RRC 2898.
PasswordDeriveBytes secretKey = new PasswordDeriveBytes(KEY, salt);
//Creates a symmetric encryptor object.
ICryptoTransform encryptor = rijndaelCipher.CreateEncryptor(secretKey.GetBytes(32), secretKey.GetBytes(16));
MemoryStream memoryStream = new MemoryStream();
//Defines a stream that links data streams to cryptographic transformations
CryptoStream cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write);
cryptoStream.Write(plainBytes, 0, plainBytes.Length);
//Writes the final state and clears the buffer
cryptoStream.FlushFinalBlock();
byte[] cipherBytes = memoryStream.ToArray();
memoryStream.Close();
cryptoStream.Close();
string encryptedData = Convert.ToBase64String(cipherBytes);
return encryptedData;
}
private string decryptRijndael(string cipherText)
{
RijndaelManaged rijndaelCipher = new RijndaelManaged();
byte[] encryptedData = Convert.FromBase64String(cipherText);
byte[] salt = Encoding.ASCII.GetBytes(KEY.Length.ToString());
//Making of the key for decryption
PasswordDeriveBytes secretKey = new PasswordDeriveBytes(KEY, salt);
//Creates a symmetric Rijndael decryptor object.
ICryptoTransform decryptor = rijndaelCipher.CreateDecryptor(secretKey.GetBytes(32), secretKey.GetBytes(16));
MemoryStream memoryStream = new MemoryStream(encryptedData);
//Defines the cryptographics stream for decryption.THe stream contains decrpted data
CryptoStream cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read);
byte[] plainText = new byte[encryptedData.Length];
int decryptedCount = cryptoStream.Read(plainText, 0, plainText.Length);
memoryStream.Close();
cryptoStream.Close();
//Converting to string
string decryptedData = Encoding.Unicode.GetString(plainText, 0, decryptedCount);
return decryptedData;
}
private string getMd5Sum(string inputText)
{
// First we need to convert the string into bytes, which
// means using a text encoder.
Encoder encoder = System.Text.Encoding.Unicode.GetEncoder();
// Create a buffer large enough to hold the string
byte[] unicodeText = new byte[inputText.Length * 2];
encoder.GetBytes(inputText.ToCharArray(), 0, inputText.Length, unicodeText, 0, true);
// Now that we have a byte array we can ask the CSP to hash it
MD5 md5 = new MD5CryptoServiceProvider();
byte[] result = md5.ComputeHash(unicodeText);
// Build the final string by converting each byte
// into hex and appending it to a StringBuilder
StringBuilder sb = new StringBuilder();
for (int i = 0; i < result.Length; i++)
{
sb.Append(result[i].ToString("X2"));
}
// And return it
return sb.ToString();
}
private string getSha256Hash(string plainText)
{
byte[] plainTextBytes = Encoding.UTF8.GetBytes(plainText);
byte[] saltBytes = Encoding.UTF8.GetBytes(IV);
byte[] plainTextWithSaltBytes =
new byte[plainTextBytes.Length + saltBytes.Length];
for (int i = 0; i < plainTextBytes.Length; i++)
plainTextWithSaltBytes[i] = plainTextBytes[i];
for (int i = 0; i < saltBytes.Length; i++)
plainTextWithSaltBytes[plainTextBytes.Length + i] = saltBytes[i];
HashAlgorithm hash = new SHA256Managed();
byte[] hashBytes = hash.ComputeHash(plainTextWithSaltBytes);
byte[] hashWithSaltBytes = new byte[hashBytes.Length +
saltBytes.Length];
for (int i = 0; i < hashBytes.Length; i++)
hashWithSaltBytes[i] = hashBytes[i];
for (int i = 0; i < saltBytes.Length; i++)
hashWithSaltBytes[hashBytes.Length + i] = saltBytes[i];
string hashValue = Convert.ToBase64String(hashWithSaltBytes);
return hashValue;
}
}
}
using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;
using MyCryptographyService.BusinessEntities;
namespace MyCryptographyService.DataAccess
{
///
/// This is the class where all the action happens, and all gathering of external
/// resources (DB, XML files, registry, etc) should be done from here. Ideally, in this
/// scenerio, you would store your keys external to the application and the
/// transformations would be done here with those keys after they were received.
///
/// NOTE: These implementations are derivations (or outright copying) from samples
/// posted on the internet. Included are:
///
/// http://blog.stevex.net/index.php/c-code-snippet-creating-an-md5-hash-string/
/// http://www.codeproject.com/KB/cs/NET_Encrypt_Decrypt.aspx
/// http://www.obviex.com/samples/hash.aspx
///
public class CryptManager
{
// Note: This is not a tutorial on encryption. Key management
// is paramount in good encryption and of course storing your
// key in plain text in the code is not a good idea. This is
// merely sample code and should not be considered good
// cryptography practice.
private const string KEY = "$0ftw@r3";
private const string IV = "@p3t30n&";
private DESCryptoServiceProvider des = new DESCryptoServiceProvider();
public CryptManager()
{
des.Key = ASCIIEncoding.ASCII.GetBytes(KEY);
des.IV = ASCIIEncoding.ASCII.GetBytes(IV);
}
public string HashString(HashObject hashObject)
{
switch (hashObject.HashType)
{
case HashType.MD5:
return getMd5Sum(hashObject.StringToHash);
case HashType.SHA256:
return getSha256Hash(hashObject.StringToHash);
default:
throw new NotImplementedException("That Hashing Algorithm is not supported");
}
}
public string EncryptString(EncryptionObject encryptionObject)
{
string cryptText = string.Empty;
switch (encryptionObject.EncryptionAlgorithm)
{
case EncryptionAlgorithm.DES:
cryptText = encryptDes(encryptionObject.Text);
break;
case EncryptionAlgorithm.Rijndael:
cryptText = encryptRijndael(encryptionObject.Text);
break;
default:
throw new NotImplementedException("You provided an algorithm that is not implemented.");
}
return cryptText;
}
public string DecryptString(EncryptionObject encryptionObject)
{
string plainText = string.Empty;
switch (encryptionObject.EncryptionAlgorithm)
{
case EncryptionAlgorithm.DES:
plainText = decryptDes(encryptionObject.Text);
break;
case EncryptionAlgorithm.Rijndael:
plainText = decryptRijndael(encryptionObject.Text);
break;
default:
throw new NotImplementedException("You provided an algorithm that is not implemented.");
}
return plainText;
}
private string encryptDes(string plainText)
{
MemoryStream memoryStream = new MemoryStream();
CryptoStream cryptoStream = new CryptoStream(memoryStream, des.CreateEncryptor(), CryptoStreamMode.Write);
StreamWriter writer = new StreamWriter(cryptoStream);
writer.Write(plainText);
writer.Flush();
cryptoStream.FlushFinalBlock();
writer.Flush();
return Convert.ToBase64String(memoryStream.GetBuffer(), 0, (int)memoryStream.Length);
}
private string decryptDes(string cryptText)
{
MemoryStream memoryStream = new MemoryStream(Convert.FromBase64String(cryptText));
CryptoStream cryptoStream = new CryptoStream(memoryStream, des.CreateDecryptor(), CryptoStreamMode.Read);
StreamReader reader = new StreamReader(cryptoStream);
return reader.ReadToEnd();
}
private string encryptRijndael(string plainText)
{
RijndaelManaged rijndaelCipher = new RijndaelManaged();
byte[] plainBytes = Encoding.Unicode.GetBytes(plainText);
byte[] salt = Encoding.ASCII.GetBytes(KEY.Length.ToString());
//This class uses an extension of the PBKDF1 algorithm defined in the PKCS#5 v2.0
//standard to derive bytes suitable for use as key material from a password.
//The standard is documented in IETF RRC 2898.
PasswordDeriveBytes secretKey = new PasswordDeriveBytes(KEY, salt);
//Creates a symmetric encryptor object.
ICryptoTransform encryptor = rijndaelCipher.CreateEncryptor(secretKey.GetBytes(32), secretKey.GetBytes(16));
MemoryStream memoryStream = new MemoryStream();
//Defines a stream that links data streams to cryptographic transformations
CryptoStream cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write);
cryptoStream.Write(plainBytes, 0, plainBytes.Length);
//Writes the final state and clears the buffer
cryptoStream.FlushFinalBlock();
byte[] cipherBytes = memoryStream.ToArray();
memoryStream.Close();
cryptoStream.Close();
string encryptedData = Convert.ToBase64String(cipherBytes);
return encryptedData;
}
private string decryptRijndael(string cipherText)
{
RijndaelManaged rijndaelCipher = new RijndaelManaged();
byte[] encryptedData = Convert.FromBase64String(cipherText);
byte[] salt = Encoding.ASCII.GetBytes(KEY.Length.ToString());
//Making of the key for decryption
PasswordDeriveBytes secretKey = new PasswordDeriveBytes(KEY, salt);
//Creates a symmetric Rijndael decryptor object.
ICryptoTransform decryptor = rijndaelCipher.CreateDecryptor(secretKey.GetBytes(32), secretKey.GetBytes(16));
MemoryStream memoryStream = new MemoryStream(encryptedData);
//Defines the cryptographics stream for decryption.THe stream contains decrpted data
CryptoStream cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read);
byte[] plainText = new byte[encryptedData.Length];
int decryptedCount = cryptoStream.Read(plainText, 0, plainText.Length);
memoryStream.Close();
cryptoStream.Close();
//Converting to string
string decryptedData = Encoding.Unicode.GetString(plainText, 0, decryptedCount);
return decryptedData;
}
private string getMd5Sum(string inputText)
{
// First we need to convert the string into bytes, which
// means using a text encoder.
Encoder encoder = System.Text.Encoding.Unicode.GetEncoder();
// Create a buffer large enough to hold the string
byte[] unicodeText = new byte[inputText.Length * 2];
encoder.GetBytes(inputText.ToCharArray(), 0, inputText.Length, unicodeText, 0, true);
// Now that we have a byte array we can ask the CSP to hash it
MD5 md5 = new MD5CryptoServiceProvider();
byte[] result = md5.ComputeHash(unicodeText);
// Build the final string by converting each byte
// into hex and appending it to a StringBuilder
StringBuilder sb = new StringBuilder();
for (int i = 0; i < result.Length; i++)
{
sb.Append(result[i].ToString("X2"));
}
// And return it
return sb.ToString();
}
private string getSha256Hash(string plainText)
{
byte[] plainTextBytes = Encoding.UTF8.GetBytes(plainText);
byte[] saltBytes = Encoding.UTF8.GetBytes(IV);
byte[] plainTextWithSaltBytes =
new byte[plainTextBytes.Length + saltBytes.Length];
for (int i = 0; i < plainTextBytes.Length; i++)
plainTextWithSaltBytes[i] = plainTextBytes[i];
for (int i = 0; i < saltBytes.Length; i++)
plainTextWithSaltBytes[plainTextBytes.Length + i] = saltBytes[i];
HashAlgorithm hash = new SHA256Managed();
byte[] hashBytes = hash.ComputeHash(plainTextWithSaltBytes);
byte[] hashWithSaltBytes = new byte[hashBytes.Length +
saltBytes.Length];
for (int i = 0; i < hashBytes.Length; i++)
hashWithSaltBytes[i] = hashBytes[i];
for (int i = 0; i < saltBytes.Length; i++)
hashWithSaltBytes[hashBytes.Length + i] = saltBytes[i];
string hashValue = Convert.ToBase64String(hashWithSaltBytes);
return hashValue;
}
}
}
在最后完成本部分演练之前,我们还需要做一件事,我们需要将实现的方法联系起来,这将是我们的endpoints(节点)。当我们确定了我们的服务契约,我们定义了一些方法。我们必须实现这些方法,用我们的业务层配合他们,并最终实现我们的数据访问层。
然而,我们每次对服务契约的设计所做任何更改,由于生成的代码将因覆盖而改变,因此我们必须创建另一个partial class代码文件。根据我们自己的实现情况,重写其中的方法,根据提交的请求实现并返回结果。现在MyCryptographyService.ServiceImplementation项目下的CryptImplementation.cs 。文件中,你应该有以下的代码。
using MyCryptographyService.BusinessLogic;
using MyCryptographyService.MessageContracts;
namespace MyCryptographyService.ServiceImplementation
{
public partial class CryptService : CryptServiceBase
{
protected CryptographyServiceBusinessLogic businessLogic = new CryptographyServiceBusinessLogic();
public override EncryptStringResponse EncryptString(EncryptStringRequest request)
{
EncryptStringResponse response = new EncryptStringResponse();
response.ReturnValue = businessLogic.EncryptString(
EncryptionObjectTranslator.TranslateEncryptionObjectToEncryptionObject(request.EncryptionObject));
return response;
}
public override DecryptStringResponse DecryptString(DecryptStringRequest request)
{
DecryptStringResponse response = new DecryptStringResponse();
response.ReturnValue = businessLogic.DecryptString(
EncryptionObjectTranslator.TranslateEncryptionObjectToEncryptionObject(request.EncryptionObject));
return response;
}
public override HashStringResponse HashString(HashStringRequest request)
{
HashStringResponse response = new HashStringResponse();
response.ReturnValue = businessLogic.HashString(
HashObjectTranslator.TranslateHashObjectToHashObject(request.HashObject));
return response;
}
}
}
我们使用Translators的方法创建一些代码,他们对业务实体和数据契约进行了映射。代码应该是非常简单,但如果您有任何问题,请在此留下您的意见,我会尽快答复。
在演练的第4部分,我们会创建托管主机,部署和测试WSDL。敬请关注。
Web服务软件工厂(WSSF)演练之四:创建托管主机,部署和测试WSDL
转载于:https://www.cnblogs.com/LiYunQi/archive/2009/02/07/1385968.html
Web服务软件工厂(WSSF)演练之三:创建服务契约和实现方法相关推荐
- 【转】Web服务软件工厂
patterns & practices开发中心 摘要 Web服务软件工厂(英文为Web Service Software Factory,也称作服务工厂)是一个集成的工具.模式.源代码和规范 ...
- Mac系统home目录无法创建文件最全解决方法
问题 mac 10.15及以上,默认开启了SIP系统保护,开发中普通用户是无法创建文件. 我们开发中有时会把一些文件目录设置为/home,比如日志:/home/logs/ 此时启动服务会报错,无法创建 ...
- 蚂蚁海图切片工具linux版本,Web海图服务软件 - 海图在线
WMS 海图服务软件 很多服务,例如,海洋气象预报.海上船舶移动目标的监控等,都需要显示电子海图作为背景.ChartServer产品就是为满足此类需求 - Web海图服务而开发设计的. ChartSe ...
- 关于系统架构和WEB服务软件的概述
关于系统架构 系统架构的两种形式 C/S架构 B/S架构 C/S架构 : Client / Server 即客户端 / 服务器 C/S架构的软件或者说系统有哪些 QQ(先去腾讯官网下载一个QQ软件,几 ...
- 软件2.0时代之三:毛新生谈他眼中的Web2.0
软件2.0时代之三:毛新生谈他眼中的Web2.0 2007.08.31 来自:CSDN 共有评论(7)条 发表评论 [收藏到我的网摘] Web是一个"活物",是一个 ...
- 《企业软件交付:敏捷与高效管理精要》——3.4 企业软件交付的软件工厂方法...
3.4 企业软件交付的软件工厂方法 正如我们前面讨论的,今天的机构面对的商业环境正以前所未有的速度发生变化.与此同时,这些机构还要管理和降低整个机构的运营成本.这就直接意味着,他们不仅要最大限度地减少 ...
- Android 图形架构 之三—— 创建Layer、Surface、SurfaceControl
前言 上一篇我们分析了,app与SurfaceFlinger建立连接的过程,现在我们就可以继续往下分析,看下创建Surface的过程. 我们可以将Surface理解为一个绘图表面,Android应用程 ...
- 工控用Web组态软件比组态软件更高效
从事相关工作的对"组态软件"应该都不陌生,那Web组态软件又是什么呢?本文将对Web组态可视化软件(下称"Web组态软件")做简单介绍,可视化编辑器是Web组态 ...
- 主流七款WEB服务器软件点评
如今互联网的WEB平台种类繁多,各种软硬件组合的WEB系统更是数不胜数,下面就来介绍一下几种常用的WEB服务器: 1.Microsoft IIS Microsoft的Web服务器产品为Internet ...
最新文章
- QQ音乐爬取(python实现)
- 15.Three Sum
- Spring 常用注入注解(annotation)和其对应xml标签
- 世界各国的教育差距有多大?这几部全世界都在热议的教育纪录片,揭开一切.........
- NOI-砝码称重v2 多重背包 生成函数
- 2022年后人工智能/深度学习八大应用方向
- python大神的成长之路_Python大神成长之路: 第三次学习记录 集合 函数 装饰 re...
- ROS入门笔记(三):二进制包与源代码包
- STM32 I2C通信(读写eeprom)
- acer台式电脑怎么重装系统_电脑怎么重装系统 手把手教你
- Kubernetes 小白学习笔记(11)--搭建一个kubernetes集群-为worker节点配置命令行交互环境
- 计算机office软件版本,office哪个版本最好用,你在用的office是哪个版本
- 安装SHARP MX-3618NC PCL6打印机驱动程序
- 操作系统——信号量机制(PV操作)
- JavasScript 第二天课 课后笔记 2022.3.26
- 24种游戏化设计工具
- 页面插件集成:博客详情页
- 二维码之zxing二维码解析图片资源
- 用计算机发传真,如何用电脑发网络传真?在电脑里怎么发传真?
- css icon设置,CSS之字体图标 icon 的多种实现
热门文章
- 红外技术如何应用在 3D 电视
- JDK1.8 IdentityHashMap
- (74)信号发生器DDS三角波设计(二)(第15天)
- (11)Verilog HDL变量:wire型
- (30)System Verilog设计SPI发送
- MicroBlaze软核介绍
- nc65 单据非向导开发 源代码_最受Java开发者喜爱的5款开发工具
- html 左边固定右边自动,七种实现左侧固定,右侧自适应两栏布局的方法
- matlab两个数组竖向叠加,matlab - 过滤器同时使用两个数组中的元素 - 堆栈内存溢出...
- 进程通信方法的特点以及使用场景