本篇內容包括:Socket 套接字的簡介、Socket 套接字的分類、Java 中的 Socket 即 java.net.ServerSocket、java.net.Socket 的使用,以及Java 使用套接字 Scoket 編程的Demo。
一、Socket 簡介
TCP(傳輸控制協議)是一種面向連接的、可靠的、基于字節流的通信協議,數據在傳輸前要建立連接,傳輸完畢后還要斷開連接。TCP 協議提供的是點對點的通信,每條 TCP 連接由兩端的套接字唯一確定。可以理解為 TCP 連接兩端的套接字來連起來就形成了管道,管道的兩端或者說管道的端口就是 Socket 套接字。
Socket 的原意是“插座”,在計算機通信領域,Socket 被翻譯為“套接字”,它是計算機之間進行通信的一種約定或一種方式。通過 socket 這種約定,一臺計算機可以接收其他計算機的數據,也可以向其他計算機發送數據。
Socket 的典型應用就是 Web 服務器和瀏覽器:瀏覽器獲取用戶輸入的 URL,向服務器發起請求,服務器分析接收到的 URL,將對應的網頁內容返回給瀏覽器,瀏覽器再經過解析和渲染,就將文字、圖片、視頻等元素呈現給用戶。
二、Socket 分類
TCP/IP 協議族提供三種常見的 Socket 類型:流式 Socket(SOCK_STREAM)流式套接字、數據報 Socket(SOCK_DGRAM)數據報套接字、原始 Socket(SOCK_RAW)原始套接字。
1、流式套接字(SOCK_STREAM)
用于提供面向連接、可靠的數據傳輸服務。該服務將保證數據能夠實現無差錯、無重復發送,并按順序接收。流套接字之所以能夠實現可靠 的數據服務,原因在于其使用了傳輸控制協議 TCP。 這類套接字中,傳輸數據之前必須在兩個應用進程之間建立一條通信連接, 這就確保了參與通信的兩個應甩進程都是活動并具響應的e當連接建立之卮應用進程只要通過套接字向 TCP 層發送數據流,而另一個應用進程便可以接收到相應的數據流,它們不需要知道傳輸層是如何對數據流進行處理。特別責要注意的是通信連接必須顯式建文。該套接字類型適食傳輸大量的數據,但不支持廣播和多播方式。
2、數據報套接字(SOCK_DGRAM)
提供了一種無連接的服務,通信雙方不需要建立任何顯式連接,數據可以發送到指定的套接字,并且可以從指定的套接字接收數據。該服務并不能保證數據傳輸的可靠性,數據有可能在傳輸過程中丟失或出現數據重復,且無法保證順序地接收到數據。數據報套接字使用UDP進行數據的傳輸。由于數據包套接字不能保證數據傳輸的可靠性,對于有可能出現的數據丟失情況,需要在程序中做相應的處理。與數據報套接字相比,使用流式套接字是一個更為可靠的方法,但對于某些應用,建立一個顯式連接所導致的系統開銷是令人難以接收的,并且數據報套接字支持廣播和多播方式。
3、原始套接字(SOCK_RAW)
與標準套接字(標準套接字指的是前面介紹的流套接字和數據報套接字)的區別在于:原始套接字可以讀寫內核沒有處理的 IP 數據包,而流套接字只能讀取 TCP 的數據,數據報套接字只能讀取 UDP 的數據。使用原始套接字的主要目的是為了避開 TCP/IP 處理機制,被傳送的數據包可以被直接傳送給需要它的應用程序。因此,其主要是在編寫自定義底層協議的應用程序時使用,例如各種不同的 TCP/IP 實用程序
三、Java 中的 Socket
Java 中對 Socket 的使用是基于兩個類 java.net.ServerSocket、java.net.Socket
1、java.net.Socket 構造方法
//不含參構造方法
Socket();
// 創建一個流套接字并將其連接到指定 IP 地址的指定端口號。
Socket(InetAddress address, int port)
// 創建一個流套接字并將其連接到指定主機上的指定端口號
Socket(String host, int port)
// 創建一個套接字并將其連接到指定遠程地址上的指定遠程端口
Socket(InetAddress address, int port, InetAddress localAddr, int localPort)
// 創建一個套接字并將其連接到指定遠程主機上的指定遠程端口
Socket(String host, int port, InetAddress localAddr, int localPort)
2、java.net.Socket 常用方法
// 將此套接字連接到服務器
connect(SocketAddress endpoint)
// 將此套接字連接到服務器,并指定一個超時值
connect(SocketAddress endpoint, int timeout)// 返回服務端的ip地址
getInetAddress();
// 獲取服務端的端口號
getPort();
// 獲取本地客戶端的ip地址
getLocalAddress();
// 獲取本地客戶端的端口號
getLocalPort();
// 返回此套接字的輸入流
getInputStream()
// 返回此套接字的輸出流
getOutputStream()// 根據連接是否關閉返回一個boolean值,關閉則返回true,否則返回false
isClose();
// 如果連接是否曾被連接過則返回true,否則返回false
isConnect();
// 如果Socket已經與本地的一個端口綁定,返回true,否則返回false
isBound();// 關閉輸入流
shutdownInput();
// 關閉輸出流
shutdownOutput();// 關閉Socket
close();
3、java.net.ServerSocket 構造方法
// 創建綁定到特定端口的服務器套接字
ServerSocket(int port)
4、java.net.ServerSocket 常用方法
// 偵聽并接受到此套接字的連接。
accept()
// 返回此服務器套接字的本地地址
getInetAddress()
四、Java Socket Demo
Demo:編程實現基于 TCP 的 Socket 服務器端和客戶端的通信
1、Demo 服務端
服務端的 Socket Demo 流程思路:
- 創建 ServerSocket 對象,綁定監聽端口;
- 通過
accept()
方法監聽客戶端請求; - 鏈接建立后,通過輸入流讀取客戶端發送的請求信息;
- 通過輸出流向客戶端發送響應信息;
- 關閉相關資源。
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;/** 基于TCP協議的Socket通信,實現用戶登錄* 服務器端*/
public class Server {public static void main(String[] args) {try {// 1、創建一個服務器Socket,即ServerSocket,指定綁定的端口,并監聽此端口ServerSocket serverSocket = new ServerSocket(8888);// 2、調用()方法開始監聽,等待客戶端的連接System.out.println("服務器即將啟動,等待客戶端的連接");Socket socket = serverSocket.accept();// 就會處于阻塞的狀態,等待監聽// 3、獲取輸入流,病讀取客戶端信息InputStream is = socket.getInputStream();// 字節輸入流// 將字節流轉換為字符流InputStreamReader isr = new InputStreamReader(is);// 為輸入流添加緩沖BufferedReader br = new BufferedReader(isr);String info = null;while((info = br.readLine())!=null){System.out.println("我是服務器,讀取客戶端發過來的信息:"+info);}socket.shutdownInput();//關閉輸入流// 關閉資源br.close();isr.close();is.close();socket.close();serverSocket.close();} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}}}
2、Demo 客戶端
客戶端的 Socket Demo 流程思路:
-
創建 Socket對象,指明需要連接的服務器的地址和端口號;
-
連接建立后,通過輸出流向服務器端發送請求信息;
-
通過輸入流獲取服務器響應的信息;
-
關閉相關資源。
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.UnknownHostException;/** 客戶端*/
public class Client {public static void main(String[] args) {// 1、創建客戶端Socket,指定服務器地址和端口try {Socket socket = new Socket("localhost", 8888);// 2、獲取輸出流,向服務器端發送信息OutputStream os = socket.getOutputStream();// 獲取字節輸出流// 將輸出流包裝為打印流PrintWriter pw = new PrintWriter(os);pw.write("用戶名:user 密碼:pawd");pw.flush();socket.shutdownInput();//關閉輸出流// 3、關閉資源pw.close();os.close();socket.close();} catch (UnknownHostException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}}
}