Socket心路历程

后端篇

这是从朋友那得到的一个 socket类

package com.communication.firstjob.util;

import org.springframework.stereotype.Component;

import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;

@ServerEndpoint("/webSocket/{sid}")
@Component
public class WebSocketUtil {
    //静态变量,用来记录当前在线连接数。应该把它设计成线程安全的。
    private static AtomicInteger onlineNum = new AtomicInteger();

    //concurrent包的线程安全Set,用来存放每个客户端对应的WebSocketServer对象。
    private static ConcurrentHashMap<String, Session> sessionPools = new ConcurrentHashMap<>();

    //发送消息
    public void sendMessage(Session session, String message) throws IOException {
        if(session != null){
            //表示该session对象相同时锁起作用,synchronized

            synchronized (session) {
//                System.out.println("发送数据:" + message);
                session.getBasicRemote().sendText(message);
            }
        }
    }
    //给指定用户发送信息
    public void sendInfo(String userName, String message){
        Session session = sessionPools.get(userName);
        try {
            sendMessage(session, message);
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    //建立连接成功调用
    @OnOpen
    public void onOpen(Session session, @PathParam(value = "sid") String userName){
        sessionPools.put(userName, session);  //存放信息
        addOnlineCount(); //onlineNum+1
        System.out.println(userName + "加入webSocket!当前人数为" + onlineNum);
        try {
            sendMessage(session, "欢迎" + userName + "加入连接!");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    //关闭连接时调用
    @OnClose
    public void onClose(@PathParam(value = "sid") String userName){
        sessionPools.remove(userName);
        subOnlineCount();
        System.out.println(userName + "断开webSocket连接!当前人数为" + onlineNum);
    }

    //收到客户端信息
    @OnMessage
    public void onMessage(String message) throws IOException{
        message = "客户端:" + message + ",已收到";
        System.out.println(message);
        for (Session session: sessionPools.values()) {
            try {
                sendMessage(session, message);
            } catch(Exception e){
                e.printStackTrace();
                continue;
            }
        }
    }

    //错误时调用
    @OnError
    public void onError(Session session, Throwable throwable){
        System.out.println("发生错误");
        throwable.printStackTrace();
    }
    public static void addOnlineCount(){
        onlineNum.incrementAndGet();
    }
    public static void subOnlineCount() {
        onlineNum.decrementAndGet();
    }

}

1.@Component是个啥?

链接: 注解诠释大全.
@component (把普通pojo实例化到spring容器中,相当于配置文件中的)
泛指各种组件,就是说当我们的类不属于各种归类的时候(不属于@Controller、@Services等的时候),我们就可以使用@Component来标注这个类

2.ConcurrentHashMap<String, Session> 是个啥?

链接: 各种map的介绍.

3.那你为什么用ConcurrentHashMap不用别的map?

关键在于这句话:
Hashtable的synchronized是针对整张Hash表的,即每次锁住整张表让线程独占,ConcurrentHashMap允许多个修改操作并发进行,其关键在于使用了锁分离技术
而且比起hashmap线程不安全 ConcurrentHashMap线程安全

当时我就想,独占很简单:就是我能改别人不能改;
允许多个修改:是我能改别人也能改。
现在场景是我登录账号A,你登陆账号B,如果是hashmap,那么我发信息,你得等我发完,你才能发,这谁能干,所以用ConcurrentHashMap

项目hashmapConcurrentHashMap
安全性不安全安全
线程独占并发

4.锁是怎么玩的

看下这段代码

  public void sendMessage(Session session, String message) throws IOException {
        if(session != null){   
            synchronized (session) {
                session.getBasicRemote().sendText(message);
            }
        }
    }

链接: synchronized同步锁.
说实话,没咋看明白这个
主要作用:当锁住的内容执行完或者在执行过程中抛出异常,才会自动释放锁。
当两个并发线程(thread1和thread2)访问同一个对象(syncThread)中的synchronized代码块时,在同一时刻只能有一个线程得到执行,另一个线程受阻塞,必须等待当前线程执行完这个代码块以后才能执行该代码块。Thread1和thread2是互斥的,因为在执行synchronized代码块时会锁定当前的对象,只有执行完该代码块才能释放该对象锁,下一个线程才能执行并锁定该对象。
哦,那就是如果你用户不为空了,我再把你设定成并发锁,也就是当我发送消息(调用该方法时),我得等我发完这条我才能发下一条。

所以总而言之:ConcurrentHashMap实现的是A和B可以同时调用系统同一函数
synchronized是A(或B)内部发消息的一种并发等待锁,实现效果是让你一条一条的发

5.如何实现【我给你发一条】的效果的?

  //给指定用户发送信息
    public void sendInfo(String userName, String message){
        Session session = sessionPools.get(userName);
        try {
            sendMessage(session, message);
        }catch (Exception e){
            e.printStackTrace();
        }
    }

比如user A
user.sendInfo(“B”,“HELLO B”);
A通过调取sendInfo中的sendMessage()函数将话扔给B
B接收,并显示( 显示由代码session.getBasicRemote().sendText(message);实现)

  public void sendMessage(Session session, String message) throws IOException {
        if(session != null){
            //表示该session对象相同时锁起作用,synchronized

            synchronized (session) {
//                System.out.println("发送数据:" + message);
                session.getBasicRemote().sendText(message);
            }
        }
    }

前端篇

<template>
  <div class="box">
    <div class="titleBg">
      Let's talk here
    </div>
    <el-row>
      <em-col :span="8">
        <el-button type="info" @click="openScoket">上线</el-button></em-col
      >
      <em-col :span="8">
        <el-button type="info" @click="closeScoket">下线</el-button></em-col
      >
      <em-col :span="8">
        <el-button type="info" @click="sendToAll">广播</el-button></em-col
      >
    </el-row>
  </div>
</template>

<script>
export default {
  name: "IndexView",
  data() {
    return {
      socket: {},
      readyState: this.socket.readyState
    };
  },
  props: {},
  components: {},
  mounted() {},
  methods: {
    openScoket() {
      console.log("你点击了上线");
      if (typeof WebSocket == "undefined") {
        console.log("您的浏览器不支持WebSocket");
      } else {
        //建立socket
        this.socket = new WebSocket("ws://127.0.0.1:9000/webSocket/123");
        console.log(this.socket.readyState);

        if (this.socket.readyState == 1) {
          console.log("websocket已建立");
        }
      }
    },
    closeScoket() {
      console.log("你点击了下线");
      this.socket.close();
    },
    sendToAll() {
      console.log("你点击了广播");
      this.socket.send("你好");
      this.socket.message();

    }
  },
  watch: {
    readyState(newValue, oldVaue) {
      console.log(oldVaue);
      console.log(newValue);
    }
  }
};
</script>

1.与后端有什么关联?

sendToAll() {
      console.log("你点击了广播");
      this.socket.send("你好");
      this.socket.message();
    }

socket.send 方法哪来的????

后端也没写这个方法啊
首先发现了一个事

   this.socket = new WebSocket("ws://127.0.0.1:9000/webSocket/123");

这哥们真正类型是websocket, 哦,那我百度websocket
链接: websocket的readystate.

方法:
WebSocket.send(data)对要传输的数据进行排队。

事件:使用 addEventListener() 或将一个事件监听器赋值给本接口的 oneventname 属性,来监听下面的事件。
close当一个 WebSocket 连接被关闭时触发。也可以通过 onclose 属性来设置。
error当一个 WebSocket 连接因错误而关闭时触发,例如无法发送数据时。也可以通过 onerror 属性来设置.
message当通过 WebSocket 收到数据时触发。也可以通过 onmessage 属性来设置。
open当一个 WebSocket 连接成功时触发。也可以通过 onopen 属性来设置。

示例:

// Create WebSocket connection.
const socket = new WebSocket('ws://localhost:8080');

// Connection opened
socket.addEventListener('open', function (event) {
    socket.send('Hello Server!');
});

// Listen for messages
socket.addEventListener('message', function (event) {
    console.log('Message from server ', event.data);
});

噢,此时我恍然大悟,原来前端的.message()就规定,让你触发后端的onmessage

之后还是不咋明白,怎么建立连接,怎么传的,系统:我咋知道我啥时候接到数据了?

 this.socket = new WebSocket("ws://127.0.0.1:9000/webSocket/123");

这段代码就是建立连接
然后触发后端onopen

      this.socket.send("你好");
      this.socket.message();

WebSocket.send(“你好”)对要传输的“你好”数据进行排队
官网说:通过 WebSocket 先收到数据后触发onmessage
那系统傻啊,我哪知道我啥时候收到数据?
快递员放包裹到门口了(send)后大喊一声快递!(事件:message),然后我听着声我才能去取
这里不像上一个系统知道我建连接了,这里系统不知道他接收了信息,所以,系统不会自己触发onmessage,而是我强制你去.message方法,来让你走onmessage这个函数。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值