詳解什麼是 socket、套接字、「插座」!

    歡迎你們搜索「小猴子的技術筆記」關注個人公衆號,有問題能夠及時和我交流。編程

    你知道插座嗎?你知道網絡編程中的插座嗎?也許你會有點迷惑,什麼是插座!可是我若是說出「套接字」、「socket」這樣的關鍵字你就會恍然大悟。tomcat

    所謂的「插座」叫作套接字又叫作socket,用來表示一個端點,能夠與網絡中其餘的socket進行鏈接,而後進行數據的傳輸。安全

    咱們都知道在網絡上中能夠經過IP地址肯定惟一的一臺主機,而後主機和主機之間進行通信。可是準確來講:網絡通信中的雙方並非主機,而是主機中的進程。這就須要肯定主機中那個進程進行的網絡通信,所以還須要一個端口號來肯定主機中的惟一進程。微信

    若是從操做系統的角度來講:套接字是使用標準的Unix文件描述符來與其餘計算機進行通信的一種方式。網絡

    IP+PORT的組合就構成了網絡中惟一標識符「套接字」。端口號的範圍是0-65535,可是低於256的端口號進行了保留,做爲系統的標準的應用程序端口。好比HTTP的默認的80,POP3的110,Telent的23,FTP的21等。app

    套接字容許兩個進程進行通信,這兩個進程可能運行在同一個機器上,也可能運行在不一樣機器上。
在這裏插入圖片描述
    套接字主要有兩類:流式套接字和數據報套接字。框架

    流式套接字提供了面向鏈接、可靠的數據傳輸服務,能夠很是準確的實現按照順序接收數據。若是你經過流式套接字發送"h","e","l","l","o"五個字符,它到達另外一端的順序也將會是"h","e","l","l","o"。緣由就在於流式套接字使用的是TCP進行數據傳輸,可以保證數據的安全性。流式套接字是最常使用的,一些衆所周知的協議使用的都是它,如HTTP,TCP,SMTP,POP3等。
在這裏插入圖片描述
    數據報套接字:提供無鏈接的服務,你不須要像流式套接字那樣創建一個鏈接,而只須要將地址信息一同打包而後發出去。該服務使用的是UDP進行傳輸,延遲小,效率高可是不能保證數據傳輸的準確性。socket

    若是咱們建立一個ServerSocket須要經歷如下幾個步驟:bind-->listen-->accpet-->recv-->write-->close。這也是底層Linux/或者Unix對一個端口監聽經歷的步驟。this

    bind:綁定一個地址和端口,確認端點的信息。編碼

ServerSocket serverSocket = new ServerSocket(8888);

    這裏也許你看到了並無指明IP,那麼「ServerSocket」會自動獲取本機的ip地址做爲填充,源碼以下:

public ServerSocket(int port) throws IOException {
    this(port, 50, null);
}
public ServerSocket(int port, int backlog, InetAddress bindAddr) throws IOException {
    setImpl();
    if (port < 0 || port > 0xFFFF)
        throw new IllegalArgumentException("Port value out of range: " + port);
    if (backlog < 1)
      backlog = 50;
    try {
        bind(new InetSocketAddress(bindAddr, port), backlog);
    } catch(SecurityException e) {
        close();
        throw e;
    } catch(IOException e) {
        close();
        throw e;
    }
}
public InetSocketAddress(InetAddress addr, int port) {
    holder = new InetSocketAddressHolder(
                    null,
                    addr == null ? InetAddress.anyLocalAddress() : addr,
                    checkPort(port));
}

    會對「InetAddress」進行判斷,若是爲空了就獲取本地的地址。

    listen:當端口和ip綁定以後就會進行到監聽狀態,這個時候會監聽是否有鏈接請求上來。

    accept:若是客戶端的ip和端口符合服務端,也就是插頭可以對應上插座了,就接收這個客戶端的鏈接。這裏「ServerSocket.accept()」將「listen」和「accept」進行了整合,監聽而且接受一個鏈接。

Socket socket = serverSocket.accept();

在這裏插入圖片描述
    send:發送數據,須要先得到socket的的輸出流,而後經過輸出流進行數據的發送。

OutputStream out = socket.getOutputStream();

    拿到的是輸出流,所以發送的是字節,須要將字符串進行字節的轉換以後,在進行編碼以後發送。若是沒有進行編碼的話,默認採用的是系統的默認編碼。

out.write("hello".getBytes(StandardCharsets.UTF_8));

    recv: 接受數據,也須要先獲取到socket的輸入流。

InputStream inputStream = socket.getInputStream();

    獲取輸入流以後按照字節進行讀取數據。若是數據發送完畢了,也就是調用了「socket.close()」就會讀取到「-1」,表示沒有數據能夠讀到了。

InputStream in = socket.getInputStream();
StringBuffer sb = new StringBuffer();
int len;
while ((len = in.read(bytes)) != -1) {
    sb.append(new String(bytes, 0, len));
}
System.out.println("接收到消息:" + sb.toString());

    close:關閉(這個不是必須的,若是是長連接,那麼將不會關閉socket,除非服務發生異常)

serverSocket.close();

在這裏插入圖片描述
    上述的客戶端是阻塞式的,一次只能接受一個socket客戶端進行鏈接。

    客戶端要經歷:connect-->recv/send-->close

Socket socket = new Socket("localhost", 8888);

    recv:獲取到輸入流,讀取字節數字。

byte[] bytes = new byte[1024];
InputStream in = socket.getInputStream();
StringBuffer sb = new StringBuffer();
int len;
while ((len = in.read(bytes)) != -1) {
    sb.append(new String(bytes, 0, len));
}
System.out.println("接收到消息:" + sb.toString());

    write:獲取到輸出流,將數據按照字節輸出給服務端。

OutputStream out = socket.getOutputStream();
out.write("hello".getBytes(StandardCharsets.UTF_8));

    close:關閉資源

in.close();
out.close();
socket.close();
System.out.println("數據接收完畢");

在這裏插入圖片描述    至此基於一個簡單的socket網絡編程就完成了。咱們須要重點了解,socket是基於IP+Port組合的,用於網絡中的通訊之間的創建。網絡編程會在咱們的平常開發中起到很重要的做用,不少框架都是基於socket演變而來的,好比tomcat、netty等。    最後歡迎你們添加個人微信公衆號號"小猴子的技術筆記",若是有問題能夠及時和我交流。

相關文章
相關標籤/搜索