websocket由浅入深

websocket是一种通信协议,建立在 TCP 协议之上,也依赖于http协议。

什么情况下会用websocket 呢?

我们经常用的http协议是一种单工协议,就是只能从client发出请求然后server响应请求,单方面的请求。有这样一种场景就是当server端的数据更新后要实时的推送给client,此时http协议能做的就是通过ajax定时去轮询的请求server,显然这种方式是不合理的。这时就需要websocket出马,websocket的特点就是服务器可以主动向客户端推送信息,客户端也可以主动向服务器发送信息,是双向平等对话,协议标识符是ws(相当于http)(如果加密,则为wss(相当于https)

websocket简单使用

在tomcat 中为我们提供了学习websocket 的案例,我们需要下载一个tomcat然后配置tomcat环境(此处省略配置)。我们配置好tomcat后,在他的examples中可以找到,如下图所示,这里面就是提供的案例

 我们把tomcat启动起来,在浏览器中输入http://localhost:8080/examples/websocket/index.xhtml,就会看到提供的四个例子,首先点击进去第一个例子。

点击进去以后,我们按f12 打开监控台,在输入框中输入 ws://localhost:8080/examples/websocket/echoAnnotation  服务器的连接地址,然后点击connect 按钮,在右边会打印出 opened,同时在监控台中会看到捕捉到了一个http 的GET请求,请求地址就是我们输入的地址,并且在请求头中会携带一些特殊的请求头,如图中。然后你在点击Echo message 按钮,右边框会有消息打印出,但是监控台是没有变化的,这就是简单案例

到这里,我们说下websocket 的通信分为,握手建立连接通道,连接建立消息通信,通道关闭三个步骤。上图中的3 就是建立连接通道的的过程,通过http协议建立连接,使用GET方式并在请求头中添加了通信凭证等消息。连接建立以后,就相当于client和server中间就有了一条管道,消息通过管道进行双向传输,这条管道通过client和server的心跳消息维持,直到服务器关闭或者页面关闭,通道一直存在,所以在4发送消息的过程中没有捕捉到新的请求

websocket服务端开发

添加依赖

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-websocket</artifactId>
        </dependency>

配置类 

@Configuration
public class WebSocketConfig {
    /**
     * 这个Bean会自动注册使用@ServerEndpoint注解声明的websocket endpoint
     * @return
     */
    @Bean
    public ServerEndpointExporter serverEndpointExporter() {
        return new ServerEndpointExporter();
    }
}

Endpoint编写

//使用ServerEndpoint注解 标识一个websocket的终端,就相当controller里面的访问路径
    //{id}相当于传递的参数
@ServerEndpoint("/socket/{id}")
@Component
@Slf4j
public class WebSocketEndpoint {
    /**
     * 连接建立成功调用的方法*/
    @OnOpen
    public void onOpen(Session session,@PathParam(value = "id") String id) {
        log.info(this.hashCode() +":onOpen session="+session.getId() +"  参数id="+id);
    }

    /**
     * 连接关闭调用的方法
     */
    @OnClose
    public void onClose(@PathParam(value = "id") String id) {
        log.info("onClose  参数id="+id);
    }

    /**
     * 收到客户端消息后调用的方法
     *
     * @param message 客户端发送过来的消息*/
    @OnMessage
    public void onMessage(String message, Session session,@PathParam(value = "id") String id) {
        log.info(this.hashCode() +":onMessage session="+session.getId()+" message ="+message+"  参数id="+id);
    }

    /**
     *
     * @param session
     * @param error
     */
    @OnError
    public void onError(Session session, Throwable error) {
        log.error("onError");
    }
}

启动类

@RestController
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class);
    }

}

把服务端启动起来,我们还使用刚才那个前端,我们同时打开两个页面

我们分别点解A 和 B 的connect按钮,查看服务端的打印结果

两次连接的WebSocketEndpoint的hashCode 不一样,说明对于每次连接都会创建一个WebSocketEndpoint对象来处理,而且session也不一样,说明不是一个会话,即使在同一个浏览器。

