Java網路程式設計入門(五)之TCP程式設計——複用Socket連線
如何複用Socket連線?
在前面的示例中,客戶端中建立了一次連線,只發送一次資料就關閉了,這就相當於撥打電話時,電話打通了只對話一次就關閉了,其實更加常用的應該是撥通一次電話以後多次對話,這就是複用客戶端連線。
那 麼如何實現建立一次連線,進行多次資料交換呢?其實很簡單,建立連線以後,將資料交換的邏輯寫到一個迴圈中就可以了。這樣只要迴圈不結束則連線就不會被關 閉。按照這種思路,可以改造一下上面的程式碼,讓該程式可以在建立連線一次以後,傳送三次資料,當然這裡的次數也可以是多次,示例程式碼如下:
package tcp; import java.io.*; import java.net.*; /** * 複用連線的Socket客戶端 * 功能為:傳送字串“Hello”到伺服器端,並打印出伺服器端的反饋 */ public class MulSocketClient { public static void main(String[] args) { Socket socket = null; InputStream is = null; OutputStream os = null; //伺服器端IP地址 String serverIP = "127.0.0.1"; //伺服器端埠號 int port = 10000; //傳送內容 String data[] ={"First","Second","Third"}; try { //建立連線 socket = new Socket(serverIP,port); //初始化流 os = socket.getOutputStream(); is = socket.getInputStream(); byte[] b = new byte[1024]; for(int i = 0;i < data.length;i++){ //傳送資料 os.write(data[i].getBytes()); //接收資料 int n = is.read(b); //輸出反饋資料 System.out.println("伺服器反饋:" + new String(b,0,n)); } } catch (Exception e) { e.printStackTrace(); //列印異常資訊 }finally{ try { //關閉流和連線 is.close(); os.close(); socket.close(); } catch (Exception e2) {} } } }
該示例程式和前面的程式碼相比,將資料交換部分的邏輯寫在一個for迴圈的內容,這樣就可以建立一次連線,依次將data陣列中的資料按照順序傳送給伺服器端了。
如果還是使用前面示例程式碼中的伺服器端程式執行該程式,則該程式的結果是:
java.net.SocketException: Software caused connection abort: recv failed
at java.net.SocketInputStream.socketRead0(Native Method)
at java.net.SocketInputStream.read(SocketInputStream.java:129)
at java.net.SocketInputStream.read(SocketInputStream.java:90)
at tcp.MulSocketClient.main(MulSocketClient.java:30)
伺服器反饋:First
顯然,客戶端在實際執行時出現了異常,出現異常的原因是什麼呢?如果仔細閱讀前面的程式碼,應該還記得前面示例程式碼中的伺服器端是對話一次資料以後就關閉了連線,如果伺服器端程式關閉了,客戶端繼續傳送資料肯定會出現異常,這就是出現該問題的原因。
按照客戶端實現的邏輯,也可以複用伺服器端的連線,實現的原理也是將伺服器端的資料交換邏輯寫在迴圈中即可,按照該種思路改造以後的伺服器端程式碼為:
package tcp;
import java.io.*;
import java.net.*;
/**
* 複用連線的echo伺服器
* 功能:將客戶端傳送的內容反饋給客戶端
*/
public class MulSocketServer {
public static void main(String[] args) {
ServerSocket serverSocket = null;
Socket socket = null;
OutputStream os = null;
InputStream is = null;
//監聽埠號
int port = 10000;
try {
//建立連線
serverSocket = new ServerSocket(port);
System.out.println("伺服器已啟動:");
//獲得連線
socket = serverSocket.accept();
//初始化流
is = socket.getInputStream();
os = socket.getOutputStream();
byte[] b = new byte[1024];
for(int i = 0;i < 3;i++){
int n = is.read(b);
//輸出
System.out.println("客戶端傳送內容為:" + new String(b,0,n));
//向客戶端傳送反饋內容
os.write(b, 0, n);
}
} catch (Exception e) {
e.printStackTrace();
}finally{
try{
//關閉流和連線
os.close();
is.close();
socket.close();
serverSocket.close();
}catch(Exception e){}
}
}
}
在該示例程式碼中,也將資料傳送和接收的邏輯寫在了一個for迴圈內部,只是在實現時硬性的將迴圈次數規定成了3次,這樣程式碼雖然比較簡單,但是通用性比較差。
以該伺服器端程式碼實現為基礎執行前面的客戶端程式時,客戶端的輸出為:
伺服器反饋:First
伺服器反饋:Second
伺服器反饋:Third
伺服器端程式的輸出結果為:
伺服器已啟動:
客戶端傳送內容為:First
客戶端傳送內容為:Second
客戶端傳送內容為:Third
在該程式中,比較明顯的體現出了“請求-響應”模型,也就是在客戶端發起連線以後,首先發送字串“First”給伺服器端,伺服器端輸出客戶端傳送的內容“First”,然後將客戶端傳送的內容再反饋給客戶端,這樣客戶端也輸出伺服器反饋“First”,這樣就完成了客戶端和伺服器端的一次對話,緊接著客戶端傳送“Second”給伺服器端,服務端輸出“Second”,然後將“Second”再反饋給客戶端,客戶端再輸出“Second”,從而完成第二次會話,第三次會話的過程和這個一樣。在這個過程中,每次都是客戶端程式首先發送資料給伺服器端,伺服器接收資料以後,將結果反饋給客戶端,客戶端接收到伺服器端的反饋,從而完成一次通訊過程。
在該示例中,雖然解決了多次傳送的問題,但是客戶端和伺服器端的次數控制還不夠靈活,如果客戶端的次數不固定怎麼辦呢?是否可以使用某個特殊的字串,例如quit,表示客戶端退出呢,這就涉及到網路協議的內容了,會在後續的網路應用示例部分詳細介紹。下面開始介紹另外一個網路程式設計的突出問題。