其他分享
首页 > 其他分享> > 分布式学习笔记(二)——远程通信协议

分布式学习笔记(二)——远程通信协议

作者:互联网

远程通信原理

网络分层模型

http 协议的通信是基于 tcp/ip 协议之上的一个应用层协议,应用层协议除了 http 还有FTP、DNS、SMTP、Telnet 等。涉及到网络协议,一定需要知道 OSI 七层网络模型和 TCP/IP 四层概念模型,OSI 七层网络模型包含(应用层、表示层、会话层、传输层、网络层、数据链路层、物理层)、TCP/IP 四层概念模型包含(应用层、传输层、网络层、数据链路层)。
在这里插入图片描述

http请求原理

http协议是基于TCP/IP的,一个http请求在经过各个层时,其中每一层对收到的数据都要增加一些首部信息(有时还要增加尾部信息)
发送请求:
在这里插入图片描述
接收请求:
在这里插入图片描述

分层负载

二层负载均衡
二层负载是针对 MAC,负载均衡服务器对外依然提供一个 VIP(虚 IP),集群中不同的机器采用相同 IP 地址,但是机器的 MAC 地址不一样。当负载均衡服务器接受到请求之后,通过改写报文的目标 MAC 地址的方式将请求转发到目标机器实现负载均衡。
三层负载均衡
三层负载是针对 IP,和二层负载均衡类似,负载均衡服务器对外依然提供一个 VIP(虚 IP),但是集群中不同的机器采用不同的 IP 地址。当负载均衡服务器接受到请求之后,根据不同的负载均衡算法,通过 IP 将请求转发至不同的真实服务器。
四层负载均衡
四层负载均衡工作在 OSI 模型的传输层,由于在传输层,只有 TCP/UDP 协议,这两种协议中除了包含源 IP、目标 IP 以外,还包含源端口号及目的端口号。四层负载均衡服务器在接受到客户端请求后,以后通过修改数据包的地址信息(IP+端口号)将流量转发到应用服务器。
七层负载均衡
七层负载均衡工作在 OSI 模型的应用层,应用层协议较多,常用 http、radius、dns 等。七层负载就可以基于这些协议来负载。这些应用层协议中会包含很多有意义的内容。比如同一个Web 服务器的负载均衡,除了根据 IP 加端口进行负载外,还可根据七层的 URL、浏览器类别来决定是否要进行负载均衡。

TCP报文格式

在这里插入图片描述
其中比较重要的字段有:

(1)序号(sequence number):seq序号,占32位,用来标识从TCP源端向目的端发送的字节流,发起方发送数据时对此进行标记。

(2)确认号(acknowledgement number):ack序号,占32位,只有ACK标志位为1时,确认序号字段才有效,ack=seq+1。

(3)标志位(Flags):共6个,即URG、ACK、PSH、RST、SYN、FIN等。具体含义如下:

不要将确认序号Ack与标志位中的ACK搞混了。确认方ack=发起方seq+1,两端配对。

TCP的三次握手

TCP协议是可靠的协议,可靠性来自于有效的连接建立。在数据进行传输前,需要通过三次握手建立一个连接,所谓的三次握手,就是在建立 TCP 链接时,需要客户端和服务端总共发送 3 个包来确认连接的建立。在 socket 编程中,这个过程由客户端执行 connect 来触发。
在这里插入图片描述
握手之前主动打开连接的客户端结束CLOSED阶段,被动打开的服务器端也结束CLOSED阶段,并进入LISTEN阶段。随后开始“三次握手”:
1、客户端向服务器发送一段TCP报文。包含SYN=1,标识位,表示请求建立连接,seq=x,带上自己的序号。然后进入SYN-SENT状态
2、服务器接收到客户端的报文,然后回复一份报文。包含标志位SYN=1、ACK=1,表示收到客户端申请建立连接的请求;seq=y,带上自己的序号,ack=x+1(,表示在收到建立连接的请求的基础上,将客户端的序号seq加1作为确认号ack的值)。然后结束LISTEN状态,进入SYN-RCVD状态。
3、客户端收到服务器回复的报文后,结束SYN-SENT状态,然后发送最后一份报文,包含ACK=1,表示确认收到服务器端同意连接的信号,seq=x+1,表示在收到“服务器同意建立连接”信号的基础上,将确认号作为自己的序号;ack=y+1,表示在收到信号的基础上将服务器端的序号+1作为确认号,随后客户端进入ESTABLISHED阶段。

