套接字socket的理解

首先必须理解TCP的的11种状态,三次握手的作用,四次挥手的作用,为什么要有TCP,那就是HTTP的弊端,HTTP是一个释放连接的无状态协议,是在应用层,其实它底层也是新建了TCP协议的来进行传输,只是短暂的任务完成即刻断开了,那么它底层的TCP也是断开的,当请求次数特别频繁时就有弊端,比如说聊天,实时信息。。。
其次就是要了解端口和端点的区别,端点也可以理解为一个插口,是用TCP协议连接起来的两个连接点。
还要注意区别socket的数据接口(webSocket API)和http请求的url

插口包含了端口,因为插口 = (IP地址,端口号)。插口是TCP连接的端点。
插口(socket)有多种意思。当使用API时,插口往往被看成是操作系统的一种抽象,这时,插口和一个文件描述符是很相似的,并且是应用编程接口API的一部分。插口由应用程序产生,并指明它将由客户还是服务器来使用。当应用进程创建一个插口时,要指明该插口使用的端口号。

端口则是应用层服务的的一种代号,它用来标志应用层的进程。端口是一个16 bit的整数。各种服务器使用的端口号都是保留端口号【什么是保留端口号?】,以便使客户能够找到服务器。例如万维网服务器使用的端口号是80。

在发送数据时,应用层的数据通过端口向下交付到运输层。在接收数据时,运输层的数据通过适当的端口向上交付到应用层的某个应用程序。

解读前后端代码

首先,前端每执行一句var ws =new WebSocket(“ws://localhost:8080/websocket/1”)就相当于新建了一个和后端相连接的所谓端点(插口),同时会自动生成一个session (这里的session和之前说的session有点不一样,这里是WsSession,每打开一个网页,或者刷新网页,前端js都会执行new 语句,都是不一样的WsSession,(见下图),后端都会自动执行一个 Onopen方法,这里理解为:每执行一句 new 相当于建了一个端点,那就自动生成一个session 后端也是主要通过这个session和后端进行传输的)。。
奇怪的现象:每刷新一个页面或者打开新的窗口都是不一样的WsSession(由打印可以知道),但是每一次都加到CopyOnWriteArraySet这个集合里面,但是当打印这个集合的数量的时候是和实际不符的,在这里插入图片描述
后端这个被@ServerEndpoint("/websocket/{sid}")注解过的类表明用这个类来在服务端建一个端点,等待连接,每一连接上了就会生成一个这个类的实例对象,(注意:在使用了@ServerEndpoint注解的类是无法直接使用@Autowired的,因为@ServerEndpoint表明当前类是websocket的服务端点,在spring容器启动时会初始化一次该类,当有新的websocket连接的时候,也会进行该类实例的创建(每一次连接时都会创建一个实例));要保存全部连接也就是seesion,那么就要给这个类新建很多的静态成员,也就类变量;这个类里面基本上所有的方法都加了一个Session参数,在 onOpen()方法里面 this.session = session;就是把一开始的session赋值给新建的实例对象的session属性。。
为什么说session重要呢??因为这个端点连接要发送信息都是通过这个session来发送的,后端执行this.session.getBasicRemote().sendText(message);就可以发送,前端发送就是用js里面WebSocket类的方法;执行ws.send(“ws.send()发信息!!!”);语句,后端接受信息的方法就是在@onMessage注解的方法里用String message参数接受;这个message是一个参数名字可以变。。
后端的session把数据传过来了之后就是用js里面的 WebSocket类的onmessage()方法来接受,ev.data就是传来的数据(代码如下),向服务器传数据就是用WebSocket类的send方法来传过去的。。

ws.onmessage =function (ev) {
var receive_msg=ev.data;
alert(receive_msg)
console.log(receive_msg)
}

@ServerEndpoint("/websocket/{sid}")
@Component
public class WebSocketServer {
    static Log log=LogFactory.getLog(WebSocketServer.class);
    //静态变量,用来记录当前在线连接数。应该把它设计成线程安全的。
    private static int onlineCount = 0;
    //concurrent包的线程安全Set,用来存放每个客户端对应的MyWebSocket对象。
    private static CopyOnWriteArraySet<WebSocketServer> webSocketSet = new CopyOnWriteArraySet<WebSocketServer>();



    //接收sid
    private String sid="";
    /**
     * 连接建立成功调用的方法*/
    //与某个客户端的连接会话,需要通过它来给客户端发送数据
    private Session session;/*一个管道代表一个session*/


    @OnOpen
    public void onOpen(Session session,@PathParam("sid") String sid) {
        this.session = session;
        webSocketSet.add(this); //加入set中
        addOnlineCount(); //在线数加1
        log.info("有新窗口开始监听:"+sid+"sessionId:"+session.getId()+",当前在线人数为" + getOnlineCount());
        this.sid=sid;
        try {
            sendMessage("连接成功");
        } catch (IOException e) {
            log.error("websocket IO异常");
        }
    }


    /**
     * 连接关闭调用的方法
     */
    @OnClose
    public void onClose(Session session) {
        webSocketSet.remove(this); //从set中删除
        subOnlineCount(); //在线数减1
        log.info("有一连接关闭!当前在线人数为" + getOnlineCount());
    }

    /**
     * 收到客户端消息后调用的方法
     *
     * @param message 客户端发送过来的消息*/
    @OnMessage
    public void onMessage(String message, Session session) {
        log.info("收到来自窗口"+sid+"的信息:"+message);
//群发消息

        for (WebSocketServer item : webSocketSet) {
            try {
                item.sendMessage(message);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }


    /**
     *
     * @param session
     * @param error
     */
    @OnError
    public void onError(Session session, Throwable error) {
        log.error("发生错误");
        error.printStackTrace();
    }
    /**
     * 实现服务器主动推送
     */
    public void sendMessage(String message) throws IOException {
        log.info("向前端发信息");
        message="后端也发给你:"+message;
        this.session.getBasicRemote().sendText(message);
    }


    /**
     * 群发自定义消息
     * */
    public static void sendInfo(String message,@PathParam("sid") String sid) throws IOException {
        log.info("推送消息到窗口"+sid+",推送内容:"+message);
        for (WebSocketServer item : webSocketSet) {
            try {
//这里可以设定只推送给这个sid的,为null则全部推送
                if(sid==null) {
                    item.sendMessage(message);
                }else if(item.sid.equals(sid)){
                    item.sendMessage(message);
                }
            } catch (IOException e) {
                continue;
            }
        }
    }

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

    public static synchronized void addOnlineCount() {
        WebSocketServer.onlineCount++;
    }
    public static synchronized void subOnlineCount() {
        WebSocketServer.onlineCount--;
    }
}
<script type="application/javascript" >
        var  ws =new WebSocket("ws://localhost:8080/websocket/1")

        ws.onopen =function (ev) {
            console.log("开始连接");
        }
        ws.onmessage =function (ev) {
            var  receive_msg=ev.data;
            alert(receive_msg)
            console.log(receive_msg)
        }

        ws.onclose = function (ev) {
            console.log("关闭连接");
        }

        ws.onerror =function (ev) {
             alert("erro" +ev)
        }
        
        function sendMessage() {

            ws.send("ws.send()发信息!!!");
            
        }

    </script>


</head>
<body>
<button type="button" id="send" class="btn btn-primary"
        onclick="sendMessage();">
    Send!
</button>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值