MyBatis入門學習-連接oracle實現CURD基本操作

目錄

  • 前言
  • 導入oracle庫
  • 配置
    • 環境配置
    • 配置引用
    • 配置映射
  • 查詢
    • 單條件查詢
    • 多條件查詢
      • 通過類字段傳遞參數
      • 通過Map接口傳參
      • Param註解
  • 插入
  • 更新
  • 刪除
  • 字段映射
  • 參考文獻

前言

本篇記錄使用mybatis連接oracle數據庫實現基本的CURD操作。

導入oracle庫

由於oracle收費, 因此maven沒有oracle庫包,需要我們自己導入,可以手工導入外部包,也可以將oracle的jar導入到maven庫種。具體導入步驟可以查看Maven添加Oracle的依賴及驅動

導入mybatis庫包,我本地使用的是3.5.5版本。最後的配置如下所示


<dependency>
  <groupId>org.mybatis</groupId>
  <artifactId>mybatis</artifactId>
  <version>3.5.5</version>
</dependency>

<dependency>
  <groupId>com.oracle.jdbc</groupId>
  <artifactId>ojdbc6</artifactId>
  <version>11.2.0.1.0</version>
</dependency>

配置

準備mybatis的配置,在resources目錄下新建一個mybatis-config.xml文件,配置如下

 <?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<!-- 根標籤 -->
<configuration>
    <properties>
        <property name="driver" value="oracle.jdbc.driver.OracleDriver" />
        <property name="url" value="jdbc:oracle:thin:@10.60.45.239:1521:devdb" />
        <property name="username" value="fgmain10001" />
        <property name="password" value="test1" />
    </properties>

    <!-- 環境,可以配置多個,default:指定採用哪個環境 -->
    <environments default="test">
        <!-- id:唯一標識 -->
        <environment id="test">
            <!-- 事務管理器,JDBC類型的事務管理器 -->
            <transactionManager type="JDBC" />
            <!-- 數據源,池類型的數據源 -->
            <dataSource type="POOLED">
                <property name="driver" value="oracle.jdbc.driver.OracleDriver" />
                <property name="url" value="jdbc:oracle:thin:@10.60.45.239:1521:devdb" />
                <property name="username" value="fgmain10001" />
                <property name="password" value="test1" />
            </dataSource>
        </environment>
        <environment id="development">
            <!-- 事務管理器,JDBC類型的事務管理器 -->
            <transactionManager type="JDBC" />
            <!-- 數據源,池類型的數據源 -->
            <dataSource type="POOLED">
                <property name="driver" value="${driver}" /> <!-- 配置了properties,所以可以直接引用 -->
                <property name="url" value="${url}" />
                <property name="username" value="${username}" />
                <property name="password" value="${password}" />
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <mapper resource="singleTransMapper.xml" />
    </mappers>
</configuration>

環境配置

 <environment id="test">
      <!-- 事務管理器,JDBC類型的事務管理器 -->
      <transactionManager type="JDBC" />
      <!-- 數據源,池類型的數據源 -->
      <dataSource type="POOLED">
          <property name="driver" value="oracle.jdbc.driver.OracleDriver" />
          <property name="url" value="jdbc:oracle:thin:@10.60.45.239:1521:devdb" />
          <property name="username" value="fgmain10001" />
          <property name="password" value="test1" />
      </dataSource>
  </environment>

其中environments可以配置多個環境的oracle數據源。

id是環境變量的編號,在<environments>default中可以設置當前的環境值。
dataSource中設置數據源。類型有3種。包括:POOLED池化,UNPOOLED非池化和JNDI從其他配置元加載。
driver配置的類名,oracle填寫oracle.jdbc.driver.OracleDriver
url為配置的數據源,使用239測試庫jdbc:oracle:thin:@10.60.45.239:1521:devdb
username是用戶名。
password是密碼。

配置引用

在value中可以填寫如${變量名}的配置引用,通過在properties/propertie添加對應的實際的配置值。

<configuration>
    <properties>
        <property name="driver" value="oracle.jdbc.driver.OracleDriver" />
    </properties>
    
    <environments default="test">
        ...
        <environment id="test">
            ...
            <dataSource type="POOLED">
                <property name="driver" value="${driver}" /> <!-- 配置了properties,所以可以直接引用 -->
                ...
            </dataSource>
        </environment>
    </environments>
</configuration>

配置映射

配置好數據源后,需要添加對應的表映射,映射包括CRUD對應的SQL語句以及與類之間的映射關係。


<configuration>
  ...
    <mappers>
        <mapper resource="singleTransMapper.xml" />
    </mappers>
</configuration>

在resources目錄下新建一個singleTransMapper.xml文件,MyBatis會將singleTransMapper.xml映射到對應的類

除了resources以外MyBatis還支持classurlpackage共四種配置

class可以配置具體類名,如com.mybatistest.DAO.SingleTransMapper
url可以配置完整的文件路徑,如file:///var/mappers/PostMapper.xml
package可以配置package名稱,註冊所有接口。

查詢


public class SingletransDTO {

    public String EnterpriseNum;

    public String TransNo;

    public String CommandCode;

    public int State;
}

單條件查詢

增加一個查詢單筆的語句,通過輸入流水號,返回查詢到的單筆信息。

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="DAO.SingleTransMapper">
    <select id="selectSingle" parameterType="String" resultType="DTO.SingletransDTO">
    select * from se_singletrans where transno = #{transno}
  </select>
</mapper>

namespace需要對應到java中的類,參數和返回類型也需要一致。

在mapper節點下添加select表示select語句
parameterType為輸入的參數
resultType為返回的類型,返回類型需要對應java中的類


public interface SingleTransMapper {
    SingletransDTO selectSingle(String transNo);
}


String resource = "mybatis-config.xml";
//加載資源
InputStream inputStream = Resources.getResourceAsStream(resource);
//創建session
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
try (SqlSession session = sqlSessionFactory.openSession()) {
  //獲取單筆映射對象
    SingleTransMapper mapper = session.getMapper(SingleTransMapper.class);
    //根據流水號查詢
    SingletransDTO blog = mapper.selectSingle("642EHDCS899XKF8P");

    if(blog != null) {
        System.out.println(blog.ENTERPRISENUM);
        System.out.println(blog.TRANSNO);
        System.out.println(blog.COMMANDCODE);
    }else{
        System.out.println("not found");
    }

}catch (Exception exception)
{
    System.out.println(exception.getMessage());
}

多條件查詢

通過類字段傳遞參數

添加一個查詢配置selectSingleByParam

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="DAO.SingleTransMapper">
   
   ...
    <select id="selectSingleByClass" resultType="DTO.SingletransDTO">
    select * from se_singletrans where transno = #{TransNo} and commandCode= #{CommandCode}
  </select>
</mapper>

對應的映射類添加方法對應的方法,MyBatis可以通過反射將類的字段映射到SQL的參數,需要注意的是類的字段名和sql中配置的大小寫需要一致。


public interface SingleTransMapper {
  ...
  SingletransDTO selectSingleByClass(SingleCondition singleCondition);
}
public class SingleCondition {
    /**
     * 流水號
     */
    public String TransNo;

    /**
     * 指令類型
     */
    public String CommandCode;

    public SingleCondition(String transNo, String commandCode)
    {
        TransNo = transNo;
        CommandCode = commandCode;
    }
}

調用構造函數類的多條件查詢方案

...
SingleTransMapper mapper = session.getMapper(SingleTransMapper.class);
SingletransDTO blog = mapper.selectSingleByClass(new SingleCondition( "642EHDCS899XKF8P","10009"));

通過Map接口傳參

另一種方案可以通過傳入HashMap,MayBatis會根據key自動映射到對應的參數。
下面實現通過流水號和指令類型查詢。
添加一個查詢配置selectSingleByMultCondition

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="DAO.SingleTransMapper">
    ...
    <select id="selectSingleByMultCondition" resultType="DTO.SingletransDTO">
    select * from se_singletrans where transno = #{transNo} and commandCode= #{commandCode}
  </select>
</mapper>

添加對應的方法,傳入參數為HashMap<String,Object> param

public interface SingleTransMapper {
  ...
    SingletransDTO selectSingleByMultCondition(HashMap<String,Object> param);
}

修改調用新的多條件查詢方法

...
//獲取單筆映射對象
SingleTransMapper mapper = session.getMapper(SingleTransMapper.class);
//根據流水號查詢
HashMap<String,Object> param = new HashMap<String,Object>();
param.put("transNo","642EHDCS899XKF8P");
param.put("commandCode","10009");
SingletransDTO blog = mapper.selectSingle2(param);
...

需要注意的是,由於HashMap的key是不區分大小寫的,因此需要和配置文件sql的參數大小寫一致。

Param註解

通過類參數和Map進行多條件查詢都需要創建額外的對象,另一種比較好的方式可以通過在方法參數上添加Param註解的方式配置方法參數和SQL參數的映射關係。

添加一個查詢配置selectSingleByParam

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="DAO.SingleTransMapper">
   
   ...
    <select id="selectSingleByParam" resultType="DTO.SingletransDTO">
    select * from se_singletrans where transno = #{param1} and commandCode= #{param2}
  </select>
</mapper>

對應的映射類添加方法對應的方法,這樣MyBatis就知道參數映射規則,就會自動映射,需要注意的數參數和sql中配置的大小寫也需要一致。


public interface SingleTransMapper {
    SingletransDTO selectSingle(String transNo);
    SingletransDTO selectSingleByMultCondition(HashMap<String,Object> param);
    SingletransDTO selectSingleByParam(@Param("param1")String transNo, @Param("param2") String commandCode);
}

調用註解傳參方法

...
//獲取單筆映射對象
SingleTransMapper mapper = session.getMapper(SingleTransMapper.class);
SingletransDTO blog = mapper.selectSingleByParam("642EHDCS899XKF8P","10009");
...

插入

