TCP服務器與客戶端的搭建需要借助以下API
??API介紹
??ServerSocket API
ServerSocket 是創(chuàng)建TCP服務端Socket的API。
ServerSocket 構造方法:
方法簽名 | 方法說明 |
---|---|
ServerSocket(int port) | 創(chuàng)建一個服務端流套接字Socket,并綁定到指定端口 |
ServerSocket 方法:
方法簽名 | 方法說明 |
---|---|
Socket accept() | 開始監(jiān)聽指定端口(創(chuàng)建時綁定的端口),有客戶端連接后,返回一個服務端Socket對象,并基于該Socket建立與客戶端的連接,否則阻塞等待 |
void close() | 關閉此套接字 |
??Socket API
Socket 是客戶端Socket,或服務端中接收到客戶端建立連接(accept方法)的請求后,返回的服務端Socket。
不管是客戶端還是服務端Socket,都是雙方建立連接以后,保存的對端信息,及用來與對方收發(fā)數(shù)據(jù)的。
Socket 構造方法:
方法簽名 | 方法說明 |
---|---|
Socket(String host, intport) | 創(chuàng)建一個客戶端流套接字Socket,并與對應IP的主機上,對應端口的進程建立連接 |
Socket 方法:
方法簽名 | 方法說明 |
---|---|
InetAddress getInetAddress() | 返回套接字所連接的地址 |
InputStream getInputStream() | 返回此套接字的輸入流 |
OutputStream getOutputStream() | 返回此套接字的輸出流 |
??TCP中的長短連接
博主在前面的博文里面說到,TCP是面向連接的通信方式,TCP發(fā)送數(shù)據(jù)時,需要先建立連接,而這個連接又分為長短連接:
-
短連接:每次接收到數(shù)據(jù)并返回響應后,都關閉連接,即是短連接。也就是說,短連接只能一次收發(fā)數(shù)據(jù)。
-
長連接:不關閉連接,一直保持連接狀態(tài),雙方不停的收發(fā)數(shù)據(jù),即是長連接。也就是說,長連接可以多次收發(fā)數(shù)據(jù)
對比以上長短連接,兩者區(qū)別如下:
-
建立連接、關閉連接的耗時:短連接每次請求、響應都需要建立連接,關閉連接;而長連接只需要第一次建立連接,之后的請求、響應都可以直接傳輸。相對來說建立連接,關閉連接也是要耗時的,長連接效率更高。
-
主動發(fā)送請求不同:短連接一般是客戶端主動向服務端發(fā)送請求;而長連接可以是客戶端主動發(fā)送請求,也可以是服務端主動發(fā)。
-
兩者的使用場景有不同:短連接適用于客戶端請求頻率不高的場景,如瀏覽網(wǎng)頁等。長連接適用于客戶端與服務端通信頻繁的場景,如聊天室,實時游戲等
拓展:
-
基于BIO(同步阻塞IO)的長連接會一直占用系統(tǒng)資源。對于并發(fā)要求很高的服務端系統(tǒng)來說,這樣的消耗是不能承受的。
-
由于每個連接都需要不停的阻塞等待接收數(shù)據(jù),所以每個連接都會在一個線程中運行。一次阻塞等待對應著一次請求、響應,不停處理也就是長連接的特性:一直不關閉連接,不停的處理請求
-
實際應用時,服務端一般是基于NIO(即同步非阻塞IO)來實現(xiàn)長連接,性能可以極大的提升。
??建立TCP回顯客戶端與服務器
??TCP搭建服務器
我們分為以下幾步來實現(xiàn):
- 創(chuàng)建TcpEchoServer類來表示我們的服務器,并創(chuàng)建ServerSocket對象,初始值為null
- 在TcpEchoServer的構造方法里進行ServerSocket對象的實例化
- 用一個start()方法表示啟動程序
- 在該方法內(nèi)我們首先要使用accept()進行連接,并用Socket對象進行接收
- 我們再用一個processConnection(Socket clientSocket)方法處理我們的連接
由于我們的TCP傳輸是以流的形式傳播的,所以我們這里用到了讀寫數(shù)據(jù)流的方法來進行書寫,不會這一部分的小伙伴,可以去看看博主所寫【Java EE】文件內(nèi)容的讀寫?數(shù)據(jù)流進行查看學習
接下來我們書寫這個processConnection(Socket clientSocket)方法
- 讀取請求,構造輸入流的Scanner,并判斷后面如果沒有數(shù)據(jù)就關閉連接
- 然后我們將讀取的數(shù)據(jù)交給我們的 response()構造響應
- 響應后的數(shù)據(jù)寫入該套接字的輸出流中,最后flush(),進行刷新,確保寫入
為了釋放資源,我們每一次交互完畢都需要對我們的套接字進行關閉,這里我們使用fially來進行處理
代碼如下:
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;
public class TcpEchoServer {
private ServerSocket serverSocket = null;
public TcpEchoServer(int port) throws IOException {
serverSocket = new ServerSocket(port);
}
public void start() throws IOException {
System.out.println("啟動服務器");
Socket socket = serverSocket.accept();
processConnection(socket);
}
// 使用這個方法來處理一個連接.
// 這一個連接對應到一個客戶端. 但是這里可能會涉及到多次交互.
private void processConnection(Socket clientSocket) {
System.out.printf("[%s:%d] 客戶端上線!\n", clientSocket.getInetAddress().toString(), clientSocket.getPort());
// 基于上述 socket 對象和客戶端進行通信
try (InputStream inputStream = clientSocket.getInputStream();
OutputStream outputStream = clientSocket.getOutputStream()) {
// 由于要處理多個請求和響應, 也是使用循環(huán)來進行.
while (true) {
// 1. 讀取請求
Scanner scanner = new Scanner(inputStream);
if (!scanner.hasNext()) {
// 沒有下個數(shù)據(jù), 說明讀完了. (客戶端關閉了連接)
System.out.printf("[%s:%d] 客戶端下線! \n", clientSocket.getInetAddress().toString(), clientSocket.getPort());
break;
}
// 注意!! 此處使用 next 是一直讀取到換行符/空格/其他空白符結束, 但是最終返回結果里不包含上述 空白符 .
String request = scanner.next();
// 2. 根據(jù)請求構造響應
String response = process(request);
// 3. 返回響應結果.
// OutputStream 沒有 write String 這樣的功能. 可以把 String 里的字節(jié)數(shù)組拿出來, 進行寫入;
// 也可以用字符流來轉換一下.
PrintWriter printWriter = new PrintWriter(outputStream);
// 此處使用 println 來寫入. 讓結果中帶有一個 \n 換行. 方便對端來接收解析.
printWriter.println(response);
// flush 用來刷新緩沖區(qū), 保證當前寫入的數(shù)據(jù), 確實是發(fā)送出去了.
printWriter.flush();
System.out.printf("[%s:%d] req: %s; resp: %s \n", clientSocket.getInetAddress().toString(), clientSocket.getPort(),
request, response);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
// 更合適的做法, 是把 close 放到 finally 里面, 保證一定能夠執(zhí)行到!!
try {
clientSocket.close();
clientSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
public String process(String request) {
return request;
}
public static void main(String[] args) throws IOException {
TcpEchoServer server = new TcpEchoServer(9090);
server.start();
}
}
??TCP搭建客戶端
搭建客戶端我們也可以分為以下幾步:
- 創(chuàng)建TcpEchoClient類表示我們的客戶端,創(chuàng)建Soket對象用于與客戶端通信·
- 再TcpEchoClient構造方法里進行實例化Socket的對象
- 創(chuàng)建start()方法用于我們的操作
- 讀取鍵盤所要輸入的數(shù)據(jù)
- 將所讀的數(shù)據(jù)通過輸出流進行寫入
- 讀取響應的輸入流,進行打印
- main函數(shù)中進行啟動
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 TcpEchoClient {
private Socket socket = null;
public TcpEchoClient(String serverIp, int serverPort) throws IOException {
// Socket 構造方法, 能夠識別 點分十進制格式的 IP 地址. 比 DatagramPacket 更方便.
// new 這個對象的同時, 就會進行 TCP 連接操作.
socket = new Socket(serverIp, serverPort);
}
public void start() {
System.out.println("客戶端啟動!");
Scanner scanner = new Scanner(System.in);
try (InputStream inputStream = socket.getInputStream();
OutputStream outputStream = socket.getOutputStream()) {
while (true) {
// 1. 先從鍵盤上讀取用戶輸入的內(nèi)容
System.out.print("> ");
String request = scanner.next();
if (request.equals("exit")) {
System.out.println("goodbye");
break;
}
// 2. 把讀到的內(nèi)容構造成請求, 發(fā)送給服務器.
PrintWriter printWriter = new PrintWriter(outputStream);
printWriter.println(request);
// 此處加上 flush 保證數(shù)據(jù)確實發(fā)送出去了.
printWriter.flush();
// 3. 讀取服務器的響應
Scanner respScanner = new Scanner(inputStream);
String response = respScanner.next();
// 4. 把響應內(nèi)容顯示到界面上
System.out.println(response);
}
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) throws IOException {
TcpEchoClient client = new TcpEchoClient("127.0.0.1", 9090);
client.start();
}
}
文章來源:http://www.zghlxwxcb.cn/news/detail-838834.html
?總結
關于《【網(wǎng)絡原理】使用Java基于TCP實現(xiàn)簡單客戶端與服務器通信》就講解到這兒,感謝大家的支持,歡迎各位留言交流以及批評指正,如果文章對您有幫助或者覺得作者寫的還不錯可以點一下關注,點贊,收藏支持一下!文章來源地址http://www.zghlxwxcb.cn/news/detail-838834.html
到了這里,關于【網(wǎng)絡原理】使用Java基于TCP搭建簡單客戶端與服務器通信的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網(wǎng)!