Java利用Socket實(shí)現(xiàn)網(wǎng)絡(luò)通信功能
一. Socket編程
1. 簡(jiǎn)介
Socket編程是基于TCP/IP協(xié)議的網(wǎng)絡(luò)編程技術(shù),它給我們提供了一種可以用于網(wǎng)絡(luò)通信的機(jī)制,讓我們能在不同的計(jì)算機(jī)之間進(jìn)行數(shù)據(jù)交換。我們可以利用Socket編程實(shí)現(xiàn)客戶端/服務(wù)器程序的開(kāi)發(fā),如聊天室、FTP客戶端等項(xiàng)目。
2. 通信流程
對(duì)于Socket編程,我們要重點(diǎn)搞清楚客戶端與服務(wù)器端之間的通信流程。
在Socket編程中有兩種類型的Socket:服務(wù)器Socket和客戶端Socket。服務(wù)器Socket可以在服務(wù)器上創(chuàng)建用于監(jiān)聽(tīng)客戶端請(qǐng)求的端口,客戶端Socket則可以在客戶端上創(chuàng)建用于連接服務(wù)器的Socket??蛻舳薙ocket向服務(wù)器Socket發(fā)送請(qǐng)求,服務(wù)器Socket可以接收該請(qǐng)求,并創(chuàng)建一個(gè)新的Socket用于與客戶端通信。通過(guò)這種方式,客戶端和服務(wù)器端之間就可以進(jìn)行數(shù)據(jù)交換。
在Socket編程中,客戶端和服務(wù)器之間會(huì)通過(guò)TCP/IP協(xié)議進(jìn)行通信??蛻舳薙ocket首先回連接到服務(wù)器Socket的IP地址和端口號(hào),這樣就會(huì)建立起一個(gè)TCP連接。一旦連接建立,客戶端和服務(wù)器之間就可以進(jìn)行數(shù)據(jù)傳輸。數(shù)據(jù)會(huì)被分割成數(shù)據(jù)包,并通過(guò)TCP/IP協(xié)議在客戶端和服務(wù)器之間傳輸。
然后服務(wù)器Socket會(huì)在服務(wù)器端創(chuàng)建一個(gè)用于監(jiān)聽(tīng)客戶端請(qǐng)求的端口,客戶端Socket則在客戶端上創(chuàng)建用于連接服務(wù)器的Socket??蛻舳薙ocket向服務(wù)器Socket發(fā)送連接請(qǐng)求,服務(wù)器Socket接收該請(qǐng)求并創(chuàng)建一個(gè)新的Socket用于與客戶端通信。通過(guò)這種方式,客戶端和服務(wù)器端之間就可以進(jìn)行數(shù)據(jù)交換。
這樣,客戶端和服務(wù)器端之間就通過(guò)TCP/IP協(xié)議實(shí)現(xiàn)了Socket通信。
3. 核心API
我們可以使用java.net包中的API來(lái)實(shí)現(xiàn)Socket編程,這些常用的API包括:
- ServerSocket類:用于創(chuàng)建服務(wù)器端Socket,監(jiān)聽(tīng)客戶端發(fā)來(lái)的請(qǐng)求。
- Socket類:用于創(chuàng)建客戶端Socket,可以連接服務(wù)器Socket。
- InputStream和OutputStream類:用于在Socket之間傳輸數(shù)據(jù)的輸入輸出流。
4. 基本案例-單向通信
接下來(lái)先給大家編寫(xiě)一個(gè)可以實(shí)現(xiàn)單向通信的基本案例,包括一個(gè)服務(wù)器端和一個(gè)客戶端。這個(gè)案例中,客戶端給服務(wù)器端發(fā)送一條消息,然后服務(wù)器把客戶端發(fā)來(lái)的消息打印出來(lái),代碼如下:
4.1 服務(wù)器端
下面是服務(wù)器端的代碼案例。在本案例中,主要是利用ServerSocket對(duì)象定義了一個(gè)服務(wù)器,并通過(guò)accept()方法來(lái)獲取與該服務(wù)器綁定的Socket客戶端對(duì)象。這里要注意端口號(hào)“1234”是我們自己隨意定義的,只要不與其他程序的端口號(hào)重合即可。然后我們可以在Socket客戶端對(duì)象上通過(guò)getInputStream()方法來(lái)獲取一個(gè)InputStream輸入流,進(jìn)而讀取客戶端發(fā)來(lái)的消息。最后大家要記得把各種IO流資源釋放。
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;
/**
* @author 一一哥Sun
* @company 千鋒教育
*/
public class MySocketServer {
public static void main(String[] args) throws IOException {
//設(shè)置服務(wù)器的端口號(hào)
int portNumber = 1234;
// 創(chuàng)建服務(wù)器套接字并綁定端口號(hào)
ServerSocket serverSocket = new ServerSocket(portNumber);
//獲取與ServerSocket關(guān)聯(lián)的Socket客戶端對(duì)象
Socket acceptSocket = serverSocket.accept();
// 服務(wù)器接收客戶端發(fā)來(lái)的消息
InputStream serverInputStream = acceptSocket.getInputStream();
BufferedReader in = new BufferedReader(new InputStreamReader(serverInputStream));
String response = in.readLine();
if (response != null) {
System.out.println("服務(wù)器接收客戶端發(fā)來(lái)的消息===>: " + response);
}
// 關(guān)閉套接字和流
serverSocket.close();
acceptSocket.close();
serverInputStream.close();
}
}
大家要注意,在今天的案例中,沒(méi)有使用循環(huán)來(lái)一直進(jìn)行消息的收發(fā),如果我們想實(shí)現(xiàn)不間斷的消息收發(fā),可以把相關(guān)代碼寫(xiě)在循環(huán)體中。所以在今天的案例中,消息收發(fā)一次之后,項(xiàng)目就會(huì)停掉。
4.2 客戶端
接著又定義了一個(gè)客戶端程序。在該程序中,定義了一個(gè)Socket客戶端對(duì)象,該對(duì)象通過(guò)IP地址和端口號(hào)來(lái)關(guān)聯(lián)服務(wù)端對(duì)象。因?yàn)?strong>小編的項(xiàng)目,客戶端和服務(wù)端是在同一臺(tái)機(jī)器上,所以這里的IP地址我們可以使用localhost,當(dāng)然也可以用服務(wù)端的實(shí)際IP地址。然后利用字符流和字節(jié)輸出流給服務(wù)端發(fā)送信息,最后釋放IO流資源。
import java.io.IOException;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Scanner;
/**
* @author 一一哥Sun
* @company 千鋒教育
*/
public class MySocketClient {
public static void main(String[] args) throws IOException {
try {
// 創(chuàng)建Socket對(duì)象并連接到服務(wù)器,關(guān)聯(lián)服務(wù)器的ip地址與端口號(hào)
Socket socket = new Socket("localhost", 1234);
// 創(chuàng)建輸入輸出流,通過(guò)套接字發(fā)送和接收數(shù)據(jù)
Scanner scanner = new Scanner(System.in);
String nextLine = scanner.nextLine();
// 輸出流,客戶端向服務(wù)器發(fā)送消息
PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
out.println(nextLine);
// 關(guān)閉套接字和流
out.close();
scanner.close();
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
在這個(gè)案例中,主要是利用ServerSocket、Socket和IO流這三種API,就實(shí)現(xiàn)了客戶端給服務(wù)端發(fā)送消息的功能。但是這個(gè)案例中,我們只能是客戶端給服務(wù)端發(fā)送消息,服務(wù)端卻不能給客戶端回復(fù)消息,所以接下來(lái)要把這個(gè)案例改進(jìn)一下,實(shí)現(xiàn)客戶端與服務(wù)端之間互相傳遞消息。
5. 改進(jìn)案例-雙向通信
接下來(lái)我們就把上面的案例改進(jìn)一下,實(shí)現(xiàn)客戶端與服務(wù)器端之間的雙向通信,即客戶端給服務(wù)器端發(fā)送消息,服務(wù)器端收到消息之后,再給客戶端返回消息。
5.1 服務(wù)器端
在下面的代碼中,小編把服務(wù)器端的代碼改進(jìn)了一下,在if語(yǔ)句中增加了利用OutputStream向客戶端返回信息的代碼,如下所示:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
/**
* @author 一一哥Sun
* @company 千鋒教育
*/
public class MyServer {
public static void main(String[] args) throws IOException {
int portNumber = 1234;
// 創(chuàng)建服務(wù)器套接字并綁定端口號(hào)
ServerSocket serverSocket = new ServerSocket(portNumber);
// 注意:服務(wù)器只能接受一次客戶端!用該socket對(duì)象既可以接收客戶端發(fā)來(lái)的消息,也可以給客戶端回復(fù)消息
Socket acceptSocket = serverSocket.accept();
// 服務(wù)器接收客戶端發(fā)來(lái)的消息
InputStream serverInputStream = acceptSocket.getInputStream();
BufferedReader in = new BufferedReader(new InputStreamReader(serverInputStream));
String response = in.readLine();
if (response != null) {
System.out.println("服務(wù)器接收客戶端發(fā)來(lái)的消息===>: " + response);
// 服務(wù)器向客戶端發(fā)送響應(yīng)信息
OutputStream serverOutputStream = acceptSocket.getOutputStream();
String serverResponse = "我是服務(wù)器,你的消息已收到!";
serverOutputStream.write(serverResponse.getBytes());
serverOutputStream.flush();
serverOutputStream.close();
}
// 關(guān)閉套接字和流
serverSocket.close();
acceptSocket.close();
serverInputStream.close();
//serverOutputStream.close();
}
}
5.2 客戶端
在下面的代碼中,把客戶端的代碼也改進(jìn)了一下,主要是增加了利用BufferedReader和InputStream,讀取服務(wù)器返回信息的代碼,如下所示:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Scanner;
/**
* @author 一一哥Sun
* @company 千鋒教育
*/
public class MyClient {
public static void main(String[] args) throws IOException {
try {
//創(chuàng)建Socket對(duì)象并連接到服務(wù)器
Socket socket = new Socket("localhost", 1234);
//創(chuàng)建輸入輸出流,通過(guò)套接字發(fā)送和接收數(shù)據(jù)
Scanner scanner=new Scanner(System.in);
String nextLine = scanner.nextLine();
//輸出流,客戶端向服務(wù)器發(fā)送消息
PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
out.println(nextLine);
//輸入流,客戶端從服務(wù)器接收消息
BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String response = in.readLine();
System.out.println("Server服務(wù)器返回的響應(yīng)信息===>: " + response);
//關(guān)閉套接字和流
out.close();
in.close();
scanner.close();
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
經(jīng)過(guò)以上代碼的改造,我們就實(shí)現(xiàn)了服務(wù)器與客戶端之間的雙向通信,大家可以把小編的代碼案例運(yùn)行起來(lái)看看效果。
6. 注意事項(xiàng)
大家要注意,在進(jìn)行Socket通信開(kāi)發(fā)時(shí),稍有不慎可能就會(huì)出現(xiàn)各種問(wèn)題,所以我們要注意以下事項(xiàng):
- 在每個(gè)Socket連接中,我們都應(yīng)該嚴(yán)格按照協(xié)議規(guī)定的格式進(jìn)行數(shù)據(jù)的發(fā)送和接收,否則就可能會(huì)導(dǎo)致數(shù)據(jù)傳輸失敗或被誤解。
- 在Socket編程中,應(yīng)該使用多線程或異步機(jī)制等技術(shù)來(lái)避免阻塞。如果阻塞時(shí)間過(guò)長(zhǎng),可能會(huì)導(dǎo)致客戶端或服務(wù)器崩潰。在上面的案例中,是把收發(fā)消息的代碼之間寫(xiě)在了主線程中,其實(shí)我們可以把這種耗時(shí)的操作放在Thread或線程池中進(jìn)行實(shí)現(xiàn)。
- 服務(wù)器端應(yīng)該可以同時(shí)處理多個(gè)客戶端的請(qǐng)求,否則有可能會(huì)導(dǎo)致客戶端的請(qǐng)求被拒絕或服務(wù)器崩潰。
- 我們要保證Socket編程的保安全性,例如防止黑客攻擊、拒絕服務(wù)攻擊等惡意行為。
- 我們還應(yīng)該有適當(dāng)?shù)腻e(cuò)誤處理,例如必須處理網(wǎng)絡(luò)連接失敗、數(shù)據(jù)傳輸?shù)氖〉犬惓G闆r。
除了以上幾點(diǎn)注意事項(xiàng),還有其他一些細(xì)節(jié)需要注意。在Socket編程中,必須對(duì)網(wǎng)絡(luò)通信有深入的理解和掌握,才能確保程序的正確性和安全性。
二. 結(jié)語(yǔ)
今天的文章,主要是給大家介紹了Socket編程的實(shí)現(xiàn)過(guò)程。其實(shí)Socket通信主要就是分為服務(wù)器端和客戶端,有著比較清晰的實(shí)現(xiàn)邏輯,學(xué)習(xí)起來(lái)并不難,大家重點(diǎn)理解和掌握Socket通信的實(shí)現(xiàn)流暢即可。
相關(guān)文章
SpringBoot全局異常處理機(jī)制和配置攔截器方式
這篇文章主要介紹了SpringBoot全局異常處理機(jī)制和配置攔截器方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-12-12
Springboot?jpa使用sum()函數(shù)返回結(jié)果如何被接收
這篇文章主要介紹了Springboot?jpa使用sum()函數(shù)返回結(jié)果如何接收,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-02-02
spring boot集成mongodb的增刪改查的示例代碼
這篇文章主要介紹了spring boot集成mongodb的增刪改查的示例代碼,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-03-03
解決try-catch捕獲異常信息后Spring事務(wù)失效的問(wèn)題
這篇文章主要介紹了解決try-catch捕獲異常信息后Spring事務(wù)失效的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-06-06
Springboot文件上傳功能的實(shí)現(xiàn)
這篇文章主要介紹了SpringBoot文件上傳功能的實(shí)現(xiàn),文中通過(guò)代碼示例介紹的非常詳細(xì),具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們可以參考閱讀2023-04-04
Java?SpringMVC實(shí)現(xiàn)自定義攔截器
這篇文章主要為大家詳細(xì)介紹了SpringMVC實(shí)現(xiàn)自定義攔截器,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來(lái)幫助2022-03-03
Mybatis中攔截器的簡(jiǎn)單實(shí)現(xiàn)方法
這篇文章主要給大家介紹了關(guān)于Mybatis中攔截器的簡(jiǎn)單實(shí)現(xiàn)方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用Mybatis具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-08-08
教你怎么在IDEA中創(chuàng)建java多模塊項(xiàng)目
這篇文章主要介紹了教你怎么在IDEA中創(chuàng)建java多模塊項(xiàng)目,文中有非常詳細(xì)的代碼示例,對(duì)正在學(xué)習(xí)java的小伙伴們有非常好的幫助,需要的朋友可以參考下2021-04-04
spring boot security 沒(méi)有合適的構(gòu)造器問(wèn)題
這篇文章主要介紹了spring boot security 沒(méi)有合適的構(gòu)造器問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-12-12

