深入理解Java Socket通信
簡(jiǎn)述
Java中Socket分為普通Socket和NioSocket兩種,這里介紹Socket。
我們可以把Socket比作兩個(gè)城市間的交通工具,有了它可以在兩城之間來(lái)回穿梭,交通工具有很多種,每種交通工具也有相應(yīng)的交通規(guī)則。Socket也一樣,也有多種。大多情況下使用的是TCP/IP的流套接字,它是一種穩(wěn)定的通信協(xié)議。(TCP/IP與UDP的對(duì)比)
Java中的網(wǎng)絡(luò)通信是通過(guò)Socket實(shí)現(xiàn)的,Socket分為ServerSocket和Socket兩大類(lèi),ServerSocket用于服務(wù)端,通過(guò)accept方法監(jiān)聽(tīng)請(qǐng)求,監(jiān)聽(tīng)到請(qǐng)求后返回Socket,Socket用于具體完成數(shù)據(jù)傳輸,客戶端直接使用Socket發(fā)起請(qǐng)求并傳輸數(shù)據(jù)。
ServerSocket的使用可以分為三步:
1.創(chuàng)建ServerSocket。ServerSocket的構(gòu)造方法一共有5個(gè),通常用的是ServerSocket(int port),只需要端口號(hào)(port)即可。
2.調(diào)用創(chuàng)建出來(lái)的ServerSocket的accept方法進(jìn)行監(jiān)聽(tīng)。accept方法時(shí)阻塞方法,也就是說(shuō)調(diào)用accept方法后程序會(huì)停下來(lái)等待連接請(qǐng)求,在接收到請(qǐng)求之前程序?qū)⒉粫?huì)往下走。當(dāng)接收到請(qǐng)求后accept方法會(huì)返回一個(gè)Socket。
3.使用accept方法返回的Socket與客戶端進(jìn)行通信。
栗子
Client:
package IO;
import java.io.*;
import java.net.Socket;
import java.util.Date;
/**
* Created by zhengbin06 on 2017/2/2.
*/
public class Client {
public static void main(String[] args) {
String msg = "Client Data";
try {
Socket socket = new Socket("127.0.0.1", 9090);
// 先寫(xiě)、再讀
PrintWriter printWriter = new PrintWriter(socket.getOutputStream());
// 發(fā)送數(shù)據(jù)
printWriter.println(msg);
printWriter.flush();
// 獲得服務(wù)端返回的數(shù)據(jù)
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String line = bufferedReader.readLine();
System.out.println("received from server: " + line + "\ttime=" + new Date().getTime());
// 關(guān)閉資源
printWriter.close();
bufferedReader.close();
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
Server:
package IO;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Date;
/**
* Created by zhengbin06 on 2017/2/2.
*/
public class Server {
private static Socket socket = null;
private static ServerSocket serverSocket = null;
public static void main(String[] args) throws IOException {
BufferedReader bufferedReader = null;
PrintWriter printWriter = null;
try {
// 創(chuàng)建一個(gè)ServerSocket監(jiān)聽(tīng)9090端口
serverSocket = new ServerSocket(9090);
while (true) {
System.out.println("開(kāi)始等待請(qǐng)求。。。。");
// 等待請(qǐng)求
// 監(jiān)聽(tīng)并接受到此套接字的連接。此方法在連接傳入之前一直阻塞。
socket = serverSocket.accept();
System.out.println("接收到請(qǐng)求:" + socket.toString() + "\ttime=" + new Date().getTime());
// 接收到請(qǐng)求后使用socket進(jìn)行通信, 創(chuàng)建BufferedReader用于讀取數(shù)據(jù)
bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String line = bufferedReader.readLine();
System.out.println("received from client: " + line + "\ttime=" + new Date().getTime());
// 創(chuàng)建PrintWriter, 用于發(fā)送數(shù)據(jù)
printWriter = new PrintWriter(socket.getOutputStream());
printWriter.println("received data: " + line + "\ttime=" + new Date().getTime());
printWriter.flush();
}
} finally {
// 關(guān)閉所有資源
bufferedReader.close();
printWriter.close();
socket.close();
serverSocket.close();
}
}
}
細(xì)節(jié)
監(jiān)聽(tīng)請(qǐng)求:
當(dāng)一個(gè)新的Socket請(qǐng)求來(lái)到時(shí),將為這個(gè)連接創(chuàng)建一個(gè)新的套接字?jǐn)?shù)據(jù)結(jié)構(gòu),該套接字?jǐn)?shù)據(jù)的信息包含的地址和端口正式請(qǐng)求源地址和端口。這個(gè)新創(chuàng)建的數(shù)據(jù)結(jié)構(gòu)將會(huì)關(guān)聯(lián)到ServerSocket實(shí)例的一個(gè)未完成的連接數(shù)據(jù)結(jié)構(gòu)列表中。注意,這時(shí)服務(wù)端的與之對(duì)應(yīng)的Socket實(shí)例并沒(méi)有完成創(chuàng)建,而要等到與客戶端的3次握手完成后,這個(gè)服務(wù)端的Socket實(shí)例才會(huì)返回,并將這個(gè)Socket實(shí)例對(duì)應(yīng)的數(shù)據(jù)結(jié)構(gòu)從未完成列表中移動(dòng)已完成列表中。
數(shù)據(jù)傳輸:
當(dāng)連接已經(jīng)建立成功時(shí),服務(wù)端和客戶端都會(huì)擁有一個(gè)Socket實(shí)例,每個(gè)Socket實(shí)例都有一個(gè)InputStream和OutputStream,并通過(guò)這兩個(gè)對(duì)象來(lái)交換數(shù)據(jù)。
要知道網(wǎng)絡(luò)I/O都是以字節(jié)流傳輸?shù)?,?dāng)創(chuàng)建Socket對(duì)象時(shí),操作系統(tǒng)將會(huì)為InputStream和OutputStream分別分配一定大小的緩存區(qū),數(shù)據(jù)的寫(xiě)入和讀取都是通過(guò)這個(gè)緩存區(qū)完成的。
寫(xiě)入端將數(shù)據(jù)寫(xiě)到OutputStream對(duì)應(yīng)的SendQ隊(duì)列中,當(dāng)隊(duì)列填滿時(shí),數(shù)據(jù)將被轉(zhuǎn)移到另一端InputStream的RecvQ隊(duì)列中,如果這時(shí)RecvQ已經(jīng)滿了,那么OutputStream的write方法將會(huì)阻塞,直到RecvQ隊(duì)列有足夠的空間容納SendQ發(fā)送的數(shù)據(jù)。過(guò)程如下圖所示:

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
SpringMVC中@Valid不起效BindingResult讀取不到Error信息
在寫(xiě)SpringMVC項(xiàng)目時(shí),由于要對(duì)表單數(shù)據(jù)進(jìn)行校驗(yàn),需要使用@Valid進(jìn)行校驗(yàn),但是在進(jìn)行數(shù)據(jù)校驗(yàn)時(shí),BindingResult對(duì)象無(wú)法攔截非法表單數(shù)據(jù),result.hasErrors()無(wú)論怎么輸入都會(huì)返回false,本文詳細(xì)的介紹一下解決方法2021-09-09
20秒教你學(xué)會(huì)java?List函數(shù)排序操作示例
這篇文章主要為大家介紹了20秒教你學(xué)會(huì)List函數(shù)排序操作示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-09-09
如何通過(guò)zuul添加或修改請(qǐng)求參數(shù)
這篇文章主要介紹了如何通過(guò)zuul添加或修改請(qǐng)求參數(shù)的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-07-07
Web容器啟動(dòng)過(guò)程中如何執(zhí)行Java類(lèi)
這篇文章主要介紹了Web容器啟動(dòng)過(guò)程中如何執(zhí)行Java類(lèi),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-10-10
Java微信公眾平臺(tái)之群發(fā)接口(高級(jí)群發(fā))
這篇文章主要為大家詳細(xì)介紹了Java微信公眾平臺(tái)之群發(fā)接口,高級(jí)群發(fā)功能,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-05-05
SpringBoot項(xiàng)目不占用端口啟動(dòng)的方法
這篇文章主要介紹了SpringBoot項(xiàng)目不占用端口啟動(dòng)的方法,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-08-08
Mybatis Select Count(*)的返回值類(lèi)型介紹
這篇文章主要介紹了Mybatis Select Count(*)的返回值類(lèi)型,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-12-12
SpringMVC 數(shù)據(jù)校驗(yàn)方法(必看篇)
下面小編就為大家?guī)?lái)一篇SpringMVC 數(shù)據(jù)校驗(yàn)方法(必看篇)。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-06-06
spring cloud gateway 如何修改請(qǐng)求路徑Path
這篇文章主要介紹了spring cloud gateway 修改請(qǐng)求路徑Path的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-06-06