在mapper下添加insert表示插入的sql映射。

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="DAO.SingleTransMapper">
  
  ...
  <insert id="insert" parameterType="DTO.SingletransDTO">
    insert into se_singletrans(ENTERPRISENUM,TRANSNO,COMMANDCODE,STATE) values(#{EnterpriseNum},#{TransNo},#{CommandCode},#{State})
  </insert>
</mapper>

添加類對應的insert方法


public interface SingleTransMapper {

    ...
    int insert(SingletransDTO singletransDTO);
}

SqlSession默認會開啟事務,在insert完成后需要調用SqlSessioncommit()方法提交事務。


try (SqlSession session = sqlSessionFactory.openSession()) {
    SingleTransMapper mapper = session.getMapper(SingleTransMapper.class);
    SingletransDTO singletransDTO = new     SingletransDTO();
    singletransDTO.EnterpriseNum  = "QT330001";
    singletransDTO.TransNo = "MYBATIS.INSERT";
    singletransDTO.CommandCode = "10009";
    int count = mapper.insert(singletransDTO);
    session.commit();
    System.out.println("insert result:" +count);
}catch (Exception exception)
{
    System.out.println(exception.getMessage());
}

我們也可以調用SqlSession openSession(boolean autoCommit)傳入參數,自動提交。

更新

在mapper下添加update節點表示插入,插入時可以對插入的字段設置條件,達成某條件是該字段才需要更新。

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="DAO.SingleTransMapper">
  
  ...
   <update id="update" parameterType="DTO.SingletransDTO">
    update se_singletrans
    <set>
        <if test="State != null and State!=''"></if>
        STATE=#{State}
    </set>
    where transno = #{TransNo} and commandCode= #{CommandCode}
  </update>
</mapper>

添加類對應的update方法


public interface SingleTransMapper {

    ...
    int update(SingletransDTO singletransDTO);
}

SqlSession默認會開啟事務,和insert一樣,在update完成后需要調用SqlSessioncommit()方法提交事務。


try (SqlSession session = sqlSessionFactory.openSession()) {
    SingleTransMapper mapper = session.getMapper(SingleTransMapper.class);
    SingletransDTO singletransDTO = new SingletransDTO();
    singletransDTO.EnterpriseNum = "QT330001";
    singletransDTO.TransNo = "MYBATIS.INSERT";
    singletransDTO.CommandCode = "10009";
    singletransDTO.State = 2;
    int count = mapper.update(singletransDTO);
    session.commit();
    System.out.println("update result:" +count);
}catch (Exception exception)
{
    System.out.println(exception.getMessage());
}

刪除

在mapper下添加delete節點表示刪除。

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="DAO.SingleTransMapper">
  
  ...
   
  <delete id="delete">
    delete from se_singletrans where transno = #{TransNo} and commandCode= #{CommandCode}
  </delete>
</mapper>

添加類對應的delete方法,可以通過參數註解的方式指定參數。


public interface SingleTransMapper {

    ...
    int delete(@Param("TransNo")String transNo, @Param("CommandCode") String commandCode);
}

SqlSession默認會開啟事務,在delete完成后需要調用SqlSessioncommit()方法提交事務。


try (SqlSession session = sqlSessionFactory.openSession()) {
    SingleTransMapper mapper = session.getMapper(SingleTransMapper.class);
    int count = mapper.delete("MYBATIS.INSERT","10009");
    session.commit();
    System.out.println("delete result:" +count);
}catch (Exception exception)
{
    System.out.println(exception.getMessage());
}

字段映射

若字段名和數據庫的字段名不一致,可以通過配置進行映射。添加resultMap節點,配置類字段和數據庫字段的映射關係,若沒有配置的字段,則根據默認MyBatis的映射關係處理,即字段名一樣的自動映射,MyBatis會嘗試進行類型轉換,若轉換異常,則可能拋錯。我們也可以通過typeHandler自定義自己的類型處理器。

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="DAO.SingleTransMapper">

  <resultMap id="singleResultMap" type="DTO.SingletransDTO">
    <result property="TransNo"  column="transNo" />
    <result property="CommandCode" column="commandCode"/>
    <result property="SpecialProperty" typeHandler="CustomTypeHandle" column="SpecialColumn"/>
  </resultMap>
  <select id="selectSingleToReusltMap" resultMap="singleResultMap">
    select * from se_singletrans where transno = #{param1} and commandCode= #{param2}
  </select>
</mapper>

關於TypeHandle這裏不做具體闡述,有興趣的可以看下MyBatis自定義類型處理器 TypeHandler

參考文獻

  1. Maven添加Oracle的依賴及驅動
  2. MyBatis自定義類型處理器 TypeHandler

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

南投搬家公司費用需注意的眉眉角角,別等搬了再說!

新北清潔公司,居家、辦公、裝潢細清專業服務

※教你寫出一流的銷售文案?

【asp.net core 系列】12 數據加密算法

0. 前言

這一篇我們將介紹一下.net core 的加密和解密。在Web應用程序中,用戶的密碼會使用MD5值作為密碼數據存儲起來。而在其他的情況下,也會使用加密和解密的功能。

常見的加密算法分為對稱加密和非對稱加密。所謂的對稱加密是指加密密鑰和解密密鑰是同一個,非對稱加密是值加密密鑰和解密迷藥不同。而我們常應用在保存用戶登錄密碼這個過程中的MD5本質上並不是加密算法,而是一種信息摘要算法。不過MD5盡量保證了每個字符串最後計算出來的值都不一樣,所以在密碼保存中常用MD5做為保密值。

1. 常見對稱加密算法

對稱加密算法,簡單的說就是加密和解密使用相同的密鑰進行運算。對於大多數加密算法,解密和加密是一個互逆的運算。對稱加密算法的安全性取決於密鑰的長度,密鑰越長越安全。當然,不建議使用過長的密鑰。

那麼,我們來看看常見的對稱加密算法有哪些吧,以及C#該如何實現。

1.1 DES 和 DESede 算法

DES算法和DESede算法(又稱三重DES算法) 統稱DES系列算法。DES全稱為Data Encryption Standard,即數據加密標準,是一種使用密鑰加密的塊算法。而DESede就是針對同一塊數據做三次DES加密。這裏就不對原理做過多的介紹了,來看看.net core里如何實現DES加/解密吧。

在Utils項目里,創建目錄Security

在Security目錄下,創建DESHelper類:

namespace Utils.Security
{
    public class DesHelper
    {
        
    }
}

加密解密實現:

using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;

namespace Utils.Security
{
    public static class DesHelper
    {
        static DesHelper()
        {
            DesHandler =  DES.Create("DES");
            DesHandler.Key = Convert.FromBase64String("L1yzjGB2sI4=");
            DesHandler.IV = Convert.FromBase64String("uEcGI4JSAuY=");
        }

        private static DES DesHandler { get; }

        /// <summary>
        /// 加密字符
        /// </summary>
        /// <param name="source"></param>
        /// <returns></returns>
        public static string Encrypt(string source)
        {
            try
            {
                using (var memStream = new MemoryStream())
                using (var cryptStream = new CryptoStream(memStream, DesHandler.CreateEncryptor(DesHandler.Key, DesHandler.IV),
                    CryptoStreamMode.Write))
                {
                    var bytes = Encoding.UTF8.GetBytes(source);
                    cryptStream.Write(bytes, 0, bytes.Length);
                    cryptStream.FlushFinalBlock();
                    
                    return Convert.ToBase64String(memStream.ToArray());
                }
            }
            catch (Exception e)
            {
                Console.WriteLine(e);
                return null;
            }
        }

        /// <summary>
        /// 解密
        /// </summary>
        /// <param name="source"></param>
        /// <returns></returns>
        public static string Decrypt(string source)
        {
            try
            {
                using (var mStream = new MemoryStream(Convert.FromBase64String(source)))
                using (var cryptoStream =
                    new CryptoStream(mStream, DesHandler.CreateDecryptor(DesHandler.Key, DesHandler.IV), CryptoStreamMode.Read))
                using (var reader = new StreamReader(cryptoStream))
                {
                    return reader.ReadToEnd();
                }
            }
            catch (Exception e)
            {
                Console.WriteLine(e);
                return null;
            }
        }
    }
}

每次調用DesHandler = DES.Create("DES"); 都會重新獲得一個DES算法實現實例,這樣每次獲取的實例中Key、IV這兩個屬性的值也會發生變化。如果直接使用會出現這次加密的數據下次就沒法解密了,為了減少這種情況,所以代碼處手動賦值了Key、IV這兩個屬性。

1.2 AES 加密算法

AES算法(Advanced Encryption Standard)也就是高級數據加密標準算法,是為了解決DES算法中的存在的漏洞而提出的算法標準。現行的AES算法核心是Rijndael算法。當然了,這個不用太過於關心。我們直接看看是如何實現吧:

同樣,在Security目錄創建一個AesHelper類:

namespace Utils.Security
{
    public static class AesHelper
    {
        
    }
}

具體的加解密實現:

using System;
using System.IO;
using System.Security.Cryptography;

namespace Utils.Security
{
    public static class AesHelper
    {
        static AesHelper()
        {
            AesHandler = Aes.Create();
            AesHandler.Key = Convert.FromBase64String("lB2BxrJdI4UUjK3KEZyQ0obuSgavB1SYJuAFq9oVw0Y=");
            AesHandler.IV = Convert.FromBase64String("6lra6ceX26Fazwj1R4PCOg==");
        }

        private static Aes AesHandler { get; }

        public static string Encrypt(string source)
        {
            using (var mem = new MemoryStream())
            using (var stream = new CryptoStream(mem, AesHandler.CreateEncryptor(AesHandler.Key, AesHandler.IV),
                CryptoStreamMode.Write))
            {
                using (var writer = new StreamWriter(stream))
                {
                    writer.Write(source);
                }   
                return Convert.ToBase64String(mem.ToArray());
            }
            
        }

        public static string Decrypt(string source)
        {
            var data = Convert.FromBase64String(source);
            using (var mem = new MemoryStream(data))
            using (var crypto = new CryptoStream(mem, AesHandler.CreateDecryptor(AesHandler.Key, AesHandler.IV),
                CryptoStreamMode.Read))
            using (var reader = new StreamReader(crypto))
            {
                return reader.ReadToEnd();
            }
        }
    }
}

2. 常見非對稱加密算法

非對稱加密算法,指的是加密密鑰和解密密鑰並不相同。非對稱加密算法的秘鑰通常成對出現,分為公開密鑰和私有密鑰。公開密鑰可以以公開的形式發給數據交互方,而不會產生泄密的風險。因為非對稱加密算法,無法通過公開密鑰推算私有密鑰,反之亦然。

通常,非對稱加密算法是用公鑰進行加密,使用私鑰進行解密。

2.1 RSA算法

RSA算法是標準的非對稱加密算法,名字來源是三位發明者的姓氏首字母。RSA公開密鑰密碼體制是一種使用不同的加密密鑰與解密密鑰,“由已知加密密鑰推導出解密密鑰在計算上是不可行的”密碼體制 。其安全性取決於密鑰的長度,1024位的密鑰幾乎不可能被破解。

同樣,在Utils.Security下創建RSAHelper類:

namespace Utils.Security
{
    public static class RsaHelper
    {
        
    }
}

具體實現:

using System;
using System.Security.Cryptography;

namespace Utils.Security
{
    public static class RsaHelper
    {
        public static RSAParameters PublicKey { get; private set; }
        public static RSAParameters PrivateKey { get; private set; }

        static RsaHelper()
        {
            
        }

        public static void InitWindows()
        {
            var parameters = new CspParameters()
            {
                KeyContainerName = "RSAHELPER" // 默認的RSA保存密鑰的容器名稱
            };
            var handle = new RSACryptoServiceProvider(parameters);
            PublicKey = handle.ExportParameters(false);
            PrivateKey = handle.ExportParameters(true);
        }

        public static void ExportKeyPair(string publicKeyXmlString, string privateKeyXmlString)
        {
            var handle  = new RSACryptoServiceProvider();
            handle.FromXmlString(privateKeyXmlString);
            PrivateKey = handle.ExportParameters(true);
            handle.FromXmlString(publicKeyXmlString);
            PublicKey = handle.ExportParameters(false);
        }
        public static byte[] Encrypt(byte[] dataToEncrypt)
        {
            try
            {
                byte[] encryptedData;
                using (RSACryptoServiceProvider RSA = new RSACryptoServiceProvider())
                {
                    RSA.ImportParameters(PublicKey);
                    encryptedData = RSA.Encrypt(dataToEncrypt, true);
                }

                return encryptedData;
            }
            catch (CryptographicException e)
            {
                Console.WriteLine(e.Message);
                return null;
            }
        }

        public static byte[] Decrypt(byte[] dataToDecrypt)
        {
            try
            {
                byte[] decryptedData;
                using (var rsa = new RSACryptoServiceProvider())
                {
                    rsa.ImportParameters(PrivateKey);
                    decryptedData = rsa.Decrypt(dataToDecrypt, true);
                }
                return decryptedData;
            }
            catch (CryptographicException e)
            {
                Console.WriteLine(e.ToString());
                return null;
            }
        }
    }
}

因為RSA的特殊性,需要預先設置好公鑰和私鑰。C# 支持多種方式導入密鑰,這裏就不做過多介紹了。

3. 信息摘要算法

這種算法嚴格意義上並不是加密算法,因為它完全不可逆。也就是說,一旦進行使用該類型算法加密后,無法解密還原出數據。當然了,也正是因為這種特性常常被用來做密碼的保存。因為這樣可以避免某些人拿到數據庫與代碼后,可以簡單反推出用戶的密碼。

3.1 MD5算法

最常用的信息摘要算法就是MD5 加密算法,MD5信息摘要算法(英語:MD5 Message-Digest Algorithm),一種被廣泛使用的密碼散列函數,可以產生出一個128位(16字節)的散列值(hash value),用於確保信息傳輸完整一致。

原理不解釋,我們看下如何實現,照例現在Security下創建MD5Helper:

namespace Utils.Security
{
    public static class Md5Helper
    {
        
    }
}

具體實現:

using System.Security.Cryptography;
using System.Text;

namespace Utils.Security
{
    public static class Md5Helper
    {
        private static MD5 Hanlder { get; } = new MD5CryptoServiceProvider();

        public static string GetMd5Str(string source)
        {
            var data = Encoding.UTF8.GetBytes(source);
            var security = Hanlder.ComputeHash(data);
            var sb = new StringBuilder();
            foreach (var b in security)
            {
                sb.Append(b.ToString("X2"));
            }

            return sb.ToString();
        }
    }
}

4 總結

這一篇簡單介紹了四種常用的加密算法的實現,當然最常用的就是 MD5,因為這個是大多數系統用來做密碼保存的加密算法。

更多內容煩請關注我的博客《高先生小屋》

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

※想知道最厲害的網頁設計公司"嚨底家"!

※幫你省時又省力,新北清潔一流服務好口碑

※別再煩惱如何寫文案,掌握八大原則!

※產品缺大量曝光嗎?你需要的是一流包裝設計!

圖解leetcode5-10 | 和233醬一起刷leetcode系列(2)

本周我們繼續來看5道磨人的小妖精,圖解leetcode6-10~

多說一句,leetcode10 殺死了233醬不少腦細胞…

另:

沉迷算法,無法自拔。快來加入我們吧!

別忘了233醬的一條龍服務:

公眾號文章題解 -> 私信答疑 -> 刷題群答疑 -> 視頻講解

我們的目的是成為套路王~

嘿嘿,廣告完畢 , Let’s go!

leetcode6: Z 字形變換

題目描述:

將一個給定字符串根據給定的行數,以從上往下、從左到右進行 Z 字形排列。

題目示例:

輸入: s = "LEETCODEISHIRING", numRows = 4
輸出: "LDREOEIIECIHNTSG"

解釋:

L     D     R
E   O E   I I
E C   I H   N
T     S     G

解題思路:

相信小夥伴看到這道題目,也和233一樣覺得Z字形排列的字符串冥冥中有些規律。為了方便解釋 ,我們假設輸入:

字符串s=”0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15″
numRows=4
注意: s中的輸入字符依次為:為0-15,中間的空格是我為了展示清楚額外加的。

那麼s的Z字形排列如下:

需要輸出的結果是:“0 6 12 15 7 11 13 2 4 8 10 14 3 9 15”

假設我們將Z字形排列后的字符串每一行i 用一個數組arr[i]存起來,最後按行數i的順序輸出arr[i]中的值,那麼就可以得到最終的輸出結果。

如何知道字符串s中的各個字符在哪個arr數組的哪個索引位置呢?這就是我們用数字字符的字符串來舉例子的好處了,因為数字的值就對應着字符在字符串s中的下標。當我們遍歷字符串s時,是我們可以用pointer表示當前遍歷的字符所對應的行數i,代表這個字符是要放到arr[i]中的。

我們可以發現每當遍歷numRows=4 個字符,pointer就從 0->3 轉化為 3->0。所以我們可以用一個flag記錄pointer的變化量。

思路有了,我們來看一下時間空間複雜度:

  • 時間複雜度:遍歷一遍字符串s: O(n)。
  • 空間複雜度:數組arr的存儲:O(n)。

可以寫出代碼嗎:)

