閱讀853 返回首頁    go 阿裏雲 go 技術社區[雲棲]


Java中網絡編程之TCP協議

一、TCP的基本概念

TCP是專門設計用於在不可靠的英特網上提供可靠的、端到端的字節流通信的協議,是一個麵向連接的協議,TCP連接是字節流而非報文流。UDP和TCP各有65536個端口號互不影響。

 

二、單線程服務端

以下代碼隻能實現服務端和客戶端的同步對話。服務端處理完一個客戶端請求,才會處理另一個客戶端請求。服務器端的輸出效果是Client1阻塞20秒,Client2不會執行。必須等Client1阻塞結束之後,Client2才會執行。該例子可用來學習TCP的基本語法。

/**
 * TCP客戶端1
 * 
 * @author 徐越
 * 
 */
public class Client1
{
	public static void main(String[] args) throws Exception
	{
		Socket socket = new Socket("127.0.0.1", 8821);
		OutputStream os = socket.getOutputStream();
		String msg = "我是徐越1";
		Thread.sleep(20000);
		os.write(msg.getBytes());
		os.flush();
		os.close();socket.close();
	}
}


/**
 * TCP客戶端2
 * 
 * @author 徐越
 * 
 */
public class Client2
{
	public static void main(String[] args) throws Exception
	{
		Socket socket = new Socket("127.0.0.1", 8821);
		OutputStream os = socket.getOutputStream();
		String msg = "我是徐越2";
		os.write(msg.getBytes());
		os.flush();
		os.close();socket.close();
	}
}

/**
 * 單線程TCP服務端
 * 
 * @author 徐越
 * 
 */
public class Server
{
	public static void main(String[] args) throws Exception
	{
		// 創建端口為8821的TCP服務器端對象
		// 8821是服務器端的端口號而客戶端從某端口A連到8821,端口A是隨機的
		ServerSocket serverSocket = new ServerSocket(8821);
		while (true)
		{
			// 若無客戶端發送請求則線程在此阻塞,方法不繼續執行
			Socket socket = serverSocket.accept();
			System.out.println("connected");
			InputStream instream = socket.getInputStream();
			ByteArrayOutputStream bos = new ByteArrayOutputStream();
			int len = 0;
			byte[] buffer = new byte[1024];
			while ((len = instream.read(buffer)) != -1)
			{
				bos.write(buffer, 0, len);
			}
			instream.close();
			bos.flush();
			bos.close();
			String msg = bos.toString();
			System.out.println("客戶端的IP:" + socket.getInetAddress().getHostAddress());
			System.out.println("客戶端的端口:" + socket.getPort());
			System.out.println("客戶端的信息:" + msg);
		}
	}
}


執行結果

connected
客戶端的IP:127.0.0.1
客戶端的端口:3775
客戶端的信息:我是徐越1
connected
客戶端的IP:127.0.0.1
客戶端的端口:3787
客戶端的信息:我是徐越2

 

三、多線程服務器

在實際應用中是在服務器上運行一個永久的程序,接收來自其他多個客戶端的請求,提供相應的服務。需要利用多線程實現多客戶機製。服務器在指定的端口上監聽是否有客戶請求,一旦監聽到就會啟動一個專門的服務線程來響應該請求,而服務器本身在啟動完線程之後馬上又進入監聽狀態,等待下一個客戶的到來。隻要將服務端為如下代碼,Client1和Client2就會異步執行。

/**
 * 多線程服務端0
 * 
 * @author 徐越
 * 
 */
public class MultiThreadServer0
{
	// 端口號
	private int port = 8821;
	// 服務端
	private ServerSocket serverSocket;

	public MultiThreadServer0() throws IOException
	{
		// 創建服務器端
		serverSocket = new ServerSocket(port);
		System.out.println("服務器啟動");
	}

	public void service()
	{
		while (true)
		{
			Socket socket = null;
			try
			{
				// 客戶端進行連接時會觸發accept方法從而建立連接
				socket = serverSocket.accept();
				new MultiThreadHandler(socket).start();
			}
			catch (Exception e)
			{
				e.printStackTrace();
			}
		}
	}

