- TCP Socket 通信概念
- TCP Socket 通信過(guò)程
- Socket 類
- ServerSocket 類
- 案例:文件上傳工具
??TCP 是面向連接的可靠數(shù)據(jù)傳輸協(xié)議跟畅。TCP 通信過(guò)程類似于打電話鸟悴,電話接通后雙方才能通話带射,在掛斷電話之前吞杭,電話一直占線廊遍。TCP 接連一旦建立起來(lái)踪旷,會(huì)一直占用植捎,知道關(guān)閉連接衙解。此外,TCP 為了保證數(shù)據(jù)的正確性焰枢,會(huì)重發(fā)一切沒有收到的數(shù)據(jù)蚓峦,還會(huì)對(duì)數(shù)據(jù)內(nèi)容進(jìn)行驗(yàn)證并保證數(shù)據(jù)傳輸?shù)恼_順序。因此 TCP 協(xié)議對(duì)系統(tǒng)資源有很高的要求济锄。
一暑椰、TCP Socket 通信概念
??Socket 是網(wǎng)絡(luò)上的兩個(gè)程序,通過(guò)一個(gè)雙向的通信連接荐绝,實(shí)現(xiàn)數(shù)據(jù)的交流一汽。這個(gè)鏈路的一端稱為一個(gè) Socket。Socket 通常用來(lái)實(shí)現(xiàn)客戶端和服務(wù)端的連接低滩。Socket 是 TCP/IP 協(xié)議的一個(gè)十分流行的編程接口召夹,一個(gè) Socket 有一個(gè) IP 地址和一個(gè)端口號(hào)唯一確定。一旦建立連接恕沫,Socket 還會(huì)包含本機(jī)和遠(yuǎn)程主機(jī)的 IP 地址和遠(yuǎn)端口號(hào)监憎,Socket 是成對(duì)出現(xiàn)的。
二婶溯、TCP Socket 通信過(guò)程
??使用 Socket 進(jìn)行 C/S 結(jié)構(gòu)編程的通信過(guò)程如下:
??服務(wù)器端監(jiān)聽某個(gè)端口是否有連接請(qǐng)求鲸阔,此時(shí)服務(wù)器端程序處于阻塞狀態(tài)偷霉,知道客戶端向服務(wù)器端發(fā)出連接請(qǐng)求,服務(wù)器端接收客戶端請(qǐng)求褐筛,服務(wù)器會(huì)應(yīng)答請(qǐng)求并處理請(qǐng)求类少,然后將結(jié)果應(yīng)答給客戶端,這樣就會(huì)建立連接渔扎。一旦連接建立起來(lái)瞒滴,通信 Socket 可以獲得輸入輸出流對(duì)象。借助于輸入輸出流對(duì)象就可以實(shí)現(xiàn)服務(wù)器與客戶端的通信赞警,最后不要忘記關(guān)閉 Socket 和釋放一些資源(包括關(guān)閉輸入輸出流)妓忍。
三、Socket 類
??java.net
包為 TCP Socket 編程提供了兩個(gè)核心類:Socket
和 ServerSocket
愧旦,分別用來(lái)表示雙向連接的客戶端和服務(wù)器端世剖。
??下面是Socket
類常用的構(gòu)造函數(shù):
Socket(address: InetAddress!, port: Int)
:創(chuàng)建 Socket 對(duì)象,并指定遠(yuǎn)程主機(jī) IP 地址和端口號(hào)笤虫;Socket(address: InetAddress!, port: Int, localAddr: InetAddress!, localPort: Int)
:創(chuàng)建 Socket 對(duì)象旁瘫,并指定遠(yuǎn)程主機(jī) IP 地址、端口號(hào)以及本機(jī)的 IP 地址 (localAddr) 和 端口號(hào) (localPort)琼蚯;Socket(host: String!, port: Int)
:創(chuàng)建 Socket 對(duì)象酬凳,并指定遠(yuǎn)程主機(jī)名和端口號(hào),IP 地址為 null遭庶,null 表示回送地址宁仔,即 127.0.0.1;Socket(host: String!, port: Int, localAddr: InetAddress!, localPort: Int)
:創(chuàng)建 Socket 對(duì)象峦睡,并指定遠(yuǎn)程主機(jī)翎苫、端口號(hào)以及本機(jī)的 IP 地址 (localAddr) 和 端口號(hào) (localPort)。host 為主機(jī)名榨了,IP 地址為 null煎谍,null 表示回送地址,即 127.0.0.1龙屉;
??提示:“數(shù)據(jù)類型!” 表示 “平臺(tái)類型”呐粘,String! 表示 String 或 String?。什么是平臺(tái)類型转捕?
??Socket
其他的常用函數(shù)和屬性如下:
getInputStream()
函數(shù):通過(guò)此 Socket返回輸入流對(duì)象作岖。getOutputStream()
函數(shù):通過(guò)此 Socket返回輸出流對(duì)象。port: Int
屬性:返回 Socket連接到的遠(yuǎn)程端口瓜富。localPort: Int
屬性:返回 Socket綁定到本地端口鳍咱。inetAddress
屬性:返回 Socket連接地址降盹。localAddress
屬性:返回 Socket綁定的本地地址与柑。isClosed
屬性:判斷返回 Socket是否處于關(guān)閉狀態(tài)谤辜。isConnected
屬性:判斷返回 Socket是否處于連接狀態(tài)。close()
函數(shù):關(guān)閉 Socket价捧。
??注意:Socket 與 流所占用的資源類似丑念,不能通過(guò) Java 虛擬機(jī)的垃圾收集器回收,需要程序員釋放结蟋。釋放的方式有兩種脯倚,一種是可以在 finally
代碼塊調(diào)用 close()
函數(shù)關(guān)閉 Socket,釋放流所占用的資源嵌屎。另一種是通過(guò)自動(dòng)資源管理技術(shù)釋放資源推正,Socket
和 ServerSocket
都實(shí)現(xiàn)了 AutoCloseable
接口,所以 kotlin 中可以使用 use
函數(shù)宝惰。
四植榕、ServerSocket 類
??ServerSocket
類常用的構(gòu)造函數(shù):
ServerSocket(port: Int, maxQueue: Int)
。創(chuàng)建綁定到特定端口的服務(wù)器 Socket尼夺。maxQueue 設(shè)置連接請(qǐng)求的最大隊(duì)列長(zhǎng)度尊残,入多隊(duì)列滿時(shí),則拒絕該連接淤堵。默認(rèn)值是 50寝衫。ServerSocket(port: Int)
。創(chuàng)建綁定到特定端口的服務(wù)器 Socket拐邪。連接請(qǐng)求的最大隊(duì)列長(zhǎng)度是 50慰毅。
??ServerSocket
其他的常用函數(shù)和屬性如下:
getInputStream()
函數(shù):通過(guò)此 Socket返回輸入流對(duì)象。getOutputStream()
函數(shù):通過(guò)此 Socket返回輸出流對(duì)象扎阶。isClosed
屬性:判斷返回 Socket是否處于關(guān)閉狀態(tài)事富。isConnected
屬性:判斷返回 Socket是否處于連接狀態(tài)。accept()
函數(shù):偵聽并接收到 Socket 的連接乘陪。此函數(shù)在建立連接之前一直是阻塞狀態(tài) 统台。
??ServerSocket
類本身不能直接獲得 I/O 流對(duì)象,而是通過(guò) accept()
函數(shù)返回 Socket 對(duì)象啡邑,通過(guò) Socket 對(duì)象取得 I/O 流對(duì)象贱勃,進(jìn)行網(wǎng)絡(luò)通信。另外谤逼,ServerSocket
也實(shí)現(xiàn)了 AutoCloseable
接口贵扰,通過(guò)自動(dòng)資源管理技術(shù)關(guān)閉 ServerSocket
。
五流部、案例:文件上傳工具
- 服務(wù)器端代碼:
fun main(args: Array<String>?) {
println("服務(wù)器端運(yùn)行...")
ServerSocket(8080).use { server ->
server.accept().use { socket ->
BufferedInputStream(socket.getInputStream()).use { bis ->
FileOutputStream("./TestDir/subDir/fxy.png").use { fos ->
bis.copyTo(fos)
println("接收完成戚绕!")
}
}
}
}
}
??注意:ServerSocket
的 accept()
函數(shù)阻塞當(dāng)前線程,所以一般會(huì)是在子線程中執(zhí)行 accept()
函數(shù)枝冀。
- 客戶端代碼:
fun main(args: Array<String>?) {
println("客戶端運(yùn)行...")
Socket("127.0.0.1", 8080).use { socket ->
BufferedOutputStream(socket.getOutputStream()).use { bos ->
FileInputStream("./TestDir/fxy.png").use { fis ->
fis.copyTo(bos)
println("上傳成功舞丛!")
}
}
}
}