Java版本

class Solution {
    public String convert(String s, int numRows) {
        if(numRows <= 1){
            return s;
        }
        List<StringBuilder> arr = new ArrayList<>();
        for(int i = 0 ;i< numRows;i++){
            arr.add(new StringBuilder());
        }
        int flag = -1;
        int pointer = 0;
        for(int i =0;i<s.length();i++){
           char ch = s.charAt(i);
           arr.get(pointer).append(ch);
           if(pointer == 0 || pointer == numRows -1) flag = - flag;
            pointer += flag;
            
        }
        StringBuilder res = new StringBuilder();
        for(StringBuilder row : arr) res.append(row);
        return res.toString();
    }
}

leetcode7: 整數反轉

題目描述:

給出一個 32 位的有符號整數,你需要將這個整數中每位上的数字進行反轉。

題目示例:

輸入: 123
輸出: 321

輸入: -123
輸出: -321

輸入: 120
輸出: 21

注意:
假設我們的環境只能存儲得下 32 位的有符號整數,則其數值範圍為 [−231,  231 − 1]。請根據這個假設,如果反轉后整數溢出那麼就返回 0。

解題思路:
這道題考的還是 數學運算

Step1:需要分別取出十進制数字的個位,十位,百位..一直到最高位的数字。

阿姨來教你小學數學的除法運算:

所以當我們 取余再取模 就可以得到高位的数字。

Step2:將取出來的個位,十位,百位..一直到最高位的数字 依次放到 最高位,…,百位,十位,個位。

阿姨來教你小學數學的乘法運算:

至於示例中列舉的幾個邊界條件,Java中的整數是帶有符號的。剛好符合我們的乘除運算。

另外,需要判斷乘法計算時正負数字的越界問題。當然如果res用long表示,也就不需要考慮這個問題了。代碼如下:

Java版本

class Solution {
    public int reverse(int x) {
        int res = 0;
        while(x!=0){
            if(x>0 && res > ((Integer.MAX_VALUE-x%10)/10)) return 0;
            if(x<0 && res < ((Integer.MIN_VALUE-x%10)/10)) return 0;
            res = res*10 + x%10;
            x/=10;
        }
        return res;
    }
}

leetcode8: 字符串轉換整數(atoi)

題目描述:

請你來實現一個 atoi 函數,使其能將字符串轉換成整數。

首先,該函數會根據需要丟棄無用的開頭空格字符,直到尋找到第一個非空格的字符為止。接下來的轉化規則如下:

如果第一個非空字符為正或者負號時,則將該符號與之後面盡可能多的連續数字字符組合起來,形成一個有符號整數。
假如第一個非空字符是数字,則直接將其與之後連續的数字字符組合起來,形成一個整數。
該字符串在有效的整數部分之後也可能會存在多餘的字符,那麼這些字符可以被忽略,它們對函數不應該造成影響。
注意:假如該字符串中的第一個非空格字符不是一個有效整数字符、字符串為空或字符串僅包含空白字符時,則你的函數不需要進行轉換,即無法進行有效轉換。

在任何情況下,若函數不能進行有效的轉換時,請返回 0 。

提示:

本題中的空白字符只包括空格字符 ‘ ‘ 。
假設我們的環境只能存儲 32 位大小的有符號整數,那麼其數值範圍為 [−231,  231 − 1]。如果數值超過這個範圍,請返回  INT_MAX (231 − 1) 或 INT_MIN (−231) 。

題目示例:

示例 1:
輸入: "42"
輸出: 42

示例 2:
輸入: "   -42"
輸出: -42
解釋: 第一個非空白字符為 '-', 它是一個負號。
     我們盡可能將負號與後面所有連續出現的数字組合起來,最後得到 -42 。

示例 3:
輸入: "4193 with words"
輸出: 4193
解釋: 轉換截止於数字 '3' ,因為它的下一個字符不為数字。

示例 4:
輸入: "words and 987"
輸出: 0
解釋: 第一個非空字符是 'w', 但它不是数字或正、負號。
     因此無法執行有效的轉換。

示例 5:
輸入: "-91283472332"
輸出: -2147483648
解釋: 数字 "-91283472332" 超過 32 位有符號整數範圍。 
     因此返回 INT_MIN (−231) 。

解題思路:
放這麼多 題目示例 阿姨並不是為了湊字數,而是這類問題就是屬於考邊界情況的問題,邊界情況拎清了,就不會被磨到了~

假設輸入一個字符串 ” -4193 with words” , 我們可以從左到右遍歷這個字符串,用k 表示當前遍歷到的字符:

另外,我們還需要注意 示例5的情況,當乘法計算時的值超過INT_MAX or INT_MIN時,結束並返回 INT_MAX or INT_MIN.

Java版本

class Solution {
    public int myAtoi(String str) {
        int res = 0;
        int k = 0;

        while(k< str.length() &&  ' ' == str.charAt(k))k++;
        int minus = 1;
        if(str.length() == k) return res;
        if('-' == str.charAt(k)) {
            minus = -1;
            k++;
        }else if('+' == str.charAt(k)){
            k++;
        }

        while(k<str.length() && str.charAt(k) >= '0' && str.charAt(k) <='9'){
            int x = str.charAt(k) - '0';
            if(minus >0 && res> (Integer.MAX_VALUE - x)/ 10){
                return Integer.MAX_VALUE;
            }
            //-res * 10 - str.charAt(k) < Integer.MIN_VALUE
            if(minus <0 && -res < (Integer.MIN_VALUE + x)/10) 
                return Integer.MIN_VALUE;
            //最大的負數是存不下來的
            if((-res * 10 - x) == Integer.MIN_VALUE ) {
                return Integer.MIN_VALUE;
            }
            res = res* 10 + x;
            k++;
        }
        res *= minus;
        return res;

    }
}

leetcode9: 迴文數

題目描述:

判斷一個整數是否是迴文數。迴文數是指正序(從左向右)和倒序(從右向左)讀都是一樣的整數。

題目示例:

示例 1:

輸入: 121
輸出: true
示例 2:

輸入: -121
輸出: false
解釋: 從左向右讀, 為 -121 。 從右向左讀, 為 121- 。因此它不是一個迴文數。
示例 3:

輸入: 10
輸出: false
解釋: 從右向左讀, 為 01 。因此它不是一個迴文數。

解題思路:

上篇文章中我們講過最長迴文子串的查找。再來看這道題就很easy了。這道題的解法也很多:
比如我們可以把它變為字符串。然後reverse一下,判斷前後兩個字符串是否相等。

但是我們用一種更簡單的方式,只需要反轉整數,然後判斷兩個整數是否相等,就可以確定是不是迴文整數。又回到leetcode7了,有沒有覺得阿姨的乘除法運算還是有幫助的:)

Java版本

class Solution {
    public boolean isPalindrome(int x) {
        
        if(x<0) return false;
        if(x<=9) return true;
        int oringin = x;
        int res = 0;
        while(x>0){
            //如果越界了說明不對稱
            res = res*10 + x%10;
            x/=10;
        }
        return oringin == res;
    }
}

leetcode10: 正則表達式匹配

題目描述:

給你一個字符串 s 和一個字符規律 p,請你來實現一個支持 ‘.’ 和 ‘*’ 的正則表達式匹配。

‘.’ 匹配任意單個字符
‘*’ 匹配零個或多個前面的那一個元素
所謂匹配,是要涵蓋 整個 字符串 s的,而不是部分字符串。

說明:

  • s 可能為空,且只包含從 a-z 的小寫字母。
  • p 可能為空,且只包含從 a-z 的小寫字母,以及字符 . 和 *。

題目示例:

示例 1:
輸入:
s = "aa"
p = "a*"
輸出: true
解釋: 因為 '*' 代表可以匹配零個或多個前面的那一個元素, 在這裏前面的元素就是 'a'。因此,字符串 "aa" 可被視為 'a' 重複了一次。

示例 2:
輸入:
s = "ab"
p = ".*"
輸出: true
解釋: ".*" 表示可匹配零個或多個('*')任意字符('.')。

神奇的.*來了,Hard模式,大家坐好~

判斷 字符串s 是否與 一個 可能還有“.” or “*” 的字符規律 p 匹配,其實就是從 p 代表的所有的字符串中枚舉出一個 匹配值。 簡單暴力枚舉的時間複雜度是指數級的。我們需要考慮對於求解一個最優解 或 匹配解的類似問題,有哪些可以降低時間複雜度的方案?

好了,不饒彎子了,動態規劃 要來了。

溫馨後記:寫着寫着就列舉了一堆動態規劃的理論,比較了解的朋友可以直接翻過這段看後面這一題的圖解。

解題之前,我們先了解下:動態規劃是什麼?為什麼動態規劃能降低時間複雜度?什麼類型的問題又能用動態規劃去解決?如何構造解題步驟?

動態規劃是什麼

動態規劃與分治方法相似,都是通過組合子問題的解來求解原問題。

分治算法將問題劃分為互不相交的子問題,遞歸地求解子問題,再將他們的解組合起來,求出原問題的解。如歸併排序,劃分的左右排序子問題是對不同的数字序列進行排序的,最後再把他們合併起來。

