IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 网络协议 -> 从理论+实战的角度分析Http,Socket 与 WebSocket 三者的联系 -> 正文阅读

[网络协议]从理论+实战的角度分析Http,Socket 与 WebSocket 三者的联系

Http

Http (超文本传输协议)是应用层协议,是一个简单的请求-响应协议,基于 TCP 进行连接,它指定了客户端可能发送给服务器什么样的消息以及得到什么样的响应。请求和响应头以 ASCII 形式给出;而消息内容则具有一个类似 MIME的格式。Http就是基于这样的模型进行请求响应的。

Http 只能由客户端发起消息,由服务端接收,服务端无法主动向客户端发送消息。Http连接是短连接,即客户端向服务端发送一次请求,服务端响应后连接就会断掉。

Tcp:传输层协议

Ip:网络层协议

Socket

概念

Socket(套接字)是对网络中不同主机的应用进程之间进行双向通信的端点的抽象,一个套接字就是网络上进程通信的一端,Socket由 IP+Port 组合而成,提供了应用层进程利用网络协议交换数据的机制。

套接字上联应用进程,下联网络协议栈,是应用程序通过网络协议进行通信的接口,是应用程序与网络协议进行交互的接口。

Socket 是对 TCP/IP 协议的封装,Socket本身不是协议,而是一个调用接口(API),通过 Socket ,我们才能使用 TCP/IP 协议,所以说是Socket是应用程序与网路协议进行交互的接口。

Socket连接与 Http不同,是长连接,理论上客户端与服务端一旦建立连接,则不会主动断开,但是由于各种环境因素可能会断开连接,比如,服务端/客户端 宕机,网络故障,或者二者之间长时间没有数据的传输,那么为了维持连续的连接需要发送心跳消息,具体心跳消息格式是开发者自己定义的。

由于 Socket 是对TCP/IP的调用API,所以搞定 Socket必须要搞定 TCP

客户端与服务端进行连接时要经过TCP三次握手,三次握手成功后才开始传输数据,理想状态下,TCP连接一旦建立,在通讯双方中的任何一方主动断开连接之前TCP连接都会一直保持下去。当然,除了 TCP,Socket 也可以使用 UDP 协议来传输数据。

实例

Socket基于TCP

