摘要:服務器端線程實現首先將服務器獨立成一個線程服務器線程接受客戶端連接請求在構造函數中我們初始化服務器的,然后等待客戶端的連接。
這次在java實驗的時候,要求使用server socket編寫服務器和客戶端的網絡通信。最開始認為應該是挺簡單的,但是后來發現低估了它。出現了不少的問題,所以也在這里與大家分享。
問題描述服務器程序的處理規則如下:初步實現
1) 向客戶端程序發送Verifying Server!。
2) 若讀口令次數超過3次,則發送Illegal User!給客戶端,程序退出。否則向下執行步驟3)。
3) 讀取客戶端程序提供的口令。
4) 若口令不正確,則發送PassWord Wrong!給客戶端,并轉步驟2),否則向下執行步驟5)。
5) 發送Registration Successful!給客戶端程序。客戶端程序的處理規則如下:
1) 讀取服務器反饋信息。
2) 若反饋信息不是Verifying Server!,則提示Server Wrong!,程序退出。否則向下執行步驟3)
3) 提示輸入PassWord并將輸入的口令發送給服務器。
4) 讀取服務器反饋信息。
5) 若反饋信息是Illegal User!,則提示Illegal User!,程序退出。否則向下執行步驟6)
6) 若反饋信息是PassWord Wrong!,則提示PassWord Wrong!,并轉步驟3),否則向下執行步驟。
7) 輸出Registration Successful!。
首先,我們一定要清楚,這次和之前的程序不同,雖然都是在本地上,但是服務器和客戶端需要兩個啟動程序來實現,以達到我們模擬遠程連接的效果。
然后就是如何利用socket實現我們的功能了。
通過上面的圖示,我們可以知道,首先需要先開啟服務器,然后等待客戶端的連接。
當客戶端通過socket進行連接后,服務器端也會建立一個socket對象來幫助實現服務器和客戶端的通信。
這時候就建立了一個TCP連接,我們就可以在服務器寫數據,然后在客戶端讀取了,實現雙方通信。
最后,當有一方決定通信結束,就會關閉連接。通信結束。
下面是初步實現的代碼:
import java.io.*; import java.net.ServerSocket; import java.net.Socket; /** * 服務器 */ 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(); // 訪問失敗 if (!aline.equals("verifying server!")) { System.out.println("Server Wrong!"); return; } // 獲取響應結果 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(); } // 關閉連接 socket.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(); // 訪問失敗 if (!aline.equals("verifying server!")) { System.out.println("Server Wrong!"); return; } // 主要修改這里,不先獲取響應結果 // 初始化響應結果 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(); } // 關閉連接 socket.close(); } catch (IOException e) { e.printStackTrace(); } } }
成功實現!
升級:利用多線程實現功能成功實現之后,還不能滿足于當前的狀況。由于在實際使用socket的時候,很多時候都不是簡單的一對一的情況,這種情況下,就需要使用多線程來幫助我們了。
使用多線程,我們可以實現一個服務器多個客戶端的情況,每當有一個客戶端請求連接,我們就會建立一個線程。
1.服務器端線程實現首先將服務器獨立成一個線程:
/** * 服務器線程 */ public class ServerThread extends Thread { private ServerSocket serverSocket; private Socket clientSocket; public ServerThread(int port) { try { serverSocket = new ServerSocket(port); // 接受客戶端連接請求 clientSocket = serverSocket.accept(); } catch (IOException e) { e.printStackTrace(); } } }
在構造函數中我們初始化服務器的socket,然后等待客戶端的連接。接著就是最主要的部分了,線程主要執行的邏輯功能: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; // 密碼校驗,允許輸入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沒有什么可以說的,主要就是實現服務器與客戶端交互的時候執行的功能。然后在執行線程的時候,會自動調用run方法。
最后就是啟動服務器端了:
/** * 服務器端 */ public class ServiceTest { public static void main(String[] args) { ServerThread serverThread = new ServerThread(8080); serverThread.start(); } }
同樣,啟用8080端口。
2.客戶端線程實現類似服務器端,首先先建立客戶端線程:
/** * 客戶端線程 */ 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(); } } }
在構造函數中,建立客戶端的socket對象。然后實現run方法。
@Override public void run() { try { // 獲取輸入流 DataInputStream dataInputStream = new DataInputStream(socket.getInputStream()); String aline = dataInputStream.readUTF(); // 連接服務器失敗 if (!aline.equals("verifying server!")) { System.out.println("Server Wrong!"); return; } // 獲取響應結果 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!"); 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); // 獲取響應結果 feedback = dataInputStream.readUTF(); } } catch (IOException e) { e.printStackTrace(); } }
run方法中,主要實現了輸入密碼的功能。
最后就是啟動客戶端線程:
/** * 客戶端 */ public class ClientTest { public static void main(String[] args) { ClientThread clientThread = new ClientThread("127.0.0.1", 8080); clientThread.start(); } }總結
這次的實驗給我提了個醒,看似簡單的東西,也永遠不要輕視它。只有拿出應有的實例去解決問題,問題才是你眼中那個簡單的問題。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/71987.html
摘要:具備發送和接受功能,在進行傳輸時,需要明確一個是發送端,一個是接收端。指定發送端口,不指定系統會隨機分配。傳輸兩個端點的建立連接后會有一個傳輸數據的通道,這通道稱為流,而且是建立在網絡基礎上的流,稱之為流。 端口:物理端口:邏輯端口: 用于標識進程的邏輯地址,不同進程的標識;有效端口:0~65535,其中0~1024系統使用或保留端口。 java 中ip對象:InetAddress....
摘要:綁定完成后允許套接字進行連接并等待連接。服務端根據報文返回響應,并關閉連接。單線程服務器多進程及多線程服務器復用服務器復用的多線程服務器單線程服務器一次只處理一個請求,直到其完成為止。 前言 本篇文章將涉及以下內容: IO實現Java Socket通信 NIO實現Java Socket通信 閱讀本文之前最好了解過: Java IO Java NIO Java Concurrenc...
摘要:在設定時間內接收到相應操作的請求則返回可以處理請求的數量,否則在超時后返回,程序繼續執行。使用接收請求并處理接收到請求后調用返回的集合。保存了處理當前請求的和,并提供了不同的操作類型。默認值為且其值必須小于的值。 Java中的Socket可以分為普通Socket和NioSocket兩種。 普通Socket的用法 Java中的網絡通信是通過Socket實現的,Socket分為Server...
摘要:為解決這問題,我們發現元兇處在一線程一請求上,如果一個線程能同時處理多個請求,那么在高并發下性能上會大大改善。這樣一個線程可以同時發起多個調用,并且不需要同步等待數據就緒。表示當前就緒的事件類型。 JAVA NIO 一步步構建I/O多路復用的請求模型 摘要:本文屬于原創,歡迎轉載,轉載請保留出處:https://github.com/jasonGeng88/blog 文章一:JAVA ...
閱讀 3044·2021-11-22 09:34
閱讀 3636·2021-08-31 09:45
閱讀 3836·2019-08-30 13:57
閱讀 1669·2019-08-29 15:11
閱讀 1680·2019-08-28 18:04
閱讀 3218·2019-08-28 17:59
閱讀 1558·2019-08-26 13:35
閱讀 2187·2019-08-26 10:12