目錄
TCP兩個核心類
服務端
1.用ServerSocker類創建對象并且手動指定端口號
2.accept阻塞連接服務端與客戶端
3.給客戶端提供處理業務方法
4.處理業務
整體代碼
客戶端
1.創建Socket對象,并連接服務端的ip與端口號
2.獲取Socket流對象,寫入數據,阻塞等待服務端響應
整體代碼
jconsole用來監控java線程
TCP兩個核心類
ServerSocket 服務器使用socket
accept沒有參數 返回值是一個Socket對象
功能是等待服務器和客戶端建立連接,建立成功后則會把這個連接獲取到進程中。接下來就通過Scoket返回的對象來進行交互
Scoket服務器和客戶端都使用socket
通過socket對象就可以進行發送接收數據
socket內部包含了輸入流對象(接收) 輸出流對象 (發送)
服務端
1.用ServerSocker類創建對象并且手動指定端口號
private ServerSocket serverSocket=null;public Test1(int port) throws IOException {serverSocket=new ServerSocket(port);
2.accept阻塞連接服務端與客戶端
當我們用UDP那樣連接客戶端會出現一個問題
UDP是無連接的,他發送數據時直接用地址端口號發送
而TCP是有連接的,當我們用accept連接時,ProcessConnect方法會一直被循環執行單個客戶端,而其他客戶端要連接時,無法執行到accept,必須等待ProcessConnect方法執行結束,所以會被阻塞。簡單來說就是當有一個客戶端連接時,其他客戶端必須等待連接的客戶端斷開連接才能連接,還是一個個來連接。
所以ProcessConnect方法我們直接交給線程去執行,這樣其他客戶端來連接時,直接讓線程去執行處理客戶端業務。
所以我們可以用到多線程
既然用到了多線程,就可以用出線程池,效率會更高。
public void start() throws IOException {System.out.println("服務器啟動");while (true){//阻塞等待服務器與客戶端建立連接Socket socket=serverSocket.accept();//若沒線程 當一個客戶端連接后 會一直在ProcessConnect內//其他客戶端連接時 必須要等待ProcessConnect結束,進入下一次循環,才能執行到Socketsocket=serverSocket.accept(); 才能連接下一個客戶端
// // ProcessConnect(socket);//所以執行處理客戶端請求,我們讓線程去干,這樣就不會在一個線程內阻塞
// Thread thread=new Thread(()->{
// try {
// ProcessConnect(socket);
// } catch (IOException e) {
// throw new RuntimeException(e);
// }
// });
// thread.start();//線程池ExecutorService executorService= Executors.newCachedThreadPool();executorService.submit(new Runnable() {@Overridepublic void run() {try {ProcessConnect(socket);} catch (IOException e) {throw new RuntimeException(e);}}});}}
3.給客戶端提供處理業務方法
我們可以直接獲取Socket內的流對象,直接寫入讀出即可
需要注意,寫入數據時,因為時流對象接收,要\n等待其他結束,而我們輸入數據時,流對象是不會寫入\n的,所以我們可以在數據后自動添加\n 或者用println默認有個\n
public void ProcessConnect(Socket socket) throws IOException {//實際他們交流數據是一個為socket類型的文件System.out.printf("[地址:%s:端口號%d]建立連接成功\n",socket.getInetAddress().toString(),socket.getPort());try (InputStream inputStream = socket.getInputStream();//獲取socket內部的input流OutputStream outputStream = socket.getOutputStream())//獲取socket內部的output流{Scanner scanner = new Scanner(inputStream);PrintWriter printWriter = new PrintWriter(outputStream);//長連接寫法while (true) {if(!scanner.hasNext()){System.out.printf("[地址:%s:端口號%d]斷開連接",socket.getInetAddress().toString(),socket.getPort());}//從客戶端接收數據String resqust = scanner.next();System.out.println("服務器接收數據");//處理數據String response = process(resqust);System.out.println("服務器處理數據");//因為這用流接收 我們按下換行是被scanner接收但并未添加到數據內 而服務器接收流收到的數據內//是沒有換行符的 就會一直阻塞 所以我們要在數據后再加個\n//服務器發送數據//printWriter.write(response+'\n');//或者直接使用println自帶\nprintWriter.println(response);System.out.println("服務器發送數據");//刷新緩沖區printWriter.flush();System.out.printf("[地址:%s:端口號:%d]接收數據:%s 響應數據:%s",socket.getInetAddress().toString(),socket.getPort(),resqust,response);}}}
4.處理業務
這里是為了演示TCP連接,所以只寫個簡單回傳
public String process(String s){return s;}
整體代碼
package TestTCP;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.Scanner;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;//服務器
public class Test1 {private ServerSocket serverSocket=null;public Test1(int port) throws IOException {serverSocket=new ServerSocket(port);}public void start() throws IOException {System.out.println("服務器啟動");while (true){//阻塞等待服務器與客戶端建立連接Socket socket=serverSocket.accept();//若沒線程 當一個客戶端連接后 會一直在ProcessConnect內//其他客戶端連接時 必須要等待ProcessConnect結束,進入下一次循環,// 才能執行到Socketsocket=serverSocket.accept(); 才能連接下一個客戶端
// ProcessConnect(socket);//所以執行處理客戶端請求,我們讓線程去干,這樣就不會在一個線程內阻塞
// Thread thread=new Thread(()->{
// try {
// ProcessConnect(socket);
// } catch (IOException e) {
// throw new RuntimeException(e);
// }
// });
// thread.start();//線程池ExecutorService executorService= Executors.newCachedThreadPool();executorService.submit(new Runnable() {@Overridepublic void run() {try {ProcessConnect(socket);} catch (IOException e) {throw new RuntimeException(e);}}});}}//給當前客戶端提供服務方法public void ProcessConnect(Socket socket) throws IOException {//實際他們交流數據是一個為socket類型的文件System.out.printf("[地址:%s:端口號%d]建立連接成功\n",socket.getInetAddress().toString(),socket.getPort());try (InputStream inputStream = socket.getInputStream();//獲取socket內部的input流OutputStream outputStream = socket.getOutputStream())//獲取socket內部的output流{Scanner scanner = new Scanner(inputStream);PrintWriter printWriter = new PrintWriter(outputStream);//長連接寫法while (true) {if(!scanner.hasNext()){System.out.printf("[地址:%s:端口號%d]斷開連接",socket.getInetAddress().toString(),socket.getPort());}//從客戶端接收數據String resqust = scanner.next();System.out.println("服務器接收數據");//處理數據String response = process(resqust);System.out.println("服務器處理數據");//因為這用流接收 我們按下換行是被scanner接收但并未添加到數據內 而服務器接收流收到的數據內//是沒有換行符的 就會一直阻塞 所以我們要在數據后再加個\n//服務器發送數據//printWriter.write(response+'\n');//或者直接使用println自帶\nprintWriter.println(response);System.out.println("服務器發送數據");//刷新緩沖區printWriter.flush();System.out.printf("[地址:%s:端口號:%d]接收數據:%s 響應數據:%s",socket.getInetAddress().toString(),socket.getPort(),resqust,response);}}}public String process(String s){return s;}public static void main(String[] args) throws IOException {Test1 t1=new Test1(8080);t1.start();}
}
客戶端
1.創建Socket對象,并連接服務端的ip與端口號
private Socket socket=null;public Test2() throws IOException {socket=new Socket("127.0.0.1",8080);
2.獲取Socket流對象,寫入數據,阻塞等待服務端響應
public void start() throws IOException {try(InputStream inputStream=socket.getInputStream();OutputStream outputStream=socket.getOutputStream()){while (true){Scanner scanner=new Scanner(inputStream);PrintWriter printWriter=new PrintWriter(outputStream);Scanner scanner1=new Scanner(System.in);System.out.println("輸入數據>");String requst=scanner1.next();//因為這用流接收 我們按下換行是被scanner接收但并未添加到數據內 而服務器接收流收到的數據內//是沒有換行符的 就會一直阻塞 所以我們要在數據后再加個\n//printWriter.write(requst+'\n');//或者直接使用println自帶\nprintWriter.println(requst);//刷新緩沖區printWriter.flush();String response=scanner.next();System.out.println(response);}}finally {socket.close();}}
整體代碼
package TestTCP;import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Scanner;//客戶端
public class Test2 {private Socket socket=null;public Test2() throws IOException {socket=new Socket("127.0.0.1",8080);}public void start() throws IOException {try(InputStream inputStream=socket.getInputStream();OutputStream outputStream=socket.getOutputStream()){while (true){Scanner scanner=new Scanner(inputStream);PrintWriter printWriter=new PrintWriter(outputStream);Scanner scanner1=new Scanner(System.in);System.out.println("輸入數據>");String requst=scanner1.next();//因為這用流接收 我們按下換行是被scanner接收但并未添加到數據內 而服務器接收流收到的數據內//是沒有換行符的 就會一直阻塞 所以我們要在數據后再加個\n//printWriter.write(requst+'\n');//或者直接使用println自帶\nprintWriter.println(requst);//刷新緩沖區printWriter.flush();String response=scanner.next();System.out.println(response);}}finally {socket.close();}}public static void main(String[] args) throws IOException {Test2 t2=new Test2();t2.start();}
}
jconsole用來監控java線程
jconsole在路徑 jdk/bin/jconsole.exe
例:
我們需要看util最后一局,可以看出線程是在next阻塞著。