動態規劃應用於子問題重疊的情況,即不同的子問題具有公共的子子問題。這種情況下分治算法需要對子子問題反覆求解,而動態規劃算法只對子子問題求解一次,將其結果保存到備忘錄中 or 按照 自底向下 的順序 求解每個子問題(也就是保證在求解子問題時,它所依賴的子子問題的解已經求出來了)這兩種方式,避免不必要的計算工作,降低時間複雜度。

舉一個簡單的斐波那契數列的例子:

斐波那契數列指的是這樣一個數列:
1、1、2、3、5、8…

相信小夥伴們都知道,它的遞推規律是:

假設求f(10),則遞推公式展開為:

可以看到其中有大量的重複子問題:f(6),f(5) 等。

動態規劃的兩種做法就是:
1.用 遞歸的代碼求解時,將第一次計算的f(6)保存起來,如f(8)中的f(6). 這樣再求解f(7)中的f(6)就可以直接獲取到結果了
2.按照求f(3), ->(4)->…->f(10)的自底向下的順序求解,這樣再求 f(8)時,只需要保存下來 f(7) 和 f(6)的值,就可以求出了,f(10)同理。這種方式大多是循環的寫法。

動態規劃解決的問題類型

初步明白后,我們再來看下動態規劃解決問題的類型:

極客時間的王爭大佬 概括為: 一個模型,三個特徵

一個模型:多階段決策最優解模型
我們一般是用動態規劃來解決最優問題。而解決問題的過程,需要經歷多個決策階段。每個決策階段都對應着一組狀態。然後我們尋找一組決策序列,經過這組決策序列,能夠產生最終期望求解的最優值。
特徵1:最優子結構

指的是,問題的最優解包含子問題的最優解。反過來說就是,我們可以通過子問題的最優解,推導出問題的最優解。如果我們把最優子結構,對應到我們前面定義的動態規劃問題模型上,那我們也可以理解為,後面階段的狀態可以通過前面階段的狀態推導出來。

特徵2:無後效性

無後效性有兩層含義,第一層含義是,在推導後面階段的狀態的時候,我們只關心前面階段的狀態值,不關心這個狀態是怎麼一步一步推導出來的。第二層含義是,某階段狀態一旦確定,就不受之後階段的決策影響。無後效性是一個非常“寬鬆”的要求。只要滿足前面提到的動態規劃問題模型,其實基本上都會滿足無後效性。

特徵3. 重複子問題
這個就是我們前面提到的,不同的決策序列,到達某個相同的階段時,可能會產生重複的狀態。

動態規劃的解題步驟

Step1.刻畫一個最優解的結構特徵
也就是能夠把問題抽象轉化為一種數學描述,通俗說 就是 狀態的定義。如上述斐波那契數列 中 f(n)就是狀態的定義。

Step2.遞歸地定義最優解的值。
就是問題與子問題之間的遞推表達式是什麼,通俗說 就是 狀態轉移方程的定義。如上述斐波那契數列 中的f(n) = f(n-1) + f(n-2)

Step3.計算最優解的值
就是採用的動態規劃具體計算的做法,包括 遞歸+備忘錄 or 循環+自底向下 求解兩種方式。

Step4.利用計算出的信息構造一個最優解
因為我們步驟一定義的狀態有時並不是我們直接要求的最優解,所以這一步就是利用狀態和狀態轉移方式 表達出我們最終要求的最優解怎麼得到。

我們會根據leetcode10來理解這些理論知識。

解題思路:

Step1.抽象出狀態

這個問題實際求的是字符串s能否從字符規律p代表的所有字符串集合中找出一個匹配值。一般求兩個字符串的匹配問題的狀態用二維的數組來定義,為什麼。。聽大佬說:靠經驗,靠悟。我們定義:
dp[i,j] : 代表 所有 字符串s[0,i-1] (前i個字符) 和 字符規律p[0,j-1] (前j個字符)的匹配方案 集合。
dp[i,j] 的值: 代表是否存在一種方案 使得 字符規律p 匹配 字符串s。這個值就是我們這個問題的解。true:存在。false:不存在。

Step2.遞歸地定義最優解的值。

這一步其實就是求狀態遞推式,找出問題dp[i,j] 和子問題之間的關係。

對於字符串s[i] 和 p[j] 是否匹配,因為p[j] 可能是* or . 。我們需要枚舉出p所代表的所有字符串。我們我們可以從最後的字符 s[i] 和 p[j]來考慮。

可分為p[j] == * or p[j] != * 兩種情況。因為 ‘*’ 代表着0-多個字符,會影響p的枚舉數。’.’ 我們只需要把它當成一個萬能字符就好,’.’ 不會影響p的枚舉數量。

  • p[j] != '*' 時,則 s 與 p 是否匹配 取決於 s[i] 是否等於 p[j] && dp[i][j] 是否為true

  • p[j] == '*' 時,我們需要枚舉* 代表的從0-多個字符的字符序列集合中,s 是否與他們其中之一匹配。

如圖所示,考慮p[j] == '*' 所代表的字符數,我們需要列舉出 組成dp[i+1,j+1] 的所有可能情況,同時我們其實靠yy也能推斷出:
dp[i+1,j+1] 和 它的子問題:dp[i,j+1] 的關係,圖中我也有列舉出公式推導來源。

這裡有一點需要注意: dp[i+1,j+1]才表示s[0,i] 和 p[0,j] 匹配。因為s[0]就代表了第一個字符。而我們也需要表示 s長度為0的dp[0,..]的值。不然會影響到我們遞推公式的求值。

好了,到這裏我們先總結下 這個問題動態規劃解法的狀態和狀態轉移方程:

Step3.計算最優解的值。
這個步驟就是具體計算遞推公式dp[i+1,j+1]的過程了,我們可以採用 循環+ 自底向下的方式來求解,也就是對於二維數組先填第0行的值,再填第0列的值,以此類推。
假設s=”aa”, p=”a*” 。則它的二維填狀態表的順序和結果為:

Step4.利用計算出的信息構造一個最優解

在Step1的時候,我們其實就定義了。 s與p是否匹配 等價於 dp[i+1][j+1] 的值 是否為 true。 所以我們只需要返回 dp[i+1][j+1]的值 就是這道題的結果。

徹底完了,看懂了沒,上代碼吧。

Java版本

class Solution {
    public boolean isMatch(String s, String p) {
        int slen = s.length();
        int plen = p.length();
        //需要分別取出s和p為空的情況,所以dp數組大小+1
        boolean[][] dp = new boolean[slen + 1][plen + 1];
        //初始化dp[0][0]=true,dp[0][1]和dp[1][0]~dp[s.length][0]默認值為false所以不需要顯式初始化
        dp[0][0] = true;
        //填寫第一行dp[0][2]~dp[0][p.length]
        for (int k = 2; k <= plen; k++) {
            //p字符串的第2個字符是否等於'*',此時j元素需要0個,所以s不變p減除兩個字符
            dp[0][k] = p.charAt(k - 1) == '*' && dp[0][k - 2];
        }
        //填寫dp數組剩餘部分
        for (int i = 0; i < slen; i++) {
            for (int j = 0; j < plen; j++) {
                //p第j個字符是否為*
                if (p.charAt(j) == '*') {
                    //兩種情況:1.s不變[i+1],p移除兩個元素[j+1-2]。
                    // 2.比較s的i元素和p的j-1(因為此時j元素為*)元素,相等則移除首元素[i+1-1],p不變。
                    dp[i + 1][j + 1] = dp[i + 1][j - 1] ||
                            (dp[i][j + 1] && headMatched(s, p, i, j - 1));
                } else {
                    //s的i元素和p的j元素是否相等,相等則移除s的i元素[i+1-1]和p的j元素[j+1-1]
                    dp[i + 1][j + 1] = dp[i][j] && headMatched(s, p, i, j);
                }
            }
        }
        return dp[slen][plen];
    }

    //判斷s第i個字符和p第j個字符是否匹配
    public boolean headMatched(String s, String p, int i, int j) {
        return s.charAt(i) == p.charAt(j) || p.charAt(j) == '.';
    }

}

能看到這裏看來是真愛了,233醬都要對你豎起大拇指,要不要也在看,轉發 對233醬豎起大拇指 …… ^ _ ^。不管對文章是否有疑問,都歡迎可愛的你加入我們的刷題群,有疑問233醬會在群里答疑哦~

參考資料:
[1].《算法導論》
[2].https://time.geekbang.org/column/article/75702

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

新北清潔公司,居家、辦公、裝潢細清專業服務

※別再煩惱如何寫文案,掌握八大原則!

※教你寫出一流的銷售文案?

※超省錢租車方案

FB行銷專家,教你從零開始的技巧

也來聊聊 HTTPS.

前言: 網上聊 HTTPS 的文章已經數都數不過來了吧,厚着臉皮,整理下讀書筆記,結合平常項目的實踐,也來聊聊 HTTPS。

一、為什麼需要 HTTPS?

眾所周知,HTTP 協議具有無連接、不可靠、盡最大努力的特點,這也為 HTPP 協議帶來信息竊聽或身份偽裝等安全問題。主要體現在幾個方面:

  • 通信使用明文(不加密),內容可能會被竊聽。
  • 不驗證通信方的身份,因此有可能遭遇偽裝。
  • 無法證明報文的完整性,所以有可能已遭篡改。

那要如何做到防止竊聽保護信息呢?最為普及的就是加密技術。

  • 通信的加密:用 SSL(Secure Socket Layer,安全套接層)或 TLS(Transport Layer Security,安全層傳輸協議)建立安全通信線路之後,就可以在這條線路上進行 HTTP 通信了。與 SSL/TLS 組合使用的 HTTP 就是 HTTPS,通常 HTTP 直接和 TCP 通信,當使用 SSL 時,則演變成先與 SSL 通信,再由 SSL 和 TCP 通信了,所以 HTTPS 並不是一種新的協議。
  • 內容的加密: 對 HTTP 協議傳輸的內容本身加密,即把 HTTP 報文里所含的內容進行加密處理。

SSL 協議最初是由瀏覽器開發商網景通信公司率先倡導的,開發過 SSL3.0 之前的版本。IETF 以 SSL3.0 為基準,后又制定了 TLS1.0、TLS1.1 和 TLS1.2。TSL 是以 SSL為原型開發的協議,有時會統一稱該協議為 SSL。當前主流的版本是 SSL3.0 和 TLS1.0。

SSL 不僅提供加密處理,而且還使用了一種被稱為證書的手段,可用於確定通信方。

二、HTTPS 怎麼來保障通信安全的?

HTTPS 具有加密、認證以及完整性保護的功能。

1. 加密

客戶端和服務端想要進行安全的通信,首先想到的就是對通信雙方的內容進行加密處理。客戶端利用“密鑰”加密內容,服務端利用“密鑰”解密內容,反之亦然。這種方式稱為對稱(共享密鑰)加密。

對稱加密客戶端和服務端的“密鑰”是一致的,因此,客戶端和服務端之間的“密鑰”傳輸不可避免,如果“密鑰”在傳輸途中被盜用,那麼加密處理就沒有意義了。

那麼如何保護“密鑰”的傳輸安全呢?實踐的思路是非對稱(公開密鑰)加密,服務端擁有 公鑰(public key)+ 私鑰(private key)的密鑰對,公鑰任何人都可以獲取,私鑰只保存在服務端。以下是 SSL 建立安全通信線路的過程。

  1. 服務端將公鑰傳輸給客戶端。
  2. 客戶端通過公鑰加密“密鑰”(客戶端生成)得到一個加密串並傳輸給服務端。
  3. 服務端根據私鑰解密加密串得到“密鑰”。
  4. 雙方通過“密鑰”加密傳輸。

非對稱加密“密鑰”的方式很好的保障了“密鑰”的安全傳輸,因為即使傳輸過程中加密串被盜用了,由於盜用者沒有私鑰信息,也無法得到加密串中的“密鑰”信息。

