1. 程式人生 > >Java Socket實現多個客戶端連線同一個服務端

Java Socket實現多個客戶端連線同一個服務端

使用Socket實現多個客戶端和同一客戶端通訊;首先客戶端連線服務端傳送一條訊息,服務端接收到訊息後進行處理,完成後再回復客戶端一條訊息。本人通過自己的思維編寫了一份服務端和客戶端實現的程式碼,望能與大家相互學習,共同進步。


  服務端程式碼

/**
 * Socket服務端<br>
 * 功能說明:
 * 
 * @author 大智若愚的小懂
 * @Date 2016年8月30日
 * @version 1.0
 */
public class Server {

	/**
	 * 入口
	 * 
	 * @param args
	 * @throws IOException
	 */
	public static void main(String[] args) throws IOException {
		// 為了簡單起見,所有的異常資訊都往外拋
		int port = 8899;
		// 定義一個ServiceSocket監聽在埠8899上
		ServerSocket server = new ServerSocket(port);
		System.out.println("等待與客戶端建立連線...");
		while (true) {
			// server嘗試接收其他Socket的連線請求,server的accept方法是阻塞式的
			Socket socket = server.accept();
			/**
			 * 我們的服務端處理客戶端的連線請求是同步進行的, 每次接收到來自客戶端的連線請求後,
			 * 都要先跟當前的客戶端通訊完之後才能再處理下一個連線請求。 這在併發比較多的情況下會嚴重影響程式的效能,
			 * 為此,我們可以把它改為如下這種非同步處理與客戶端通訊的方式
			 */
			// 每接收到一個Socket就建立一個新的執行緒來處理它
			new Thread(new Task(socket)).start();

		}
		// server.close();
	}

	/**
	 * 處理Socket請求的執行緒類
	 */
	static class Task implements Runnable {

		private Socket socket;

		/**
		 * 建構函式
		 */
		public Task(Socket socket) {
			this.socket = socket;
		}

		@Override
		public void run() {
			try {
				handlerSocket();
			} catch (Exception e) {
				e.printStackTrace();
			}
		}

		/**
		 * 跟客戶端Socket進行通訊
		 * 
		 * @throws IOException
		 */
		private void handlerSocket() throws Exception {
			// 跟客戶端建立好連線之後,我們就可以獲取socket的InputStream,並從中讀取客戶端發過來的資訊了
			/**
			 * 在從Socket的InputStream中接收資料時,像上面那樣一點點的讀就太複雜了,
			 * 有時候我們就會換成使用BufferedReader來一次讀一行
			 * 
			 * BufferedReader的readLine方法是一次讀一行的,這個方法是阻塞的,直到它讀到了一行資料為止程式才會繼續往下執行,
			 * 那麼readLine什麼時候才會讀到一行呢?直到程式遇到了換行符或者是對應流的結束符readLine方法才會認為讀到了一行,
			 * 才會結束其阻塞,讓程式繼續往下執行。
			 * 所以我們在使用BufferedReader的readLine讀取資料的時候一定要記得在對應的輸出流裡面一定要寫入換行符(
			 * 流結束之後會自動標記為結束,readLine可以識別),寫入換行符之後一定記得如果輸出流不是馬上關閉的情況下記得flush一下,
			 * 這樣資料才會真正的從緩衝區裡面寫入。
			 */
			BufferedReader br = new BufferedReader(
					new InputStreamReader(socket.getInputStream(), "UTF-8"));
			StringBuilder sb = new StringBuilder();
			String temp;
			int index;
			while ((temp = br.readLine()) != null) {
				if ((index = temp.indexOf("eof")) != -1) { // 遇到eof時就結束接收
					sb.append(temp.substring(0, index));
					break;
				}
				sb.append(temp);
			}
			System.out.println("Form Cliect[port:" + socket.getPort()
					+ "] 訊息內容:" + sb.toString());

			// 迴應一下客戶端
			Writer writer = new OutputStreamWriter(socket.getOutputStream(),
					"UTF-8");
			writer.write(String.format("Hi,%d.天朗氣清,惠風和暢!", socket.getPort()));
			writer.flush();
			writer.close();
			System.out.println(
					"To Cliect[port:" + socket.getPort() + "] 回覆客戶端的訊息傳送成功");

			br.close();
			socket.close();
		}

	}

}
  客戶端程式碼
<pre name="code" class="java">import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.Reader;
import java.io.Writer;
import java.net.Socket;

/**
 * Socket客戶端<br>
 * 功能說明:
 *
 * @author 大智若愚的小懂
 * @Date 2016年8月30日
 * @version 1.0
 */
public class Client {

	/**
	 * 入口
	 * @param args
	 */
	public static void main(String[] args) {
        // 開啟三個客戶端,一個執行緒代表一個客戶端
        for (int i = 0; i < 3; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        TestClient client = TestClientFactory.createClient();
                        client.send(String.format("Hello,Server!I'm %d.這週末天氣如何。", client.client.getLocalPort()));
                        client.receive();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }).start();
        }
    }

    /**
     * 生產測試客戶端的工廠
     */
    static class TestClientFactory {

        public static TestClient createClient() throws Exception {
            return new TestClient("127.0.0.1", 8899);
        }

    }

    /**
     * 測試客戶端
     */
    static class TestClient {

        /**
         * 建構函式
         * @param host 要連線的服務端IP地址
         * @param port 要連線的服務端對應的監聽埠
         * @throws Exception
         */
        public TestClient(String host, int port) throws Exception {
            // 與服務端建立連線
            this.client = new Socket(host, port);
            System.out.println("Cliect[port:" + client.getLocalPort() + "] 與服務端建立連線...");
        }

        private Socket client;

        private Writer writer;

        /**
         * 傳送訊息
         * @param msg
         * @throws Exception
         */
        public void send(String msg) throws Exception {
            // 建立連線後就可以往服務端寫資料了
            if(writer == null) {
                writer = new OutputStreamWriter(client.getOutputStream(), "UTF-8");
            }
            writer.write(msg);
            writer.write("eof\n");
            writer.flush();// 寫完後要記得flush
            System.out.println("Cliect[port:" + client.getLocalPort() + "] 訊息傳送成功");
        }

        /**
         * 接收訊息
         * @throws Exception
         */
        public void receive() throws Exception {
            // 寫完以後進行讀操作
            Reader reader = new InputStreamReader(client.getInputStream(), "UTF-8");
            // 設定接收資料超時間為10秒
            client.setSoTimeout(10*1000);
            char[] chars = new char[64];
            int len;
            StringBuilder sb = new StringBuilder();
            while ((len = reader.read(chars)) != -1) {
                sb.append(new String(chars, 0, len));
            }
            System.out.println("Cliect[port:" + client.getLocalPort() + "] 訊息收到了,內容:" + sb.toString());
            reader.close();

            // 關閉連線
            writer.close();
            client.close();
        }

    }

}


接下來模擬一下:

1.首先執行服務端


2.接著執行客戶端(開三個客戶端請求)

  為了演示有所區分,服務端使用的是Eclipse工具,客戶端使用的是IntelliJ IDEA工具。這時可以看到客戶端在控制檯打印出來的訊息


    一個Port埠號代表一個客戶端,回過來看下服務端在控制檯打印出來的訊息