摘要:服務(wù)器端線程實(shí)現(xiàn)首先將服務(wù)器獨(dú)立成一個(gè)線程服務(wù)器線程接受客戶端連接請(qǐng)求在構(gòu)造函數(shù)中我們初始化服務(wù)器的,然后等待客戶端的連接。
這次在java實(shí)驗(yàn)的時(shí)候,要求使用server socket編寫(xiě)服務(wù)器和客戶端的網(wǎng)絡(luò)通信。最開(kāi)始認(rèn)為應(yīng)該是挺簡(jiǎn)單的,但是后來(lái)發(fā)現(xiàn)低估了它。出現(xiàn)了不少的問(wèn)題,所以也在這里與大家分享。
問(wèn)題描述服務(wù)器程序的處理規(guī)則如下:初步實(shí)現(xiàn)
1) 向客戶端程序發(fā)送Verifying Server!。
2) 若讀口令次數(shù)超過(guò)3次,則發(fā)送Illegal User!給客戶端,程序退出。否則向下執(zhí)行步驟3)。
3) 讀取客戶端程序提供的口令。
4) 若口令不正確,則發(fā)送PassWord Wrong!給客戶端,并轉(zhuǎn)步驟2),否則向下執(zhí)行步驟5)。
5) 發(fā)送Registration Successful!給客戶端程序。客戶端程序的處理規(guī)則如下:
1) 讀取服務(wù)器反饋信息。
2) 若反饋信息不是Verifying Server!,則提示Server Wrong!,程序退出。否則向下執(zhí)行步驟3)
3) 提示輸入PassWord并將輸入的口令發(fā)送給服務(wù)器。
4) 讀取服務(wù)器反饋信息。
5) 若反饋信息是Illegal User!,則提示Illegal User!,程序退出。否則向下執(zhí)行步驟6)
6) 若反饋信息是PassWord Wrong!,則提示PassWord Wrong!,并轉(zhuǎn)步驟3),否則向下執(zhí)行步驟。
7) 輸出Registration Successful!。
首先,我們一定要清楚,這次和之前的程序不同,雖然都是在本地上,但是服務(wù)器和客戶端需要兩個(gè)啟動(dòng)程序來(lái)實(shí)現(xiàn),以達(dá)到我們模擬遠(yuǎn)程連接的效果。
然后就是如何利用socket實(shí)現(xiàn)我們的功能了。
通過(guò)上面的圖示,我們可以知道,首先需要先開(kāi)啟服務(wù)器,然后等待客戶端的連接。
當(dāng)客戶端通過(guò)socket進(jìn)行連接后,服務(wù)器端也會(huì)建立一個(gè)socket對(duì)象來(lái)幫助實(shí)現(xiàn)服務(wù)器和客戶端的通信。
這時(shí)候就建立了一個(gè)TCP連接,我們就可以在服務(wù)器寫(xiě)數(shù)據(jù),然后在客戶端讀取了,實(shí)現(xiàn)雙方通信。
最后,當(dāng)有一方?jīng)Q定通信結(jié)束,就會(huì)關(guān)閉連接。通信結(jié)束。
下面是初步實(shí)現(xiàn)的代碼:
import java.io.*; import java.net.ServerSocket; import java.net.Socket; /** * 服務(wù)器 */ public class ServerTest { public static void main(String[] args) { try { ServerSocket serverSocket = new ServerSocket(8080); Socket clientSocket = serverSocket.accept(); String welcome = "verifying server!"; OutputStream outputStream = clientSocket.getOutputStream(); outputStream.write(welcome.getBytes()); InputStream inputStream = clientSocket.getInputStream(); int time = 0; BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream)); String password = bufferedReader.readLine(); // 獲取登錄信息,允許3次登錄 while (time < 3) { if (password.equals("123")) { outputStream.flush(); outputStream.write("Registration Successful!".getBytes()); break; } else { outputStream.write("PassWord Wrong!".getBytes()); outputStream.flush(); password = bufferedReader.readLine(); time++; } } if (time >= 3) { outputStream.flush(); outputStream.write("Illegal User!".getBytes()); } outputStream.close(); clientSocket.close(); serverSocket.close(); } catch (IOException e) { e.printStackTrace(); } } }
import java.io.*; import java.net.Socket; /** * 客戶端 */ public class ClientTest { public static void main(String[] args) { try { Socket socket = new Socket("127.0.0.1", 8080); String aline = new String(); // 獲取輸入流 BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream())); aline = bufferedReader.readLine(); // 訪問(wèn)失敗 if (!aline.equals("verifying server!")) { System.out.println("Server Wrong!"); return; } // 獲取響應(yīng)結(jié)果 String feedback = bufferedReader.readLine(); while (feedback == null || feedback.equals("PassWord Wrong!")) { if (feedback != null) { if (feedback.equals("PassWord Wrong!")) { System.out.println("PassWord Wrong!"); } else if (feedback.equals("Registration Successful!")) { System.out.println("Registration Successful!"); break; } } System.out.println("輸入密碼:"); // 輸入密碼 Scanner scanner = new Scanner(System.in); OutputStream outputStream = socket.getOutputStream(); String password = scanner.nextLine(); outputStream.write(password.getBytes()); feedback = bufferedReader.readLine(); } // 關(guān)閉連接 socket.close(); } catch (IOException e) { e.printStackTrace(); } } }
初步實(shí)現(xiàn)后,運(yùn)行:一片空白。什么都沒(méi)有發(fā)生。
出問(wèn)題了很正常,斷點(diǎn)調(diào)試一下吧。發(fā)現(xiàn)問(wèn)題所在:
客戶端執(zhí)行到這一行時(shí),停止,然后服務(wù)器端接著執(zhí)行;
同樣的服務(wù)器端也到這行就停止了。
原因是服務(wù)器一方等著輸入密碼,而客戶端一方等著響應(yīng)結(jié)果,然后又沒(méi)有開(kāi)始輸入密碼。所以雙方就這么一直等著,誰(shuí)都不動(dòng)了。
解決通過(guò)上面的分析,我們只要將僵局打破就能夠解決問(wèn)題。所以就先輸入密碼,然后再獲取響應(yīng)結(jié)果。這樣就不會(huì)出問(wèn)題了。
所以服務(wù)器端的代碼并不需要做什么改動(dòng),只用修改客戶端程序就行了。
import java.io.*; import java.net.Socket; /** * 客戶端 */ public class ClientTest { public static void main(String[] args) { try { Socket socket = new Socket("127.0.0.1", 8080); String aline = new String(); // 獲取輸入流 BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream())); aline = bufferedReader.readLine(); // 訪問(wèn)失敗 if (!aline.equals("verifying server!")) { System.out.println("Server Wrong!"); return; } // 主要修改這里,不先獲取響應(yīng)結(jié)果 // 初始化響應(yīng)結(jié)果 String feedback = null; while (true) { if (feedback != null) { if (feedback.equals("PassWord Wrong!")) { System.out.println("PassWord Wrong!"); } else if (feedback.equals("Registration Successful!")) { System.out.println("Registration Successful!"); break; } } System.out.println("輸入密碼:"); // 輸入密碼 Scanner scanner = new Scanner(System.in); OutputStream outputStream = socket.getOutputStream(); String password = scanner.nextLine(); outputStream.write(password.getBytes()); feedback = bufferedReader.readLine(); } // 關(guān)閉連接 socket.close(); } catch (IOException e) { e.printStackTrace(); } } }
成功實(shí)現(xiàn)!
升級(jí):利用多線程實(shí)現(xiàn)功能成功實(shí)現(xiàn)之后,還不能滿足于當(dāng)前的狀況。由于在實(shí)際使用socket的時(shí)候,很多時(shí)候都不是簡(jiǎn)單的一對(duì)一的情況,這種情況下,就需要使用多線程來(lái)幫助我們了。
使用多線程,我們可以實(shí)現(xiàn)一個(gè)服務(wù)器多個(gè)客戶端的情況,每當(dāng)有一個(gè)客戶端請(qǐng)求連接,我們就會(huì)建立一個(gè)線程。
1.服務(wù)器端線程實(shí)現(xiàn)首先將服務(wù)器獨(dú)立成一個(gè)線程:
/** * 服務(wù)器線程 */ public class ServerThread extends Thread { private ServerSocket serverSocket; private Socket clientSocket; public ServerThread(int port) { try { serverSocket = new ServerSocket(port); // 接受客戶端連接請(qǐng)求 clientSocket = serverSocket.accept(); } catch (IOException e) { e.printStackTrace(); } } }
在構(gòu)造函數(shù)中我們初始化服務(wù)器的socket,然后等待客戶端的連接。接著就是最主要的部分了,線程主要執(zhí)行的邏輯功能:run
@Override public void run() { try { // 提示連接成功 DataOutputStream dataOutputStream = new DataOutputStream(clientSocket.getOutputStream()); dataOutputStream.writeUTF("verifying server!"); // 獲取輸入流 InputStream inputStream = clientSocket.getInputStream(); DataInputStream dataInputStream = new DataInputStream(inputStream); String password = dataInputStream.readUTF(); // 獲取登錄信息,允許3次登錄 int time = 0; // 密碼校驗(yàn),允許輸入3次 while (time < 3) { if (password.equals("123")) { dataOutputStream.writeUTF("Registration Successful!"); break; } else { dataOutputStream.writeUTF("PassWord Wrong!"); password = dataInputStream.readUTF(); time++; } } if (time >= 3) { dataOutputStream.writeUTF("Illegal User!"); } } catch (IOException e) { e.printStackTrace(); } }
run沒(méi)有什么可以說(shuō)的,主要就是實(shí)現(xiàn)服務(wù)器與客戶端交互的時(shí)候執(zhí)行的功能。然后在執(zhí)行線程的時(shí)候,會(huì)自動(dòng)調(diào)用run方法。
最后就是啟動(dòng)服務(wù)器端了:
/** * 服務(wù)器端 */ public class ServiceTest { public static void main(String[] args) { ServerThread serverThread = new ServerThread(8080); serverThread.start(); } }
同樣,啟用8080端口。
2.客戶端線程實(shí)現(xiàn)類似服務(wù)器端,首先先建立客戶端線程:
/** * 客戶端線程 */ public class ClientThread extends Thread { private Socket socket; public ClientThread(String host, int port) { try { this.socket = new Socket(host, port); } catch (IOException e) { e.printStackTrace(); } } }
在構(gòu)造函數(shù)中,建立客戶端的socket對(duì)象。然后實(shí)現(xiàn)run方法。
@Override public void run() { try { // 獲取輸入流 DataInputStream dataInputStream = new DataInputStream(socket.getInputStream()); String aline = dataInputStream.readUTF(); // 連接服務(wù)器失敗 if (!aline.equals("verifying server!")) { System.out.println("Server Wrong!"); return; } // 獲取響應(yīng)結(jié)果 String feedback = null; // 進(jìn)行密碼輸入 while (true) { if (feedback != null) { if (feedback.equals("PassWord Wrong!")) { System.out.println("PassWord Wrong!"); } else if (feedback.equals("Registration Successful!")) { System.out.println("Registration Successful!"); System.exit(1); } } System.out.println("輸入密碼:"); Scanner scanner = new Scanner(System.in); DataOutputStream dataOutputStream = new DataOutputStream(socket.getOutputStream()); String password = scanner.nextLine(); dataOutputStream.writeUTF(password); // 獲取響應(yīng)結(jié)果 feedback = dataInputStream.readUTF(); } } catch (IOException e) { e.printStackTrace(); } }
run方法中,主要實(shí)現(xiàn)了輸入密碼的功能。
最后就是啟動(dòng)客戶端線程:
/** * 客戶端 */ public class ClientTest { public static void main(String[] args) { ClientThread clientThread = new ClientThread("127.0.0.1", 8080); clientThread.start(); } }總結(jié)
這次的實(shí)驗(yàn)給我提了個(gè)醒,看似簡(jiǎn)單的東西,也永遠(yuǎn)不要輕視它。只有拿出應(yīng)有的實(shí)例去解決問(wèn)題,問(wèn)題才是你眼中那個(gè)簡(jiǎn)單的問(wèn)題。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/71987.html
摘要:具備發(fā)送和接受功能,在進(jìn)行傳輸時(shí),需要明確一個(gè)是發(fā)送端,一個(gè)是接收端。指定發(fā)送端口,不指定系統(tǒng)會(huì)隨機(jī)分配。傳輸兩個(gè)端點(diǎn)的建立連接后會(huì)有一個(gè)傳輸數(shù)據(jù)的通道,這通道稱為流,而且是建立在網(wǎng)絡(luò)基礎(chǔ)上的流,稱之為流。 端口:物理端口:邏輯端口: 用于標(biāo)識(shí)進(jìn)程的邏輯地址,不同進(jìn)程的標(biāo)識(shí);有效端口:0~65535,其中0~1024系統(tǒng)使用或保留端口。 java 中ip對(duì)象:InetAddress....
摘要:綁定完成后允許套接字進(jìn)行連接并等待連接。服務(wù)端根據(jù)報(bào)文返回響應(yīng),并關(guān)閉連接。單線程服務(wù)器多進(jìn)程及多線程服務(wù)器復(fù)用服務(wù)器復(fù)用的多線程服務(wù)器單線程服務(wù)器一次只處理一個(gè)請(qǐng)求,直到其完成為止。 前言 本篇文章將涉及以下內(nèi)容: IO實(shí)現(xiàn)Java Socket通信 NIO實(shí)現(xiàn)Java Socket通信 閱讀本文之前最好了解過(guò): Java IO Java NIO Java Concurrenc...
摘要:在設(shè)定時(shí)間內(nèi)接收到相應(yīng)操作的請(qǐng)求則返回可以處理請(qǐng)求的數(shù)量,否則在超時(shí)后返回,程序繼續(xù)執(zhí)行。使用接收請(qǐng)求并處理接收到請(qǐng)求后調(diào)用返回的集合。保存了處理當(dāng)前請(qǐng)求的和,并提供了不同的操作類型。默認(rèn)值為且其值必須小于的值。 Java中的Socket可以分為普通Socket和NioSocket兩種。 普通Socket的用法 Java中的網(wǎng)絡(luò)通信是通過(guò)Socket實(shí)現(xiàn)的,Socket分為Server...
摘要:為解決這問(wèn)題,我們發(fā)現(xiàn)元兇處在一線程一請(qǐng)求上,如果一個(gè)線程能同時(shí)處理多個(gè)請(qǐng)求,那么在高并發(fā)下性能上會(huì)大大改善。這樣一個(gè)線程可以同時(shí)發(fā)起多個(gè)調(diào)用,并且不需要同步等待數(shù)據(jù)就緒。表示當(dāng)前就緒的事件類型。 JAVA NIO 一步步構(gòu)建I/O多路復(fù)用的請(qǐng)求模型 摘要:本文屬于原創(chuàng),歡迎轉(zhuǎn)載,轉(zhuǎn)載請(qǐng)保留出處:https://github.com/jasonGeng88/blog 文章一:JAVA ...
閱讀 3051·2021-11-22 09:34
閱讀 3644·2021-08-31 09:45
閱讀 3855·2019-08-30 13:57
閱讀 1679·2019-08-29 15:11
閱讀 1686·2019-08-28 18:04
閱讀 3229·2019-08-28 17:59
閱讀 1568·2019-08-26 13:35
閱讀 2194·2019-08-26 10:12