基於TCP協議的通訊(基於Java語言)
端與端通訊,經常由客戶端和服務端兩者組成,其中客戶端傳送請求給服務端,而服務端則響應請求。這兩者的通訊可以通過Socket來實現兩端的資料傳輸。其中Java自帶的Socket類可以建立客戶端socket,而ServerSocket可以建立一個服務端Socket。
1.服務端-客戶端的通訊建立以及工作流程
由於Socket是兩臺主機之間的一個連線,Socket簡化了網路的底層細節,比如傳輸資料過程中的錯誤檢測、包大小、包分解、包重傳、網路地址等,簡化了網路程式設計的部分工作。以下分別講解服務端、客戶端建立以及工作流程
1.1.服務端Socket的建立流程
1.使用一個ServerSocket()建構函式在一個特定埠建立一個新的ServerSocket。
2.ServerSocket使用其accept()方法監聽這個埠的入站連線。accept()方法會一直阻塞,直到一個客戶端嘗試建立連線,此時accept(0將返回一個連線客戶端和伺服器的socket物件。
3.根據伺服器的型別,會呼叫socket的getInputStream()方法或getOutputStream()方法,或者兩者都呼叫,以獲得與客戶端通訊的輸入和輸出流。
4.服務端和客戶端根據已協商好的協議進行互動,直到關閉連線。
5.服務端關閉連線
6.伺服器返回步驟2,等待下一次連線。
1.2.客戶端Socket的建立流程
1.使用一個Socket建構函式建立一個連線遠端ip,特定埠的主機的socket。與服務端建立通訊。
2.呼叫socket的getInputStream()方法和getOutputStream()方法,以獲得與服務端通訊的輸入輸出流。
3.通過將輸出流寫入socket,向服務端傳送請求;並通過獲取socket的輸入流返回服務端的響應。
4.關閉socket。
2.通訊過程中常用的一些類
Java中JDK提供了許多API實現網路程式設計,以下便是幾個常用的類。
2.1.Socket類
一般我們都是通過Socket類的建構函式建立Socket,而一個socket物件需要一個遠端ip和對應埠兩個引數才能確定。因此有如下建構函式:
Socket(InetAddress/String remoteAddress,int port):建立連線到指定遠端主機、遠端埠的socket,該構造器沒有指定本地地址、本地埠,預設使用本地主機的IP地址,預設使用系統動態分配的埠。
Socket(InetAddress/String remoteAddress,int port,InetAddress localAddr,int localPort):建立連線到指定遠端主機、遠端埠的socket,並指定本地IP地址和本地埠,適用於本地主機有多個IP地址的情形。
2.2.ServerSocket類
ServerSocket類用於服務端建立ServerSocket物件,建立監聽連線。具有以下建構函式:
ServerSocket(int port):用指定的埠port來建立一個ServerSocket。該埠應該有一個有效的埠整數值,即0~65535.
ServerSocket(int port,int backlog):增加一個用來改變連線佇列長度的引數backlog。
ServerSocket(int port,int backlog,InetAddress localAddr):在機器存在多個IP地址的情況下,允許通過localAddr引數來指定將ServerSocket繫結到指定的IP地址。
一般服務端與客戶端相互連線之後,需要用accept方法用於監聽來自客戶端的Socket連線。如果沒有連線,將一直處於等待狀態,如果有連線,則返回socket物件。
Socket accept():如果接收到一個客戶端Socket的連線請求,該方法將返回一個與客戶端Socket對應的Socket,否則一直處於等待狀態,執行緒也被阻塞。
3.用例
該例子的目標是在服務端實現字串的翻轉。即客戶端通過在控制檯寫入一行字串,然後傳送請求給服務端,服務端進行翻轉,然後返回給客戶端輸出。由於客戶端與服務端都在本地,所以客戶端連線的遠端ip為本地ip即可。
客戶端:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.Socket;
import java.net.UnknownHostException;
public class Client {
public static BufferedReader bufferedReader = null;
public static PrintStream pStream = null;
public static BufferedReader keyIn= null;//用於鍵盤輸入
public static void main(String[] args) throws UnknownHostException, IOException {
// TODO Auto-generated method stub
keyIn = new BufferedReader(new InputStreamReader(System.in));
//建立socket連線,連線對應ip指定port
Socket socket = new Socket("127.0.0.1", 3002);
String in = keyIn.readLine();//獲取鍵盤輸入
pStream = new PrintStream(socket.getOutputStream());
pStream.println(in);//將請求寫入socket傳送
bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));//獲取輸入流,即獲取服務端響應。
String anString = bufferedReader.readLine();
if(anString==null) {
System.out.println("網路通訊出現故障");
}
else {
System.out.println("服務端響應結果:"+anString);
}
//將資源關閉
pStream.close();
bufferedReader.close();
keyIn.close();
socket.close();
}
}
服務端:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;
public class Server {
public static BufferedReader bufferedReader = null;
public static PrintStream pStream = null;
public static String reverse(String line) {
return new StringBuilder(line).reverse().toString();
}
public static void main(String[] args) throws Exception{
// TODO Auto-generated method stub
//建立監聽的serversocket
ServerSocket serverSocket = new ServerSocket(3002);
//利用死迴圈來接收客戶端的請求
while(true) {
Socket socket = serverSocket.accept();
//獲取客戶端請求
bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
//輸出流,用於寫入服務端響應
pStream = new PrintStream(socket.getOutputStream());
String line = bufferedReader.readLine();
String out = null;
if(line!=null) {
out = reverse(line);
}
if(out==null) {
pStream.println("該請求無法實現!");
}
else {
pStream.println("請求響應成功:"+out);
}
//關閉資源
pStream.close();
bufferedReader.close();
socket.close();
}
}
}
先啟動服務端,開啟監聽,等待客戶端請求傳送,然後啟動服務端,並在控制檯輸入一行字串,便可獲取響應。
這個簡單例子說明了socket作為客戶端和服務端通訊的虛擬鏈路的連線。說明了如何獲取輸入,傳送請求,以及獲取響應的過程。