先來看一段單線程的原始代碼(代碼中有詳細的注釋):
伺服器(TCPServer.java):
import java.net.*;import java.io.*;public class TCPServer{ public static void main(String[] args) throws Exception{ ServerSocket ss = new ServerSocket(5566); //建立一個Socket伺服器,監聽5566連接埠 int i=0; //利用死迴圈不停的監聽連接埠 while(true){ Socket s = ss.accept(); //利用Socket伺服器的accept()方法擷取用戶端Socket對象。 i++; System.out.println("第" + i +"個用戶端成功串連。"); DataInputStream dis = new DataInputStream(s.getInputStream()); //擷取用戶端Socket對象的輸入資料流,並在外邊加一層DataInputStream管道,目的是方便讀取資料 System.out.println(dis.readUTF()); //讀出流中的資料,DataInputStream對象的readUTF()方法可以讀出流中的資料,而且支援中文 dis.close(); //關閉管道串連 s.close(); //關閉Socket串連 } }}
用戶端(TCPClient.java):
import java.net.*;import java.io.*;public class TCPClient{ public static void main(String[] args) throws Exception{ Socket s = new Socket("192.168.24.177",5566); //建立一個Socket對象,串連IP地址為192.168.24.177的伺服器的5566連接埠 DataOutputStream dos = new DataOutputStream(s.getOutputStream()); //擷取Socket對象的輸出資料流,並且在外邊包一層DataOutputStream管道,方便輸出資料 dos.writeUTF("用戶端訊息"); //DataOutputStream對象的writeUTF()方法可以輸出資料,並且支援中文 dos.flush(); //確保所有資料都已經輸出 dos.close(); //關閉輸出資料流 s.close(); //關閉Socket串連 }}
以上代碼利用Socket對象和ServerSocket對象進行簡單的網路互動,即用戶端通過DataOutputStream對象的writeUTF()方法向伺服器發送訊息,伺服器利用DataInputStream對象的readUTF()方法讀出資料。
看上去挺好,但ServerSocket對象的accept()方法是阻塞的方法,它會一直等待,直到有用戶端串連。
同理,DataInputStream對象的readUTF()方法也是阻塞的方法,它也會一直等待,直到用戶端調用writeUTF()方法。
因此,假如某個用戶端成功串連伺服器,但是遲遲不調用writeUTF()方法發送資料,伺服器就要一直等待,直到用戶端調用writeUTF()方法為止,此期間整個伺服器是阻塞的,無法再接受其他用戶端串連,顯然這不符合實際情況。
要解決這個問題,當然要用多線程。
如果每個用戶端都專屬一個線程,讓readUTF()方法阻塞用戶端專屬的線程,而不去阻塞伺服器主線程,這樣伺服器就可以同時接受多個用戶端串連,而不用考慮用戶端何時調用writeUTF()方法發送資料。代碼如下:
伺服器(TCPServer.java):
import java.net.*;import java.io.*;public class TCPServer{ public static void main(String[] args) throws Exception{ ServerSocket ss = new ServerSocket(5566); //建立一個Socket伺服器,監聽5566連接埠 int i=0; //利用死迴圈不停的監聽連接埠 while(true){ Socket s = ss.accept();//利用Socket伺服器的accept()方法擷取用戶端Socket對象。 i++; System.out.println("第" + i +"個用戶端成功串連。"); Client c = new Client(i,s); //建立用戶端處理線程對象 Thread t =new Thread(c); //建立用戶端處理線程 t.start(); //啟動線程 } }}//用戶端處理線程類(實現Runnable介面)class Client implements Runnable{ int clientIndex = 0; //儲存用戶端id Socket s = null; //儲存用戶端Socket對象 Client(int i,Socket s){ clientIndex = i; this.s = s; } public void run(){ //列印出用戶端資料 try{ DataInputStream dis = new DataInputStream(s.getInputStream()); System.out.println("第" + clientIndex + "個用戶端發出訊息:" + dis.readUTF()); dis.close(); s.close(); } catch(Exception e) {} }}
用戶端(TCPClient.java):
import java.net.*;import java.io.*;public class TCPClient{ public static void main(String[] args) throws Exception{ Socket s = new Socket("192.168.24.177",5566); //建立一個Socket對象,串連IP地址為192.168.24.177的伺服器的5566連接埠 DataOutputStream dos = new DataOutputStream(s.getOutputStream()); //擷取Socket對象的輸出資料流,並且在外邊包一層DataOutputStream管道,方便輸出資料 Thread.sleep((int)(Math.random()*3000)); //讓用戶端不定時向伺服器發送訊息 dos.writeUTF("用戶端訊息"); //DataOutputStream對象的writeUTF()方法可以輸出資料,並且支援中文 dos.flush(); //確保所有資料都已經輸出 dos.close(); //關閉輸出資料流 s.close(); //關閉Socket串連 }}
運行結果如下(參考結果,不一定相同。):
明顯看出第2、3、4用戶端都沒有向伺服器端發出訊息,但都成功串連,而且第2、3、4用戶端向伺服器發出訊息也沒有順序。
通過多線程,實現了多個用戶端同時串連伺服器,並且伺服器能即時處理多個用戶端發出的訊息。
以上僅僅是作為初學者的一些想法,僅供參考。