websocket实例之多人聊天

众所周知,websocket是用于消息推送的,而最好的理解它的方法就是做网页实时聊天功能了,所以这里实现了一个简单的网页即时聊天实例:
服务器端:
这里用的maven依赖有:

<dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-websocket</artifactId>
        <version>4.3.10.RELEASE</version>
    </dependency>

    <!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>4.3.10.RELEASE</version>
    </dependency>

    <!-- https://mvnrepository.com/artifact/org.springframework/spring-core -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-core</artifactId>
        <version>4.3.10.RELEASE</version>
    </dependency>

    <!-- https://mvnrepository.com/artifact/org.springframework/spring-beans -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-beans</artifactId>
        <version>4.3.10.RELEASE</version>
    </dependency>

    <!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-webmvc</artifactId>
        <version>4.3.10.RELEASE</version>
    </dependency>

    <!-- https://mvnrepository.com/artifact/org.springframework/spring-messaging -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-messaging</artifactId>
        <version>4.3.10.RELEASE</version>
    </dependency>

1 .实现WeSocketConfigurer接口的registerWeSocketHandlers方法:配置wesocket允许访问的域

@Configuration
@EnableWebMvc
@EnableWebSocket
public class WebSocketConfig extends WebMvcConfigurerAdapter implements WebSocketConfigurer {

    @Autowired
    @Lazy
    private SimpMessagingTemplate template;

    /** {@inheritDoc} */
    @Override
    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
        registry.addHandler(logWebSocketHandler(), "/log").addInterceptors(new WebSocketHandshakeInterceptor());  
    }

    @Bean
    public WebSocketHandler logWebSocketHandler() {
        return new LogWebSocketHandler(template);
    }

}

2.消息转发核心类,可以将消息转发给任何一个用户。这里的消息类型由TextWebSocketHandler决定,可其他的handler类型。

public class LogWebSocketHandler extends TextWebSocketHandler {
    private static final Map<String, WebSocketSession> users;
    private static final String String = null;

    static {
        users = new HashMap<>();
    }

    private SimpMessagingTemplate template;

    public LogWebSocketHandler(SimpMessagingTemplate template) {
        this.template = template;
        System.out.println(" handler");
    }

    @Override
    protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
        String text = message.getPayload();
        System.out.println("handMessage:" + text);
        System.out.println("handMessage:" + message.toString());
        // template.convertAndSend("/log", text);
        sendMessageToAllUsers(message);

    }

    /**
     * 发送给所有人
     * 
     * 
     * @param message
     * @return
     */
    public boolean sendMessageToAllUsers(TextMessage message) {
        boolean allSendSuccess = true;
        Set<String> clientIds = users.keySet();
        WebSocketSession session = null;
        for (String clientId : clientIds) {
            try {
                session = users.get(clientId);
                if (session.isOpen()) {
                    session.sendMessage(message);
                }
            } catch (IOException e) {
                e.printStackTrace();
                allSendSuccess = false;
            }
        }

        return allSendSuccess;
    }

    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
        // TODO Auto-generated method stub
        System.out.println("connection closed...");
        super.afterConnectionClosed(session, status);
    }

    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
        // TODO Auto-generated method stub
        System.out.println("connection established...");
        String userid = (String) session.getAttributes().get("userid");
        System.out.println("add userId " + userid);
        users.put(userid, session);
        super.afterConnectionEstablished(session);
    }
}

3.握手类,也可以说是拦截器,是在用户连接时的响应操作。这里将用户信息存进session,记录需要转发消息的用户传递给handler。

public class WebSocketHandshakeInterceptor implements HandshakeInterceptor {

    @Override
    public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler,
            Map<String, Object> attributes) throws Exception {
        // TODO Auto-generated method stub
        HttpServletRequest ServletHttpRequest = ((ServletServerHttpRequest) request).getServletRequest();
        String userid = ServletHttpRequest.getParameterMap().get("userid")[0];
        System.out.println("intercepter userid : " + userid);
        HttpSession session = ServletHttpRequest.getSession();
        if (session != null) {
            attributes.put("userid", userid);
        }
        return true;
    }

    @Override
    public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler,
            Exception exception) {
        // TODO Auto-generated method stub

    }

}

4.访问聊天页面的controller

@Controller
public class TestController {

    @RequestMapping("test")
    public String test() {
        return "/jsp/websoketTest.jsp";
    }
}

客户端:
1.页面

<%@page import="com.common.SysInfo"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<%
      String path = request.getContextPath();
      String basePath = request.getScheme() + "://"
                  + request.getServerName() + ":" + request.getServerPort()
                  + path + "/";
%>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
<script type="text/javascript" src="<%=basePath %>javascript/jquery-min.js" ></script>
<script type="text/javascript" src="<%=basePath %>javascript/websoket1.js" ></script>
<script type="text/javascript" src="<%=basePath %>javascript/sockjs.min.js" ></script>
<link rel="stylesheet" href="<%=basePath %>css/websokect1.css" type="text/css"/>
</head>
<body>

<div id="skm_LockPane" class="LockOff"></div>