HTTPS 採用對稱(共享密鑰)加密和非對稱(公開密鑰)加密兩者並用的混合加密機制。之所以要這麼複雜,是因為非對稱加密的處理速度相較於對稱加密要慢,因此,我們一般在交換“密鑰”環節使用非對稱加密,之後的建立通信交換報文階段則使用對稱加密方式。

2. 認證

遺憾的是,非對稱加密傳輸“密鑰”的方式仍然有缺陷,那就是無法證明服務器公鑰本身就是貨真價實的公鑰。比如,接收到某台服務器的公鑰,如何證明公鑰就是原本預想的那台服務器發行的公鑰呢?或許在公鑰傳輸途中,真正的公鑰已經被攻擊者替換掉了。

計算機科學領域的任何問題都可以通過增加一個間接的中間層來解決。

這裏我們引入的中間層就是数字證書認證機構(CA,Certificate Authority),数字證書認證機構處於客戶端與服務器雙方都可信賴的第三方機構的立場上,以下是数字證書認證機構的業務流程。

  1. 服務器的運營人員向数字證書認證機構提出公開密鑰的申請。
  2. 数字證書認證機構在判明提出申請者的身份之後,會對已申請的公開密鑰做数字簽名,然後分配這個已簽名的公開密鑰,並將該公開密鑰放入公鑰證書。
  3. 服務器將公鑰證書下發給客戶端。
  4. 客戶端使用公鑰證書的公開密鑰,對那張證書上的数字簽名進行驗證,一旦驗證通過,客戶端便可明確兩件事:一,認證服務器的公開密鑰的是真實有效的数字證書認證機構頒發的。二,服務器的公開密鑰是值得信賴的。

HTTPS 中還可以使用客戶端證書,以客戶端證書進行客戶端認證,證明服務器正在通信的對方始終是預料之內的客戶端,其作用跟服務器證書如出一轍。

使用 OpenSSL 這套開源程序,每個人都可以構建一套屬於自己的認證機構,從而自己給自己頒發服務器證書,但該服務器證書在互聯網上不可作為證書使用,因為個人並不是可信任的三方機構。

3. 完整性保護

基於 SSL 進行 HTTP 通信時,應用層發送數據會附加一種叫做 MAC(Message Authentication Code)的報文摘要,MAC 能夠查知報文是否遭到篡改,從而保護報文的完整性。

三、HTTPS 的通信過程

CBC 模式(Cipher Block Chaining)又名密碼分組鏈接模式。在此模式下,將前一個明文塊加密處理后和下一個明文塊做 XOR 運算,使之重疊,然後再對運算結果做加密處理。 對第一個明文塊做加密時,要麼使用前一段密文的最後一塊,要麼利用外部生成的初始向量(initial vector, IV)。

四、HTTPS 的缺點?

處理速度上,由於 HTTPS 還需要做服務器、客戶端雙方加密及解密過程,因此會消耗 CPU 和內存等硬件資源。

通信上,和單純 HTTP 通信相比,SSL 通信會消耗部分網絡資源。

綜上所述,相較於 HTTP 通信來說,HTTPS 通信速度會變慢。針對速度變慢這一問題,並沒有根本性的解決方案,我們會使用 SSL 加速器這種(專用服務器)硬件來改善該問題。 該硬件為 SS通信專用硬件,相對軟件來講,能夠提高數倍 SSL 的計算速度。

另外,SSL 證書的費用開銷也是使用 HTTPS 的考慮因素之一(阿里雲/騰訊雲有免費的 SSL 證書可以申請使用)。

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

新北清潔公司,居家、辦公、裝潢細清專業服務

※別再煩惱如何寫文案,掌握八大原則!

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

※超省錢租車方案

※教你寫出一流的銷售文案?

網頁設計最專業,超強功能平台可客製化

蓄意污染亞馬遜雨林 雪佛龍在厄瓜多憲法法庭敗訴

環境資訊中心外電;姜唯 翻譯;林大利 審校;稿源:ENS

美國雪佛龍(Chevron)公司的世紀污染訴訟案,有了最新進展。厄瓜多憲法法庭稍早駁回了雪佛龍對95億美元污染判決的最終上訴,認定該公司蓄意將數十億加侖的有毒油廢料傾倒在亞馬遜熱帶雨林的原民土地上。

長達151頁的判決書中,以8比0一致判定原告原民團體勝訴,並駁回了雪佛龍的所有說法,例如雪佛龍自己也是詐欺受害者,以及厄瓜多法院對此案無管轄權等都遭到駁回。

前油田運營商德士古(後被雪佛龍收購)蓄意將數十億加侖的有毒油廢料傾倒在亞馬遜熱帶雨林。圖片來源:Caroline Bennett / Rainforest Action Network

環境集體訴訟案 第四次獲勝

厄瓜多憲法法院僅審理憲法問題,是厄瓜多第三大上訴法院,也是該國第四個維持2011年判決結果的法院,連同厄瓜多最高民事法庭和國家法院一致裁定雪佛龍須做出鉅額賠償。

這個針對雪佛龍公司的集體訴訟案,判決書於7月11日發布,原告是守護亞馬遜陣線(Frente de Defensa de la Amazonia),代表厄瓜多亞馬遜北部地區80個原民和農民社區的草根團體。

該訴訟最初由約3萬名熱帶雨林村民於1993年在紐約聯邦法院提出,被告是前油田運營商德士古(Texaco,已被雪佛龍公司收購)。2001年雪佛龍公司接受厄瓜多具有管轄權後,一名美國聯邦法官將此案移交給厄瓜多法院。

「這個判決是厄瓜多人民20年來為了爭取環境正義,對抗世界級污染者、流氓企業的再一次巨大勝利,」環保金人獎得主、1993年訴訟案起草者楊澤(Luis Yanza)表示。他目前也是守護亞馬遜陣線的主席。

楊澤說:「在雪佛龍開始賠償厄瓜多人民前,任何國家都不應該與雪佛龍做生意。」

致癌油廢料污染 雪佛龍八年拖延纏訟

厄瓜多首席律師薩拉查(Patricio Salazar)表示,「司法已認定雪佛龍非法攻擊代表原住民社群的律師,而不是根據是非來訴訟。雪佛龍現在極有可能必須全額支付賠償金,因為它在法律上和道德上都有義務這樣做。」

厄瓜多初審法院依據105份技術證據報告,在2011年判決該公司致癌油廢料污染了1500平方英里的亞馬遜土地。但是八年來,雪佛龍以拖延戰術因應訴訟。

生活在熱帶雨林中的數千人,包括許多原住民,已經死於癌症,而成千上萬的人必須忍受這場世界級的公衛災難。

代表厄瓜多村民的美國律師唐齊格(Steven Donziger)。圖片來源:Steven Donziger

拒支付賠償金 村民仍沒有醫療

收受雪佛龍公司400口油井特許使用費的厄瓜多政府,沒有給受害者太大幫助。

哈佛大學畢業,代表厄瓜多村民的美國律師唐齊格(Steven Donziger)曾走訪受影響地區數十次,他表示受影響地區並沒有醫療服務,許多人甚至一次醫生都沒看,也沒接受任何治療就死於癌症。

「雪佛龍在厄瓜多引發了一場史無前例的人道主義危機。再不清除污染,未來幾年將有數萬人死亡。全世界都必須關注,雪佛龍的股東和管理層必須立即採取行動,解決這個日益嚴重的問題。」唐齊格說。

雪佛龍多年來一直拒絕支付判決賠償金,目前連本帶利已經來到120億美元(約新台幣1361億元)。

雪佛龍公司人員甚至威脅原民,若繼續堅持訴訟,他們可和原告「終身纏訟」。

卡普蘭法官 採信偽證風波

這份最新判決也是對美國紐約南區地方法院法官卡普蘭(Lewis A. Kaplan)的重大打擊。

2014年,這位具有爭議性的法官僅憑一位承認收賄的雪佛龍證人的假證詞便裁定,厄瓜多最高法院對雪佛龍提出的95億美元賠償判決是透過欺詐和脅迫手段取得。卡普蘭拒絕讓公正的事實調查員陪審團參與,也拒絕考量任何有關雪佛龍污染厄瓜多環境的證據。

但新事證顯示,雪佛龍支付厄瓜多前法官格拉(Alberto Guerra)大筆賄款之後,卡普蘭的判決被推翻。雪佛龍幫助格拉一家搬到美國後,格拉承認在雪佛龍律師的指導下作偽證。

卡普蘭僅憑格拉的偽證做出判決,也是全世界唯一做出有利雪佛龍判決的法官。

「出於省錢的錯誤營運決策」 法庭定調蓄意污染

基於比卡普蘭所能取得更完整的證據,17名厄瓜多法官作出有利厄瓜多村民的判決。12名加拿大法官,包括該國最高法院,也針對多個技術性問題作出有利厄瓜多村民的判決。

厄瓜多憲法法庭強調,雪佛龍污染環境造成嚴重後果不是意外造成,而是處心積慮為公司和股東省錢的運營決策,加上20年來蓄意拖延的額外罪行所導致。

厄瓜多原告也在加拿大法院取得了幾項上訴勝利,他們在加國蒐集雪佛龍資產,以迫使其遵守厄瓜多的判決。

Chevron Defeated in Ecuador’s Constitutional Court QUITO, Ecuador, July 31, 2018 (ENS)

In a benchmark pollution case, Ecuador’s Constitutional Court has rejected Chevron’s final appeal of a $9.5 billion pollution judgment that found the company deliberately dumped billions of gallons of toxic oil waste onto Indigenous lands in the Amazon rainforest.

Photo: Caroline Bennett / Rainforest Action Network

The unanimous 8-0 decision, issued in a 151-page document published July 11, was a total victory for the Indigenous groups that brought the case and a rejection of all of Chevron’s claims.

The Court rejected Chevron’s allegations that it was victimized by fraud, and the court threw out the company’s claim that Ecuadorian courts had no jurisdiction over the matter.

Ecuador’s Constitutional Court, which deals only with Constitutional issues, is the third major appellate court in Ecuador and the fourth court overall in the country to uphold the trial-level decision against Chevron, which was issued in 2011. Ecuador’s highest civil court, the National Court of Justice, has ruled unanimously to affirm the judgment against Chevron.

The class action case against Chevron was spearheaded by the Frente de Defensa de la Amazonia, the Amazon Defense Front, a grassroots group representing 80 Indigenous peoples and farmer communities in Ecuador’s northern Amazon region.

The case was originally filed in 1993 in federal court in New York against the former oil field operator Texaco, now part of Chevron Corporation, on behalf of an estimated 30,000 rainforest villagers. But in 2001 a U.S. federal judge moved it to Ecuador’s courts at Chevron’s request after the company accepted jurisdiction there.

“This decision is another huge victory for the people of Ecuador in their historic two-decade battle for environmental justice against the world’s worst corporate polluter and rogue operator,” said Luis Yanza, a Goldman Prize winner who initiated the lawsuit against Chevron in U.S. federal court in 1993, and serves as president of the Frente de Defensa de la Amazonia, Amazon Defense Front.

“No country should ever do business with Chevron until the company first pays for the harm it caused to the people of Ecuador,” Yanza said.

Patricio Salazar, the lead Ecuadorian lawyer on the case, said, “Justice has prevailed over Chevron’s illegal attempts to engage in constant attacks on lawyers who defend the Indigenous communities rather than litigate in good faith on the merits. It is now highly likely that Chevron will pay every last dollar of the judgment against it, as it is legally and ethically obligated to do.”

After eight years of proceedings slowed by Chevron’s strategy of deliberate delay, Ecuador’s trial court relied on 105 technical evidentiary reports to find in 2011 that the company poisoned a 1,500 square mile area of the Amazon with carcinogenic oil waste.

Thousands who live in the rainforest, including many Indigenous peoples, have died of cancer while tens of thousands must endure what is one of the world’s worst ongoing public health catastrophes.

Photo: Jonathan McIntosh / Rainforest Action Network

Ecuador’s government, which received royalties from Chevron’s operation of 400 well sites, has been of little help to the victims. Medical care in the affected region is non-existent, and many people perish from cancer without even visiting a doctor and after receiving no treatment, said Steven Donziger, the Harvard educated U.S. legal representative of the Ecuadorian communities, who has taken dozens of trips to the affected area.

