1. 程式人生 > >JAVA——點對面通訊(Socket基於TCP/IP協議)

JAVA——點對面通訊(Socket基於TCP/IP協議)

點對面通訊(Socket基於TCP/IP協議)


1.要求

大多情況下,網路通訊經常需要多個客戶機同一個伺服器進行通訊,如FTP伺服器是同時接收多個客戶訪問的伺服器。本例介紹點對面通訊,即一個伺服器監聽多個客戶端的請求的通訊。

2.原理

  • 建立多客戶連線的Sockets通訊方式是在伺服器端建立客戶連線請求的監聽執行緒,一且客戶端發起請求,則伺服器端建立用於與此客戶端通訊的執行緒和Socket,伺服器把與此客戶的通訊交給此執行緒進行處理。同時,繼續在伺服器指定埠進行監聽,來響應其他客戶的服務請求。
  • 每一個容戶端和眼務器中的執行緒可以認為是單客戶端通訊模式下的客戶和伺服器。一旦Socket和執行緒建立,伺服器主程式會把與某個客戶的通訊完全交給執行緒去處理,並利用相應的 Socket完成與客戶的通訊。

3.語法

  • FaceServer類繼承伺服器端Socket類,因此繼承伺服器端設定埠的方法。在構造方法中運用super(port)方法繼承伺服器端的方法,運用標識為真條件進行迴圈,根據基類的accept()方法建立 Socket物件,迴圈掛起等待客戶端的請求,並根據埠和 Socket物件建立服務執行緒,監聽來自客戶端的資訊。
  • ServerThread類繼承執行緒Thread類,繼承 Thread必須實現其run()方法。 ServerThread類的構造方法中初始化埠和 Socket通訊物件,並運用通訊物件的 getInputStream()方法建立衝讀物件,用來獲取客戶端的資料流。運用通訊物件的 getOutputStream()方法建立寫往客戶端的資料輸出流,其中true引數是資料自動重新整理從快取寫入到指定位置。呼叫star()方法啟動執行緒。
  • ServerThread類中的run()方法根據標識為真條件進行迴圈,按每行從獲得的資料渣中讀取客戶端的內容。當讀取的內容為exit時設定標識為假,退出迴圈。
  • rw2901類的構造方法初始主機和埠並呼叫 connectSocket()方法來呼叫相關主機連線和獲得伺服器端資訊。
  • connectSocket()方法判斷傳入的主機,如果是本機則建立本地Soke連線物件,否則建立遠端Sokcket連線物件。根據封裝從鍵盤輸入的流建立緩衝讀物件,根據 getOutputStream()方法獲得伺服器端寫內容的資料流建立寫資料流。運用標識為條件迴圈讀取鍵盤輸入人的每行字元,如果讀取的資料為exit則退出迴圈。緩衝讀物件的 readLine()方法獲得從伺服器端讀取的資訊。

4.伺服器端

