Socket TCP 協議實現服務端和客戶端的簡單通信-結合線程池的使用

前言

沒啥說的,就是記錄一下代碼,方便以後查看
對了,本篇客戶端邏輯中有一個製造慢服務的 thread.sleep ,模擬客戶端很慢很慢對情況
服務端和客戶端都使用了線程池

當前模式的弊端

網絡通信目前流行 NIO 模式,將網絡IO 等待時間從業務處理線程中抽離出來。本文並沒有體現 NIO,當客戶端是一個長連接當時候,服務器資源就會被佔用——等待戈多(客戶端發送數據)。

服務端代碼

啓動 main 方法,服務端啓動,注意是 while(true)哦

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * 使用線程池實現 Socket 服務端 處理 Socket 請求
 * @author jie.wu
 *
 */
public class ConcurentSocketServer {
	private static ExecutorService threadPool = Executors.newCachedThreadPool();

	/**
	 * 處理客戶端請求的線程類
	 * */
	static class HandleMsg implements Runnable {
		// 客戶端套接字對象
		private Socket clientSocket;
		// 新增構造方法
		public HandleMsg(Socket clientSocket) {
			this.clientSocket = clientSocket;
		}
		@Override
		public void run() {
			// 獲取客戶端輸入流
			InputStream inputStream = null;
			OutputStream outputStream = null;
			PrintWriter printWriter = null;
			try {
				// 開始時間
				Long beginTime = System.currentTimeMillis();
				//處理客戶端信息
				inputStream = clientSocket.getInputStream();
				byte b[] = new byte[1024];
				StringBuffer sbf = new StringBuffer();
				for (int n; (n = inputStream.read(b)) != -1;) {
					sbf.append(new String(b, 0, n));
				}
				clientSocket.shutdownInput();
				
				// 向客戶端反饋信息
				outputStream = clientSocket.getOutputStream();
				printWriter = new PrintWriter(outputStream);
				printWriter.write("我是服務端");
				printWriter.flush();
				clientSocket.shutdownOutput();
				
				// 結束時間
				long endTime = System.currentTimeMillis();
				System.out.println("耗時" + (endTime - beginTime) + "ms 客戶端傳遞進來的信息爲:" + sbf.toString());
			} catch (IOException e) {
				e.printStackTrace();
			} finally {
				// 關閉資源
				try {
					if (inputStream != null)
						inputStream.close();
					if (printWriter != null)
						printWriter.close();
					if (outputStream != null)
						outputStream.close();
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}
	}

	@SuppressWarnings("resource")
	public static void main(String[] args) {
		ServerSocket serverSocket = null;
		Socket clientSocket = null;
		try {
			serverSocket = new ServerSocket(10000);
		} catch (IOException e) {
			e.printStackTrace();
		}

		// 對客戶端請求對處理
		while (true) {
			try {
				clientSocket = serverSocket.accept();
				// 使用多線程進行處理
				threadPool.execute(new HandleMsg(clientSocket));
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}

}
客戶端代碼

也使用了線程池,同時模擬了客戶端慢調用,使用 thread.sleep 對發送信息過程進行了減速
運行main 方法,線程池將發送10條請求

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * 使用線程池實現 Socket 客戶端 發送 Socket 請求
 * 
 * @author jie.wu
 *
 */
public class ConcurentSocketClient {
	private static ExecutorService threadPool = Executors.newCachedThreadPool();

	static class HandleMsg implements Runnable {
		//記錄線程編號
		private int threadNumber;
		//重寫構造方法
		public HandleMsg(int threadNumber) {
			this.threadNumber = threadNumber;
		}

		@Override
		public void run() {
			Socket socket = null;
			OutputStream outputStream = null;
			PrintWriter printWriter = null;
			InputStream inputStream = null;
			try {
				socket = new Socket("127.0.0.1", 10000);
				outputStream = socket.getOutputStream();
				printWriter = new PrintWriter(outputStream);
				printWriter.write("你好,");
				// 客戶端睡眠
				try {
					Thread.sleep(1000);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				printWriter.write("我是客戶端" + threadNumber);
				printWriter.flush();
				socket.shutdownOutput();

				// 獲取服務器端返回的信息
				inputStream = socket.getInputStream();
				byte b[] = new byte[1024];
				StringBuffer sbf = new StringBuffer();
				for (int n; (n = inputStream.read(b)) != -1;) {
					sbf.append(new String(b, 0, n));
				}
				System.out.println("服務器端反饋進來的信息爲:" + sbf.toString());
				socket.shutdownInput();
			} catch (UnknownHostException e) {
				e.printStackTrace();
			} catch (IOException e) {
				e.printStackTrace();
			} finally {
				// 關閉資源
				try {
					if (printWriter != null)
						printWriter.close();
					if (outputStream != null)
						outputStream.close();
					if (inputStream != null)
						inputStream.close();
					if (socket != null)
						socket.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}

	public static void main(String[] args) {
		for (int i = 0; i < 10; i++) {
			threadPool.execute(new HandleMsg(i));
		}
	}

}
運行結果
客戶端
服務器端反饋進來的信息爲:我是服務端
服務器端反饋進來的信息爲:我是服務端
服務器端反饋進來的信息爲:我是服務端
服務器端反饋進來的信息爲:我是服務端
服務器端反饋進來的信息爲:我是服務端
服務器端反饋進來的信息爲:我是服務端
服務器端反饋進來的信息爲:我是服務端
服務器端反饋進來的信息爲:我是服務端
服務器端反饋進來的信息爲:我是服務端
服務器端反饋進來的信息爲:我是服務端
服務端
耗時1027ms 客戶端傳遞進來的信息爲:你好,我是客戶端4
耗時1028ms 客戶端傳遞進來的信息爲:你好,我是客戶端2
耗時1026ms 客戶端傳遞進來的信息爲:你好,我是客戶端6
耗時1030ms 客戶端傳遞進來的信息爲:你好,我是客戶端5
耗時1033ms 客戶端傳遞進來的信息爲:你好,我是客戶端0
耗時1033ms 客戶端傳遞進來的信息爲:你好,我是客戶端3
耗時1034ms 客戶端傳遞進來的信息爲:你好,我是客戶端1
耗時1032ms 客戶端傳遞進來的信息爲:你好,我是客戶端9
耗時1034ms 客戶端傳遞進來的信息爲:你好,我是客戶端8
耗時1036ms 客戶端傳遞進來的信息爲:你好,我是客戶端7
參考文獻

[1]、https://blog.csdn.net/bestcxx/article/details/73753649
[2]、《Java 高併發程序設計》 2016年8月

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章