JAVA - 基於Socket的多執行緒通訊

20170405發表於2020-08-08

  程式碼簡介

  程式碼思想:

  實現"服務端-客戶端"之間的通訊需要解決兩點問題

  (1),如何實現通訊

  (2),通訊中如何避免單執行緒先接收後傳送(或先傳送後接收)的問題;

  解決方案:

  (1),實現通訊:使用ServerSocket監聽指定埠後,Socket進行連線;然後客戶端與服務端都獲取Socket物件的輸入輸出流存入PrintWrite後不斷重新整理,即可實現通訊;

  (2),彌補單執行緒缺陷:建立訊息接收類和訊息傳送類分別繼承Thread;然後透過開啟兩個執行緒完避免單執行緒必須先接收後傳送(或先傳送後接收)的問題;

  詳細程式碼如下,如有最佳化,歡迎留言:

  服務端

  package x_Socket;

  import java.io.BufferedReader;

  import java.io.IOException;

  import java.io.InputStreamReader;

  import java.io.PrintWriter;

  import java.net.ServerSocket;

  import java.net.Socket;

  public class x_Server{

  /** 宣告全域性物件,讓x_Server的內部類可以共同呼叫這些物件 **/

  static ServerSocket serverSocket = null;//宣告靜態全域性的socket類物件;讓x_Server的靜態方法可以直接呼叫該物件;

  static Socket socket = null; //宣告靜態全域性的socket類物件;讓x_Server的靜態方法可以直接呼叫該物件;

  BufferedReader buffSend = null; //資訊傳送資料流

  BufferedReader buffReciever = null; //資訊接收資料流

  PrintWriter printWriter = null; //資料讀寫資料流

  /** 主函式入口 **/

  public static void main(String[] args) throws IOException {

  serverSocket = new ServerSocket(5556); //透過Socket的建構函式,監聽的5556埠(即等待socket物件向該埠請求連線)

  socket = serverSocket.accept(); //如果有socket的物件請求連線了5556埠,則serverSocket的accept返回一個socket物件;

  System.out.println("有使用者成功連線客戶端");

  Thread xsend = new Thread(new x_Server().new x_Send()); //透過Thread類的建構函式,建立一個x_Send類的執行緒物件

  Thread xreciver = new Thread(new x_Server().new x_Receiver()); //透過外部類物件去例項化內部類,再透過Thread的建構函式,建立內部類x_Reciever的執行緒二物件;

  xsend.start(); //啟動x_Send的執行緒,讓傳送與接收資料可以同時進行

  xreciver.start(); //啟動x_Receiver的執行緒,讓傳送與接收資料可以同時進行

  }

  /** 用於服務端傳送資訊的x_Send類(執行緒一) **/

  class x_Send extends Thread{

  public void run(){ //繼承Thread類,必須重寫run()方法

  try {

  buffSend = new BufferedReader(new InputStreamReader(System.in));//獲取從控制檯輸入資料的物件buffSend

  printWriter = new PrintWriter(socket.getOutputStream()); //透過PrintWriter類的構造方法,獲取向客戶端傳送資訊的輸出流物件

  String msg = null; //msg:儲存服務端傳送的資訊

  do{

  msg = buffSend.readLine(); //獲得從控制檯輸入一行的資訊

  printWriter.println(msg); //將msg傳到向服務端傳送資訊的輸出流物件

  printWriter.flush(); //重新整理輸入輸出流,客戶端端可以立刻收到輸入輸出流的更新資訊

  }while(!msg.equals("end")); //客戶輸入"end"表示結束通訊

  socket.close(); //關閉埠5556的連線

  serverSocket.close(); //不再監聽埠5556(即不再接收來自該埠的連線)

  } catch (IOException e) {

  System.out.println("客戶端連線已斷開");

  e.printStackTrace();

  }

  }

  }

  /** 用於服務端接收資訊的x_Reciever類(執行緒二) **/

  class x_Receiver extends Thread{

  public void run(){ //繼承Thread類,必須重寫run()方法

  try {

  buffReciever = new BufferedReader(new InputStreamReader(socket.getInputStream())); //獲取從socket輸入(客戶端傳輸過來)資料的輸入流物件

  while(true){

  System.out.println("接收客戶端訊息:"+buffReciever.readLine()); //輸出客戶端傳輸過來的資訊

  }

  } catch (IOException e) {

  System.out.println("客戶端連線已斷開:\n");

  e.printStackTrace();

  }

  }

  }

  }

  客戶端

  package x_Socket;

  import java.io.BufferedReader;

  import java.io.IOException;

  import java.io.InputStreamReader;

  import java.io.PrintWriter;

  import java.net.Socket;

  import java.net.UnknownHostException;

  public class x_Client {

  /** 宣告全域性物件,讓x_Client的內部類可以共同呼叫這些物件 **/

  static Socket socket = null; //宣告靜態全域性的socket類物件;讓x_Client的靜態方法可以直接呼叫該物件;

  BufferedReader buffSend = null; //資訊傳送資料流

  BufferedReader buffReciever = null; //資訊接收資料流

  PrintWriter printWriter = null; //資料讀寫資料流

  /** 主函式入口 **/

  public static void main(String[] args) throws UnknownHostException, IOException {

  socket =new Socket("127.0.0.1",5556); //透過Socket的建構函式,連線本地ip的5556埠

  System.out.println("連線服務端資訊如下:\nip:127,0.0.1 port:5556");

  Thread xsend = new Thread(new x_Client().new x_SendMsg()); //透過Thread類的建構函式,建立一個x_SendMsg類的執行緒物件

  Thread xreciver = new Thread(new x_Client().new x_RecieverMsg()); //透過外部類物件去例項化內部類,再透過Thread的建構函式,建立內部類x_RecieverMsg的執行緒二物件;

  xsend.start(); //啟動x_SendMsg的執行緒;

  xreciver.start(); //啟動x_RecieverMsg的執行緒;兩個讓傳送與接收資料可以同時進行

  }

  /** 用於客戶端傳送資訊的x_SendMsg類(執行緒一)**/

  class x_SendMsg extends Thread{

  public void run(){ //繼承Thread類,必須重寫run()方法

  try {

  buffSend = new BufferedReader(new InputStreamReader(System.in));//獲取從控制檯輸入資料的物件buffSend

  printWriter = new PrintWriter(socket.getOutputStream()); //透過PrintWriter類的構造方法,獲取向服務端傳送資訊的輸出流物件

  String msg = null; //msg:儲存客戶端傳送的資訊

  do{

  msg = buffSend.readLine(); //獲得從控制檯輸入一行的資訊

  printWriter.println(msg); //將msg傳到向服務端傳送資訊的輸出流物件

  printWriter.flush(); //重新整理輸入輸出流,服務端可以立刻收到輸入輸出流的更新資訊

  }while(!msg.equals("end")); //客戶輸入"end"表示結束通訊

  socket.close(); //關閉埠5556的連線

  } catch (IOException e) {

  System.out.println("服務端埠已關閉"); //異常提示資訊顯示

  e.printStackTrace();  

  }

  }

  }

  /** 用於客戶端接收資訊的x_RecieverMsg類(執行緒二) **/

  class x_RecieverMsg extends Thread{

  public void run(){ //繼承Thread類,必須重寫run()方法

  try {

  buffReciever = new BufferedReader(new InputStreamReader(socket.getInputStream())); //獲取從socket輸入(服務端傳輸過來)資料的輸入流物件

  while(true){

  System.out.println("收到服務端訊息:"+buffReciever.readLine()); //輸出服務端傳輸過來的資訊

  }

  } catch (IOException e) {

  System.out.println("服務端埠已關閉"); //異常提示資訊顯示

  e.printStackTrace();

  }

  }


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69979119/viewspace-2710192/,如需轉載,請註明出處,否則將追究法律責任。

相關文章