摘要:單個請求范圍之外的異常可能會關閉服務器。客戶端可能超時或崩潰,用戶可能取消事務,網絡可能在流量高峰期間癱瘓,黑客可能發動拒絕服務攻擊。如果默認長度不夠大,一些的構造函數還允許改變這個隊列的長度,不能不能超過操作系統支持的最大值。
ServerSocket的生命周期
一個ServerSocket的基本生命周期:
1)使用一個ServerSocket構造函數在一個特定端口創建一個新的ServerSocket
2)ServerSocket使用accept方法監聽這個端口的入站連接,accept方法會一直阻塞,直到一個客戶端嘗試建立連接,此時accept將返回一個連接客戶端和服務器的Socket對象
3)根據服務器類型,會調用Socket的getInputSteam或getOutputStream方法,或者兩個方法都調用,以獲取與客戶端通信的輸入和輸出流
4)服務器和客戶端根據已協商的協議交互,直到要關閉連接
5)服務器或客戶端(或二者)關閉連接
6)服務器返回到步驟2,等待下一次連接
有兩類異常,一類異常可能關閉服務器并記錄一個錯誤信息,另一類異常只關閉活動連接,區分這兩類異常非常重要。某個特性連接范圍內的異常會關閉這個連接,但是不會影響其他異常或者關閉服務器。
單個請求范圍之外的異常可能會關閉服務器。
結束處理時,一定要關閉Socket,客戶端不能依賴連接的另一端關閉Socket,對于服務器尤其如此。客戶端可能超時或崩潰,用戶可能取消事務,網絡可能在流量高峰期間癱瘓,黑客可能發動拒絕服務攻擊。出于諸如此類的眾多原因,你不能依賴于客戶端關閉Socket,即使協議有這個要求也不能完全相信客戶端一定會關閉Socket。
請求隊列操作系統把指向某個特定端口的入站連接請求存儲在一個先進先出的隊列中,默認地,Java將這個隊列的長度設置為50,但不同的操作系統會有所不同。FreeBSD默認最大隊列長度為128。在這些系統中,Java服務器socket的隊列長度將是操作系統所允許的最大值(小于等于50)。隊列中填入的未處理連接達到最大容量時,主機會拒絕這個端口上額外的連接,直到隊列騰出新的位置出來為止。很多客戶端在首次連接被拒絕后還會多次嘗試建立連接。
如果默認長度不夠大,一些ServerSocket的構造函數還允許改變這個隊列的長度,不能不能超過操作系統支持的最大值。
package network.serversocket; import java.net.*; import java.io.*; import java.util.Date; public class DaytimeServer { public final static int PORT = 13; public static void main(String[] args) { try (ServerSocket server = new ServerSocket(PORT)) { while (true) { try (Socket connection = server.accept()) { Writer out = new OutputStreamWriter(connection.getOutputStream()); Date now = new Date(); out.write(now.toString() +" "); out.flush(); connection.close(); } catch (IOException ex) {} } } catch (IOException ex) { System.err.println(ex); } } }
不管怎樣,我們都希望能夠比新連接到來的速度更快地清空隊列。
每個連接對應一個線程解決方法是為每個連接提供它自己的一個線程,與接入站連接放入隊列的那個線程分開。生成一個線程來處理每個入站的連接,這樣可以防止一個慢客戶端阻塞所有其他客戶端,這種是“每個連接對應一個線程”的設計。
package network.serversocket; import java.net.*; import java.io.*; import java.util.Date; public class ThreadPerConnectionDemo { public final static int PORT = 13; public static void main(String[] args) { try (ServerSocket server = new ServerSocket(PORT)) { while (true) { try { Socket connection = server.accept(); Thread task = new DaytimeThread(connection); task.start(); } catch (IOException ex) {} } } catch (IOException ex) { System.err.println("Couldn"t start server"); } } private static class DaytimeThread extends Thread { private Socket connection; DaytimeThread(Socket connection) { this.connection = connection; } @Override public void run() { try { Writer out = new OutputStreamWriter(connection.getOutputStream()); Date now = new Date(); out.write(now.toString() +" "); out.flush(); } catch (IOException ex) { System.err.println(ex); } finally { try { connection.close(); } catch (IOException e) { // ignore; } } } } }連接池的版本:
package network.serversocket; import java.io.*; import java.net.*; import java.util.*; import java.util.concurrent.*; public class PooledDaytimeServer { public final static int PORT = 13; public static void main(String[] args) { ExecutorService pool = Executors.newFixedThreadPool(50); try (ServerSocket server = new ServerSocket(PORT)) { while (true) { try { Socket connection = server.accept(); Callable優雅地關閉sockettask = new DaytimeTask(connection); pool.submit(task); } catch (IOException ex) {} } } catch (IOException ex) { System.err.println("Couldn"t start server"); } } private static class DaytimeTask implements Callable { private Socket connection; DaytimeTask(Socket connection) { this.connection = connection; } @Override public Void call() { try { Writer out = new OutputStreamWriter(connection.getOutputStream()); Date now = new Date(); out.write(now.toString() +" "); out.flush(); } catch (IOException ex) { System.err.println(ex); } finally { try { connection.close(); } catch (IOException e) { // ignore; } } return null; } } }
程序員通常會在一個try-finally塊中采用close-if-not-null模式了來關閉Socket,可以使用無參構造器來稍加改進,無參構造器不會拋出任何異常,也不綁定到一個端口,因此構造放在try外邊,finally里頭直接close。
ServerSocket server = new ServerSocket(); try { SocketAddress address = new InetSocketAddress(port); server.bind(address); // ... work with the server socket } finally { try { server.close(); } catch (IOException ex) { // ignore } }
或者直接采用Java7的AutoCloseable模式:
try (ServerSocket server = new ServerSocket(port)) { // ... work with the server socket }catch (IOException ex) { logger.log(Level.SEVERE, "Could not start server", ex); }
如果要記錄異常信息,可以catch下異常,否則可以不用catch。
要測試ServerSocket是否打開:
public static boolean isOpen(ServerSocket ss) { return ss.isBound() && !ss.isClosed(); }
isBound方法有歧義,它是表示是否曾經綁定到某個端口,即使它目前已經關閉,isBound仍然會返回true,因此還需要判斷是否是close。
構建ServerSocketServerSocket httpd = new ServerSocket(80, 50);
這里指定了綁定80端口,同時隊列一次最多可保存50個入站連接,如果試圖設置超過操作系統的最大隊列長度,則會使用最大隊列長度。
ServerSocket server = new ServerSocket(0);
0表示監聽未指定的端口,操作系統會為你選擇可用的端口,稱之為匿名端口。
如果沒有綁定任何端口,則getLocalPort返回-1。
ServerSocket選項 1)SO_TIMEOUT為0的話,表示永不超時,一般服務端都設置為永不超時。客戶端則要指定。
設置該值必須在調用accppt之前。
確定是否允許一個新的Socket綁定到之前使用過的一個端口,而此時可能還有一些發送到原來Socket的數據正在網絡上傳輸。
3)SO_RCVBUF設置了服務器Socket接受的客戶端Socket默認接收緩沖區的大小。
setReceiveBufferSize來設置,必須在accept之前設置。
為TCP定義了4個通用的業務流模型:
A、低成本
B、高可靠性
C、最大吞吐量
D、最小延遲
可以使用setPerformancePreferences方法描述
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/65945.html
SSL,Secure Sockets Layer,安全Socket層TLS,Transport Layer Security,傳輸層安全協議 package network.secure; import java.io.*; import javax.net.ssl.*; public class HTTPSClient { public static void main(Strin...
摘要:上,數據按有限大小的包傳輸,這些包成為數據報,每個數據報包含一個首部和一個有效載荷。不過,由于數據報長度有限,通常必須將數據分解為多個包,再在目的地重新組合。這兩個構造函數,在返回之前會與遠程主機建立一個活動的網絡連接。 Internet上,數據按有限大小的包傳輸,這些包成為數據報(datagram),每個數據報包含一個首部(header)和一個有效載荷(payload)。首部包含包發...
閱讀 5076·2023-04-25 19:30
閱讀 2173·2023-04-25 15:09
閱讀 2618·2021-11-16 11:45
閱讀 2171·2021-11-15 18:07
閱讀 1458·2021-11-11 17:22
閱讀 2115·2021-11-04 16:06
閱讀 3576·2021-10-20 13:47
閱讀 3036·2021-09-22 16:03