目錄
簡介
Java中Socket通訊簡介
單線程一對一伺服器1——>1用戶端
單線程一對一伺服器1<——>1用戶端
多線程一對多伺服器1<——>N用戶端【非聊天室的伺服器通過使用者輸入發送資料】
多線程一對多伺服器1<——>N用戶端【聊天室】
多線程最終伺服器和用戶端整合一體【swing程式】
【多線程】一對多伺服器1<——>N用戶端(非聊天室的伺服器通過使用者輸入發送資料) 對,解決方案,就是發送資料和接受資料不在同一個進程,這樣,這兩個進程互不影響。 解決方案如下:伺服器端和用戶端如下:
當然,若下文要考慮,多個用戶端串連同一個服務端時,所以,應該對每個sockt串連開啟一個線程。但是對於用戶端而言,只要把發送和接受分離即可。所以在用戶端中,可以把發送資料和主進程放在一塊。如:
代碼實現如下: 伺服器端:
package com17.tcp;import java.io.IOException;import java.net.ServerSocket;import java.net.Socket;public class MyServer { public static void main(String[] args) { try { ServerSocket ss=new ServerSocket(30000); while(true) { //此行代碼會阻塞,將一直等待別人的串連 Socket s=ss.accept(); if(s.isConnected()) { System.out.println("一個用戶端串連此伺服器"+s.getInetAddress()); } //每當用戶端串連後啟動一條ServerThread線程為該用戶端服務 new Thread( new ServerThread(s)).start(); //發送資料到用戶端 new Thread(new ServerThread2(s)).start(); } } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } }}
接受資料的並列印的線程:
package com17.tcp;import java.io.BufferedReader;import java.io.IOException;import java.io.InputStreamReader;import java.io.PrintStream;import java.net.Socket;import java.util.Scanner;public class ServerThread implements Runnable { //定義當前線程所處理的socket Socket s=null; //該線程所處理的socket所對應的輸入資料流 BufferedReader br=null; public ServerThread(Socket s) { try { this.s=s; //初始化socket對應的輸入資料流 br=new BufferedReader(new InputStreamReader(s.getInputStream())); } catch(IOException e) { e.printStackTrace(); } } @Override public void run() { try { String content=null; //採用迴圈不斷從Socket中讀取用戶端發送過來的資料 while((content=readFromClient())!=null) { System.out.println("來自用戶端訊息:"+content); } System.out.println("訊息:"+content); PrintStream ps=new PrintStream(s.getOutputStream()); ps.println(br.readLine()); }catch(Exception e) { try { s.close(); }catch(IOException ex) { ex.printStackTrace(); } } } //定義讀取用戶端資料的方法 private String readFromClient() { try { return br.readLine(); } //如果捕捉到異常,表明該socket對應的用戶端已經關閉 catch(IOException e) { e.printStackTrace(); } return null; }}
發送資料到用戶端的線程:
package com17.tcp;import java.io.BufferedReader;import java.io.IOException;import java.io.InputStreamReader;import java.io.PrintStream;import java.net.Socket;public class ServerThread2 implements Runnable { private Socket s; //用來處理髮送資料的 private PrintStream ps=null; public ServerThread2(Socket s) { try { this.s=s; ps=new PrintStream(s.getOutputStream()); } catch(IOException e) { try { s.close(); } catch(IOException ex) { ex.printStackTrace(); } } } @Override public void run() { try { //發送資料到用戶端 String line=null; //不斷讀取鍵盤的輸入 BufferedReader br=new BufferedReader(new InputStreamReader(System.in)); while((line=br.readLine())!=null) { //將使用者的鍵盤輸入內容寫入socket對應的輸出資料流 ps.println(line); } } catch(IOException e) { e.printStackTrace(); } }}
用戶端:
package com17.tcp;import java.io.BufferedReader;import java.io.IOException;import java.io.InputStreamReader;import java.io.PrintStream;import java.net.Socket;//負責讀取使用者的鍵盤輸入,並將使用者輸入的資料寫入socket對應的輸出資料流中//一條負責讀取socket對應輸入資料流中的資料(從伺服器發送過來的資料)//並將這些資料列印輸出。其中負責讀取使用者鍵盤輸入的線程由MyClient負責,也就是由程式的主線程負責。public class MyClient {public static void main(String[] args) {try{ Socket s=new Socket("127.0.0.1",30000); //用戶端啟動clientThread縣城不斷讀取來自伺服器的資料 new Thread(new ClientThread(s)).start(); //擷取該socket對應的輸出資料流 PrintStream ps=new PrintStream(s.getOutputStream()); String line=null; //不斷讀取鍵盤的輸入 BufferedReader br=new BufferedReader(new InputStreamReader(System.in)); while((line=br.readLine())!=null) { //將使用者的鍵盤輸入內容寫入socket對應的輸出資料流 ps.println(line); }}catch(IOException e){e.printStackTrace();} }}
用戶端線程:
package com17.tcp;import java.io.BufferedReader;import java.io.IOException;import java.io.InputStreamReader;import java.net.Socket;public class ClientThread implements Runnable {//該線程負責處理的socketprivate Socket s;//該線程所處理的socket所對應的輸入資料流BufferedReader br=null;public ClientThread(Socket s) throws IOException{this.s=s;br=new BufferedReader(new InputStreamReader(s.getInputStream()));}@Overridepublic void run() { try { String content=null; //不斷讀取socket輸入資料流中的內容,講這些內容列印輸出 while((content=br.readLine())!=null) { System.out.println("來自伺服器端訊息:"+content); } } catch(IOException e) { e.printStackTrace(); }}}
效果如下:
但是我們通過以上的代碼,要實現這種如下:
雖然上述代碼中使用了多線程,每個用戶端請求,都會產生一個讀線程和寫線程。但是也不能完成的要求。 為什麼呢? 主要在於寫的線程。 因為寫的線程,雖然捕捉了發送的socket通訊端,但是寫線程中是時刻監聽使用者輸入System.in。 這樣,當多個用戶端串連伺服器端時,則伺服器端有多個線程監聽使用者輸入System.in。但是令人頭疼的是,伺服器端只有一個dos視窗,而不像用戶端一個dos視窗監聽一個使用者輸入System.in。 訪問中伺服器端結果如下:
所以伺服器想通過使用者輸入形式發送給多個用戶端,這種形式是不可行的。 代碼中,永遠只能發給具有活動的線程。結果不是你想象的那樣,發給你想象的線程。 當然,若是伺服器不是通過監聽使用者輸入則當然可以實現上述的要求的 我們改一下伺服器端:不通過監聽使用者輸入。 伺服器端中,接受線程中,一接受到客戶的資料,則發送資料。 伺服器端,線程
接受線程中修改上述的demo
//採用迴圈不斷從Socket中讀取用戶端發送過來的資料 while((content=readFromClient ())!=null ) { System. out.println( "來自用戶端訊息:" +content); ps.println( "用戶端"+s.getInetAddress()+"訊息已經收入到,訊息為:" +content); }
如下:
【多線程】一對多伺服器1<——>N用戶端(聊天室)
我想通過上述幾個例子,想必對通訊的方式已經瞭解,並且也掌握通訊的原理以及通訊方法。 那我們再說,聊天室通訊,就是類似我們qq群。一人發表了看法,則其他的人都能看到此訊息。 猜想qq實現方式,就是通過伺服器轉寄【僅此個人看法】 那我們這個例子實現方式如:
那這個例子 不在貼代碼,我們說一下思路,在伺服器端有個列表,專門用來儲存用戶端的socket。只要用戶端串連伺服器,就把用戶端的socket添加到伺服器列表中。當接受到任何用戶端資料時,就把資料轉寄給列表中任何一個用戶端socket。 部分代碼如下:
//採用迴圈不斷從Socket中讀取用戶端發送過來的資料 while((content=readFromClient())!= null) { //便利socketList中的每一個socket //將讀到的內容向每個socket發送一次 for(Socket s:MyServer. socketList) { PrintStream ps= new PrintStream(s.getOutputStream()); ps.println(content); } }