目錄html
兩人一組結對編程:java
上傳測試代碼運行結果截圖和碼雲連接git
1.棧的一個應用是用來對四則運算表達式進行求值。算法
表達式Exp = S1 + OP + S2(S1 ,S2是兩個操做數,OP爲運算符)有三種標識方法:編程
OP + S1 + S2 爲前綴表示法數組
S1 + OP + S2 爲中綴表示法安全
S1 + S2 + OP 爲後綴表示法服務器
前綴式: + * a b * - c / d e f網絡
中綴式: a * b + c - d / e * fsocket
後綴式: a b * c d e / - f * +
能夠看出:
- 操做數之間的相對次序不變; - 運算符的相對次序不一樣; - 中綴式丟失了括弧信息,導致運算次序不肯定; - 前綴式的運算規則爲:連續出現的兩個操做數和在它們以前且緊靠它們的運算符構成一個最小表達式; - 後綴式的運算規則爲:運算符在式中出現的順序恰爲表達式的運算順序;每一個運算符和在它以前出現且緊靠它的兩個操做數構成一個最小表達式。
後綴表示法是波蘭邏輯學家J.Lukasiewicz於1929年提出的,又叫作逆波蘭表達式。
Linux命令dc
能夠用來對逆波蘭式表達式進行求值,dc的打印類命令:p:打印棧頂元素並換行
n
: 打印棧頂元素並將其彈出棧,完畢後不換行
P
: putchar ( int(棧頂元素) % 256) 並彈棧頂,不換行
f
: 從棧頂至棧底打印棧中全部值,每一個一行
dc
的運算符:
+
: 依次彈出w1與w2,將w2+w1壓棧。精度爲結果值精度
-
: 依次彈出w1與w2,將w2-w1壓棧
*
: 依次彈出w1與w2,將w2*w1壓棧。精度爲結果值精度與precision中較大值
/
: 依次彈出w1與w2,將w2/w1壓棧。精度爲precision
%
: 依次彈出w1與w2,將w2-w2/w1*w1壓棧
~
: 依次彈出w1與w2,依次將w2/w1與w2%w1壓棧
^
: 依次彈出w1與w2,將w2^((int)w1)壓棧。精度爲w2精度與precision中較大值
|
: 依次彈出w1 w2與w3,將 w3 ^ ((int)w2) (mod w1) 壓棧。w1 w3 需爲整數
v
: 彈出w1,將sqrt(v)壓棧。精度爲precision
dc
支持棧操做:
c
: 清空棧
d
: 將棧頂元素複製並壓棧
r
: 交換棧頂兩元素 XXX
咱們如何實現dc
? 這要用到棧。對逆波蘭式求值時,不須要再考慮運算符的優先級,只需從左到右掃描一遍後綴表達式便可。求值僞代碼以下:
- 設置一個操做數棧,開始棧爲空; - 從左到右掃描後綴表達式,遇操做數,進棧; - 若遇運算符,則從棧中退出兩個元素,先退出的放到運算符的右邊,後退出的放到運算符左邊,運算後的結果再進棧,直到後綴表達式掃描完畢。此時,棧中僅有一個元素,即爲運算的結果。
Linux中另一個計算器bc
就是用來計算中綴表達式的:
如何由中綴式求得後綴式?
由中綴式求得後綴式可使用棧,僞代碼以下:
- 設立一個棧,存放運算符,首先棧爲空; - 從左到右掃描中綴式,若遇到操做數,直接輸出,並輸出一個空格做爲兩個操做數的分隔符; - 若遇到運算符,則與棧頂比較,比棧頂級別高則進棧,不然退出棧頂元素並輸出,而後輸出一個空格做分隔符; - 若遇到左括號,進棧;若遇到右括號,則一直退棧輸出,直到退到左括號止。 - 當棧變成空時,輸出的結果即爲後綴表達式。
import java.util.Stack; public class MyBC { MyBC(){} public static String infixToSuffix(String exp){ Stack<String> s = new Stack<String>(); // 建立操做符堆棧 String suffix = ""; // 要輸出的後綴表達式字符串 String suffix1 = ""; //上一次的後綴表達式 String suffix2 = ""; String str[] = exp.split(" "); int length = str.length; // 輸入的中綴表達式的長度 String temp=""; for (int i = 0; i < length; i++) { // 對該中綴表達式的每個字符並進行判斷 switch (str[i]) { case " ":break; // 忽略空格 case "(": s.push(str[i]); // 若是是左括號直接壓入堆棧 break; case "+": case "-": if(s.size() != 0){ // 碰到'+' '-',將棧中的全部運算符所有彈出去,直至碰到左括號爲止,輸出到隊列中去 temp = s.pop(); if (temp.equals("(")) { // 將左括號放回堆棧,終止循環 s.push(temp); s.push(str[i]); break; } else{ s.push(str[i]); suffix2 = suffix2 + temp + " "; break; } } else{ s.push(str[i]); // 說明是當前爲第一次進入或者其餘前面運算都有括號等狀況致使棧已經爲空,此時須要將符號進棧 break; } // 若是是乘號或者除號,則彈出全部序列,直到碰到加好、減號、左括號爲止,最後將該操做符壓入堆棧 case "*": case "÷": if(s.size()!=0){ temp = s.pop(); if(temp.equals("+")||temp.equals("-")||temp.equals("(")){ s.push(temp); s.push(str[i]); break; } else{ s.push(str[i]); suffix2 = suffix2+temp+" "; break; } } else { s.push(str[i]); //當前爲第一次進入或者其餘前面運算都有括號等狀況致使棧已經爲空,此時須要將符號進棧 break; } // 若是碰到的是右括號,則距離棧頂的第一個左括號上面的全部運算符彈出棧並拋棄左括號 case ")": while (!s.isEmpty()) { temp = s.pop(); if (temp.equals("(")) { break; } else { suffix2 = suffix2+temp+" "; } } break; // 默認狀況,若是讀取到的是數字,則直接送至輸出序列 default: suffix2 = suffix2+str[i]+" "; break; } } // 若是堆棧不爲空,則把剩餘運算符一次彈出,送至輸出序列 while (s.size() != 0) { suffix2 = suffix2+s.pop()+" "; } if(suffix1.equals("")){ //第一個題目 suffix1 = suffix2; suffix = suffix2; } else{ if(suffix2.equals(suffix1)) suffix = ""; else suffix = suffix2; } suffix1 = suffix2; return suffix; } }
結對編程:1人負責客戶端,一人負責服務器
基於UDP的通訊和基於TCP的通訊不一樣,基於UDP的信息傳遞更快,但不提供可靠性保證。
package 實驗五.Assign2; import 實驗五.Assign1.MyDC; import java.io.*; import java.net.*; public class Sever { public static void main(String[] args) { ServerSocket serverForClient = null; Socket socketOnServer = null; DataOutputStream out = null; DataInputStream in = null; MyDC myDC = new MyDC(); try{ serverForClient = new ServerSocket(2010); }catch (IOException e1){ System.out.println("我是豬,我又餓了"+e1); } try{ System.out.println("等待小猴投喂"); socketOnServer = serverForClient.accept(); out = new DataOutputStream(socketOnServer.getOutputStream()); in = new DataInputStream(socketOnServer.getInputStream()); String suffix = in.readUTF(); System.out.println("胖礫收到小猴的提問"+suffix); out.writeUTF(myDC.evaluate(suffix)+""); Thread.sleep(500); }catch (Exception e){ System.out.println("小猴已斷開鏈接"); } } }
加密結對編程:1人負責客戶端,一人負責服務器
上傳測試結果截圖和碼雲連接
KeyGenerator kg=KeyGenerator.getInstance("DESede")
kg.init(168)
SecretKey k=kg.generateKey( )
服務端DES解密
獲取密鑰長度,String keylength = in.readUTF()
獲取密鑰
byte []kb = new byte[Integer.parseInt(keylength)]; for(int i=0; i<Integer.parseInt(keylength); i++){ String t = in.readUTF(); kb[i] = Byte.parseByte(t); }
獲取密文長度,String clength = in.readUTF()
獲取密文
byte []ctext = new byte[Integer.parseInt(clength)]; for(int i=0; i<Integer.parseInt(clength); i++){ String temp = in.readUTF(); ctext[i] = Byte.parseByte(temp); }
建立密碼器(Cipher對象)Cipher cp=Cipher.getInstance("DESede")
初始化密碼器cp.init(Cipher.DECRYPT_MODE, k)
執行解密byte []ptext=cp.doFinal(ctext)
import javax.crypto.Cipher; import javax.crypto.spec.SecretKeySpec; import java.io.*; import java.net.*; public class Sever1 { public static void main(String[] args) { ServerSocket serverForClient = null; Socket socketOnServer = null; DataOutputStream out = null; DataInputStream in = null; MyDC myDC = new MyDC(); try{ serverForClient = new ServerSocket(2010); }catch (IOException e1){ System.out.println("我是豬,我又餓了"+e1); } try{ System.out.println("等待小猴投喂"); socketOnServer = serverForClient.accept(); out = new DataOutputStream(socketOnServer.getOutputStream()); in = new DataInputStream(socketOnServer.getInputStream()); String keylength = in.readUTF(); byte []kb = new byte[Integer.parseInt(keylength)]; for(int i=0; i<Integer.parseInt(keylength); i++){ String t = in.readUTF(); kb[i] = Byte.parseByte(t); } String clength = in.readUTF(); byte []ctext = new byte[Integer.parseInt(clength)]; for(int i=0; i<Integer.parseInt(clength); i++){ String temp = in.readUTF(); ctext[i] = Byte.parseByte(temp); } SecretKeySpec k=new SecretKeySpec(kb,"DESede"); Cipher cp=Cipher.getInstance("DESede"); cp.init(Cipher.DECRYPT_MODE, k); byte []ptext=cp.doFinal(ctext); String suffix = new String(ptext,"UTF8"); System.out.println("胖礫收到小猴的提問"+suffix); out.writeUTF(myDC.evaluate(suffix)+""); Thread.sleep(500); }catch (Exception e){ System.out.println("小猴已斷開鏈接"); } } }
密鑰分發結對編程:1人負責客戶端,一人負責服務器
執行密鑰協定的標準算法是DH算法(Diffie-Hellman算法)。
程序最後將公鑰和私鑰以對象流的形式保存在文件中,文件名經過命令行參數指定,第一個命令行參數對應的文件保存公鑰,第二個命令行參數對應的文件保存私鑰。
運行程序:
創建兩個目錄A和B,模擬須要祕密通訊的A、B雙方,因爲DH算法須要A和B各自生成DH公鑰和私鑰,所以在這兩個目錄下都拷貝編譯後文件Key_DH。
首先由A建立本身的公鑰和私鑰,即在A目錄下輸入「java Key_DH Apub.dat Apri.dat」運行程序,這時在目錄A下將產生文件Apub.dat和Apri.dat,前者保存着A的公鑰,後者保存着A的私鑰。
而後由B建立本身的公鑰和私鑰,即在B目錄下輸入「java Key_DH Bpub.dat Bpri.dat」運行程序,這時在目錄B下將產生文件Bpub.dat和Bpri.dat,前者保存着B的公鑰,後者保存着B的私鑰。
最後發佈公鑰,A將Apub.dat拷貝到B目錄,B將Bpub.dat拷貝到A的目錄。
這樣,A、B雙方的DH公鑰和私鑰已經建立並部署完畢。
Java中KeyAgreement類實現了密鑰協定,它使用init( )方法傳入本身的私鑰,使用doPhase( )方法傳入對方的公鑰,進而可使用generateSecret( )方法生成共享的信息具體步驟以下:
(1) 讀取本身的DH私鑰和對方的DH公鑰
(2) 建立密鑰協定對象
(3) 初始化密鑰協定對象
(4) 執行密鑰協定
(5) 生成共享信息
程序最後將共享信息打印了出來,以便直觀地對比A和B獲得的信息是否相同。將程序KeyAgree編譯後分別拷貝在A和B兩個目錄,首先在A目錄輸入「java KeyAgree Bpub.dat Apri.dat」運行程序,它使用文件Bpub.dat中對方的公鑰和文件Apri.dat中本身的私鑰建立了一段共享的字節數組。
import javax.crypto.Cipher; import javax.crypto.spec.SecretKeySpec; import java.io.*; import java.net.*; public class Server4 { public static void main(String[] args) { ServerSocket serverForClient = null; Socket socketOnServer = null; DataOutputStream out = null; DataInputStream in = null; MyDC myDC = new MyDC(); try{ serverForClient = new ServerSocket(2010); }catch (IOException e1){ System.out.println("我是豬,我又餓了"+e1); } try{ System.out.println("等待小猴投喂"); socketOnServer = serverForClient.accept(); out = new DataOutputStream(socketOnServer.getOutputStream()); in = new DataInputStream(socketOnServer.getInputStream()); //獲取密文 String clength = in.readUTF(); byte []ctext = new byte[Integer.parseInt(clength)]; for(int i=0; i<Integer.parseInt(clength); i++){ String temp = in.readUTF(); ctext[i] = Byte.parseByte(temp); } //獲取密鑰 String keylength = in.readUTF(); byte []ckey = new byte[Integer.parseInt(keylength)]; for(int i=0; i<Integer.parseInt(keylength); i++){ String temp = in.readUTF(); ckey[i] = Byte.parseByte(temp); } //密鑰解密 SecretKeySpec k1 = KeyAgree.KeyAgree("Clientpub.dat","Serverpri.dat"); Cipher cp=Cipher.getInstance("DESede"); cp.init(Cipher.DECRYPT_MODE, k1); byte []pkey=cp.doFinal(ckey); //密文解密 SecretKeySpec k=new SecretKeySpec(pkey,"DESede"); cp.init(Cipher.DECRYPT_MODE, k); byte []ptext=cp.doFinal(ctext); String suffix = new String(ptext,"UTF8"); System.out.println("胖礫收到小猴的提問"+suffix); out.writeUTF(myDC.evaluate(suffix)+""); Thread.sleep(500); }catch (Exception e){ System.out.println("小猴已斷開鏈接"); } } }
完整性校驗結對編程:1人負責客戶端,一人負責服務器
上傳測試結果截圖和碼雲連接
Java摘要算法- MD5:使用Java計算指定字符串的消息摘要。java.security包中的MessageDigest類提供了計算消息摘要的方法,首先生成對象,執行其update()方法能夠將原始數據傳遞給該對象,而後執行其digest( )方法便可獲得消息摘要。具體步驟以下:
(1) 生成MessageDigest對象
MessageDigest m=MessageDigest.getInstance("MD5");
(2) 傳入須要計算的字符串
m.update(x.getBytes("UTF8" ));
(3) 計算消息摘要
byte s[ ]=m.digest( );
(4) 處理計算結果
必要的話可使用以下代碼將計算結果s轉換爲字符串。
String result=""; for (int i=0; i<s.length; i++){ result+=Integer.toHexString((0x000000ff & s[i]) | 0xffffff00).substring(6); }
MessageDigest m = MessageDigest.getInstance("MD5"); m.update(suffix.getBytes("UTF8")); byte s[] = m.digest(); String result=""; for (int i=0; i<s.length; i++){ result+=Integer.toHexString((0x000000ff & s[i]) | 0xffffff00).substring(6); }
本次實驗是對以前的知識點的應用,實驗涉及的內容主要是網絡編程方面的,感受難度比較大,不少東西都不是很理解。經過和結對夥伴的共同研究,不斷查閱資料,才最終完成實驗,收穫仍是很大的。