JAVA通訊(二)——實現客戶機和伺服器通訊

Alexwym發表於2018-07-22

前面一篇部落格我們簡單地理清了JAVA建立伺服器,以及客戶機連線伺服器的流程,今天我們繼續往下學習,實現客戶機和伺服器的通訊。由於基本概念我們已經在前一篇部落格說清楚了,這裡我們就不再進行贅述。(不清楚的可以先閱讀下我的前一篇部落格JAVA通訊(一)——輸入資料到客戶端

具體程式碼

package communicatetest1;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;

//建立一個測試類
public class communicateTest {

	public static void main(String[] args) throws IOException {
		//建立一個伺服器物件
		ServerSocket server=new ServerSocket(9005);
		//輸出伺服器的埠資訊
		System.out.println("伺服器建立成功,埠號為:"+server.getLocalPort());
		while(true) {
			//建立一個Socket物件來連線,這裡不需要新建一個物件,它只要直接引用server即可
			Socket socket=server.accept();
			
			//利用socket來接收輸出輸入流的資料
			//這裡有一點需要注意,OutputStream是向客戶機輸出資訊,而InputStream是讀取客戶機傳送過來的資訊
			OutputStream output=socket.getOutputStream();
			InputStream input=socket.getInputStream();
			
			//接著開始進行通訊測試
			String s="Hello,Welcome to My ServerSocket!";
			//這條訊息是當客戶機連線上我們建立的伺服器時,伺服器傳送給客戶機的一條資訊
			//也就是我們要向客戶機傳送訊息,那麼我們應該用的是OutputStream
			
			//首先我們要先將傳送資訊轉化為byte型別,因為輸出流的寫入方法write()中的引數是byte型別
			byte[] dataout=s.getBytes();
			//接著呼叫輸出流的寫入方法,把資訊傳送給客戶機
			output.write(dataout);
			//然後讓這條資訊在命令列中顯示出來,以便我們檢測資訊是否真的已經被髮送出去
			output.flush();
			
			//接收每一個來自客戶機的字元
			int ascii=input.read();
			//如果接收到回車字元就結束迴圈
			while(ascii!=13) {
				char accept=(char) ascii;
				//輸出客戶機發出的,伺服器收到的每一個字元
				System.out.print(accept);
				ascii=input.read();
			}
			//關閉連線
			socket.close();
		}
	}
	
}

我們在前一篇部落格程式碼的基礎上再加上一段程式碼。利用InputStream的read()方法來實現把資訊傳送到伺服器。需要注意的是read()方法得到的是字元的ascii碼,因此我們要用int的變數接收,再把它轉化為char型字元。

二、執行命令列如下

輸入telnet localhost 9006 後,連線如下

我們嘗試輸入一些字元,會看到如下介面

每次在命令列中輸入一個字元,伺服器就會接收到一個字元。而不是我們想象中的寫完一句話,按回車後才會傳送給伺服器。但是這種傳送形式不是很適用於我們的日常生活。而且它是不能進行刪除的。因為我們每寫完一個字元,它就馬上被髮送到伺服器了。當你按下刪除鍵時,它會被識別成一個字元傳送給伺服器。

三、設定讀取傳送規則

如果要實現客戶機和伺服器的正常通訊我們就必須為它們設定一定的讀取規則,而不能一個個字元地讀取。比如我們可以規定Enter鍵為一條完整訊息的結束。每次讀取到Enter鍵時伺服器再一次性地接收訊息。當功能逐漸增多時,我們最好把它封裝成一個類,而不要總是在主函式裡面測試。程式碼修改如下:

package communicatetest2;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;

//定義一個通訊類
public class ServerChat {
	OutputStream output;//輸出流
	InputStream input;//輸入流
	ServerSocket server;//設定一個伺服器物件屬性
	
	//定義一個建立伺服器的方法
	private void setUpServer(int port) throws IOException {
		//將輸入的埠設定為伺服器
		server=new ServerSocket(port);
		//輸出當前伺服器的埠號
		System.out.println("伺服器建立成功,埠號:"+server.getLocalPort());
		//定義一個作為中介的接收物件Socket
		Socket socket=server.accept();
		
		//為輸入輸出流賦值
		output=socket.getOutputStream();
		input=socket.getInputStream();
		
		//開始通訊
		//傳送資訊給客戶機
		String outS="Hello,welcome to my ServerSocket!\r\n";
		out(outS);
		
		//傳送資訊給伺服器
		ReadString();
	}	
	
	
	
	//定義一個輸出資訊到客戶機的方法
	private void out(String outS) throws IOException {
		//將字串轉化為byte陣列
		byte[] dataout=outS.getBytes();
		//呼叫write()將資訊傳送客戶機
		output.write(dataout);
		//強制輸出到命令列的介面中
		output.flush();
	}

	//定義一個傳送字串給伺服器的方法
	public void ReadString() throws IOException {
		String inputS="";
		//讀取第一個字元
		int AsciiNumber=input.read();
		while(AsciiNumber!=13) {
			//將ascii碼轉化為相應的char型字元
			inputS+=(char)AsciiNumber;
			//接收下一個字元
			AsciiNumber=input.read();
		}
		System.out.println(inputS);
                output.close();
	}
	
	//主函式入口
	public static void main(String[] args) throws IOException {
		//建立一個通訊類的物件
		ServerChat server=new ServerChat();
		server.setUpServer(9009);
	}
}

命令列執行結果如下

如此一下我們就可以實現回車後再進行顯示了。但是實質上他還是一個一個字元地傳輸,只不過當伺服器接收到字元時,我們不立即將其顯示出來,而是等接受完整句話,我們才把它顯示出來。但是它現在還有一個問題,那就是隻能傳送一句話,這就很難受了。誰說話只說一句呀,因此我們還要對它進行改進。當我們接收完一句話時不要立即關閉客戶端和伺服器的連線,而是等到使用者輸入“bye”的時候再斷開連線。相關部分的程式碼更改如下:

	public void ReadString() throws IOException {
		String inputS="";
		while(!inputS.equals("bye")) {
			//讀取第一個字元
			int AsciiNumber=input.read();
			while(AsciiNumber!=13) {
				//將ascii碼轉化為相應的char型字元
				inputS+=(char)AsciiNumber;
				//接收下一個字元
				AsciiNumber=input.read();
			}
			System.out.println(inputS);
		}
		//關閉連線
		output.close();
	}

執行命令列結果如下

現在我們就可以進行多條資訊的交流啦~

當然現在我們只允許伺服器和一個客戶機進行交流,當有一個客戶機在和伺服器在一起交流時,如果第二個客戶機嘗試和伺服器進行連線,就會出現如下報錯。

原因很簡單,當前我們的只是一個單執行緒的程式,當有一個客戶機在和伺服器進行通訊時,這個執行緒就已經被佔用了。後期我會繼續推出一篇部落格來講解JAVA多執行緒通訊的實現,支援多人聊天。

 

相關文章