HTTP通信中Client發送的每次請求都需要Server回送響應,在請求結束后,Client會主動釋放連接。從建立連接到隔壁連接的過程成為一次連接。要保持Client程序的在線狀態,需要不斷地向Server發送請求。通常的做法是,即使不需要獲取任何數據,Client也保持每隔一段固定的時間向Server發送一次保持連接的請求,Server在收到該請求后對Client進行回復,表明知道Client在線。若Server長時間無法收到Client的請求,則認為Client下線,若Client長時間無法收到Server的回復,則認為網絡已經斷開。
Socket(套接字)用於描述IP地址和端口。App常常通過Socket向網絡發出請求或者應答網絡請求。Socket是支持TCP/IP協議的網絡通信的基本操作單元,是網絡通信過程中端點的抽象表示,包含進行網絡通信必需的5種信息:網絡協議、本地IP地址、本地端口、遠程IP地址、遠程端口。
Socket有兩種傳輸模式:面向連接和無連接。面向連接的Socket操作就像一部電話,必須建立一個連接。所有的數據達到時的順序和它們發送時的順序是一樣的。無連接的Socket操作就像一個郵件投遞,多個郵件達到時的順序可能和發送的順序不一樣。到底用哪種模式是由應用程序的需要決定。如果可靠性更重要,用面向連接的模式會更好。確保數據的有序性和正確性需要額外的操作,這會帶來內存消耗,降低系統的效率。無連接的操作使用數據報協議。一個數據報是一個獨立的單元,它包含了這次投遞的所有信息(目的地址、要發送的內容)。這種模式下的Socket不需要連接目的Socket,它只是簡單地投出數據報。無連接的操作時快速的和高效的,但數據安全性不佳。面向連接的操作使用TCP協議。面向連接的Socket必須在發送數據之前和目的地Socket取得連接。一旦建立了連接,Socket就可以使用一個流接口來進行打開、讀/寫、關閉操作。所有發送的信息都會在另一端以同樣的順序接收。面向連接比無連接效率更低,但是數據更安全。
java.net 包中提供Socket和ServerSocket表示雙向連接的Client和Server。
在選擇端口時必須小心。每個端口提供一種特定的服務,只有給出正確的端口,才能獲得相應的服務。端口號0~1023為系統保留。例如80是http服務的,21是telnet服務的,23是ftp服務的。我們在選擇端口號時,最好選擇一個大於1023的數,防止發生沖突。在創建Socket或ServerSocket時,如果產生錯誤,將會拋出IOException。
要想在Client使用Socket來與一個Server通信,就必須在Client創建一個Socket,指定ServerIP地址和端口。
例如:Socket socket = new Socket(“192.168.1.110”, 5555);
在Server創建ServerSocket,指定監聽的端口。
例如:ServerSocket serverSocket = new ServerSocket(5555);
實際應用中ServerSocket總是不停地循環調用accept()方法,一旦收到請求就會創建線程來處理和響應。accept()是一個阻塞方法,接收到請求后會返回一個Socket來與Client進行通信。
Socket 提供了getInputStream()和getOutputStream()用來得到輸入流和輸出流進行讀寫操作,這兩個方法分別返回InputStream和OutputStream。為了方便讀寫,我們常常在InputStream和OutputStream基礎上進行包裝得到DataInputStream, DataOutputStream, PrintStream, InputStreamReader, OutputStreamWriter, printWriter等。
示例代碼:
PrintStream printStream = new PrintStream(new BufferedOutputStream(socket.getOutputStream()));
PrintWriter printWriter = new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream(), true)));
printWriter.println(String msg);
DataInputStream dis = new DataInputStream(socket.getInputStream());
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String line = br.readLine();
在關閉socket之前,應將與其有關的stream全部關閉,以釋放所有的資源。