	public static void main(String[] args) throws IOException
	{
		new MultiThreadServer1().service();
	}
}

/**
 * 多線程處理類
 */
class MultiThreadHandler extends Thread
{
	private Socket socket;

	public MultiThreadHandler(Socket socket)
	{
		this.socket = socket;
	}

	private BufferedReader getReader(Socket socket) throws IOException
	{
		InputStream socketIn = socket.getInputStream();
		// InputStreamReader為轉換流
		// InputStream本是字節流,現加一個Reader,表示用字符流的方式讀取字節流
		InputStreamReader isr = new InputStreamReader(socketIn);
		return new BufferedReader(isr);
	}

	public void run()
	{
		try
		{
			BufferedReader br = getReader(socket);
			String msg = null;
			while ((msg = br.readLine()) != null)
			{
				System.out.println("客戶端的IP:" + socket.getInetAddress().getHostAddress());
				System.out.println("客戶端的端口:" + socket.getPort());
				System.out.println("客戶端的信息:" + msg);
			}
		}
		catch (IOException e)
		{
			e.printStackTrace();
		}
		finally
		{
			try
			{
				if (socket != null)
				{
					socket.close();
				}
			}
			catch (IOException e)
			{
				e.printStackTrace();
			}
		}
	}
}


/**
 * 多線程服務端1
 */
public class MultiThreadServer1
{
	// 端口號
	private int port = 8821;
	// 服務端
	private ServerSocket serverSocket;
	// 線程池
	private ExecutorService executorService;
	// 單個CPU線程池大小
	private final int POOL_SIZE = 10;

	public MultiThreadServer1() throws IOException
	{
		// 創建服務器端
		serverSocket = new ServerSocket(port);
		// 獲取當前係統的CPU數目
		int cpuNums = Runtime.getRuntime().availableProcessors();
		// 根據係統資源情況靈活定義線程池大小
		executorService = Executors.newFixedThreadPool(cpuNums * POOL_SIZE);
		System.out.println("服務器啟動");
	}

	public void service()
	{
		while (true)
		{
			Socket socket = null;
			try
			{
				// 客戶進行連接就會觸發accept方法從而建立連接
				socket = serverSocket.accept();
				// 調用線程池操作
				executorService.execute(new Handler(socket));
			}
			catch (Exception e)
			{
				e.printStackTrace();
			}
		}
	}

	public static void main(String[] args) throws IOException
	{
		new MultiThreadServer1().service();
	}

}

/**
 * 多線程處理類
 */
class Handler implements Runnable
{
	private Socket socket;

	public Handler(Socket socket)
	{
		this.socket = socket;
	}

	private BufferedReader getReader(Socket socket) throws IOException
	{
		InputStream socketIn = socket.getInputStream();
		// InputStreamReader為轉換流
		// InputStream本是字節流,現加一個Reader,表示用字符流的方式讀取字節流
		InputStreamReader isr = new InputStreamReader(socketIn);
		return new BufferedReader(isr);
	}

	public void run()
	{
		try
		{
			BufferedReader br = getReader(socket);
			String msg = null;
			while ((msg = br.readLine()) != null)
			{
				System.out.println("客戶端的IP:" + socket.getInetAddress().getHostAddress());
				System.out.println("客戶端的端口:" + socket.getPort());
				System.out.println("客戶端的信息:" + msg);
			}
		}
		catch (IOException e)
		{
			e.printStackTrace();
		}
		finally
		{
			try
			{
				if (socket != null)
				{
					socket.close();
				}
			}
			catch (IOException e)
			{
				e.printStackTrace();
			}
		}
	}
}

 

兩個多線程服務端執行結果相同

服務器啟動
客戶端的IP:127.0.0.1
客戶端的端口:3931
客戶端的信息:我是徐越2
客戶端的IP:127.0.0.1
客戶端的端口:3928
客戶端的信息:我是徐越1


參考地址:https://www.2cto.com/kf/201209/158518.html

最後更新:2017-04-04 07:03:12

  上一篇:go 2013年 穀歌欲在雲計算市場戰勝微軟
  下一篇:go 2012年IDC行業十大事件評選啟動