“Chevron has caused a humanitarian crisis in Ecuador of epic proportions that is ongoing to this day,” he said. “Tens of thousands of people will die in the coming years if nothing is done to clean up the pollution. The world must pay attention and Chevron shareholders and management must act immediately to address this worsening problem.”

Chevron has refused for years to pay the Ecuador judgment, now worth $12 billion with interest. Company officials have threatened the Indigenous groups with a “lifetime of litigation” if they persist.

The latest Ecuadorean court decision is also a major blow to controversial Judge Lewis A. Kaplan, a judge of the U.S. District Court for the Southern District of New York.

In 2014, Judge Kaplan ruled that the $9.5 billion Lago Agrio judgment leveled against Chevron by Ecuador’s highest court, was obtained by way of fraud and coercion.

Kaplan relied on false testimony from an admittedly corrupt Chevron witness to find that the Ecuador judgment was procured by fraud. Kaplan refused to seat a jury of impartial fact finders, and he refused to consider any evidence of Chevron’s environmental contamination in Ecuador.

But Kaplan’s decision was disproven after evidence emerged that Chevron paid large sums to Alberto Guerra, a former Ecuadorian judge booted from the bench after he admitted taking bribes. Guerra was moved with his family by Chevron to the United States and later admitted lying on the stand after being coached for 53 days by Chevron lawyers headed by Randy Mastro at Gibson Dunn.

Kaplan based his core findings largely on Guerra’s false testimony. And Kaplan remains the only judge in the world to have ruled in favor of Chevron.

Seventeen Ecuador judges, who had access to a fuller evidentiary record than Kaplan, ruled in favor of the affected communities. Twelve judges from Canada, including the country’s entire Supreme Court, have also ruled in favor of the Ecuadorians on various technical issues.

The Ecuador decision confronts Chevron on the brutal human consequences of both its original environmental crimes, which the Court emphasizes were not the result of an accident, but rather of deliberate operational decision-making designed to save money and enrich the company’s shareholders and executives, and the additional offense of its two-decade campaign of distraction and delay.

The Ecuadorian plaintiffs also have picked up several appellate victories in Canadian courts as they attempt to collect Chevron assets in that country to force compliance with the Ecuador judgment.

※ 全文及圖片詳見:

作者

如果有一件事是重要的,如果能為孩子實現一個願望,那就是人類與大自然和諧共存。

於特有生物研究保育中心服務,小鳥和棲地是主要的研究對象。是龜毛的讀者,認為龜毛是探索世界的美德。

延伸閱讀

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※為什麼 USB CONNECTOR 是電子產業重要的元件?

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

※台北網頁設計公司全省服務真心推薦

※想知道最厲害的網頁設計公司"嚨底家"!

新北清潔公司,居家、辦公、裝潢細清專業服務

※推薦評價好的iphone維修中心

野花如何用多樣性抗氣候變遷 基因研究說分明

環境資訊中心綜合外電;姜唯 編譯;林大利 審校

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

USB CONNECTOR掌控什麼技術要點? 帶您認識其相關發展及效能

台北網頁設計公司這麼多該如何選擇?

※智慧手機時代的來臨,RWD網頁設計為架站首選

※評比南投搬家公司費用收費行情懶人包大公開

※幫你省時又省力,新北清潔一流服務好口碑

※回頭車貨運收費標準

八張圖徹底了解JDK8 GC調優秘籍-附PDF下載

目錄

  • 簡介
  • 分代垃圾回收器的內存結構
  • JDK8中可用的GC
  • 打印GC信息
  • 內存調整參數
  • Thread配置
  • 通用GC參數
  • CMS GC
  • G1參數
  • 總結

簡介

JVM的參數有很多很多,根據我的統計JDK8中JVM的參數總共有1853個,正式的參數也有680個。

這麼多參數帶給我們的是對JVM的細粒度的控制,但是並不是所有的參數都需要我們自己去調節的,我們需要關注的是一些最常用的,對性能影響比較大的GC參數即可。

為了更好的讓大家理解JDK8中 GC的調優的秘籍,這裏特意準備了八張圖。在本文的最後,還附帶了一個總結的PDF all in one文檔,大家把PDF下載回去,遇到問題就看兩眼,不美嗎?

分代垃圾回收器的內存結構

為了更好的提升GC的效率,現代的JVM都是採用的分代垃圾回收的策略(ZGC不是)。

java運行時內存可以分為JVM內存和非JVM內存。

JVM內存又可以分為堆內存和非堆內存。

堆內存大家都很熟悉了,YoungGen中的Eden,Survivor和OldGen。

非堆內存中存儲的有thread Stack,Code Cache, NIO Direct Buffers,Metaspace等。

注意這裏的Metaspace元空間是方法區在JDK8的實現,它是在本地內存中分配的。

JDK8中可用的GC

JDK8中到底有哪些可以使用的GC呢?

這裏我們以HotSpot JVM為例,總共可以使用4大GC方式:

其中對於ParallelGC和CMS GC又可以對年輕代和老年代分別設置GC方式。

大家看到上圖可能有一個疑問,Parallel scavenge和Parallel有什麼區別呢?

其實這兩個GC的算法是類似的,Parallel Scavenge收集器也經常稱為“吞吐量優先”收集器,Parallel Scavenge收集器提供了兩個參數用於精確控制吞吐量; -XX:MaxGCPauseMillis:控制最大垃圾收集停頓時間; -XX:GCTimeRatio:設置吞吐量大小。

同時Parallel Scavenge收集器能夠配合自適應調節策略,把內存管理的調優任務交給虛擬機去完成。

JDK8中默認開啟的是ParallelGC。

打印GC信息

如果想研究和理解GC的內部信息,GC信息打印是少不了的:

上圖提供了一些非常有用的GC日誌的控制參數。

內存調整參數

JVM分為Heap區和非Heap區,各個區又有更細的劃分,下面就是調整各個區域大小的參數:

Thread配置

TLAB大家還記得嗎?TLAB的全稱是Thread-Local Allocation Buffers。TLAB是在Eden區間分配的一個一個的連續空間。然後將這些連續的空間分配個各個線程使用。

因為每一個線程都有自己的獨立空間,所以這裏不涉及到同步的概念。

上圖就是TLAB的參數。

通用GC參數

雖然JDK8的GC這麼多,但是他們有一些通用的GC參數:

這裏講解一下Young space tenuring,怎麼翻譯我不是很清楚,這個主要就是指Young space中的對象經過多少次GC之後會被提升到Old space中。

CMS GC

CMS全稱是Concurrent mark sweep。是一個非常非常複雜的GC。

複雜到什麼程度呢?光光是CMS調優的參數都有一百多個!

下圖是常用的CMS的參數。

CMS這裏就不多講了,因為在JDK9之後,CMS就已經被廢棄了。

主要原因是CMS太過複雜,如果要向下兼容需要巨大的工作量,然後就直接被廢棄了。

在JDK9之後,默認的GC是G1。

G1參數

G1收集器是分代的和region化的,也就是整個堆內存被分為一系列大小相等的region。在啟動時,JVM設置region的大小,根據堆大小的不同,region的大小可以在1MB到32MB之間變動,region的數量最多不超過2048個。Eden區、Survivor區、老年代是這些region的邏輯集合,它們並不是連續的。

G1中的垃圾收集過程:年輕代收集和混合收集交替進行,背後有全局的併發標記周期在進行。當老年代分區佔用的空間達到或超過初始閾值,就會觸發併發標記周期。

下圖是G1的調優參數:

總結

上面總共8副圖,我把他們做成了一個PDF,預覽界面大概是這樣子的:

大家可以通過下面的鏈接直接下載PDF版本:

JDK8GC-cheatsheet.pdf

如果遇到問題可以直接拿過來參考。這種東西英文名字應該叫JDK8 GC cheatsheet,翻譯成中文應該就是JDK8 GC調優秘籍!

本文作者:flydean程序那些事

本文鏈接:http://www.flydean.com/jdk8-gc-cheatsheet/

本文來源:flydean的博客

歡迎關注我的公眾號:程序那些事,更多精彩等着您!

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※為什麼 USB CONNECTOR 是電子產業重要的元件?

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

※台北網頁設計公司全省服務真心推薦

※想知道最厲害的網頁設計公司"嚨底家"!

新北清潔公司,居家、辦公、裝潢細清專業服務

※推薦評價好的iphone維修中心

Newtonsoft 六個超簡單又實用的特性,值得一試 【下篇】

一:講故事

上一篇介紹的 6 個特性從園子里的反饋來看效果不錯,那這一篇就再帶來 6 個特性同大家一起欣賞。

二:特性分析

1. 像弱類型語言一樣解析 json

大家都知道弱類型的語言有很多,如: nodejs,python,php,它們有一個的地方就是處理json,不需要像 強類型語言 那樣還要給它配一個類,什麼意思呢? 就拿下面的 json 說事。


{
  "DisplayName": "新一代算法模型",
  "CustomerType": 1,
  "Report": {
    "TotalCustomerCount": 1000,
    "TotalTradeCount": 50
  },
  "CustomerIDHash": [1,2,3,4,5]
}

這個 json 如果想灌到 C# 中處理,你就得給它定義一個適配的類,就如 初篇 的客戶算法模型類,所以這裏就有了一個需求,能不能不定義類也可以自由解析上面這串 json 呢??? 哈哈,當然是可以的, 反序列化成 Dictionary 即可,就拿提取 Report.TotalCustomerCountCustomerIDHash 這兩個字段演示一下。


        static void Main(string[] args)
        {
            var json = @"{
                           'DisplayName': '新一代算法模型',
                           'CustomerType': 1,
                           'Report': {
                             'TotalCustomerCount': 1000,
                             'TotalTradeCount': 50
                           },
                           'CustomerIDHash': [1,2,3,4,5]
                         }";

            var dict = JsonConvert.DeserializeObject<Dictionary<object, object>>(json);

            var report = dict["Report"] as JObject;
            var totalCustomerCount = report["TotalCustomerCount"];

            Console.WriteLine($"totalCustomerCount={totalCustomerCount}");

            var arr = dict["CustomerIDHash"] as JArray;
            var list = arr.Select(m => m.Value<int>()).ToList();

            Console.WriteLine($"list={string.Join(",", list)}");
        }

2. 如何讓json中的枚舉保持更易讀的字符串型

這句話是什麼意思呢? 默認情況下, SerializeObject 會將 Model 中的 Enum 變成數值型,大家都知道數值型語義性是非常差的,如下代碼所示:


    static void Main(string[] args)
    {
        var model = new ThreadModel() { ThreadStateEnum = System.Threading.ThreadState.Running };

        var json = JsonConvert.SerializeObject(model);

        Console.WriteLine(json);
    }

    class ThreadModel
    {
        public System.Threading.ThreadState ThreadStateEnum { get; set; }
    }

對吧,確實語義特別差,那能不能直接生成 Running 這種字符串形式呢? 當然可以了。。。改造如下:


  var json = JsonConvert.SerializeObject(model, new StringEnumConverter());

這裏可能就有人鑽牛角尖了,能不能部分指定讓枚舉生成 string,其他的生成 int ,沒關係,這也難不倒我,哪裡使用就用 JsonConverter 標記哪裡。。。


        static void Main(string[] args)
        {
            var model = new ThreadModel()
            {
                ThreadStateEnum = System.Threading.ThreadState.Running,
                TaskStatusEnum = TaskStatus.RanToCompletion
            };

            var json = JsonConvert.SerializeObject(model);

            Console.WriteLine(json);
        }

        class ThreadModel
        {
            public System.Threading.ThreadState ThreadStateEnum { get; set; }

            [JsonConverter(typeof(StringEnumConverter))]
            public TaskStatus TaskStatusEnum { get; set; }
        }        

3. 格式化 json 中的時間類型

