1. 程式人生 > >基於TCP協議的通訊(基於Java語言)

基於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作為客戶端和服務端通訊的虛擬鏈路的連線。說明了如何獲取輸入,傳送請求,以及獲取響應的過程。