程式碼:

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.io.*;
class FaceServer extends ServerSocket{
	private int port;  //埠
	public FaceServer(int port)throws IOException {
		super(port);  //繼承其類port
		this.port=port;
		System.out.println("伺服器已啟動,監聽埠號為:"+port);
		System.out.println("正在等待客戶連線............");
		try {
			while(true) {
				//迴圈掛起等待客戶的請求
				Socket socketCon = accept();
				new ServerThread(socketCon,port);//建立服務執行緒
			}
		}catch(IOException e) {
				System.out.println("沒有監聽到客戶端的資訊........");
			}finally{
				close();//關閉通訊資源
			}
	}
}
class ServerThread extends Thread{
	//繼承執行緒類實現服務執行緒
	private int port;//埠
	private Socket socketCon;
	private BufferedReader in;
	private PrintWriter out;
	public ServerThread(Socket s,int port)throws IOException{
		this.port =port;
		this.socketCon=s;
		in=new BufferedReader(new InputStreamReader(socketCon.getInputStream(),"gb2312"));//讀取客戶端的資料流
		//獲取寫往客戶端的資料輸出流,true表示自動重新整理
		out = new PrintWriter(socketCon.getOutputStream(),true);
		out.println("伺服器端連線成功.......");//向客戶端傳送連線資訊
		out.println("輸入exit斷開與伺服器的連線");
		start();//啟動執行緒
	}
	public String infoUpperCase(String line) {//處理資訊
		return line.toUpperCase();//將字串大寫
	}
	public void run() {
		try {
			boolean done =false;
			while(!done) {
				String line=in.readLine();//讀取客戶端每行的內容
				if(line==null)
					done=true;
				else {
					//顯示客戶端傳送的內容
					System.out.println("客戶端傳來的內容:"+line);
					String message=infoUpperCase(line);//資訊處理
					//向客戶端傳送資訊
					out.println("從伺服器埠傳送的內容:"+message);
					if(line.trim().equals("exit"))//退出判斷
							done = true;
				}
			}
			System.out.println("客戶端終止傳送資訊.......");
			socketCon.close();//關閉通訊資源
		}catch(Exception e) {  //捕獲異常
			System.out.println("啟動伺服器端出現錯誤"+e.getMessage());
		}
	}
}
public class rw29 {
	//操作socket伺服器端的類
	public static void main(String[] args) {
		// TODO Auto-generated method stu
		try {
			new FaceServer(80); //傳入埠例項化物件
		}catch(Exception e) {  //捕獲異常
			System.out.println("伺服器端進行監聽出現異常:"+e.getMessage());
		}
		
	}

}

5.客戶端

客戶端1程式碼如下:

import java.io.*;
import java.net.*;
class PointClient{
	//Socket點客戶端
	private String host;//IP地址(域名)
	private int port; //埠號
	public PointClient(String host,int port) {
		//帶引數構造方法進行初始化
		this.host=host;
		this.port=port;
		connectSocket();//呼叫連線方法
	}
	public void connectSocket(){//連線方法
		try {
			Socket socketConn;//宣告Socket連線
			//判斷IP地址(域名)
			if(host.equals("localhost")||host.equals("127.0.0.1")) {
				//建立本地連線
				socketConn=new Socket(InetAddress.getLocalHost(),port);
			}else {  //建立遠端連線
				socketConn=new Socket(InetAddress.getByName(host),port);
			}
			BufferedReader stdin=new BufferedReader(new InputStreamReader(System.in));//獲得從鍵盤輸入的流
			PrintWriter out = new PrintWriter(socketConn.getOutputStream(),true);//獲得伺服器寫內容的資料流
			BufferedReader in=new BufferedReader(new InputStreamReader(socketConn.getInputStream()));//獲得接手伺服器傳送內容的緩衝流
			System.out.println("伺服器1資訊:"+in.readLine());//從伺服器獲得資訊
			System.out.println("伺服器1資訊:"+in.readLine());
			System.out.println("請輸入>>");//使用者輸入
			boolean done=false;
			while(!done) {
				String line=stdin.readLine();//獲得從鍵盤輸入的每行字元
				out.println(line); //傳送到服務端
				if(line.equalsIgnoreCase("exit"))//讀到exit則結束迴圈
					done=true;
				String info =in.readLine();//從伺服器讀取字串
				System.out.println("伺服器1資訊:"+info);//顯示從伺服器傳送來的資料
				//使用者輸入
				if(!done)
					System.out.print("請輸入>>");
			}
			socketConn.close();//關閉資源
		}catch(SecurityException e) {//捕獲安全性錯誤時引發的異常
			System.out.println("連線伺服器出現安全問題!");
		}catch(IOException e) { 
			//捕獲IO流異常
			System.out.println("連線伺服器出現I/O錯誤!");
		}
	}
}
public class rw2901 {
    //
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		try {
			new PointClient("localhost",80);//IP地址為本機,埠為8080
		}catch(Exception e){
			//捕獲異常
			System.out.println("測試客戶端連接出錯:"+e.getMessage());
		}
	}

}

與客戶端1原理相同建立客戶端2,在本例中其程式碼檔名為rw2902,在接下來的結果中將會執行此程式。

6.執行結果

伺服器端的輸出如下圖所示:
在這裡插入圖片描述
客戶端1的輸出如下圖所示:
在這裡插入圖片描述
客戶端2的輸出如下圖所示:
在這裡插入圖片描述