在 model 轉化成 json 的過程中,總少不了 時間類型,為了讓時間類型 可讀性更高,通常會 格式化為 YYYY年/MM月/dd日 ,那如何實現呢? 很簡單撒,在 JsonConvert 中也是一個 枚舉 幫你搞定。。。


        static void Main(string[] args)
        {
            var json = JsonConvert.SerializeObject(new Order()
            {
                OrderTitle = "女裝大佬",
                Created = DateTime.Now
            }, new JsonSerializerSettings
            {
                DateFormatString = "yyyy年/MM月/dd日",
            });

            Console.WriteLine(json);
        }
        public class Order
        {
            public string OrderTitle { get; set; }
            public DateTime Created { get; set; }
        }   

對了,我記得很早的時候,C# 自帶了一個 JavaScriptSerializer, 也是用來進行 model 轉 json的,但是它會將 datetime 轉成 時間戳,而不是時間字符串形式,如果你因為特殊原因想通過 JsonConvert 將時間生成時間戳的話,也是可以的, 用 DateFormatHandling.MicrosoftDateFormat 枚舉指定一下即可,如下:

4. 對一些常用設置進行全局化

在之前所有演示的特性技巧中都是在 JsonConvert 上指定的,也就是說 100 個 JsonConvert 我就要指定 100 次,那有沒有類似一次指定,整個進程通用呢? 這麼強大的 Newtonsoft 早就支持啦, 就拿上面的 Order 舉例:


        JsonConvert.DefaultSettings = () =>
        {
            var settings = new JsonSerializerSettings
            {
                Formatting = Formatting.Indented
            };
            return settings;
        };

        var order = new Order() { OrderTitle = "女裝大佬", Created = DateTime.Now };

        var json1 = JsonConvert.SerializeObject(order);
        var json2 = JsonConvert.SerializeObject(order);

        Console.WriteLine(json1);
        Console.WriteLine(json2);

可以看到,Formatting.Indented 對兩串 json 都生效了。

5. 如何保證 json 到 model 的嚴謹性 及提取 json 未知字段

有時候我們有這樣的需求,一旦 json 中出現 model 未知的字段,有兩種選擇: 要麼報錯,要麼提取出未知字段,在 Newtonsoft 中默認的情況是忽略,場景大家可以自己找哈。

  • 未知字段報錯

        static void Main(string[] args)
        {
            var json = "{'OrderTitle':'女裝大佬', 'Created':'2020/6/23','Memo':'訂單備註'}";

            var order = JsonConvert.DeserializeObject<Order>(json, new JsonSerializerSettings
            {
                MissingMemberHandling = MissingMemberHandling.Error
            });

            Console.WriteLine(order);
        }

        public class Order
        {
            public string OrderTitle { get; set; }
            public DateTime Created { get; set; }
            public override string ToString()
            {
                return $"OrderTitle={OrderTitle}, Created={Created}";
            }
        }        

  • 提取未知字段

我依稀的記得 WCF 在這種場景下也是使用一個 ExtenstionDataObject 來存儲客戶端傳過來的未知字段,有可能是客戶端的 model 已更新,server端還是舊版本,通常在 json 序列化中也會遇到這種情況,這裏只要使用 JsonExtensionData 特性就可以幫你搞定,在 OnDeserialized 這種AOP方法中進行攔截,如下代碼:


    static void Main(string[] args)
    {
        var json = "{'OrderTitle':'女裝大佬', 'Created':'2020/6/23','Memo':'訂單備註'}";

        var order = JsonConvert.DeserializeObject<Order>(json);

        Console.WriteLine(order);
    }

    public class Order
    {
        public string OrderTitle { get; set; }

        public DateTime Created { get; set; }

        [JsonExtensionData]
        private IDictionary<string, JToken> _additionalData;

        public Order()
        {
            _additionalData = new Dictionary<string, JToken>();
        }

        [OnDeserialized]
        private void OnDeserialized(StreamingContext context)
        {
            var dict = _additionalData;
        }

        public override string ToString()
        {
            return $"OrderTitle={OrderTitle}, Created={Created}";
        }
    }        

6. 開啟 JsonConvert 詳細日誌功能

有時候在查閱源碼的時候開啟日誌功能更加有利於理解源碼的內部運作,所以這也是一個非常實用的功能,看看如何配置吧。


        static void Main(string[] args)
        {
            var json = "{'OrderTitle':'女裝大佬', 'Created':'2020/6/23','Memo':'訂單備註'}";

            MemoryTraceWriter traceWriter = new MemoryTraceWriter();

            var account = JsonConvert.DeserializeObject<Order>(json, new JsonSerializerSettings
            {
                TraceWriter = traceWriter
            });

            Console.WriteLine(traceWriter.ToString());
        }

        public class Order
        {
            public string OrderTitle { get; set; }

            public DateTime Created { get; set; }

            public override string ToString()
            {
                return $"OrderTitle={OrderTitle}, Created={Created}";
            }
        }

三:總結

嘿嘿,這篇 6 個特性就算說完了, 結合上一篇一共 12 個特性,是不是非常簡單且實用,後面準備給大家帶來一些源碼解讀吧! 希望本篇對您有幫助,謝謝!

如您有更多問題與我互動,掃描下方進來吧~

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理
【其他文章推薦】

USB CONNECTOR掌控什麼技術要點? 帶您認識其相關發展及效能

台北網頁設計公司這麼多該如何選擇?

※智慧手機時代的來臨,RWD網頁設計為架站首選

※評比南投搬家公司費用收費行情懶人包大公開

※幫你省時又省力,新北清潔一流服務好口碑

※回頭車貨運收費標準

GitHub 熱點速覽 Vol.25:距離優雅編程你差個它

作者:HelloGitHub-小魚乾

摘要:如何優雅地誇一個程序員呢?vscode-rainbow-fart 作為一個彩虹屁的項目,深得程序員心,能在你編程時瘋狂稱讚你的除了你自己,還有它。除了鼓勵之外,Super Linte 是官方出品的旨在保證代碼和文檔一致性的工具,有了它,你可以更優雅地進行編程。說完優雅編程,來說下優雅使用 k8s,那就不得不提 Lens,一個專業管理 k8s 工具。

以下內容摘錄自微博@HelloGitHub 的 GitHub Trending,選項標準:新發布 | 實用 | 有趣,根據項目 release 時間分類,發布時間不超過 7 day 的項目會標註 New,無該標誌則說明項目 release 超過一周。由於本文篇幅有限,還有部分項目未能在本文展示,望周知

  • 本文目錄
      1. 本周特推
      • 1.1 GitHub 官方出品:super-linter
      • 1.2 彩虹屁 VSCode 插件:vscode-rainbow-fart
      1. GitHub Trending 周榜
      • 2.1 Python 實用編程:practical-python
      • 2.2 有碼變高清:pulse
      • 2.3 刷題模版:algorithm-pattern
      • 2.4 一分鐘一個小 case:python-small-examples
      • 2.5 專業管理 k8s:lens
      • 2.6 益智遊戲:shapez
      • 2.7 數據科學:GoPlus
      1. 本周 GitHub Trending #量化投資# 主題的主力軍
      • 3.1 量化交易框架:vnpy
      • 3.2 量化交易組件:easytrader
      • 3.3 30 天掌握量化交易:stock
      1. 推薦閱讀

1. 本周特推

1.1 GitHub 官方出品:super-linter

本周 star 增長數:3100+

GitHub Super Linter 是由 GitHub Services DevOps 工程團隊開源的提供給 Action 調用的存儲庫,目的是保持我們文檔和代碼的一致性,同時提升整個公司之間的交流和協作的效率。特性包括:

  • 防止將損壞的代碼上傳到主分支;
  • 幫助建立多種語言的編碼最佳實踐;
  • 制訂代碼布局和格式的指南;
  • 自動化流程以幫助簡化代碼審查;

GitHub 地址→https://github.com/github/super-linter/

1.2 彩虹屁 VSCode 插件:vscode-rainbow-fart

本周 star 增長數:1800+

Newvscode-rainbow-fart 是一個彩虹屁 VSCode 插件,在你編程時瘋狂稱讚你,可以根據代碼關鍵字播放貼近代碼意義的真人語音,誇你寫代碼牛逼。

GitHub 地址→https://github.com/SaekiRaku/vscode-rainbow-fart

2. GitHub Trending 周榜

2.1 Python 實用編程:practical-python

本周 star 增長數:1850+

practical-python 是一個從事 Python 編程近三十年的工程師出的 Python 核心課程,它需要你 3、4 天的學習時間,大約 25-35 小時的時間,包括 130 多個項目實踐。

GitHub 地址→https://github.com/dabeaz-course/practical-python

2.2 有碼變高清:pulse

本周 star 增長數:1500+

Newpulse 是一個可以將馬賽克圖片百年變成高清圖的工具,近日由杜克大學(Duke University)研究團隊開發了。作為一款 AI 修圖黑科技 PULSE,可以解決所有低像素煩惱。據說它能夠將圖像原始分辨率放大 64 倍,任何渣畫質都可以秒變高清、逼真圖像,甚至被打了馬賽克的人臉圖像,毛孔、皺紋,頭髮也都能被清晰還原。

GitHub 地址→https://github.com/adamian98/pulse

2.3 刷題模版:algorithm-pattern

本周 star 增長數:2800+

Newalgorithm-pattern 是項目作者找工作時,從 0 開始刷 LeetCode 的心得記錄,通過各種刷題文章、專欄、視頻等總結的一套自己的刷題模板。

GitHub 地址→https://github.com/greyireland/algorithm-pattern

2.4 一分鐘一個小 case:python-small-examples

本周 star 增長數:10900+

python-small-examples 是一個告別枯燥,60 秒學會一個 Python 小例子的項目,目前庫已有 223 個實用的小例子 。

GitHub 地址→https://github.com/jackzhenguo/python-small-examples

2.5 專業管理 k8s:lens

本周 star 增長數:800+

Len 是一個開源、免費可用的 IDE,可方便管理 Kubernetes 的工具。

GitHub 地址→https://github.com/lensapp/lens

2.6 益智遊戲:shapez.io

本周 star 增長數:600+

shapez.io 是一個受 Factorio 啟發的搭建遊戲。你要做的事情就是簡單地通過切割,旋轉,合併和繪製形狀的零件來產生形狀。

GitHub 地址→https://github.com/tobspr/shapez.io

2.7 數據科學:GoPlus

本周 star 增長數:1800+

NewGoPlus 是數據科學的 Go+ 語言。

GitHub 地址→https://github.com/qiniu/goplus

3. 本周 GitHub Trending #投資量化#主題的主力軍

在本期主題模塊,小魚乾這裏選取了 3 個和量化相關的小工具,希望能增加你的收入,養肥你的錢包。

3.1 量化交易框架:vnpy

vn.py 是一套基於 Python 的開源量化交易系統開發框架。

GitHub 地址→https://github.com/vnpy/vnpy

3.2 量化交易組件:easytrader

easytrader 是一個提供同花順客戶端/國金/華泰客戶端/雪球的基金、股票自動程序化交易以及自動打新,支持跟蹤 joinquant /ricequant 模擬交易和實盤雪球組合的量化交易組件。特性:

  • 進行自動的程序化股票交易
  • 支持跟蹤 joinquant, ricequant 的模擬交易
  • 支持跟蹤雪球組合調倉
  • 支持通用的同花順客戶端模擬操作
  • 實現自動登錄
  • 支持通過 webserver 遠程操作客戶端
  • 支持命令行調用,方便其他語言適配
  • 基於 Python 3.6, Win。注: Linux 僅支持雪球

GitHub 地址→https://github.com/shidenggui/easytrader

3.3 30 天掌握量化交易:stock

stock 是作者作為業餘投機者(韭菜)一枚,自學量化交易,把經歷寫成代碼推送到 GitHub 的項目。

GitHub 地址→https://github.com/Rockyzsu/stock

推薦閱讀

  • GitHub 熱點速覽 Vol.24:程序員自我增值,優雅賺零花錢
  • GitHub 熱點速覽 Vol.23:前後端最佳實踐
  • GitHub 熱點速覽 Vol.22:如何打造超級技術棧

以上為 2020 年第 23 個工作周的 GitHub Trending 如果你 Pick 其他好玩、實用的 GitHub 項目,記得來 HelloGitHub issue 區和我們分享下喲

