Java เขียนเซิร์ฟเวอร์และไคลเอนต์ที่สามารถอัปโหลดไฟล์ได้ เนื้อหาเฉพาะมีดังนี้
ด้านเซิร์ฟเวอร์
เซิร์ฟเวอร์คลาส {โมฆะคงที่สาธารณะหลัก (สตริง [] args) โยนข้อยกเว้น {// สร้างซ็อกเก็ตเซิร์ฟเวอร์ Serversocket SS = ใหม่ Serversocket (10005); // รับซ็อกเก็ตไคลเอนต์ fileloadersocket = ss.accept (); // พิมพ์สตริงข้อมูลการเชื่อมต่อ ip = fileloadersocket.getInetAddress (). gethostaddress (); System.out.println (IP + "... Connceected"); // รับไฟล์และบันทึก inputStream ใน = fileloadersocket.getInputStream (); // อินสแตนซ์วัตถุไฟล์ outputStream filesave = ใหม่ fileOutputStream ("E: //3.mp3"); // สร้างอาร์เรย์ buf byte [] buf = ไบต์ใหม่ [1024]; int len = 0; // ปกป้องว่าการสิ้นสุดของไฟล์ถูกอ่านในขณะที่ ((len = in.read (buf))! = -1) {filesave.write (buf, 0, len); // รีเฟรช filesave.flush (); } // ส่งคืนข้อมูลการคัดลอกไฟล์ bufferedWriter out = bufferedWriter ใหม่ (ใหม่ outputStreamWriter (fileloadersocket.getOutputStream ())); out.write ("ไฟล์อัปโหลดสำเร็จ"); // รีเฟรช out.flush (); // ทรัพยากรปิด ss.close (); fileloadersocket.close (); filesave.close (); -ลูกค้า:
ไคลเอนต์คลาส {โมฆะคงที่สาธารณะหลัก (สตริง [] args) พ่นข้อยกเว้น {// สร้างซ็อกเก็ตซ็อกเก็ตซ็อกเก็ต Fileloadersocket = ซ็อกเก็ตใหม่ ("168.168.168.94", 10005); // อ่านไฟล์ในเครื่องจากไคลเอนต์และเขียนลงในช่องส่งเอาต์พุตของซ็อกเก็ตเอาท์พุทสตรีมออก = fileloadersocket.getOutputStream (); // อินสแตนซ์วัตถุ filereader inputstream fileread = ใหม่ fileInputStream ("g: //2.mp3"); // สร้างอาร์เรย์ไบต์ [] buf = ไบต์ใหม่ [1024]; int len = 0; // ตัดสินว่าจะอ่านจุดสิ้นสุดของไฟล์ในขณะที่ ((len = fileread.read (buf))! = -1) {out.write (buf, 0, len); } // บอกเซิร์ฟเวอร์ว่าไฟล์ถูกถ่ายโอน fileloadersocket.shutdownoutput (); // รับข้อมูลตอบรับจาก Server BufferedReader ใน = New BufferedReader (ใหม่ inputStreamReader (fileloadersocket.getInputStream ())); String serverback = in.readline (); System.out.println (Serverback); // ทรัพยากรปิด fileloadersocket.close (); fileread.close (); -โปรแกรมต่อไปนี้ถูกคัดลอกโดยตรงจากที่อื่นเพื่อการอ้างอิงการเรียนรู้:
การเขียนโปรแกรมซ็อกเก็ต Java
สำหรับการเขียนโปรแกรมซ็อกเก็ต Java มีสองแนวคิดหนึ่งคือ Serversocket และอื่น ๆ คือซ็อกเก็ต เซิร์ฟเวอร์และไคลเอนต์เชื่อมต่อผ่านซ็อกเก็ตจากนั้นพวกเขาสามารถสื่อสารได้ ก่อนอื่น Serversocket จะฟังพอร์ตบนเซิร์ฟเวอร์ เมื่อพบว่าไคลเอนต์มีซ็อกเก็ตเพื่อพยายามเชื่อมต่อกับมันจะยอมรับคำขอการเชื่อมต่อของซ็อกเก็ตและในเวลาเดียวกันสร้างซ็อกเก็ตที่เกี่ยวข้องบนเซิร์ฟเวอร์เพื่อสื่อสารกับมัน ด้วยวิธีนี้มีซ็อกเก็ตสองตัวหนึ่งตัวบนไคลเอนต์และอีกอันหนึ่งบนเซิร์ฟเวอร์
การสื่อสารระหว่างซ็อกเก็ตนั้นง่ายมาก เมื่อเซิร์ฟเวอร์เขียนบางสิ่งลงในสตรีมเอาต์พุตซ็อกเก็ตไคลเอนต์สามารถอ่านเนื้อหาที่เกี่ยวข้องผ่านสตรีมอินพุตซ็อกเก็ต ซ็อกเก็ตและซ็อกเก็ตเชื่อมต่อในสองทิศทางดังนั้นไคลเอนต์ยังสามารถเขียนสิ่งต่าง ๆ ลงในสตรีมเอาท์พุทซ็อกเก็ตที่สอดคล้องกันจากนั้นสตรีมอินพุตซ็อกเก็ตที่สอดคล้องกันของเซิร์ฟเวอร์สามารถอ่านเนื้อหาที่เกี่ยวข้องได้ นี่คือตัวอย่างของการสื่อสารฝั่งเซิร์ฟเวอร์กับลูกค้า:
1. การเขียนไคลเอนต์และเซิร์ฟเวอร์อ่าน
รหัส Java เซิร์ฟเวอร์
เซิร์ฟเวอร์ระดับสาธารณะ {โมฆะคงที่สาธารณะหลัก (String args []) พ่น IOException {// เพื่อความเรียบง่ายข้อมูลข้อยกเว้นทั้งหมดจะถูกโยนออกจากพอร์ต int = 8899; // กำหนดการฟัง Serversocket บนพอร์ต 8899 Serversocket Server = Serversocket ใหม่ (พอร์ต); // เซิร์ฟเวอร์พยายามรับคำขอการเชื่อมต่อจากซ็อกเก็ตอื่น ๆ วิธีการยอมรับของเซิร์ฟเวอร์คือซ็อกเก็ตซ็อกเก็ตปิดกั้น = เซิร์ฟเวอร์ Accept (); // หลังจากสร้างการเชื่อมต่อกับไคลเอนต์เราสามารถรับอินพุตสตรีมของซ็อกเก็ตและอ่านข้อมูลที่ส่งโดยไคลเอนต์ Reader Reader = new InputStreamReader (socket.getInputStream ()); ถ่านถ่าน [] = ถ่านใหม่ [64]; int len; StringBuilder sb = new StringBuilder (); ในขณะที่ ((len = reader.read (chars))! = -1) {sb.append (สตริงใหม่ (chars, 0, len)); } system.out.println ("จากไคลเอนต์:" + sb); reader.close (); Socket.close (); Server.close (); -การทำงานของเซิร์ฟเวอร์เพื่ออ่านข้อมูลจากอินพุตของซ็อกเก็ตก็ปิดกั้น หากข้อมูลไม่ได้อ่านจากสตรีมอินพุตโปรแกรมจะยังคงอยู่ที่นั่นจนกว่าไคลเอนต์จะเขียนข้อมูลลงในกระแสเอาต์พุตของซ็อกเก็ตหรือปิดสตรีมเอาต์พุตของซ็อกเก็ต แน่นอนว่าเป็นเรื่องจริงสำหรับซ็อกเก็ตลูกค้า หลังจากการดำเนินการเสร็จสิ้นอย่าลืมปิดทรัพยากรที่เกี่ยวข้องก่อนที่โปรแกรมทั้งหมดจะเสร็จสิ้นนั่นคือปิดสตรีม IO และซ็อกเก็ตที่สอดคล้องกัน
รหัส Java ไคลเอ็นต์
ไคลเอนต์คลาสสาธารณะ {โมฆะคงที่สาธารณะหลัก (สตริง args []) โยนข้อยกเว้น {// เพื่อความเรียบง่ายยกเว้นทั้งหมดจะถูกโยนออกไปโดยตรงสตริงโฮสต์ = "127.0.0.1"; // ที่อยู่ IP ของเซิร์ฟเวอร์ที่จะเชื่อมต่อคือ int port = 8899; // พอร์ตการฟังที่สอดคล้องกันของเซิร์ฟเวอร์ที่จะเชื่อมต่อ // การเชื่อมต่อถูกสร้างขึ้นด้วยเซิร์ฟเวอร์ซ็อกเก็ตไคลเอนต์ = ซ็อกเก็ตใหม่ (โฮสต์พอร์ต); // หลังจากสร้างการเชื่อมต่อคุณสามารถเขียนข้อมูลไปยัง Server Writer Writer = New OutputStreamWriter (client.getOutputStream ()); writer.write ("Hello Server."); writer.flush (); // จำไว้ว่า flush writer.close (); client.close (); -เมื่อเขียนข้อมูลไปยังสตรีมเอาท์พุทซ็อกเก็ตคุณควรให้ความสนใจกับสิ่งหนึ่ง หากโปรแกรมไม่สอดคล้องกับการปิดระบบเอาต์พุตหลังจากการดำเนินการเขียน แต่ดำเนินการบล็อกอื่น ๆ (เช่นการอ่านข้อมูลจากสตรีมอินพุต) อย่าลืมล้างมัน ด้วยวิธีนี้เซิร์ฟเวอร์สามารถรับข้อมูลที่ส่งโดยไคลเอ็นต์มิฉะนั้นอาจทำให้เกิดการรอซึ่งกันและกันไม่ จำกัด ปัญหานี้จะถูกกล่าวถึงในภายหลังเมื่อพูดถึงไคลเอนต์และเซิร์ฟเวอร์การอ่านและการเขียนในเวลาเดียวกัน
2. ไคลเอนต์และเซิร์ฟเวอร์อ่านและเขียนในเวลาเดียวกัน
ดังที่ได้กล่าวไว้ก่อนหน้านี้ซ็อกเก็ตสื่อสารกันสองทางซึ่งทั้งสองสามารถรับข้อมูลและส่งข้อมูลได้
รหัส Java เซิร์ฟเวอร์
เซิร์ฟเวอร์ระดับสาธารณะ {โมฆะคงที่สาธารณะหลัก (String args []) พ่น IOException {// เพื่อความเรียบง่ายข้อมูลข้อยกเว้นทั้งหมดจะถูกโยนออกจากพอร์ต int = 8899; // กำหนดการฟัง Serversocket บนพอร์ต 8899 Serversocket Server = Serversocket ใหม่ (พอร์ต); // เซิร์ฟเวอร์พยายามรับคำขอการเชื่อมต่อจากซ็อกเก็ตอื่น ๆ วิธีการยอมรับของเซิร์ฟเวอร์คือซ็อกเก็ตซ็อกเก็ตปิดกั้น = เซิร์ฟเวอร์ Accept (); // หลังจากสร้างการเชื่อมต่อกับไคลเอนต์เราสามารถรับอินพุตสตรีมของซ็อกเก็ตและอ่านข้อมูลที่ส่งโดยไคลเอนต์ Reader Reader = new InputStreamReader (socket.getInputStream ()); ถ่านถ่าน [] = ถ่านใหม่ [64]; int len; StringBuilder sb = new StringBuilder (); ในขณะที่ ((len = reader.read (chars))! = -1) {sb.append (สตริงใหม่ (chars, 0, len)); } system.out.println ("จากไคลเอนต์:" + sb); // เขียนประโยคหลังจากอ่านนักเขียนนักเขียน = new OutputStreamWriter (socket.getOutputStream ()); Writer.write ("Hello Client."); Writer.flush (); Writer.close (); reader.close (); Socket.close (); Server.close (); -ในรหัสข้างต้นเราจะอ่านข้อมูลที่ส่งโดยไคลเอนต์จากสตรีมอินพุตก่อนจากนั้นเขียนข้อมูลลงในสตรีมเอาต์พุตไปยังไคลเอนต์แล้วปิดไฟล์ทรัพยากรที่เกี่ยวข้อง ในความเป็นจริงรหัสข้างต้นอาจไม่ทำงานในแบบที่เราสันนิษฐานล่วงหน้าเนื่องจากการอ่านข้อมูลจากสตรีมอินพุตเป็นการดำเนินการบล็อก เมื่อข้อมูลถูกอ่านในขณะที่ลูปจะมีการดำเนินการกับลูป ในขณะที่ลูปจะหยุดเว้นแต่ว่าซ็อกเก็ตที่เกี่ยวข้องของไคลเอนต์จะถูกปิดและบล็อก การแก้ปัญหาสถานการณ์นี้ซึ่งอาจไม่เคยถูกดำเนินการคือในขณะที่ลูปจะต้องเพิ่มขึ้นอย่างมีเงื่อนไข เมื่อดูที่รหัสข้างต้นสิ่งเดียวที่เปลี่ยนแปลงคือความยาว Len และข้อมูลที่อ่าน เลนไม่สามารถใช้งานได้อีกต่อไปและสิ่งเดียวที่สามารถใช้ได้คือข้อมูลที่อ่าน ในกรณีนี้เรามักจะเห็นด้วยกับแท็กปลายทาง เมื่อข้อมูลที่ส่งโดยไคลเอนต์มีแท็กปลายทางบางอย่างหมายความว่าข้อมูลปัจจุบันได้ถูกส่งและในเวลานี้เราสามารถวนซ้ำได้ จากนั้นรหัสที่ได้รับการปรับปรุงจะมีลักษณะเช่นนี้:
รหัส Java
เซิร์ฟเวอร์ระดับสาธารณะ {โมฆะคงที่สาธารณะหลัก (String args []) พ่น IOException {// เพื่อความเรียบง่ายข้อมูลข้อยกเว้นทั้งหมดจะถูกโยนออกจากพอร์ต int = 8899; // กำหนดการฟัง Serversocket บนพอร์ต 8899 Serversocket Server = Serversocket ใหม่ (พอร์ต); // เซิร์ฟเวอร์พยายามรับคำขอการเชื่อมต่อจากซ็อกเก็ตอื่น ๆ วิธีการยอมรับของเซิร์ฟเวอร์คือซ็อกเก็ตซ็อกเก็ตปิดกั้น = เซิร์ฟเวอร์ Accept (); // หลังจากสร้างการเชื่อมต่อกับไคลเอนต์เราสามารถรับอินพุตสตรีมของซ็อกเก็ตและอ่านข้อมูลที่ส่งโดยไคลเอนต์ Reader Reader = new InputStreamReader (socket.getInputStream ()); ถ่านถ่าน [] = ถ่านใหม่ [64]; int len; StringBuilder sb = new StringBuilder (); สตริงอุณหภูมิ; ดัชนี int; ในขณะที่ ((len = reader.read (chars))! = -1) {temp = สตริงใหม่ (chars, 0, len); if ((index = temp.indexof ("eof"))! = -1) {// การรับสิ้นสุดลงเมื่อพบ EOF sb.append (temp.substring (0, ดัชนี)); หยุดพัก; } sb.append (temp); } system.out.println ("จากไคลเอนต์:" + sb); // เขียนประโยคหลังจากอ่าน Writer Writer = New OutputStreamWriter (Socket.GetOutputStream ()); Writer.write ("Hello Client."); Writer.flush (); Writer.close (); reader.close (); Socket.close (); Server.close (); -ในรหัสข้างต้นเมื่อเซิร์ฟเวอร์อ่านเครื่องหมายสิ้นสุดที่ส่งโดยไคลเอนต์นั่นคือ "EOF" การรับข้อมูลจะถูกยกเลิกและลูปจะถูกยกเลิกเพื่อให้รหัสที่ตามมาสามารถดำเนินการต่อไป
รหัส Java ไคลเอ็นต์
ไคลเอนต์คลาสสาธารณะ {โมฆะคงที่สาธารณะหลัก (สตริง args []) โยนข้อยกเว้น {// เพื่อความเรียบง่ายยกเว้นทั้งหมดจะถูกโยนออกไปโดยตรงสตริงโฮสต์ = "127.0.0.1"; // ที่อยู่ IP ของเซิร์ฟเวอร์ที่จะเชื่อมต่อพอร์ต int = 8899; // พอร์ตการฟังที่สอดคล้องกันของเซิร์ฟเวอร์ที่จะเชื่อมต่อ // การเชื่อมต่อถูกสร้างขึ้นด้วยเซิร์ฟเวอร์ซ็อกเก็ตไคลเอนต์ = ซ็อกเก็ตใหม่ (โฮสต์พอร์ต); // หลังจากสร้างการเชื่อมต่อคุณสามารถเขียนข้อมูลไปยัง Server Writer Writer = New OutputStreamWriter (client.getOutputStream ()); writer.write ("Hello Server."); Writer.flush (); // อ่านหลังจากเขียน reader reader = new InputStreamReader (client.getInputStream ()); ถ่านถ่าน [] = ถ่านใหม่ [64]; int len; StringBuffer sb = new StringBuffer (); ในขณะที่ ((len = reader.read (chars))! = -1) {sb.append (สตริงใหม่ (chars, 0, len)); } system.out.println ("จากเซิร์ฟเวอร์:" + sb); Writer.close (); reader.close (); client.close (); -ในรหัสข้างต้นเราส่งข้อมูลชิ้นหนึ่งไปยังเซิร์ฟเวอร์ก่อนจากนั้นอ่านข้อมูลที่ส่งคืนโดยเซิร์ฟเวอร์ เช่นเดียวกับเซิร์ฟเวอร์ก่อนหน้าอาจทำให้โปรแกรมแขวนอยู่ที่นั่นตลอดเวลาและไม่เคยกระโดดออกมาจากการวนซ้ำในขณะที่ รหัสนี้รวมกับรหัสแรกของเซิร์ฟเวอร์เพียงให้เราวิเคราะห์ว่าเซิร์ฟเวอร์จะได้รับข้อมูลที่นั่นเสมอและจะไม่กระโดดออกจากขณะที่ลูปดังนั้นจะไม่มีเซิร์ฟเวอร์ที่ตามมาจะส่งคืนข้อมูลไปยังไคลเอนต์และไคลเอนต์จะไม่สามารถรับข้อมูลที่ส่งคืนได้ โซลูชันจะแสดงในรหัสที่สองของเซิร์ฟเวอร์ หลังจากไคลเอนต์ส่งข้อมูลให้เขียนเครื่องหมายสิ้นสุดลงในสตรีมเอาต์พุตเพื่อบอกเซิร์ฟเวอร์ว่าข้อมูลได้ถูกส่งไปแล้ว เซิร์ฟเวอร์ยังส่งเครื่องหมายเพื่อบอกลูกค้าหลังจากส่งคืนข้อมูล จากนั้นรหัสไคลเอนต์ที่แก้ไขควรมีลักษณะเช่นนี้:
รหัส Java
ไคลเอนต์คลาสสาธารณะ {โมฆะคงที่สาธารณะหลัก (สตริง args []) โยนข้อยกเว้น {// เพื่อความเรียบง่ายยกเว้นทั้งหมดจะถูกโยนออกไปโดยตรงสตริงโฮสต์ = "127.0.0.1"; // ที่อยู่ IP ของเซิร์ฟเวอร์ที่จะเชื่อมต่อคือ int port = 8899; // พอร์ตการฟังที่สอดคล้องกันของเซิร์ฟเวอร์ที่จะเชื่อมต่อ // การเชื่อมต่อถูกสร้างขึ้นด้วยเซิร์ฟเวอร์ซ็อกเก็ตไคลเอนต์ = ซ็อกเก็ตใหม่ (โฮสต์พอร์ต); // หลังจากสร้างการเชื่อมต่อคุณสามารถเขียนข้อมูลไปยัง Server Writer Writer = New OutputStreamWriter (client.getOutputStream ()); writer.write ("Hello Server."); writer.write ("eof"); Writer.flush (); // อ่านหลังจากเขียน reader reader = new InputStreamReader (client.getInputStream ()); ถ่านถ่าน [] = ถ่านใหม่ [64]; int len; StringBuffer sb = new StringBuffer (); สตริงอุณหภูมิ; ดัชนี int; ในขณะที่ ((len = reader.read (chars))! = -1) {temp = สตริงใหม่ (chars, 0, len); if ((index = temp.indexof ("eof"))! = -1) {sb.append (temp.substring (0, index)); หยุดพัก; } sb.append (สตริงใหม่ (chars, 0, len)); } system.out.println ("จากเซิร์ฟเวอร์:" + sb); Writer.close (); reader.close (); client.close (); -รูปแบบที่พบบ่อยที่สุดที่เราใช้ในชีวิตประจำวันคือไคลเอนต์ส่งข้อมูลไปยังเซิร์ฟเวอร์และเซิร์ฟเวอร์จะได้รับข้อมูลและส่งคืนผลลัพธ์ที่สอดคล้องกันไปยังไคลเอนต์ อย่างไรก็ตามไม่มีความสัมพันธ์แบบตัวต่อตัวระหว่างไคลเอนต์และเซิร์ฟเวอร์อีกต่อไป แต่สถานการณ์ที่ลูกค้าหลายรายสอดคล้องกับเซิร์ฟเวอร์เดียวกันดังที่ได้กล่าวไว้ด้านล่าง
3. ไคลเอนต์หลายตัวเชื่อมต่อกับเซิร์ฟเวอร์เดียวกัน
สำหรับสองตัวอย่างที่กล่าวถึงข้างต้นเซิร์ฟเวอร์จะสิ้นสุดลงหลังจากได้รับคำขอของลูกค้าและไม่สามารถรับคำขอจากลูกค้ารายอื่นซึ่งมักจะไม่ตรงตามข้อกำหนดของเรา โดยปกติเราทำสิ่งนี้:
รหัส Java
เซิร์ฟเวอร์ระดับสาธารณะ {โมฆะคงที่สาธารณะหลัก (String args []) พ่น IOException {// เพื่อความเรียบง่ายข้อมูลข้อยกเว้นทั้งหมดจะถูกโยนออกจากพอร์ต int = 8899; // กำหนดการฟัง Serversocket บนพอร์ต 8899 Serversocket Server = Serversocket ใหม่ (พอร์ต); ในขณะที่ (จริง) {// เซิร์ฟเวอร์พยายามรับคำขอการเชื่อมต่อจากซ็อกเก็ตอื่น ๆ วิธีการยอมรับของเซิร์ฟเวอร์คือซ็อกเก็ตซ็อกเก็ตปิดกั้น = เซิร์ฟเวอร์ Accept (); // หลังจากสร้างการเชื่อมต่อกับไคลเอนต์เราสามารถรับอินพุตสตรีมของซ็อกเก็ตและอ่านข้อมูลที่ส่งโดยไคลเอนต์ Reader Reader = new InputStreamReader (socket.getInputStream ()); ถ่านถ่าน [] = ถ่านใหม่ [64]; int len; StringBuilder sb = new StringBuilder (); สตริงอุณหภูมิ; ดัชนี int; ในขณะที่ ((len = reader.read (chars))! = -1) {temp = สตริงใหม่ (chars, 0, len); if ((index = temp.indexof ("eof"))! = -1) {// การรับสิ้นสุดลงเมื่อพบ EOF sb.append (temp.substring (0, ดัชนี)); หยุดพัก; } sb.append (temp); } system.out.println ("จากไคลเอนต์:" + sb); // เขียนประโยคหลังจากอ่าน Writer Writer = New OutputStreamWriter (Socket.GetOutputStream ()); Writer.write ("Hello Client."); Writer.flush (); Writer.close (); reader.close (); Socket.close (); - ในรหัสข้างต้นเราใช้ลูปที่ตายแล้วโดยที่ Serversocket เรียกใช้วิธีการยอมรับเพื่อพยายามรับคำขอการเชื่อมต่อจากไคลเอนต์ เมื่อไม่ได้รับคำขอโปรแกรมจะบล็อกที่นี่จนกว่าจะได้รับคำขอการเชื่อมต่อจากลูกค้าจากนั้นสื่อสารกับลูกค้าที่ได้สร้างการเชื่อมต่อ หลังจากนั้นมันจะดำเนินการลูปร่างกายและพยายามรับคำขอการเชื่อมต่อใหม่อีกครั้ง วิธีนี้ Serversocket ของเราสามารถรับคำขอการเชื่อมต่อจากลูกค้าทั้งหมดและสื่อสารกับพวกเขา สิ่งนี้ใช้โหมดการสื่อสารอย่างง่ายกับไคลเอนต์หลายตัวบนเซิร์ฟเวอร์เดียว
ในตัวอย่างข้างต้นแม้ว่าเซิร์ฟเวอร์หนึ่งจะถูกนำไปใช้เพื่อสื่อสารกับลูกค้าหลายราย แต่ก็ยังมีปัญหาอยู่ ในตัวอย่างข้างต้นเซิร์ฟเวอร์ของเราจัดการคำขอการเชื่อมต่อของลูกค้าแบบซิงโครนัส ทุกครั้งที่เราได้รับคำขอการเชื่อมต่อจากลูกค้าเราต้องสื่อสารกับไคลเอนต์ปัจจุบันก่อนที่เราจะสามารถดำเนินการตามคำขอการเชื่อมต่อครั้งต่อไป สิ่งนี้จะส่งผลกระทบต่อประสิทธิภาพของโปรแกรมอย่างจริงจังเมื่อมีการพร้อมกันมากขึ้น ด้วยเหตุนี้เราจึงสามารถเปลี่ยนเป็นการประมวลผลแบบอะซิงโครนัสต่อไปนี้กับลูกค้า:
รหัส Java
เซิร์ฟเวอร์ระดับสาธารณะ {โมฆะคงที่สาธารณะหลัก (String args []) พ่น IOException {// เพื่อความเรียบง่ายข้อมูลข้อยกเว้นทั้งหมดจะถูกโยนออกจากพอร์ต int = 8899; // กำหนดการฟัง Serversocket บนพอร์ต 8899 Serversocket Server = Serversocket ใหม่ (พอร์ต); ในขณะที่ (จริง) {// เซิร์ฟเวอร์พยายามรับคำขอการเชื่อมต่อจากซ็อกเก็ตอื่น ๆ วิธีการยอมรับของเซิร์ฟเวอร์คือซ็อกเก็ตซ็อกเก็ตปิดกั้น = เซิร์ฟเวอร์ Accept (); // ทุกครั้งที่ได้รับซ็อกเก็ตเธรดใหม่จะถูกจัดตั้งขึ้นเพื่อจัดการกับเธรดใหม่ (งานใหม่ (ซ็อกเก็ต)) เริ่มต้น (); }} / ** * * / งานคลาสคงที่ใช้งาน Runnable {ซ็อกเก็ตซ็อกเก็ตส่วนตัว; งานสาธารณะ (ซ็อกเก็ตซ็อกเก็ต) {this.socket = ซ็อกเก็ต; } public void run () {ลอง {handlesocket (); } catch (exception e) {e.printstacktrace (); }} / *** สื่อสารกับซ็อกเก็ตไคลเอ็นต์* @throws Exception* / Void Private Void HandlesOcket () พ่นข้อยกเว้น {Reader Reader = new InputStreamReader (socket.getInputStream ()); ถ่านถ่าน [] = ถ่านใหม่ [64]; int len; StringBuilder sb = new StringBuilder (); สตริงอุณหภูมิ; ดัชนี int; ในขณะที่ ((len = reader.read (chars))! = -1) {temp = สตริงใหม่ (chars, 0, len); if ((index = temp.indexof ("eof"))! = -1) {// การรับสิ้นสุดลงเมื่อพบ EOF sb.append (temp.substring (0, ดัชนี)); หยุดพัก; } sb.append (temp); } system.out.println ("จากไคลเอนต์:" + sb); // เขียนประโยคหลังจากอ่าน Writer Writer = New OutputStreamWriter (Socket.GetOutputStream ()); Writer.write ("Hello Client."); Writer.flush (); Writer.close (); reader.close (); Socket.close (); - ในรหัสข้างต้นทุกครั้งที่ Serversocket ได้รับคำขอการเชื่อมต่อซ็อกเก็ตใหม่เธรดใหม่จะถูกสร้างขึ้นเพื่อสื่อสารกับซ็อกเก็ตปัจจุบันซึ่งจะบรรลุการประมวลผลแบบอะซิงโครนัสของการสื่อสารกับซ็อกเก็ตไคลเอ็นต์
เมื่อได้รับข้อมูลจากอินพุตของซ็อกเก็ตการอ่านเล็กน้อยเช่นข้างต้นนั้นซับซ้อนเกินไป บางครั้งเราจะใช้ bufferedreader เพื่ออ่านทีละบรรทัดเช่น:
รหัส Java
เซิร์ฟเวอร์ระดับสาธารณะ {โมฆะคงที่สาธารณะหลัก (String args []) พ่น IOException {// เพื่อความเรียบง่ายข้อมูลข้อยกเว้นทั้งหมดจะถูกโยนออกจากพอร์ต int = 8899; // กำหนดการฟัง Serversocket บนพอร์ต 8899 Serversocket Server = Serversocket ใหม่ (พอร์ต); ในขณะที่ (จริง) {// เซิร์ฟเวอร์พยายามรับคำขอการเชื่อมต่อจากซ็อกเก็ตอื่น ๆ วิธีการยอมรับของเซิร์ฟเวอร์คือซ็อกเก็ตซ็อกเก็ตปิดกั้น = เซิร์ฟเวอร์ Accept (); // ทุกครั้งที่ได้รับซ็อกเก็ตเธรดใหม่จะถูกจัดตั้งขึ้นเพื่อจัดการกับเธรดใหม่ (งานใหม่ (ซ็อกเก็ต)) เริ่มต้น (); }} / ** * * / งานคลาสคงที่ใช้งาน Runnable {ซ็อกเก็ตซ็อกเก็ตส่วนตัว; งานสาธารณะ (ซ็อกเก็ตซ็อกเก็ต) {this.socket = ซ็อกเก็ต; } public void run () {ลอง {handlesocket (); } catch (exception e) {e.printstacktrace (); }} / *** สื่อสารกับซ็อกเก็ตไคลเอ็นต์* @throws Exception* / Void Private Void HandlesOcket () พ่นข้อยกเว้น {bufferedreader br = new bufferedreader (ใหม่ inputStreamReader (socket.getInputStream ())); StringBuilder sb = new StringBuilder (); สตริงอุณหภูมิ; ดัชนี int; ในขณะที่ ((temp = br.readline ())! = null) {system.out.println (temp); if ((index = temp.indexof ("eof"))! = -1) {// สิ้นสุดการรับเมื่อ EOF พบ sb.append (temp.substring (0, ดัชนี)); หยุดพัก; } sb.append (temp); } system.out.println ("จากไคลเอนต์:" + sb); // เขียนประโยคหลังจากอ่าน Writer Writer = New OutputStreamWriter (Socket.GetOutputStream ()); Writer.write ("Hello Client."); writer.write ("eof/n"); Writer.flush (); Writer.close (); br.close (); Socket.close (); -ในเวลานี้ควรสังเกตว่าวิธี readline ของ bufferedreader อ่านทีละบรรทัด วิธีนี้ถูกบล็อก โปรแกรมจะไม่ดำเนินการต่อไปจนกว่าจะอ่านหนึ่งบรรทัดของข้อมูล แล้ว readline จะอ่านบรรทัดเมื่อใด วิธีการอ่านจะไม่คิดว่ามันได้รับการอ่านบรรทัดจนกว่าโปรแกรมจะพบกับบรรทัดใหม่หรืออักขระสุดท้ายของสตรีมที่เกี่ยวข้อง มันจะสิ้นสุดการอุดตันและปล่อยให้โปรแกรมดำเนินการต่อไป ดังนั้นเมื่อเราใช้ readline ของ bufferedReader เพื่ออ่านข้อมูลเราต้องจำไว้ว่าให้เขียนตัวแบ่งบรรทัดในสตรีมเอาต์พุตที่สอดคล้องกัน (จะถูกทำเครื่องหมายโดยอัตโนมัติเมื่อสิ้นสุดหลังจากสตรีมสิ้นสุดลงและสามารถรับรู้ได้) หลังจากเขียนเส้นแบ่งบรรทัดเราต้องจำไว้ว่าต้องล้างออกหากสตรีมเอาท์พุทไม่ได้ปิดทันทีเพื่อให้ข้อมูลจะถูกเขียนจากบัฟเฟอร์อย่างแท้จริง สอดคล้องกับรหัสด้านบนโปรแกรมไคลเอนต์ของเราควรเขียนเช่นนี้:
รหัส Java
ไคลเอนต์คลาสสาธารณะ {โมฆะคงที่สาธารณะหลัก (สตริง args []) โยนข้อยกเว้น {// เพื่อความเรียบง่ายยกเว้นทั้งหมดจะถูกโยนออกไปโดยตรงสตริงโฮสต์ = "127.0.0.1"; // ที่อยู่ IP ของเซิร์ฟเวอร์ที่จะเชื่อมต่อคือ int port = 8899; // พอร์ตการฟังที่สอดคล้องกันของเซิร์ฟเวอร์ที่จะเชื่อมต่อ // การเชื่อมต่อถูกสร้างขึ้นด้วยเซิร์ฟเวอร์ซ็อกเก็ตไคลเอนต์ = ซ็อกเก็ตใหม่ (โฮสต์พอร์ต); // หลังจากสร้างการเชื่อมต่อคุณสามารถเขียนข้อมูลไปยัง Server Writer Writer = New OutputStreamWriter (client.getOutputStream ()); writer.write ("Hello Server."); writer.write ("eof/n"); Writer.flush (); // อ่านหลังจากเขียน bufferedReader br = new bufferedReader (ใหม่ inputStreamReader (client.getInputStream ())); StringBuffer sb = new StringBuffer (); สตริงอุณหภูมิ; ดัชนี int; ในขณะที่ ((temp = br.readline ())! = null) {ถ้า ((index = temp.indexof ("eof"))! = -1) {sb.append (temp.substring (0, ดัชนี)); หยุดพัก; } sb.append (temp); } system.out.println ("จากเซิร์ฟเวอร์:" + sb); Writer.close (); br.close (); client.close (); -4. ตั้งเวลาหมดเวลา
สมมติว่ามีข้อกำหนดดังกล่าวที่ลูกค้าของเราจำเป็นต้องได้รับข้อมูล XX จากเซิร์ฟเวอร์ผ่านซ็อกเก็ตจากนั้นแสดงต่อผู้ใช้ในหน้า เรารู้ว่าซ็อกเก็ตกำลังปิดกั้นเมื่ออ่านข้อมูลและหากคุณไม่ได้อ่านข้อมูลโปรแกรมจะปิดกั้นที่นั่น เมื่อซิงโครไนซ์คำขอเราจะต้องไม่อนุญาตให้สถานการณ์ดังกล่าวเกิดขึ้น สิ่งนี้ทำให้เราต้องควบคุมการปิดกั้นการขัดจังหวะหลังจากคำขอถึงเวลาหนึ่งเพื่อให้โปรแกรมสามารถทำงานต่อไปได้ ซ็อกเก็ตให้วิธีการ setSotimeout () แก่เราในการตั้งค่าเวลาหมดเวลาของข้อมูลที่ได้รับในมิลลิวินาที เมื่อเวลาหมดเวลาตั้งค่ามากกว่า 0 และหลังจากเวลานี้ซ็อกเก็ตยังไม่ได้รับข้อมูลที่ส่งคืนซ็อกเก็ตจะโยน SocketTimeOutException
สมมติว่าเราจำเป็นต้องควบคุมลูกค้าของเราเพื่อขัดจังหวะและบล็อกก่อนที่จะอ่านข้อมูลเป็นเวลา 10 วินาทีหลังจากเริ่มอ่านข้อมูลเราสามารถทำได้:
รหัส Java
ไคลเอนต์คลาสสาธารณะ {โมฆะคงที่สาธารณะหลัก (สตริง args []) โยนข้อยกเว้น {// เพื่อความเรียบง่ายยกเว้นทั้งหมดจะถูกโยนออกไปโดยตรงสตริงโฮสต์ = "127.0.0.1"; // ที่อยู่ IP ของเซิร์ฟเวอร์ที่จะเชื่อมต่อคือ int port = 8899; // พอร์ตการฟังที่สอดคล้องกันของเซิร์ฟเวอร์ที่จะเชื่อมต่อ // การเชื่อมต่อถูกสร้างขึ้นด้วยเซิร์ฟเวอร์ซ็อกเก็ตไคลเอนต์ = ซ็อกเก็ตใหม่ (โฮสต์พอร์ต); // หลังจากสร้างการเชื่อมต่อคุณสามารถเขียนข้อมูลไปยัง Server Writer Writer = New OutputStreamWriter (client.getOutputStream ()); writer.write ("Hello Server."); writer.write ("eof/n"); Writer.flush (); // อ่านหลังจากเขียน bufferedReader br = new bufferedReader (ใหม่ inputStreamReader (client.getInputStream ())); // ตั้งค่าการหมดเวลาเป็น 10 วินาทีไคลเอนต์ SetSotimeout (10*1000); StringBuffer sb = new StringBuffer (); สตริงอุณหภูมิ; ดัชนี int; ลอง {ในขณะที่ ((temp = br.readline ())! = null) {ถ้า ((index = temp.indexof ("eof"))! = -1) {sb.append (temp.substring (0, ดัชนี)); หยุดพัก; } sb.append (temp); }} catch (sockettimeoutexception e) {system.out.println ("การอ่านข้อมูลการอ่านข้อมูล"); } system.out.println ("จากเซิร์ฟเวอร์:" + sb); Writer.close (); br.close (); client.close (); -5. รับรหัสข้อมูลที่อ่านไม่ออก
สำหรับกรณีดังกล่าวเมื่อเซิร์ฟเวอร์หรือไคลเอนต์ได้รับรหัสภาษาจีนที่อ่านไม่ออกอาจเป็นเพราะการเข้ารหัสที่ใช้เมื่อข้อมูลถูกส่งไม่สอดคล้องกับการเข้ารหัสที่ใช้เมื่อได้รับข้อมูล ตัวอย่างเช่นมีรหัสเซิร์ฟเวอร์เช่นนี้:
รหัส Java
เซิร์ฟเวอร์ระดับสาธารณะ {โมฆะคงที่สาธารณะหลัก (String args []) พ่น IOException {// เพื่อความเรียบง่ายข้อมูลข้อยกเว้นทั้งหมดจะถูกโยนออกจากพอร์ต int = 8899; // กำหนดการฟัง Serversocket บนพอร์ต 8899 Serversocket Server = Serversocket ใหม่ (พอร์ต); ในขณะที่ (จริง) {// เซิร์ฟเวอร์พยายามรับคำขอการเชื่อมต่อจากซ็อกเก็ตอื่น ๆ วิธีการยอมรับของเซิร์ฟเวอร์คือซ็อกเก็ตซ็อกเก็ตปิดกั้น = เซิร์ฟเวอร์ Accept (); // ทุกครั้งที่ได้รับซ็อกเก็ตเธรดใหม่จะถูกจัดตั้งขึ้นเพื่อจัดการกับเธรดใหม่ (งานใหม่ (ซ็อกเก็ต)) เริ่มต้น (); }} / ** * * / งานคลาสคงที่ใช้งาน Runnable {ซ็อกเก็ตซ็อกเก็ตส่วนตัว; งานสาธารณะ (ซ็อกเก็ตซ็อกเก็ต) {this.socket = ซ็อกเก็ต; } public void run () {ลอง {handlesocket (); } catch (exception e) {e.printstacktrace (); }} / *** สื่อสารกับซ็อกเก็ตไคลเอนต์* @throws Exception* / Void Private Void HandlesOcket () พ่นข้อยกเว้น {bufferedreader br = new bufferedreader (ใหม่ inputStreamReader (socket.getInputStream (), "GBK")); StringBuilder sb = new StringBuilder (); สตริงอุณหภูมิ; ดัชนี int; ในขณะที่ ((temp = br.readline ())! = null) {system.out.println (temp); if ((index = temp.indexof ("eof"))! = -1) {// การรับสิ้นสุดเมื่อพบ EOF sb.append (temp.substring (0, ดัชนี)); หยุดพัก; } sb.append (temp); } system.out.println ("ไคลเอนต์:" + sb); // เขียนประโยคหลังจากอ่าน Writer Writer = New OutputStreamWriter (Socket.GetOutputStream (), "UTF-8"); Writer.write ("สวัสดีลูกค้า"); writer.write ("eof/n"); Writer.flush (); Writer.close (); br.close (); Socket.close (); -ฉันสับสนเล็กน้อยเมื่อใช้ที่นี่เพื่อทดสอบ ในรหัสเซิร์ฟเวอร์ข้างต้นเรากำหนดการใช้การเข้ารหัส GBK อย่างชัดเจนเพื่ออ่านข้อมูลเมื่อกำหนดอินพุตสตรีมและระบุอย่างชัดเจนว่าการใช้การเข้ารหัส UTF-8 เพื่อส่งข้อมูลเมื่อกำหนดกระแสเอาต์พุต หากไคลเอนต์ไม่ส่งข้อมูลในการเข้ารหัส GBK เมื่อส่งข้อมูลไปยังไคลเอนต์ข้อมูลที่ได้รับจากเซิร์ฟเวอร์มีแนวโน้มที่จะอ่านไม่ออก ในทำนองเดียวกันหากไคลเอนต์ไม่ส่งการเข้ารหัสข้อมูลเมื่อส่งข้อมูลบนเซิร์ฟเวอร์นั่นคือการเข้ารหัส UTF-8 เพื่อรับข้อมูลก็มีแนวโน้มว่าข้อมูลจะเกิดขึ้นได้ ดังนั้นสำหรับรหัสเซิร์ฟเวอร์ข้างต้นเพื่อให้โปรแกรมของเราอ่านข้อมูลที่ส่งโดยบุคคลอื่นโดยไม่มีรหัสที่อ่านไม่ออกไคลเอนต์ของเราควรเป็นเช่นนี้:
รหัส Java
ไคลเอนต์คลาสสาธารณะ {โมฆะคงที่สาธารณะหลัก (สตริง args []) โยนข้อยกเว้น {// เพื่อความเรียบง่ายยกเว้นทั้งหมดจะถูกโยนออกไปโดยตรงสตริงโฮสต์ = "127.0.0.1"; // ที่อยู่ IP ของเซิร์ฟเวอร์ที่จะเชื่อมต่อพอร์ต int = 8899; // พอร์ตการฟังที่สอดคล้องกันของเซิร์ฟเวอร์ที่จะเชื่อมต่อ // การเชื่อมต่อถูกสร้างขึ้นด้วยเซิร์ฟเวอร์ซ็อกเก็ตไคลเอนต์ = ซ็อกเก็ตใหม่ (โฮสต์พอร์ต); // หลังจากสร้างการเชื่อมต่อคุณสามารถเขียนข้อมูลไปยัง Server Writer Writer = New OutputStreamWriter (client.getOutputStream (), "GBK"); writer.write ("สวัสดีเซิร์ฟเวอร์"); writer.write ("eof/n"); Writer.flush (); // อ่านหลังจากเขียน bufferedReader br = new bufferedReader (ใหม่ inputStreamReader (client.getInputStream (), "UTF-8")); // ตั้งค่าการหมดเวลาเป็น 10 วินาทีไคลเอนต์ SetSotimeout (10*1000); StringBuffer sb = new StringBuffer (); สตริงอุณหภูมิ; ดัชนี int; ลอง {ในขณะที่ ((temp = br.readline ())! = null) {ถ้า ((index = temp.indexof ("eof"))! = -1) {sb.append (temp.substring (0, ดัชนี)); หยุดพัก; } sb.append (temp); }} catch (sockettimeoutexception e) {system.out.println ("การอ่านข้อมูลการอ่านข้อมูล"); } system.out.println ("เซิร์ฟเวอร์:" + sb); Writer.close (); br.close (); client.close (); -บทความนี้ได้รวบรวมไว้ใน "สรุปเทคนิคการดำเนินการอัพโหลด Java" และทุกคนยินดีที่จะเรียนรู้และอ่าน
ข้างต้นเป็นเนื้อหาทั้งหมดของบทความนี้ ฉันหวังว่ามันจะเป็นประโยชน์ต่อการเรียนรู้ของทุกคนและฉันหวังว่าทุกคนจะสนับสนุน wulin.com มากขึ้น