Socket即套接字(插座),是一種軟件形式的抽象,表示兩臺機器間一個連接的“終端”。想象兩臺機器間的連接是一條虛擬的“線纜”,線纜的每一段都插入對應機器的“插座”或者“套接字”里。Socket讓我們盡可能不必知道連接的細節,而直接使用它來建立連接。
對于TCP連接有兩個基本的套接字類,ServerSocket和Socket??蛻舳擞肧ocket初始一個連接,服務端用ServerSocket中的accept方法“偵聽”連接,偵聽到了就返回一個Socket,從此得到“Socket-Socket”連接,之后可以用同樣的方式對待連接的兩端。此時用Socket中getInputStream()、getOutputStream()方法產生對應的InputStream和OutputStream對象(這些數據流必須封裝到緩沖區內),再使用轉換器轉換為Reader和Writer。
TCP服務端的工作:
1.創建一個ServerSocket類,調用accept方法等待建立一個連接
2.用那個連接產生的Socket創建一個InputStream以及OutputStream
3.將InputStream中讀到的內容反饋給outputStream,直到收到“END”內容時停止,關閉連接
TCP服務端代碼如下:
public class TcpServer { public static final int PORT = 8001; public static void main(String[] args) throws Exception { ServerSocket ss = new ServerSocket(PORT); System.out.PRintln("listening..."); try{ //blocks until a connection occurs Socket s = ss.accept(); try{ BufferedReader in = new BufferedReader(new InputStreamReader(s.getInputStream())); PrintWriter out = new PrintWriter(new OutputStreamWriter(s.getOutputStream()),true); String str = "welcome!"; out.println(str); while(true){ str = in.readLine(); if(str.equals("END")) break; System.out.println("Received:" + str); out.println("Server Received:" + str); } //close the tow sockets } finally{s.close();} } finally{ss.close();} }}每次寫完服務端,都可以用telnet進行測試:telent 127.0.0.1 8001
客戶端代碼如下:
public class client { public static void main(String[] args) throws Exception { Socket s = new Socket("127.0.0.1", 8001); try{ BufferedReader netin = new BufferedReader(new InputStreamReader(s.getInputStream())); PrintWriter out = new PrintWriter(s.getOutputStream(),true); BufferedReader keyin = new BufferedReader(new InputStreamReader(System.in)); System.out.println(netin.readLine()); while(true) { String strLine = keyin.readLine(); out.println(strLine); if(strLine.equalsIgnoreCase("END")) { break; } System.out.println(netin.readLine()); } }finally{ System.out.println("closing..."); s.close(); } }}服務器程序先啟動運行,客戶程序才能連接上TCP服務器
這里對資源的清除用以下方式:
1.假如ServerSocket構建器失敗,則main()擲出一個Exception;
2.假如ServerSocket構建器成功,其他所有方法到try-finally里尋求保護,確保無論發生什么情況,ServerSocket都能正確關閉;
3.Socket同理。若accept()成功,則后續語句必須進入try-finally塊,以保障在后續語句失敗時,Socket能得到正確清除。
這里的PrintWriter ,它有一個過載的構建器,能獲取第二個參數—— 一個布爾值標志,指向是否在每一次 println()結束的時候自動刷新輸出(但不適用于 print()語句)。每次寫入了輸出內容后(寫進out),它的緩沖區必須刷新,使信息能正式通過網絡傳遞出去。對目前這個例子來說,刷新顯得尤為重要,因為客戶和服務器在采取下一步操作之前都要等待一行文本內容的到達。若刷新沒有發生,那么信息不會進入網絡,除非緩沖區滿(溢出),這會為本例帶來許多問題。
服務器服務多個客戶
客戶端代碼可以不變
服務器能夠與多個客戶端的連接請求,需要循環調用ServerSocket.accept方法,得到Socket類;
會話過程不能相互影響,會話要在一個獨立的線程運行;一個線程服務與一個服務器端的Socket對象相關聯
服務器段代碼如下:public class Servicer implements Runnable { private Socket s; BufferedReader in; PrintWriter out; public Servicer (Socket s) throws IOException{ this.s = s; } public void run() { try{ in = new BufferedReader(new InputStreamReader(s.getInputStream())); out = new PrintWriter(s.getOutputStream(),true); out.println("welcom!"); while(true){ String strLine = in.readLine(); if(strLine.compareToIgnoreCase("END")==0) break; System.out.println("received:" + strLine); //將收到的信息翻轉順序,發送給客戶端 StringBuffer result = new StringBuffer(strLine).reverse(); out.println(strLine + "---"+result.toString()); } }catch (Exception e){ e.printStackTrace(); } finally{ out.println("closing connection"); try { s.close(); } catch (IOException e) { e.printStackTrace(); } } }}以及public class MultiTcpServer { static final int PORT = 8003; public static void main(String[] args) throws Exception { ServerSocket ss = new ServerSocket(PORT); System.out.println("Server listening..."); try{ while(true){ Socket s = ss.accept(); try{ new Thread(new Servicer(s)).start(); }catch (Exception e){ s.close(); } } }finally { ss.close(); } }}這里,Abd{backspace}c 顯示:abc 翻轉后c{backspace}dba 顯示dba
新聞熱點
疑難解答