服务器收到客户端发送的确认报文,明确了从服务器到客户端的数据传输是正常的。结束SYN-SENT阶段,进入ESTABLISHED阶段。

TCP四次挥手协议

四次挥手表示 TCP 断开连接的时候,需要客户端和服务端总共发送 4 个包以确认连接的断开。在 socket 编程中,任何一方执行 close() 操作即可产生挥手操作。
在这里插入图片描述
1、首先客户端想要释放连接,向服务器端发送一段TCP报文。包含:FIN=1,为标识位,表示“请求释放连接”;seq=u,客户端序号u。然后,进入FIN-WAIT1状态,并且停止在客户端到服务器端方向上发送数据(不包括确认报文),但是客户端仍然能接收从服务器端传输过来的数据。
2、服务器收到客户端“请求释放连接”的报文,知道了客户端想释放连接,于是结束ESTABLISHED状态,进入CLOSE-WAIT状态,然后回复一份报文。包含ACK=1,标识位,表示接收到客户端发送的断开连接请求;seq=v,服务器的序号;ack=u+1,表示将客户端的序号+1作为确认号的值。然后服务器端开始准备释放这个连接,客户端收到从服务器端发出的确认报文之后,确认了服务器收到了客户端发出的释放连接请求,随后客户端结束FIN-WAIT-1状态,进入FIN-WAIT-2状态。
3、服务器做好了释放连接的准备后,再向客户端发送一份报文,包含FIN=1,ACK=1,标识位,表示“已经准备好释放连接了”,这里的ACK并不是确认收到服务器端报文的确认报文;seq=w,服务器端的序号;ack=u+1,表示将客户端的序号+1作为确认号的值。随后服务器端结束CLOSE-WAIT状态,进入LAST-ACK状态。并且停止在服务器端到客户端的方向上发送数据,但是服务器端仍然能够接收从客户端传输过来的数据。
4、客户端收到服务器端的报文,确认了服务器端已做好释放连接的准备,结束FIN-WAIT-2状态,进入TIME-WAIT状态,并向服务器端发送一段报文,包含ACK=1,表示已经接收到服务器做好释放连接的准备的信号了;seq=u+1,表示是在收到了服务器端报文的基础上,将其确认号ack值作为本段报文序号的值;ack=w+1,表示在收到了服务器端报文的基础上,将服务器的序号+1作为确认号。随后客户端开始在TIME-WAIT状态等待2MSL,然后关闭。

之所以建立连接需要三次握手,而断开连接需要四次挥手。是因为建立连接时,服务器端收到收到客户端的SYN 连接请求报文后,可以直接回复SYN+ACK报文。而释放连接时,服务器端收到FIN 报文时,可能还有消息没有处理完,所以只能先回复一个 ACK 报文,等准备好之后,才能发送FIN报文确认,所以多了一步。

使用socket完成基于TCP的通信

Server端:

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;

public class SocketServer {
    public static void main(String[] args) {

        ServerSocket server = null;
        Socket socket = null;
        BufferedReader in = null;
        BufferedWriter out = null;
        try {
            server = new ServerSocket(80);
            socket = server.accept();

            in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            String s= in.readLine();  //读取时以\n字符作为结束
            System.out.println("client:"+s);

            out = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
            out.write("已收到!\n"); //需要加入\n,否则使用readLine()读取时,不会结束
            out.flush();

        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (server!=null){
                    server.close();
                }
                if (socket!=null){
                    socket.close();
                }
                if (in!=null){
                    in.close();
                }
                if (out!=null){
                    out.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

client端:

import java.io.*;
import java.net.Socket;

public class SocketClient {
    public static void main(String[] args) {

        Socket socket = null;
        BufferedReader in = null;
        BufferedWriter out = null;
        try {
            socket = new Socket("127.0.0.1",80);
            out = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
            out.write("服务器你好!\n");
            out.flush();
            in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            System.out.println("Server:"+in.readLine());
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {

                if (socket!=null){
                    socket.close();
                }
                if (in!=null){
                    in.close();
                }
                if (out!=null){
                    out.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

socket通信原理
在这里插入图片描述

标签:负载,服务器端,报文,通信协议,笔记,socket,分布式,连接,客户端
来源: https://blog.csdn.net/qq_34609889/article/details/123253902