-แถลงการณ์หลีกเลี่ยงคนที่ตายไปแล้ว แกนหลักของบล็อกนี้ไม่ใช่คำนำหน้า if-else+ แต่จะกำหนดโปรโตคอลส่วนตัวผ่านเฟรมเวิร์กการประมวลผล URL โปรโตคอลได้อย่างไร
ความแตกต่างระหว่าง URI และ URL
URI (ตัวระบุทรัพยากรที่เหมือนกัน) ตัวระบุทรัพยากรเครื่องแบบ; URL (ตำแหน่งทรัพยากรที่เหมือนกัน) ตัวระบุแหล่งทรัพยากรแบบสม่ำเสมอ (หรือตัวระบุตำแหน่งทรัพยากรแบบครบวงจร); URI เป็นแนวคิดที่ค่อนข้างกว้าง URL เป็นประเภทของ URI และชุดย่อยของกลไกการตั้งชื่อ URI อาจกล่าวได้ว่า URI เป็นนามธรรมและการใช้ URL เฉพาะเพื่อค้นหาทรัพยากร โดยทั่วไป URI ชี้ให้เห็นว่าไม่ใช่เส้นทางทรัพยากรทางกายภาพ แต่เป็นตัวระบุทรัพยากรที่แมปในระบบทั้งหมด URL เป็นสตริงที่ใช้บนอินเทอร์เน็ตเพื่ออธิบายแหล่งข้อมูลส่วนใหญ่ใช้กับโปรแกรมไคลเอนต์ WWW และโปรแกรมเซิร์ฟเวอร์ต่างๆ การใช้ URL สามารถใช้รูปแบบ Unified เพื่ออธิบายแหล่งข้อมูลต่าง ๆ รวมถึงไฟล์ที่อยู่เซิร์ฟเวอร์และไดเรกทอรี ฯลฯ
1. มาก่อนกันเถอะ
เราคุ้นเคยกับ http
url url = url ใหม่ (http://www.apptest.com:8080/test/ios.php);
เราต้องชินกับมันด้วย
แน่นอนเราต้องชินกับ URL ด้วย
"https", "ftp", "mailto", "telnet", "ไฟล์", "ldap", "gopher", "jdbc", "rmi", "jndi", "jar", "doc", "netdoc", "nfs",
url url = url ใหม่ ("oschina: //www.apptest.com: 8080/test/ios.php");หากคุณไม่คุ้นเคยกับมันข้อยกเว้นต่อไปนี้จะเกิดขึ้นเสมอ
java.net.malformedurlexception: โปรโตคอลที่ไม่รู้จัก
เมื่อใช้ AJAX ในเบราว์เซอร์ Android โปรโตคอลที่ไม่ได้กำหนดจะไม่ได้รับการสนับสนุน
2. ความเข้าใจเกี่ยวกับโปรโตคอลที่กำหนดเอง
โปรโตคอล: ในโลกการเขียนโปรแกรมโปรโตคอลเองเป็นชุดของกฎข้อ จำกัด อินพุต/ouput ดังนั้นโปรโตคอลที่แน่นอนของเราควรหมุนรอบ I/O ดังนั้นโปรโตคอลที่นี่สามารถเรียกได้ว่าโปรโตคอล I/O
ผู้ริเริ่มข้อตกลง: คำขอ
ผู้ตอบกลับโปรโตคอล: การตอบสนอง
เงื่อนไขสำหรับการจัดตั้งข้อตกลงคือ: การร้องขอและการตอบสนองรับรู้ชุดของข้อตกลงเดียวกันและสื่อสารตามข้อ จำกัด ของข้อตกลง
3. ความสัมพันธ์ระหว่างโปรโตคอลที่กำหนดเองและ URL
ใน Java โปรโตคอลที่กำหนดเองจำเป็นต้องใช้ URL หรือไม่?
คำตอบคือไม่
อันที่จริงรอบ I/O คำจำกัดความของกฎของเราอยู่ในมือของเราเองอย่างสมบูรณ์ ไม่ได้บอกว่าโลกจะไม่หันหลังกลับหลังจากออกจาก URL และ Java จะถูกทำลาย
ทำไมต้องปรับแต่งโปรโตคอลโดยใช้คลาส URL?
คำตอบคือเพราะ URL เป็นกรอบการประมวลผลการสื่อสารที่เป็นผู้ใหญ่
โปรโตคอล URL ที่กำหนดเองที่กล่าวถึงที่นี่เป็นหลักเกี่ยวกับการขยายโปรโตคอลผ่านกฎที่มีอยู่
4. URL การปฏิบัติโปรโตคอลส่วนตัวที่กำหนดเอง
เรารู้ว่าโปรโตคอลที่กำหนดเองต้องการการตอบสนองและการร้องขอและทั้งสองฝ่ายจำเป็นต้องเข้าใจข้อตกลงของกันและกันอย่างเต็มที่ เพื่อความสะดวกที่นี่เราใช้เซิร์ฟเวอร์โปรโตคอล HTTP เป็นการตอบสนอง
ที่นี่เราใช้ NGNIX Server + PHP + FastCGI เพื่อสร้าง reponse และรหัสการปรับใช้มีดังนี้
1. กำหนดการตอบสนอง
<? php $ raw_post_data = file_get_contents ('php: // input', 'r'); echo "-------/$ _ โพสต์ ------------------/n <br/>"; echo var_dump ($ _ โพสต์) "/n"; echo "------- php: // อินพุต ------------------/n <br/>"; echo $ raw_post_data "/n <br/>"; $ rs = json_encode ($ _ เซิร์ฟเวอร์); file_put_contents ('text.html', $ rs); echo '写入成功';2. กำหนดคำขอ
2.1 การใช้งานโรงงาน URLStreamhandlFactory ส่วนใหญ่ใช้เพื่อสร้างโปรเซสเซอร์โปรโตคอล
ระดับสาธารณะ echourlstreamhandlerfactory ใช้ urlstreamhandlerfactory {public urlstreamhandler createurlstreamhandler (โปรโตคอลสตริง) {// ปรับแต่งคำขอสคีมาที่แตกต่างกันผ่านการเบี่ยงเบนที่นี่ แน่นอนว่าคนที่มีสมองตายคิดว่านี่เป็นรหัสหลัก URL เป็นกรอบการประมวลผลโปรโตคอล ถ้า if-else เป็นแกนกลางจะเป็น Oracle จะล้มละลายถ้า (Protocol.equals ("echo") || protocol.equals ("oschina")) {ส่งคืน echourlstreamhandler ใหม่ (); // อินสแตนซ์ตัวจัดการการประมวลผลโปรโตคอล} ส่งคืน null; -2.2 การใช้งาน urlstreamhandler, ฟังก์ชั่นหลักคือการสร้างตัวเชื่อมต่อที่สอดคล้องกันของโปรโตคอล
ระดับสาธารณะ echourlstreamhandler ขยาย urlstreamhandler {@overrideprotected urlconnection openconnection (url u) โยน ioexception {return echourlconnection ใหม่ (U); // เรายังสามารถทำการเบี่ยงเบนที่สอดคล้องกันได้ที่นี่}} 2.3 ใช้ URLConnection ซึ่งเป็นการปรับแต่งกฎการสื่อสารของโปรโตคอล ที่นี่เราใช้โปรโตคอล HTTP เป็นกฎการสื่อสาร ที่นี่เราเลียนแบบคำขอโปรโตคอล HTTP
(ต่อไปนี้เป็นรหัสหลักโปรโตคอล HTTP ที่ยืมมา ที่นี่แน่นอนคุณสามารถโต้ตอบกับโปรโตคอลต่างๆโดยใช้ WebSocket, SMTP และ FTP แทนที่จะเป็นคำนำหน้า IF-ELSE+URL ที่คนตายสมองขอให้ฉันยอมรับ )
ระดับสาธารณะ echourlConnection ขยาย urlConnection {การเชื่อมต่อซ็อกเก็ตส่วนตัว = null; สาธารณะสุดท้ายคงที่ int default_port = 80; echourlConnection สาธารณะ (url url) {super (url);} public synchronized inputstream getinputstream () throws ioexception OutputStream getOutputStream () พ่น IOException {if (! เชื่อมต่อ) {เชื่อมต่อ ();} ส่งคืนการเชื่อมต่อ getOutputStream ();} สตริงสาธารณะ getContentType () {return "ข้อความ/ธรรมดา"; 65535) พอร์ต = default_port; this.connection = ซ็อกเก็ตใหม่ (url.getHost (), พอร์ต); // วิธีการที่แท้จริงในการปิดการบัฟเฟอร์ของซ็อกเก็ตและส่งข้อมูลทันที .. ค่าเริ่มต้นเป็นเท็จ // หากการใช้งานของซ็อกเก็ตไม่รองรับ TCP_NODELAY หรือไม่ this.connection.setreuseaddress (จริง); // หมายถึงการหมดเวลารอเมื่อได้รับข้อมูลในมิลลิวินาที .. ค่าเริ่มต้นคือ 0 ซึ่งหมายความว่าจะมีการรอที่ไม่มีที่สิ้นสุดและจะไม่หมดเวลา // เมื่ออ่านข้อมูลผ่านอินพุตซ็อกเก็ตหากไม่มีข้อมูล อ่านข้อมูลอีกครั้ง this.connection.setsotimeout (30000); // ระบุว่าซ็อกเก็ตพื้นฐานถูกปิดทันที // เมื่อปิดซ็อกเก็ตซ็อกเก็ตพื้นฐานจะล่าช้า 5 วินาทีจากนั้นปิดอีกครั้ง หลังจาก 5 วินาทีข้อมูลที่เหลือทั้งหมดที่ไม่ได้ส่งจะถูกยกเลิก โดยค่าเริ่มต้นหากมีการดำเนินการซ็อกเก็ต close () วิธีการจะกลับมาทันที แต่ซ็อกเก็ตพื้นฐานจะไม่ปิดจริงทันที // มันจะล่าช้าเป็นระยะเวลาหนึ่งจนกว่าข้อมูลที่เหลือทั้งหมดจะถูกส่งและซ็อกเก็ตจะปิดอย่างแท้จริง เคล็ดลับ: เมื่อโปรแกรมปิดซ็อกเก็ตเป็นไปได้ว่าชุดข้อมูลจะยังคงส่งบนเครือข่ายและยังไม่ถึงตัวรับสัญญาณ // เคล็ดลับ: "ข้อมูลที่เหลืออยู่" ที่กล่าวถึงที่นี่หมายถึงข้อมูลนี้ที่ยังคงส่งบนเครือข่ายและไม่ได้รับข้อมูลที่ได้รับการเชื่อมต่อ this.connection.setsendbuffersize (1024); // หมายถึงขนาดของบัฟเฟอร์ที่ข้อมูลได้รับสิ่งนี้การเชื่อมต่อการเชื่อมต่อ setreceiveBuffersize (1024); // ระบุขนาดของบัฟเฟอร์ที่ได้รับการเชื่อมต่อโดยอัตโนมัติ ไม่ส่งข้อมูลซึ่งกันและกัน)? จริงคือใช่ // ค่าเริ่มต้นของมันเป็นเท็จซึ่งหมายความว่า TCP จะไม่ตรวจสอบว่าการเชื่อมต่อนั้นถูกต้องหรือไม่และลูกค้าที่ไม่ได้ใช้งานอาจมีอยู่อย่างถาวรโดยไม่ต้องแจ้งให้ทราบว่าเซิร์ฟเวอร์ขัดข้องนี้ // ระบุว่ารองรับการส่งข้อมูลฉุกเฉิน TCP หนึ่งไบต์หรือไม่ Socket.sendurgentData (DATA) ใช้เพื่อส่งข้อมูลฉุกเฉิน TCP หนึ่งไบต์ // ค่าเริ่มต้นเป็นเท็จนั่นคือผู้รับจะไม่ทำการประมวลผลใด ๆ เมื่อได้รับข้อมูลฉุกเฉินและยกเลิกโดยตรง หากผู้ใช้ต้องการส่งข้อมูลฉุกเฉินควรตั้งค่าเป็นจริง // หลังจากตั้งค่าเป็นจริงผู้รับจะใส่ข้อมูลฉุกเฉินที่ได้รับในคิวเดียวกันกับข้อมูลปกติ this.connection.setoobinline (จริง); // วิธีนี้ใช้เพื่อตั้งค่าประเภทบริการ รหัสต่อไปนี้ร้องขอความน่าเชื่อถือสูงและบริการส่งสัญญาณความล่าช้าขั้นต่ำ (บิตหรือการดำเนินงานของ 0x04 และ 0x10) // คลาสซ็อกเก็ตใช้ 4 จำนวนเต็มเพื่อแสดงประเภทบริการ // 0x02: ต้นทุนต่ำ 0x10: ความล่าช้าขั้นต่ำ (บิตสุดท้ายของไบนารีครั้งที่ห้าคือ 1) สิ่งนี้การเชื่อมต่อ. settrafficClass (0x04 | 0x10); // วิธีนี้ใช้เพื่อตั้งค่าความสำคัญสัมพัทธ์ของเวลาการเชื่อมต่อการหน่วงเวลาและแบนด์วิดท์ เวลาแฝง ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ url.getPath () + "http/1.1 /r/n");/if(url.getport() <0 || url.getport ()> 65536) {sb.append ("โฮสต์:"). ผนวก (url.gethost ()). ต่อไป ("/r/n");} else {sb.append ("โฮสต์:") ต่อท้าย โฮสต์ ()). ผนวก (":"). ผนวก (url.getport ()). ผนวก ("/r/n");} sb.append ("การเชื่อมต่อ: Keep-alive/r/n"); sb.append ("วันที่: วันศุกร์, 22 เม.ย. 2559 13:17:35 GMT/R/N "); SB.Append (" Vary: Accept-encoding/R/N "); SB.Append (" เนื้อหาประเภท: แอปพลิเคชัน/x-ww-form-urlencoded, charset = utf-8/r/n "); ") .Append (" ชื่อ = จางซัน & รหัสผ่าน = 123456 ".getBytes (" UTF-8 "). ความยาว) .Append ("/r/n "); SB.Append ("/r/n "); disconnect () พ่น ioexception {if (เชื่อมต่อ) {this.connection.close (); this.connected = false;}}}} ที่นี่คำจำกัดความของโปรโตคอลเสร็จสมบูรณ์
รหัสทดสอบของเรามีดังนี้
ลองเชื่อมต่อกับ OSCHINA: // LocalHost: 8080/test/iOS.php
url.seturlstreamhandlerfactory (ใหม่ echourlstreamhandlerfactory ()); // urlconnection.setContentHandlerFactory (echocontenthandlerfactory ใหม่ ()); url url = url ใหม่ ("oschina: // localhost: 8080/test/ios.php"); การเชื่อมต่อ = (echourlConnection) url.openconnection (); connection.setDoOutput (จริง); connection.setDoInput (จริง); PrintWriter PW = New PrintWriter (ใหม่ OutputStreamWriter (Connection.getOutputStream ())); pw.write ("ชื่อ = zhangsan & รหัสผ่าน = 123456"); pw.flush (); stream inputstream = connection.getInputStream (); int len = -1; ไบต์ [] buf = ไบต์ใหม่ [256]; ในขณะที่ ((len = stream.read (buf, 0, 256))>-1) {สตริงบรรทัด = สตริงใหม่ (buf, 0, len); if (line.endswith ("/r/n0/r/n/r/n") && len <256) {// เซิร์ฟเวอร์ส่งคืนการเข้ารหัสการถ่ายโอน chunked,/r/n0/r/n/r/n หมายความว่าการอ่านสิ้นสุดลงการวิเคราะห์การเข้ารหัส line.length ()-"/r/n0/r/n/r/n" .length ()); System.out.println (บรรทัด); หยุดพัก; } else {system.out.println (บรรทัด); }} pw.close (); Stream.close ();การรันผลลัพธ์
ผลการวิจัยแสดงให้เห็นว่าโปรโตคอลได้รับการกำหนดอย่างประสบความสำเร็จ
แน่นอนการวิเคราะห์ข้อมูลข้างต้นไม่เป็นไปตามข้อกำหนดของเราเนื่องจากเป็นข้อมูลการเข้ารหัสแบบก้อน วิธีการวิเคราะห์ตรงตามข้อกำหนดโปรดย้าย :
การเข้ารหัสและการแยกวิเคราะห์ข้อมูล HTTP
5. ต่อมาตัวแยกวิเคราะห์ minetype ที่กำหนดเอง
ContentHandlerFactory มีให้ใน Java เพื่อแยกวิเคราะห์ minetype เรากำหนดตัวแยกวิเคราะห์ของเราเองที่นี่ แน่นอน JDK ให้ความอุดมสมบูรณ์มากขึ้น สิ่งที่เราทำที่นี่คือการตอบสนองความต้องการพิเศษ
Public Class EchocontentHandler ขยาย ContentHandler {วัตถุสาธารณะ getContent (การเชื่อมต่อ urlConnection) พ่น IOException {inputStream ใน = connection.getInputStream (); bufferedReader br = bufferedreader ใหม่ = connection.getInputStream (); สำหรับ (int i = 0; i <classes.length; i ++) {ถ้า (คลาส [i] == inputstream.class) กลับเข้า;การใช้งานง่ายมาก
urlConnection.setContentHandlerFactory (ใหม่ echocontentHandlerFactory ());