<form id="form1" runat="server" action="">
    <label>服务器地址</label><input type="text" id="addr"/><br/>
    <label>用户名</label><input type="text" id="username" value="太阳"/><br/>
    <input type="button" id="conn" value="连接" onclick="ToggleConnectionClicked();"/><br/>
    <div id="LogContainer" class="container"></div><br/>
    <div id="SendDataContainer">
        <input type="text" id="DataToSend" size="88"/>
        <input type="button" id="SendData" value="发送" onclick="SendDataClicked();"/><br/>
    </div>


</form>

</body>
</html>

2.websocket客户端的js代码:

var ws;
var SocketCreated = false;
var isUserloggedout = false;

function lockOn(str) {
    var lock = document.getElementById("skm_LockPane");
    if (lock) {
        lock.className = "LockOn";
    }
    lock.innerHTML = str;
}

function lockOff() {
    var lock = document.getElementById("skm_LockPane");
    lock.className = "LockOff";
}

function ToggleConnectionClicked() {
    if (SocketCreated && (ws.readyState == 0 || ws.readyState == 1)) {
        lockOn("离开聊天室...");
        sockedCreated = false;
        isUserloggedout = true;
        ws.close();
    } else {
        lockOn("准备进入聊天室...");
        Log("准备连接到聊天服务器...");
        try {
            if ("WebSocket" in window) {
                ws = new WebSocket("ws://"
                        + document.getElementById("addr").value + "?userid="
                        + document.getElementById("username").value);
            } else if ("MozWebSocket" in window) {// firefox
                ws = new MozWebSocket("ws://"
                        + document.getElementById("addr").value + "?userid="
                        + document.getElementById("username").value);
            } else {
                ws = new SockJS("http://"
                        + document.getElementById("addr").value + "?userid="
                        + document.getElementById("username").value);
            }
            SocketCreated = true;
            isUserloggedout = false;
        } catch (ex) {
            Log(ex, "ERROR");
            return;
        }
        document.getElementById("conn").value = "断开";
        ws.onopen = WSonOpen;
        ws.onmessage = WSonMessage;
        ws.onclose = WSonClose;
        ws.onerror = WSonError;
    }
}

function WSonOpen() {
    lockOff();
    Log("连接已建立。", "OK");
    $("#SendDataContainer").show();
    ws.send("login:" + document.getElementById("username").value);
}

function WSonMessage(event) {
    Log(event.data);
}

function WSonClose() {
    lockOff();
    if (isUserloggedout) {
        Log("【" + document.getElementById("username").value + "】离开了聊天室!");
    }
    document.getElementById("conn").value = "连接";
    // document.getElementById("conn").innerHTML = "连接";
    $("#SendDataContainer").hide();
}

function WSonError() {
    lockOff();
    Log("远程连接中断。", "ERROR");
}

function SendDataClicked() {
    if (document.getElementById("DataToSend").value.trim() != "") {
        ws.send(document.getElementById("username").value + "说:\""
                + document.getElementById("DataToSend").value + "\"");
        document.getElementById("DataToSend").value = "";
    }
}

function Log(Text, MessageType) {
    console.log(Text);
    if (MessageType == "OK") {
        Text = "<span style='color: green;'>" + Text + "</span>";
    }
    if (MessageType == "ERROR") {
        Text = "<span style='color: red;'>" + Text + "</span>";
    }
    document.getElementById("LogContainer").innerHTML = document
            .getElementById("LogContainer").innerHTML
            + Text + "<br/>";
    var LogContainer = document.getElementById("LogContainer");
    LogContainer.scrollTop = LogContainer.scrollHeight;
}

$(document)
        .ready(
                function() {
                    $("#SendDatacontainer").hide();
                    var WebSocketsExist = true;
                    try {
                        var dummy = new WebSocket(
                                "ws://<%=SysInfo.BASE_IP%>:<%=SysInfo.BASE_PORT%>/TProject/log");
                    } catch (ex) {
                        try {
                            webSocket = new MozWebSocket(
                                    "ws://<%=SysInfo.BASE_IP%>:<%=SysInfo.BASE_PORT%>/TProject/log");
                        } catch (ex) {
                            try {
                                websocket = new SockJS(
                                        "http://<%=SysInfo.BASE_IP%>:<%=SysInfo.BASE_PORT%>/TProject/log");
                            } catch (ex) {
                                WebSocketsExist = false;
                            }
                        }
                    }
                    if (WebSocketsExist) {
                        Log("您的浏览器支持ws,您可以尝试连接到聊天服务器!", "OK");
                        document.getElementById("addr").value = "20.16.1.22:8081/TProject/log";
                    } else {
                        Log("您的浏览器不支持ws。请选择其他的浏览器再尝试连接服务器。", "ERROR");
                        document.getElementById("addr").disabled = true;
                    }
                    $("#DataToSend").keypress(function(evt) {
                        if (evt.keyCode == 13) {
                            $("#SendData").click();
                            evt.preventDefault();
                        }
                    });
                });

猜你喜欢

转载自blog.csdn.net/yabaj/article/details/77196098
今日推荐