// 服务端
public class BIOServer {
    public static void main(String[] args) {
        ServerSocket serverSocket = null;
        Socket socket = null;
        InputStream in = null;
        OutputStream out = null;
        try {
            serverSocket = new ServerSocket(8000);
            System.out.println("服务器启动成功,监听端口为8000,等待客户端连接......");
            while (true) {
                // 阻塞
                socket = serverSocket.accept();
                System.out.println("客户端连接成功,客户信息为:" + socket.getRemoteSocketAddress());
                in = socket.getInputStream();
                byte[] bytes = new byte[1024];
                int len = 0;
                while ((len = in.read(bytes)) > 0) {
                    System.out.println(new String(bytes, 0, len));
                }
                out = socket.getOutputStream();
                out.write("hello!".getBytes());
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

// 客户端
public class BIOClient {
    public static void main(String[] args) throws IOException {
        Socket socket = new Socket("127.0.0.1", 8000);
        OutputStream outputStream = socket.getOutputStream();
        System.out.println("tcp连接成功 \n 请输入:");
        while (true) {
            byte[] bytes = new Scanner(System.in).nextLine().getBytes();
            outputStream.write(bytes);
            System.out.println("tcp协议的socket发送成功");
            // 刷新缓冲区
            outputStream.flush();
        }
    }
}

服务端先初始化Socket,包括绑定监听端口,进行监听,调用accept()阻塞,等待客户端连接。在此时如果有客户端初始化了一个Socket,连接服务端Socket成功后,这时客户端与服务端的连接就建立了。客户端发送数据请求,服务端接收并处理请求,服务端把响应数据发送给客户端,客户端读取数据,最后关闭连接,一次交互结束。

Socket基于UDP

// 服务端
public class Server {
    public static void main(String[] args) throws IOException {
        DatagramSocket datagramSocket = new DatagramSocket(8888);
        byte[] bytes = new byte[1024];
        DatagramPacket datagramPacket = new DatagramPacket(bytes, bytes.length);
        System.out.println("等待UDP协议传输数据");
        datagramSocket.receive(datagramPacket);
        System.out.println("接收消息:" + new String(bytes, 0, datagramPacket.getLength()));
        datagramSocket.close();
        System.out.println("UDP协议socket接收成功");
    }
}

// 客户端
public class Client {
    public static void main(String[] args) throws IOException {
        DatagramSocket datagramSocket = new DatagramSocket(2468);
        byte[] bytes = "UDP协议的Socket请求!".getBytes();
        InetSocketAddress inetSocketAddress = new InetSocketAddress("127.0.0.1", 8888);
        DatagramPacket datagramPacket = new DatagramPacket(bytes, bytes.length,inetSocketAddress);
        datagramSocket.send(datagramPacket);
        System.out.println("UDP协议的Socket发送成功");
        datagramSocket.close();
    }
}

UDP协议是用户数据报协议,也用于网络数据传输,虽然UDP协议是一种不太可靠的协议,但有时在需要较快的接收数据并且可以容忍错误的情况下,UDP就会有更大的优势。客户端只管发送,至于服务端能不能接收到不负责。

Socket通信的常用类

类名基于用途
SocketTCP同时工作与客户端与服务端,所有方法通用,此类有三个作用:校验包信息、发起连接、操作数据流数据
ServerSocketTCP表示为服务端,主要作用是绑定并监听服务端端口,为每个建立连接的客户端“映射”一个Socket对象,具体数据操作都是这个socket对象完成的,ServerSocket只关注如何和客户端建立连接
DatagramSocketUDP用于表示发送和接收数据报包的套接字
DatagramPacketUDP用于表示数据报包,数据报包用来实现无连接包投递服务
InetAddressIP+Port代表互联网协议地址,提供了两个静态方法来获取实例
InetSocketAddressIP+Port在Socket通信时靠 InetSocketAddress来实现IP+Port的创建,不依赖任何协议

WebSocket

WebSocket 是一种在单个 TCP连接上进行全双工通信的协议,WebSocket使得客户端与服务端的数据交换变得简单,允许服务端主动向客户端通信。在 WebSocket API 中,浏览器和服务器只需要完成一次握手,两者之间就可以创建持久性的连接,并进行双向数据传输。

概念

很多网站为了实现推送技术,都是客户端采用轮询机制访问服务端,即每隔一定时间由客户端主动发起请求,服务端再返回最新的数据。这种传统模式有很大弊端,客户端需要不断的向服务端发送请求,然而 Http 请求可能包含较长的头部,其中真正有效的数据可能只是其中很少一部分,显然这样会浪费很多带宽资源。

WebSocket使用了自定义的协议,未加密的连接不再是 http://,而是 ws://;加密的连接不再是 https://,而是wss://

使用自定义协议而非 Http协议的好处是,能在客户端和服务端之间发送非常少量的数据,而不必担心 Http那样的请求头开销,由于传输的数据包很小,因此 WebSocket 协议非常适合移动应用。

WebSocket的优势包括:

  • 较少的控制开销
  • 更强的实时性
  • 保存连接状态
  • 更好的二进制支持
  • 可以支持扩展
  • 更好的压缩效果

实例

本实例采用 Vue+SpringBoot 实现前后端通信。

客户端

export default {
  data() {
    return {
      websocket: null,
      data: {
        code: 0,
        item: "传输的数据"
      }
    };
  },
  methods: {
    onConfirm() {
      //需要传输的数据
      this.websocketsend(JSON.stringify(this.data));
    },
    initWebSocket() {
      if (typeof WebSocket === "undefined") {
        console.log("浏览器不支持websocket");
      } else {
        // 初始化weosocket
        this.websocket = new WebSocket(
          "ws://localhost:8089/websocket/feiyangyang"
        );
        this.websocket.onmessage = this.websocketonmessage;
        this.websocket.onerror = this.websocketonerror;
        this.websocket.onopen = this.websocketonopen;
        this.websocket.onclose = this.websocketclose;
      }
    },
    websocketonopen() {
      // 连接建立之后执行send方法发送数据
      this.websocketsend(JSON.stringify(this.data));
    },
    websocketonerror() {
      console.log("WebSocket连接失败");
    },
    websocketonmessage(e) {
      // 数据接收
      console.log("数据接收" + e.data);
    },
    websocketsend(Data) {
      // 数据发送
      this.websocket.send(Data);
    },
    websocketclose(e) {
      // 关闭
      console.log("已关闭连接", e);
    }
  },
  created() {
    console.log("created");
    this.initWebSocket();
  },

  destroyed() {
    this.websocket.close(); // 离开路由之后断开websocket连接
  }
};

服务端

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.70</version>
</dependency>
<dependency>
    <groupId>org.apache.tomcat</groupId>
    <artifactId>tomcat7-websocket</artifactId>
    <version>7.0.109</version>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
// 注入 ServerEndpointExporter bean
@Component
public class WebSocketConfig {
    @Bean
    public ServerEndpointExporter serverEndpointExporter() {
        return new ServerEndpointExporter();
    }
}

@ServerEndpoint("/websocket/{username}")
@Service
public class MyWebSocket {
    private static int onlineCount = 0;
    private static final Map<String, MyWebSocket> clients = new ConcurrentHashMap<>();
    private Session session;
    private String username;

    @OnOpen
    public void onOpen(@PathParam("username") String username, Session session) throws IOException {
        this.username = username;
        this.session = session;

        addOnlineCount();
        clients.put(username, this);
        System.out.println("已连接" + username);
    }

    @OnClose
    public void onClose() throws IOException {
        clients.remove(username);
        subOnlineCount();
    }

    @OnMessage
    public void onMessage(String message) throws IOException {
        System.out.println("message:" + message);
        // 发送数据给服务端
        sendMessageAll(JSON.toJSONString(message));
    }

    @OnError
    public void onError(Session session, Throwable error) {
        error.printStackTrace();
    }

    public void sendMessageTo(String message, String to) throws IOException {
        for (MyWebSocket item : clients.values()) {
            if (item.username.equals(to)) {
                item.session.getAsyncRemote().sendText(message);
            }
        }
    }

    public void sendMessageAll(String message) throws IOException {
        for (MyWebSocket item : clients.values()) {
            item.session.getAsyncRemote().sendText(message);
        }
    }

    public static synchronized int getOnlineCount() {
        return onlineCount;
    }

    public static synchronized void addOnlineCount() {
        MyWebSocket.onlineCount++;
    }

    public static synchronized void subOnlineCount() {
        MyWebSocket.onlineCount--;
    }

    public static synchronized Map<String, MyWebSocket> getClients() {
        return clients;
    }
}

服务端打印数据

已连接:feiyangyang
message:{"code":0,"item":"传输的数据"}

总结

TCP/IP协议栈主要分为四层,如下图所示。

[图片来自网络]

Http是短连接,即客户端向服务端发送一次请求,服务端响应后连接就会断开等待下次连接。

适用场景:

  • 公司OA服务
  • 互联网访问
  • 电商
  • 办公网站等

Socket是所谓的长连接,理论上客户端与服务端建立连接后将不会主动断掉。

适用场景:

  • 网络游戏
  • 银行持续交互
  • 直播
  • 在线视频等

WebSocket 是 html5 规范中的一部分,借鉴了 Socket的思想,为 web 应用程序客户端和服务端之间提供了一种全双工通信机制。

适用场景:

  • 社交聊天
  • 弹幕
  • 协同编辑文档
  • 股票基金实时报价
  网络协议 最新文章
使用Easyswoole 搭建简单的Websoket服务
常见的数据通信方式有哪些?
Openssl 1024bit RSA算法---公私钥获取和处
HTTPS协议的密钥交换流程
《小白WEB安全入门》03. 漏洞篇
HttpRunner4.x 安装与使用
2021-07-04
手写RPC学习笔记
K8S高可用版本部署
mySQL计算IP地址范围
上一篇文章      下一篇文章      查看所有文章
加:2021-08-17 15:45:03  更:2021-08-17 15:46:09 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/25 19:38:06-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码