HelloGitHub 交流群現已全面開放,添加微信號:HelloGitHub 為好友入群,可同前端、Java、Go 等各界大佬談笑風生、切磋技術~

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

南投搬家公司費用需注意的眉眉角角,別等搬了再說!

新北清潔公司,居家、辦公、裝潢細清專業服務

※教你寫出一流的銷售文案?

Netty源碼分析之自定義編解碼器

在日常的網絡開發當中,協議解析都是必須的工作內容,Netty中雖然內置了基於長度、分隔符的編解碼器,但在大部分場景中我們使用的都是自定義協議,所以Netty提供了  MessageToByteEncoder<I>  與  ByteToMessageDecoder  兩個抽象類,通過繼承重寫其中的encode與decode方法實現私有協議的編解碼。這篇文章我們就對Netty中的自定義編解碼器進行實踐與分析。

一、編解碼器的使用

下面是MessageToByteEncoder與ByteToMessageDecoder使用的簡單示例,其中不涉及具體的協議編解碼。

創建一個sever端服務

EventLoopGroup bossGroup = new NioEventLoopGroup(1);
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        final CodecHandler codecHandler = new CodecHandler();
        try {
            ServerBootstrap b = new ServerBootstrap();
            b.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class).option(ChannelOption.SO_BACKLOG, 100)
                    .handler(new LoggingHandler(LogLevel.INFO)).childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        public void initChannel(SocketChannel ch) throws Exception {
                            ChannelPipeline p = ch.pipeline();
                            if (sslCtx != null) {
                                p.addLast(sslCtx.newHandler(ch.alloc()));
                            }
                            //添加編解碼handler
                            p.addLast(new MessagePacketDecoder(),new MessagePacketEncoder());
                            //添加自定義handler
                            p.addLast(codecHandler);
                        }
                    });

            // Start the server.
            ChannelFuture f = b.bind(PORT).sync();

繼承MessageToByteEncoder並重寫encode方法,實現編碼功能

public class MessagePacketEncoder extends MessageToByteEncoder<byte[]> {

    @Override
    protected void encode(ChannelHandlerContext ctx, byte[] bytes, ByteBuf out) throws Exception {
        //進行具體的編碼處理 這裏對字節數組進行打印
        System.out.println("編碼器收到數據:"+BytesUtils.toHexString(bytes));
        //寫入並傳送數據
        out.writeBytes(bytes);
    }
}

繼承ByteToMessageDecoder 並重寫decode方法,實現解碼功能

public class MessagePacketDecoder extends ByteToMessageDecoder {
    @Override
    protected void decode(ChannelHandlerContext ctx, ByteBuf buffer, List<Object> out){
        try {
            if (buffer.readableBytes() > 0) {
                // 待處理的消息包
                byte[] bytesReady = new byte[buffer.readableBytes()];
                buffer.readBytes(bytesReady);
                //進行具體的解碼處理
                System.out.println("解碼器收到數據:"+ByteUtils.toHexString(bytesReady));
                //這裏不做過多處理直接把收到的消息放入鏈表中,並向後傳遞
                out.add(bytesReady);
            
            }
        }catch(Exception ex) {
            
        }

    }

}

實現自定義的消息處理handler,到這裏其實你拿到的已經是編解碼后的數據

public class CodecHandler extends ChannelInboundHandlerAdapter{
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) {
        System.out.println("CodecHandler收到數據:"+ByteUtils.toHexString((byte[])msg));
        byte[] sendBytes = new byte[] {0x7E,0x01,0x02,0x7e};
        ctx.write(sendBytes);
    }

    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) {
        ctx.flush();
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        // Close the connection when an exception is raised.
        cause.printStackTrace();
        ctx.close();
    }
}

運行一個客戶端模擬發送字節0x01,0x02,看一下輸出的執行結果

解碼器收到數據:0102
CodecHandler收到數據:0102
編碼器收到數據:7E01027E

 根據輸出的結果可以看到消息的入站與出站會按照pipeline中自定義的順序傳遞,同時通過重寫encode與decode方法實現我們需要的具體協議編解碼操作。

二、源碼分析

 通過上面的例子可以看到MessageToByteEncoder<I>與ByteToMessageDecoder分別繼承了ChannelInboundHandlerAdapter與ChannelOutboundHandlerAdapter,所以它們也是channelHandler的具體實現,並在創建sever時被添加到pipeline中, 同時為了方便我們使用,netty在這兩個抽象類中內置與封裝了一些其操作;消息的出站和入站會分別觸發write與channelRead事件方法,所以上面例子中我們重寫的encode與decode方法,也都是在父類的write與channelRead方法中被調用,下面我們就別從這兩個方法入手,對整個編解碼的流程進行梳理與分析。

1、MessageToByteEncoder

編碼需要操作的是出站數據,所以在MessageToByteEncoder的write方法中會調用我們重寫的encode具體實現, 把我們內部定義的消息實體編碼為最終要發送的字節流數據發送出去。

    @Override
    public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
        ByteBuf buf = null;
        try {
            if (acceptOutboundMessage(msg)) {//判斷傳入的msg與你定義的類型是否一致
                @SuppressWarnings("unchecked")
                I cast = (I) msg;//轉為你定義的消息類型
                buf = allocateBuffer(ctx, cast, preferDirect);//包裝成一個ByteBuf
                try {
                    encode(ctx, cast, buf);//傳入聲明的ByteBuf,執行具體編碼操作
                } finally {
                    /**
                     * 如果你定義的類型就是ByteBuf 這裏可以幫助你釋放資源,不需要在自己釋放
                     * 如果你定義的消息類型中包含ByteBuf,這裡是沒有作用,需要你自己主動釋放
                     */
                    ReferenceCountUtil.release(cast);//釋放你傳入的資源
                }

                //發送buf
                if (buf.isReadable()) {
                    ctx.write(buf, promise);
                } else {
                    buf.release();
                    ctx.write(Unpooled.EMPTY_BUFFER, promise);
                }
                buf = null;
            } else {
                //類型不一致的話,就直接發送不再執行encode方法,所以這裏要注意如果你傳遞的消息與泛型類型不一致,其實是不會執行的
                ctx.write(msg, promise);
            }
        } catch (EncoderException e) {
            throw e;
        } catch (Throwable e) {
            throw new EncoderException(e);
        } finally {
            if (buf != null) {
                buf.release();//釋放資源
            }
        }
    }

 MessageToByteEncoder的write方法要實現的功能還是比較簡單的,就是把你傳入的數據類型進行轉換和發送;這裡有兩點需要注意:

  • 一般情況下,需要通過重寫encode方法把定義的泛型類型轉換為ByteBuf類型, write方法內部自動幫你執行傳遞或發送操作;
  • 代碼中雖然有通過ReferenceCountUtil.release(cast)釋放你定義的類型資源,但如果定義的消息類中包含ByteBuf對象,仍需要主動釋放該對象資源;

2、ByteToMessageDecoder

從命名上就可以看出ByteToMessageDecoder解碼器的作用是把字節流數據編碼轉換為我們需要的數據格式

作為入站事件,解碼操作的入口自然是channelRead方法

 @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        if (msg instanceof ByteBuf) {//如果消息是bytebuff
            CodecOutputList out = CodecOutputList.newInstance();//實例化一個鏈表
            try {
                ByteBuf data = (ByteBuf) msg;
                first = cumulation == null;
                if (first) {
                    cumulation = data;
                } else {
                    cumulation = cumulator.cumulate(ctx.alloc(), cumulation, data);
                }
                callDecode(ctx, cumulation, out);//開始解碼
            } catch (DecoderException e) {
                throw e;
            } catch (Exception e) {
                throw new DecoderException(e);
            } finally {
                if (cumulation != null && !cumulation.isReadable()) {//不為空且沒有可讀數據,釋放資源
                    numReads = 0;
                    cumulation.release();
                    cumulation = null;
                } else if (++ numReads >= discardAfterReads) {
                    // We did enough reads already try to discard some bytes so we not risk to see a OOME.
                    // See https://github.com/netty/netty/issues/4275
                    numReads = 0;
                    discardSomeReadBytes();
                }

                int size = out.size();
                decodeWasNull = !out.insertSinceRecycled();
                fireChannelRead(ctx, out, size);//向下傳遞消息
                out.recycle();
            }
        } else {
            ctx.fireChannelRead(msg);
        }
    }

callDecode方法內部通過while循環的方式對ByteBuf數據進行解碼,直到其中沒有可讀數據 

    protected void callDecode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) {
        try {
            while (in.isReadable()) {//判斷ByteBuf是還有可讀數據
                int outSize = out.size();//獲取記錄鏈表大小

                if (outSize > 0) {//判斷鏈表中是否已經有數據
                    fireChannelRead(ctx, out, outSize);//如果有數據繼續向下傳遞
                    out.clear();//清空鏈表

                    // Check if this handler was removed before continuing with decoding.
                    // If it was removed, it is not safe to continue to operate on the buffer.
                    //
                    // See:
                    // - https://github.com/netty/netty/issues/4635
                    if (ctx.isRemoved()) {
                        break;
                    }
                    outSize = 0;
                }

                int oldInputLength = in.readableBytes();
                decodeRemovalReentryProtection(ctx, in, out);//開始調用decode方法

                // Check if this handler was removed before continuing the loop.
                // If it was removed, it is not safe to continue to operate on the buffer.
                //
                // See https://github.com/netty/netty/issues/1664
                if (ctx.isRemoved()) {
                    break;
                }

                //這裏如果鏈表為空且bytebuf沒有可讀數據,就跳出循環
                if (outSize == out.size()) {
                    if (oldInputLength == in.readableBytes()) {
                        break;
                    } else {//有可讀數據繼續讀取
                        continue;
                    }
                }

                if (oldInputLength == in.readableBytes()) {//beytebuf沒有讀取,但卻進行了解碼
                    throw new DecoderException(
                            StringUtil.simpleClassName(getClass()) +
                                    ".decode() did not read anything but decoded a message.");
                }

                if (isSingleDecode()) {//是否設置了每條入站數據只解碼一次,默認false
                    break;
                }
            }
        } catch (DecoderException e) {
            throw e;
        } catch (Exception cause) {
            throw new DecoderException(cause);
        }
    }

decodeRemovalReentryProtection方法內部會調用我們重寫的decode解碼實現

    final void decodeRemovalReentryProtection(ChannelHandlerContext ctx, ByteBuf in, List<Object> out)
            throws Exception {
        decodeState = STATE_CALLING_CHILD_DECODE;//標記狀態
        try {
            decode(ctx, in, out);//調用我們重寫的decode解碼實現
        } finally {
            boolean removePending = decodeState == STATE_HANDLER_REMOVED_PENDING;
            decodeState = STATE_INIT;
            if (removePending) {//這裏判斷標記,防止handlerRemoved事件與解碼操作衝突
                handlerRemoved(ctx);
            }
        }
    }

channelRead方法中接受到數據經過一系列邏輯處理,最終會調用我們重寫的decode方法實現具體的解碼功能;在decode方法中我們只需要ByteBuf類型的數據解析為我們需要的數據格式直接放入 List<Object> out鏈表中即可,ByteToMessageDecoder會自動幫你向下傳遞消息。

三、總結

通過上面的講解,我們可以對Netty中內置自定義編解碼器MessageToByteEncoder與ByteToMessageDecoder有一定的了解,其實它們本質上是Netty封裝的一組專門用於自定義編解碼的channelHandler實現類。在實際開發當中基於這兩個抽象類的實現非常具有實用性,所以在這裏稍作分析, 其中如有不足與不正確的地方還望指出與海涵。

 

關注微信公眾號,查看更多技術文章。

 

 

轉載說明:未經授權不得轉載,授權后務必註明來源(註明:來源於公眾號:架構空間, 作者:大凡)

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

※想知道最厲害的網頁設計公司"嚨底家"!

※幫你省時又省力,新北清潔一流服務好口碑

※別再煩惱如何寫文案,掌握八大原則!

※產品缺大量曝光嗎?你需要的是一流包裝設計!