1. 程式人生 > >http協議下:為什麼請求與響應會做到準確誤的對應。不會出現請求與響應的錯亂

http協議下:為什麼請求與響應會做到準確誤的對應。不會出現請求與響應的錯亂

網際網路通訊是套接字進行通訊的,套接字,是支援TCP/IP的網路通訊的基本操作單元,可以看做是不同主機之間的程序進行雙向通訊的端點,簡單的說就是通訊的兩方的一種約定,用套接字中的相關函式來完成通訊過程。
非常非常簡單的舉例說明下:套接字=Ip address+ TCP/UDP + port。

java 針對TCP 和 UDP進行了分別的開發封裝: Socket,DatagramSocket (早期 JAVA開發者沒有開發,DatagramSocket,TCP和UDP都是使用socket的進行開發的,後期加上了DatagramSocket後就把socket中開發UDP的API 廢棄了,
現在我們通常說socket開發tcp,DatagramSocket開發UDP)我們通常使用的網際網路開發都是tcp,http 建立在tcp之上的協議,即:一個請求一個響應,請求與響應
本質都是socket。也就是說socket與socket之間的通訊,通訊完畢就會結束連結,即無狀態的連結,即這次連線和下次連線都是獨立的互動。
請求與響應是一一對應的,不會出現A的響應給了B的請求,因為socket通訊是靠四元組進行對接通訊的
socket: 四元組 本地IP,本地Port,目標IP,目標Port

serversocket:本質也是socket,為了開發伺服器這一角色,所以將socket封裝成了serversocket,建立serversocket的時候底層會建立一個socket,然後
會繫結這個socket,同時監聽這個socket的埠號並且會提前建立一個用於響應的socket。這樣就會做到其他的socket請求該serversocket中的socket,那麼監聽器就會監聽到並觸發建立一個新的socket與請求socket進行通訊,並且重新監聽(擴充套件:如果還沒有來的及從新監聽,就在這時,突然有大量的請求過來,那麼此時伺服器很有可能會崩潰也有你可能拒絕你的請求,此時就出現了訊息中介軟體,比如kafka,activeMQ 等即,讓請求訊息先來我這裡排隊,拍好隊形在一個一個的去請求,這樣就不會突然出現高併發了,放在伺服器受不了,最後一個簡單的案例說明。 )。並且會提前建立一個socket,等待請求。
原始碼:
  public Socket accept() throws IOException {
        if (isClosed())
            throw new SocketException("Socket is closed");
        if (!isBound())
            throw new SocketException("Socket is not bound yet");
        Socket s = new Socket((SocketImpl) null); //提前建立好socket 響應
        implAccept(s);  //當沒有請求時執行緒會停在這裡,等待請求 
        return s;
    }


當客戶端建立請求socket時,Socket socket = new Socket("localhost",6472); 當請求socket連結到伺服器時,socket才算建立成功,否者建立socket失敗並會
報錯:Exception in thread "main" java.net.ConnectException: Connection refused: connect
當請求socket建立成功後: 四元組 本地IP,本地Port,目標IP,目標Port 都會初始化完成,
                                本地IP(假設為172.168.17.9),本地Port(java底層會隨機給一個未佔用的port,假設為2635)
                                目標IP(伺服器的IP,假設為192.168.17.88),目標Port(伺服器的埠 假設為 8080) 

此時accept()收到請求並且針對當前請求的socket返回一個響應socket,並且該響應socket的四元組 本地IP,本地Port,目標IP,目標Port
 都會初始化完成,                                                                                     
                                本地IP(伺服器當前的IP:192.168.17.88),本地Port(java底層會隨機給一個未佔用的port,假設為1526),
                                目標IP(請求的IP:172.168.17.9)目標Port(請求的埠:2635 ) 

                                
此時,請求與響應才會做到了準備無誤的對應。才不會出現請求與響應的錯亂。

demo:

package com.jvm.others.socket_;
import java.net.Socket;
public class Demo2Soket { //客戶端
    public static void main(String[] args) throws Exception {
        
        Socket socket = new Socket("localhost",6478);
        String hostAddress = socket.getLocalAddress().getHostAddress();
        int localPort = socket.getLocalPort();
        SocketUtiles.pringHostAndPort(socket);
        
    }
}

客戶端列印結果:
     Local socket Host : 127.0.0.1
     Local socket HostAddress : 127.0.0.1
     Local socket Port : 60737
     des socket Host : localhost
     des socket HostAddress : 127.0.0.1
     des socket Port : 6478
     當前請求時間 15 5944


package com.jvm.others.socket_;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Date;
public class Demo2ServerSocket { //伺服器
      public static void main(String[] args) throws Exception {
        ServerSocket ss = new ServerSocket(6478);
        while(true){
        Socket accept = ss.accept();
        SocketUtiles.pringHostAndPort(accept);
        }
         }
}

伺服器列印方式:
       Local socket Host : 127.0.0.1
       Local socket HostAddress : 127.0.0.1
       Local socket Port : 6478
       des socket Host : 127.0.0.1
       des socket HostAddress : 127.0.0.1
       des socket Port : 60737
       當前請求時間 15 5944
       

擴充套件程式碼       
併發案例伺服器受不了拒絕了你:

package com.jvm.others.socket_;
import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
//模擬客戶端請求
public class DemoSocket {
    public static void main(String[] args) throws Exception {
        for(int i = 0;i<1000;i++){
        Socket socket = new Socket("localhost",6466);
        String hostAddress = socket.getLocalAddress().getHostAddress();
        int localPort = socket.getLocalPort();
        SocketUtiles.pringHostAndPort(socket);
        }
    }
}

package com.jvm.others.socket_;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Date;
//模擬伺服器
public class DemoSocketServer {
    static int i= 1;
  public static void main(String[] args) throws Exception {
    ServerSocket ss = new ServerSocket(6466);
    while(true){
    Socket accept = ss.accept();
    if(i==1){
        Date date = new Date();
        System.out.println("開始睡覺 當前時間為 : "+date.getHours()+" "+date.getMinutes()+" "+date.getSeconds());
       Thread.sleep(4000); //模擬來不及監聽,停了4秒
       Date date2 = new Date();
        System.out.println("睡覺結束 當前時間為 : "+date2.getHours()+" "+date2.getMinutes()+" "+date.getSeconds());
    }
    i++;
    SocketUtiles.pringHostAndPort(accept);
    }
     }
}
        
//工具類  
package com.jvm.others.socket_;
import java.net.Socket;
import java.util.Date;
public class SocketUtiles {
    public  static void pringHostAndPort(Socket s){
        Date date = new Date();
        System.out.println(" Local socket Host : "+s.getLocalAddress().getHostName());
        System.out.println(" Local socket HostAddress : "+ s.getLocalAddress().getHostAddress());
        System.out.println(" Local socket Port : "+s.getLocalPort());
        System.out.println("  des socket Host : "+s.getInetAddress().getHostName());
        System.out.println("  des socket HostAddress : "+ s.getInetAddress().getHostAddress());
        System.out.println("  des socket Port : "+s.getPort());
        System.out.println("當前請求時間 "+date.getHours()+" "+date.getMinutes()+date.getSeconds());
    }
}

請求客戶端會報Exception in thread "main" java.net.ConnectException: Connection refused: connect 拒絕連結。