接下来我们多点击几次发送消息的按钮,看看情况,打印结果

在发送消息的时候对象的hashCode 不会变,session不会变

当把刚才的两个客户端关闭时,服务端打印出

到此我们大概对websocket的服务端编写有了一个流程上的认识

应用编程

根据上面的实现,我们大致了解要实现一个websocket应用程序,我们大体有几个步骤

  1. 建立连接
  2. 客户端给服务端发送数据(或者服务端给客户端发送数据)
  3. 服务端接收(或者客户端接收数据)
  4. 当关闭的时候断开连接
  5. 监听事件OnOpen,OnMessage,OnClose,OnError

客户端实现,客户端我使用vue ,在前端的方法里面我都把event 打印了一下,大家可以打开浏览器监控台看看event里面都有什么东西,这里就不说了

<template>
  <div>
    连接地址: <input type="text" id="conn" placeholder="ws://tmp.server.com:port/socket" v-model="connAddress"/>
    <button v-on:click="click">连接</button>
    <div>
      <textarea  v-model="msg"></textarea>
      <button v-on:click="webSocketSend">发送消息</button>
    </div>
    接收到的消息:
    <div>
      <textarea  v-model="receive"></textarea>
    </div>
  </div>
</template>

<script>
  export default {
    name: 'websocket',
    data() {
      return {
        connAddress: 'ws://localhost:8081/socket',
        msg: '',
        webSocket: null,
        receive: ''
      }
    },
    created() {
      // this.initWebSocket();

    },
    destroyed() {
      this.webSocket.close() //离开路由之后断开websocket连接
    },
    methods: {
      click() {
        this.initWebSocket();
      },
      initWebSocket(){
        this.webSocket = new WebSocket(this.connAddress);
        this.webSocket.onopen =this. webSocketOnOpen();
        this.webSocket.onmessage = this.webSocketOnMessage;
        this.webSocket.onopen = this.webSocketOnOpen;
        this.webSocket.onerror = this.webSocketOnError;
        this.webSocket.onclose = this.webSocketClose;
      },
      webSocketOnOpen(event){ //连接建立之后执行send方法发送数据
        console.log('连接建立',event);
      },
      webSocketOnError(event){//连接建立失败重连
        console.log('失败',event);
        this.initWebSocket();
      },
      webSocketOnMessage(event){ //数据接收
        console.log('数据接收',event);
        this.receive = event.data;
      },
      webSocketClose(event){  //关闭
        console.log('断开连接',event);
      },
      webSocketSend(){//数据发送
        console.log(this.msg)
        this.webSocket.send(this.msg);
      },
    }
  }
</script>

<style scoped>
  #conn {
    width: 30%;
  }

  textarea {
    width: 30%;
    height: 150px;
  }
</style>

服务端 ,这里我把构造方法加上了,当每次建立连接的时候构造方法都会执行,更说明每次都会建立新的服务端对象

@ServerEndpoint("/socket")
@Component
@Slf4j
public class WebSocketEndpoint {

    public WebSocketEndpoint(){
        log.info("构造方法执行:"+this.hashCode());
    }

    /**
     * 连接建立成功调用的方法*/
    @OnOpen
    public void onOpen(Session session ) {
        log.info( " onOpen session="+session.getId()  );
    }

    /**
     * 连接关闭调用的方法
     */
    @OnClose
    public void onClose(Session session ) {
        log.info(session.getId()+":关闭了");
    }

    /**
     * 收到客户端消息后调用的方法
     *
     * @param message 客户端发送过来的消息*/
    @OnMessage
    public void onTextMessage( Session session,String message) {
        log.info("onMessage session="+session.getId()+" message ="+message );
        session.getAsyncRemote().sendText("我是服务端:"+message);
    }

    /**
     * @param session
     * @param error
     */
    @OnError
    public void onError(Session session, Throwable error) {
        log.error("onError");
    }
}

下一篇 websocket由浅入深二 我们写一个稍微复杂一点的例子,websocket文件流的传输

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值