本文介绍: 使用websocketspringboot实现一个多人聊天室,从最底层开始一步一步把功能复杂化。不同阶段源码已经放出,自取。

websocket介绍就不多说了,简单来说就是http协议只能支持客户端访问springboot接口然后springboot返回相应的数据,但是ws协议可以主动向用户客户端发送消息,所以可以用来实时传输

接下来由浅入深,一步一步来做完这个聊天室,不同阶段的代码我会放在网盘可以根据需要自取。

首先创建一个基础的Springboot项目 

启动类,只要能启动可以

package com;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

@SpringBootApplication
@EnableSwagger2
@MapperScan(basePackages = { "com.mapper" })
public class ChatDemo {
	public static void main(String[] args) {
		SpringApplication.run(ChatDemo.class, args);
	}
}

pom.xml导入websocket环境

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

创建启动websocket支持的.java文件

这个类会在springboot启动的时候自动注入,所以复制粘贴过去就可以了。

package com.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;

//启动websocket支持
@Configuration
public class WebSocketConfig {
	@Bean
	public ServerEndpointExporter serverEndpointExporter() {
		return new ServerEndpointExporter();
	}
}

  创建websocket启动类

        下面时一个简化过的启动类,虽然我们最终目标是写聊天室,但是最基本的还是先搞清楚最基础的websocket的连接网上大部分的教程都有不同程度的魔改过,代码阅读难度较高,所以我将网上代码进行了简化方便入门和魔改。

package com.server;

import java.io.IOException;
import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import org.springframework.stereotype.Component;

/**
 * 客户端访问链接即可服务端建立连接
 */
@ServerEndpoint("/login_connect/{userId}")
@Component
public class WebSocketServer {

	//使用session连接用户发送信息
	private Session session;
	//记录连接到此socke的用户id
	private String userId = "";

	//用户连接成功后会自动调用方法
	@OnOpen
	public void onOpen(Session session, @PathParam("userId") String userId) throws             
IOException {
		this.session = session;
		this.userId = userId;
		System.out.println("连接成功,用户ID为:" + userId);
        //连接成功后向连接的用户返回 “连接成功” 信息
		this.session.getBasicRemote().sendText("连接成功");
	}
    //关闭连接后自动调用
	@OnClose
	public void onClose() {
		System.out.println("close connect");
	}
    //收到用户发来的消息调用
	@OnMessage
	public void onMessage(String message, Session session) throws IOException {
		System.out.println("收到id为:"+this.userId+"的用户发来的消息:"+message);
		
	}
    //错误时调用
	@OnError
	public void onError(Session session, Throwable error) {
	}
}

写完上述的代码后,就建立了最简单服务端

接下来客户代码

因为是在本地跑所以我是直接在HbuilderX中创建的新的前端项目

下面是代码

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title>websocket通讯</title>
	</head>
	<script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.js"></script>
	<script>
		var socket = null;

		function openSocket() {
            //获取用户输入的用户id
            var userid=document.getElementById("userid").value;
            //这个链接地址对应了springboot跑的端口和上面标注访问接口
			var socketUrl = "ws://localhost:8081/login_connect/" +userid;

			socket = new WebSocket(socketUrl);
			//下面几个方法对应了服务端的几个方法
			//连接成功
			socket.onopen = function() {
				console.log("websocket已打开");
			};

			//发生了错误事件
			socket.onerror = function() {
				console.log("websocket发生了错误");
			}

			//收到消息
			socket.onmessage = function(msg) {
				console.log("服务器发送数据:" + msg.data);
			};

			//关闭连接后运行
			socket.onclose = function() {
				console.log("websocket已关闭");
			};

		}
		//发送数据服务器
		function sendMessage() {
			socket.send(document.getElementById("message").value);
		}
	</script>
	<body>

		用户名:<input id="userid" value="test_user">
		<br>
		发送信息:<input id="message"value="test websocket connection"><br>
		<button onclick="openSocket()">连接到websocket</button><br>
		<button onclick="sendMessage()">发送消息</button>
	</body>

</html>

下面是运行效果

        1.前端运行截图

e67b5e1143f2473bb6526c387c56850e.png

       2. 前端控制台,点击

8ac4c5c6dbf74ccd8246480713cf39a3.png

        3.点击发送按钮springboot控制台

 d12c82f7055a4bf1ae0bec4b2a348b71.png

上述代码实现了最简单客户端向服务端建立连接,然后服务端发送消息给客户端告知连接成功,然后客户端发送消息服务端实现了最简单双向交互

我先把上面的代码压缩分享这里,后面进行魔改,由于原始工程文件是之前用的另外一个项目的魔改框架,还保留了很多其他依赖,可能会有点大。

       链接https://pan.baidu.com/s/1SYOZL58jugDNnaqqZ5yO-Q 
提取码:1234 
–来自百度网盘超级会员V2的分享

<!–############################分割线################################–>

然后接下来我们把这个过程慢慢复杂化,比如上述的功能服务端和客户端进行数交换,接下来我们先来实现客户端对客户端的数据交互,也就是客户端A将消息发给服务端,然后服务端将消息发给客户端B。

 首先对上面的javasocket实现类进行一些修改可以复制粘贴将原本的代码覆盖

         因为一个websocket的实现类就说明连接了一个客户端,所以如果多个客户端连接到服务器的话,我们就可以创建一个容器存放这些类,然后通过userid来区分,所以我们使用hashmap存储这些连接,将该容器设为静态,然后我们可以在任意一个连接中调用所有的连接,就可以通过这个容器向任意用户发送消息。

// 用来存放所有的连接,静态
	private static ConcurrentHashMap<String, WebSocketServer> webSocketMap = 
new ConcurrentHashMap<>();

        创建容器后,在OnOpen方法添加将连接放入容器方法

// 用户连接成功后会自动调用该方法
	@OnOpen
	public void onOpen(Session session, @PathParam("userId") String userId) throws IOException {
		this.session = session;
		this.userId = userId;
		System.out.println("连接成功,用户ID为:" + userId);
		// 连接成功后向连接的用户返回 “连接成功” 信息
		this.session.getBasicRemote().sendText("连接成功");
		// 然后将该连接存入ChatController
		webSocketMap.put(this.userId, this);
	}

        接下来我们添加一个发送消息给该客户端的方法,方便客户端的连接被其他用户调用,然后就可以发送消息给该用户。

// 发送消息给此客户端
	public void sendmessage(String message) throws IOException {
		this.session.getBasicRemote().sendText(message);
	}

         然后重写一下OnMessage方法,将用户发送过来的消息分解为json内容接收者idreceiveuserid、和发送的消息:message,当然从客户端传过来数据一条字符串,我们用JSON工具将他转化为对象,然后根据receiveruserid从SocketHashMap中取出相应的websocket连接,然后通过调用上面写的sendmessage方法将消息发送给接收者客户端。

// 收到用户发来的消息时调用
	@OnMessage
	public void onMessage(String message, Session session) throws IOException {
		System.out.println(message);
		JSONObject json = JSON.parseObject(message);
		System.out.println(json.getString("receiveuserid") + "||" + json.getString("message"));
		webSocketMap.get(json.getString("receiveuserid"))
				.sendmessage("用户:" + this.userId + "说:" + json.getString("message"));
	}

         改完后的代码如下:

package com.server;

import java.io.IOException;
import java.util.concurrent.ConcurrentHashMap;

import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;

import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestBody;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.controller.ChatController;

import bean.Message;

/**
 * 客户端访问该链接即可服务端建立连接
 */
@ServerEndpoint("/login_connect/{userId}")
@Component
public class WebSocketServer {
	// 用来存放所有的连接,静态
	private static ConcurrentHashMap<String, WebSocketServer> webSocketMap = new ConcurrentHashMap<>();

	// 使用session向连接的用户发送信息
	private Session session;
	// 记录连接到此socke的用户id
	private String userId = "";

	// 用户连接成功后会自动调用该方法
	@OnOpen
	public void onOpen(Session session, @PathParam("userId") String userId) throws IOException {
		this.session = session;
		this.userId = userId;
		System.out.println("连接成功,用户ID为:" + userId);
		// 连接成功后向连接的用户返回 “连接成功” 信息
		this.session.getBasicRemote().sendText("连接成功");
		// 然后将该连接存入ChatController
		webSocketMap.put(this.userId, this);
	}

	// 关闭连接后自动调用
	@OnClose
	public void onClose() {
		System.out.println("close connect");
	}

	// 收到用户发来的消息时调用
	@OnMessage
	public void onMessage(String message, Session session) throws IOException {
		System.out.println(message);
		JSONObject json = JSON.parseObject(message);
		System.out.println(json.getString("receiveuserid") + "||" + json.getString("message"));
		webSocketMap.get(json.getString("receiveuserid"))
				.sendmessage("用户:" + this.userId + "说:" + json.getString("message"));
	}

	// 错误时调用
	@OnError
	public void onError(Session session, Throwable error) {
		System.out.println("error");
	}

	// 发送消息给此客户端
	public void sendmessage(String message) throws IOException {
		this.session.getBasicRemote().sendText(message);
	}

}

后端的代码改完了,然后前端代码也要进行一定的适配

        主要是添加接收用户的id的输入框js代码将发送的数据格式写成了json,内容为receiverid和message。

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title>websocket通讯</title>
	</head>
	<script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.js"></script>
	<script>
		var socket = null;
		var userid = "";

		function openSocket() {
			//获取用户输入的用户id
			userid = document.getElementById("userid").value;
			//这个链接地址对应了springboot跑的端口和上面标注的访问的接口
			var socketUrl = "ws://localhost:8081/login_connect/" + userid;

			socket = new WebSocket(socketUrl);
			//下面几个方法对应了服务端的几个方法
			//连接成功
			socket.onopen = function() {
				console.log("websocket已打开");
			};

			//发生了错误事件
			socket.onerror = function() {
				console.log("websocket发生了错误");
			}

			//收到消息
			socket.onmessage = function(msg) {
				console.log("服务器发送的数据:" + msg.data);
			};

			//关闭连接后运行
			socket.onclose = function() {
				console.log("websocket已关闭");
			};

		}
		//发送数据给服务器
		function sendMessage() {
			socket.send('{"receiveuserid":"' + $("#receiveuserid").val() + '","message":"' + $("#message").val() +'"}');
		}
	</script>
	<body>

		用户名:<input id="userid" value="test_user">
		<br>
		接收人:<input id="receiveuserid" value="test_user">
		<br>
		发送的信息:<input id="message" value="test websocket connection"><br>
		<button onclick="openSocket()">连接到websocket</button><br>
		<button onclick="sendMessage()">发送消息</button>
	</body>

</html>

运行效果

        用户A界面

668252ce996f44818304fe9b44c065b4.png

670d23dbf08e45179f6977ffbd2f123a.png

        用户B界面

c7cf4e395bce474ebf43926b0d2da725.png

e025aee255ea48a5b8e5b97520fa1591.png

         后端界面

a2fedd2806f245cb9f9eaa6d71b575e7.png

 第二阶段的代码,自取

链接:https://pan.baidu.com/s/1sDpelZH1n_bFUXEd46MgdQ 
提取码:1234 
–来自百度网盘超级会员V2的分享

 <!–############################分割线################################–>

接下来我们为这个聊天添加一个群聊天室的功能

首先,当我们不再只是单纯的传输数据,而是聊天,那么消息就会复杂起来,所以我们先创建一个bean用来存储基本的消息

package com.entiry;


public class Message {
	private String userid;//发送者id
	private String message;//内容
	private String type;//消息类型: new_user 新用户上线|| server 服务器消息|| tochatroom 聊天室消息|| offline 用户下线
	
	
	public String getUserid() {
		return userid;
	}

	public void setUserid(String userid) {
		this.userid = userid;
	}

	public String getMessage() {
		return message;
	}

	public void setMessage(String message) {
		this.message = message;
	}

	public String getType() {
		return type;
	}

	public void setType(String type) {
		this.type = type;
	}

	public String getTouser() {
		return touser;
	}

	public void setTouser(String touser) {
		this.touser = touser;
	}
}

我在这边把消息类型分成了四种

new_user 新用户上线|| server 服务器消息|| tochatroom 聊天室消息|| offline 用户下线

客户端可以根据收到的消息不同的类型进行不同的操作

在我们使用Message类来进行数据传输后,就不能再跟以前一样,只传基本String了,而是把String复杂化了,也就是将Message类转化为JSON字符然后发送给前端然后前端再进行解析其实可以直接将return 类型改成Message 但是因为中途改的话要改的代码蛮多的,我就懒得弄了

然后我们继续魔改WebSocketServer

首先是添加一个向所有其他用户发送消息的方法

// 给除自己以外的所有用户发消息
	public void sendmessagetootherusers(String message) throws IOException {
		//获取所有连接中的用户id
        Enumeration<String> userkeys = webSocketMap.keys();
        //通过便利来向所有用户发送消息
		while (userkeys.hasMoreElements()) {
			String userid = userkeys.nextElement();
            //通过对比排除自己
			if (userid != userId) {
				webSocketMap.get(userid).sendmessage(message);
			}

		}
	}

然后修改一下服务端收到消息的写法,把他复杂化

// 收到用户发来的消息时调用
	@OnMessage
	public void onMessage(String message, Session session) throws IOException {
		//将收到的消息转化为JSON对象
		JSONObject json = JSON.parseObject(message);
		//判断消息类型聊天室消息就直接将消息发送给所有除自己外的用户
		if (json.getString("type").equals("tochatroom")) {
			//创建消息类,将消息类型、消息内容、发送消息者的id放进去
			Message msg = new Message();
			msg.setMessage(json.getString("message"));
			msg.setUserid(userId);
			msg.setType("tochatroom");
			//然后将消息发送给其他用户
			sendmessagetootherusers(JSON.toJSONString(msg));
		}
	}

然后是当连接关闭的方法

// 关闭连接后自动调用
	@OnClose
	public void onClose() throws IOException {
		// 将断开连接的socket从map移除
		webSocketMap.remove(userId);
		// 给所有客户端发送offline消息类型,内容为离线用户的ID
		Message msg = new Message();
		msg.setType("offline");
		msg.setMessage(userId);
		sendmessagetootherusers(JSON.toJSONString(msg));
	}

然后是当有客户端连接时

// 用户连接成功后会自动调用该方法
	@OnOpen
	public void onOpen(Session session, @PathParam("userId") String userId) throws IOException {
		this.session = session;
		this.userId = userId;
		Message message = new Message();
		message.setType("new_user");
		message.setMessage(userId);
		//将该连接加入到SocketMap中
		webSocketMap.put(this.userId, this);
		// 向所有用户广播,userid上线了
		sendmessagetootherusers(JSON.toJSONString(message));
	}

下面是完整代码

package com.server;

import java.io.IOException;
import java.util.Enumeration;
import java.util.concurrent.ConcurrentHashMap;
import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import org.springframework.stereotype.Component;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.entiry.Message;


/**
 * 客户端访问该链接即可和服务端建立连接
 */
@ServerEndpoint("/login_connect/{userId}")
@Component
public class WebSocketServer {
	// 用来存放所有的连接,静态
	public static ConcurrentHashMap<String, WebSocketServer> webSocketMap = new ConcurrentHashMap<>();

	// 使用session向连接的用户发送信息
	private Session session;
	// 记录连接到此socke的用户id
	private String userId = "";

	// 用户连接成功后会自动调用该方法
	@OnOpen
	public void onOpen(Session session, @PathParam("userId") String userId) throws IOException {
		this.session = session;
		this.userId = userId;
		Message message = new Message();
		message.setType("new_user");
		message.setMessage(userId);
		//将该连接加入到SocketMap中
		webSocketMap.put(this.userId, this);
		// 向所有用户广播,userid上线了
		sendmessagetootherusers(JSON.toJSONString(message));
	}

	// 关闭连接后自动调用
	@OnClose
	public void onClose() throws IOException {
		// 将断开连接的socket从map移除
		webSocketMap.remove(userId);
		// 给所有客户端发送offline消息类型,内容为离线用户的ID
		Message msg = new Message();
		msg.setType("offline");
		msg.setMessage(userId);
		sendmessagetootherusers(JSON.toJSONString(msg));
	}

	// 收到用户发来的消息时调用
	@OnMessage
	public void onMessage(String message, Session session) throws IOException {
		// 将收到的消息转化为JSON对象
		JSONObject json = JSON.parseObject(message);
		// 判断消息类型聊天室消息就直接将消息发送给所有除自己外的用户
		if (json.getString("type").equals("tochatroom")) {
			// 创建消息类,将消息类型、消息内容、发送消息者的id放进去
			Message msg = new Message();
			msg.setMessage(json.getString("message"));
			msg.setUserid(userId);
			msg.setType("tochatroom");
			// 然后将消息发送给其他用户
			sendmessagetootherusers(JSON.toJSONString(msg));
		}
	}

	// 错误时调用
	@OnError
	public void onError(Session session, Throwable error) {
		System.out.println("error");
	}

	// 给除自己以外的所有用户发消息
	public void sendmessagetootherusers(String message) throws IOException {
		// 获取所有连接中的用户id
		Enumeration<String> userkeys = webSocketMap.keys();
		// 通过便利来向所有用户发送消息
		while (userkeys.hasMoreElements()) {
			String userid = userkeys.nextElement();
			// 通过对比排除自己
			if (userid != userId) {
				webSocketMap.get(userid).sendmessage(message);
			}

		}
	}

	// 发送消息给此客户端
	public void sendmessage(String message) throws IOException {
		this.session.getBasicRemote().sendText(message);
	}

}

然后我们需要一个Controller来让用户能有更复杂的操作

但是因为我们的项目是前后端分离项目,所以要写一些配置文件支持外部访问

这个很简单复制粘贴就可以了

package com.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class CrossConfig implements WebMvcConfigurer {
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")
                .allowedOrigins("*")
                .allowedMethods("GET","HEAD","POST","PUT","DELETE","OPTIONS")
                .allowCredentials(true)
                .maxAge(3600)
                .allowedHeaders("*");
    }
}

然后在创建Controller之前,我们再写一个类来存用户信息,好让客户端来获取当前登录用户

这里的代码很简单,有点脱裤子放屁,但是这种方式比较方便以后的扩展,虽然这个类里面只有userID,用数组也可以实现,但是当程序复杂后数据就多起来了,所以还是得习惯这种写法

package com.entiry;

import java.io.Serializable;

public class User  {
	private String userID;//用户ID

	public String getUserID() {
		return userID;
	}

	public void setUserID(String userID) {
		this.userID = userID;
	}

}

然后是Controller方法 

package com.controller;

import com.server.WebSocketServer;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.socket.server.support.WebSocketHandlerMapping;

import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import com.entiry.User;

/**
 * WebSocketController
 * 
 * @author zhengkai.blog.csdn.net
 */
@RestController
public class ChatController {
//获取所有在线人员
	@RequestMapping("/getUsers")    
	public List<User> getUsers() {
        //获取所有的用户名然后注入到列表里面然后返回给前端
		List<User> users = new ArrayList<>();
		Enumeration<String> userkeys = WebSocketServer.webSocketMap.keys();
		while (userkeys.hasMoreElements()) {
			String userid = userkeys.nextElement();
			User tempuser = new User();
			tempuser.setUserID(userid);
			users.add(tempuser);
		}
		return users;
	}
    //查询某个用户是否在线返回布尔型
	@RequestMapping("/isOnline")
	public boolean isOnline(@RequestBody String userid) {
		System.out.println(userid);
		return WebSocketServer.webSocketMap.containsKey(userid);
	}
}

这个controller只实现两个方法,但是拓展的话还是很方便的。

然后后端的内容就全做完了,接下来是修改前端来适配后端的代码,由于体量变大了,所以我把js css分开来写了

这边我先直接放出代码

HTML:

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title id="tittle">聊天室</title>
		<link rel="stylesheet" href="css/A.css" type="text/css" />
	</head>
	<script src="js/jquery-3.4.1.js"></script>
	<script src="js/A.js"></script>
	<script>

	</script>
	<body>
		<div class="main_block">
            <!--标题-->
			<div class="top">聊天室</div>
			<div class="connect">
				<font style="float: left;">&amp;nbsp;&amp;nbsp;|&amp;nbsp;用户名:</font>
				<input class="userid_input" id="userid" placeholder="请输入用户名">
				<button id="connect_botton" class="userid_input" onclick="openSocket()">连接到多人聊天室</button>
				<font style="float: left;">&amp;nbsp;&amp;nbsp;<font id="tip"></font>
				</font>
				<font style="float: right;">当前在线:<font id="nowusercount"></font>人&amp;nbsp;&amp;nbsp;&amp;nbsp;</font>
			</div>
			<div class="line"></div>
			<div class="chat_room">
				<div class="users" id="users">
					<div class="user" style="background-color: #6e90ff;">公共聊天室</div>
				</div>
				<div class="chat">
					<dl class="messages" id="messages">
					</dl>
				</div>
			</div>
			<div class="line"></div>
			<div class="send">
				<textarea placeholder="输入消息..." class="text_input" id="sendmessage"
					></textarea>
				<div class="line"></div>
				<button id="sendbutton" onclick="sendMessage()" class="send_button">发送消息</button>
			</div>
		</div>
	</body>
</html>

CSS:

.main_block {
	width: 700px;
	height: 900px;
	margin: 0 auto;
	border: 1px solid black;
}

.top {
	width: 100%;
	height: 40px;
	background-color: #47a9ff;
	font-size: 20px;
	color: white;
	line-height: 40px;
	text-align: center;
	font-weight: bold;

}



.connect {
	width: 100%;
	height: 30px;
	line-height: 30px;
	font-size: 17px;
	background-color: white;
}

.userid_input {
	margin-top: 5px;
	float: left;
}

.line {
	float: left;
	width: 100%;
	height: 1px;
	background-color: black;
}

.chat_room {
	width: 100%;
	height: 650px;
	background-color: #fff;
}

.users {
	width: 170px;
	float: left;
	overflow-y: scroll;
	height: 100%;
}

.user {
	cursor: pointer;
	width: 100%;
	height: 40px;
	background-color: #9dcdff;
	color: white;
	line-height: 40px;
	text-align: center;
	font-style: 17px;
	margin-bottom: 3px;
}

.user:hover {
	background-color: #81baff;
}

.chat {
	overflow-y: scroll;
	width: 530px;
	height: 100%;
	float: left;

}

.messages {
	width: 95%;
	margin-left: 3%;
}

.sender_id {
	margin-bottom: 5px;
	font-weight: bold;
}

.sender_message {
	margin-bottom: 5px;
	width: 55%;
	background-color: #9dcdff;
	border-radius: 3px;
	font-size: 15px;
	padding: 7px;

}

.user_id {
	margin-left: 85%;
	margin-bottom: 5px;
	font-weight: bold;

}

.user_message {
	margin-left: 30%;
	margin-bottom: 5px;
	width: 55%;
	background-color: #69ff8c;
	border-radius: 3px;
	font-size: 15px;
	padding: 7px;
}

.send {
	width: 100%;
	height: 178px;
}

.text_input {
	font-size: 15px;
	background-color: #f3f3f3;
	resize: none;
	float: right;
	border: 0px solid red;
	width: 694px;
	height: 130px;
}

.send_button {
	margin-top: 5px;
	float: right;
	margin-right: 10px;
	height: 30px;
	width: 100px;
	font-size: 15px;
	font-weight: bold;
	border: 0px solid red;
	border-radius: 3px;
	color: white;
	background-color: #47a9ff;
}

.server_message {
	width: 100%;
	height: 30px;
	font-size: 13px;
	color: #9c9c9c;
	float: left;
	margin-left: 0%;
	line-height: 30px;
	text-align: center;
}

JS:

var socket = null;//socket连接
var userid = "";//当前用户id
var nowusercount = 0;//当前在线人数
//窗口运行时先获取一次在线列表
window.onload = function() {
	getonlineusers();

}
//获取所有在线用户,然后把用户列表更新在列表中
function getonlineusers() {
	$.ajax({
		url: "http://localhost:8081/getUsers", //请求路径
		//data: userid = "177", //要发送的数据
		contentType: "application/json;charset=UTF-8", //发送数据的格式
		type: "post", //访问方式
		dataType: "text", //回调(常用json,text)
		success: function(data) {
			console.log(data);
			var onlineusers = JSON.parse(data);
			nowusercount = onlineusers.length
			$("#nowusercount").text(nowusercount);
			var list = '<div class="user" style="background-color: #6e90ff;">公共聊天室</div>';
            //将获取到的数据遍历然后存储list中
			for (var a = 0; a < onlineusers.length; a++) {
                //排除自己
				if (onlineusers[a].userID != userid) {
					list = list + '<div class="user">' + onlineusers[a].userID +
						'</div>';
				}
			}
            //将元素渲染页面
			$("#users").html(list);
		}

	});
}
//连接方法
function openSocket() {
    //用户名不能输入为空
	if ($("#userid").val() != "") {
		userid = $("#userid").val();
        //先查询一下是否已经有人用了这个用户名
		$.ajax({
			url: "http://localhost:8081/isOnline", //请求路径
			data: userid = $("#userid").val(), //要发送的数据
			contentType: "application/json;charset=UTF-8", //发送数据的格式
			type: "post", //访问方式
			dataType: "text", //回调(常用json,text)
			success: function(data) {
				//如果已经有人在使用这个用户名,则不继续往下走
				if (data == "true") {
					$("#tip").text("用户已存在");
					setTimeout(function() {
						$("#tip").text("");
					}, 2500);
				} else {
                    //当没人用过此用户名的时候则连接服务器
					$("#tip").text("连接成功");
                    //将链接按钮和输入用户名的输入框设为不可用
					$("#connect_botton").attr("disabled", true);
					$("#userid").attr("disabled", true);
					$("#connect_botton").text("已连接");
					setTimeout(function() {
						$("#tip").text("");
					}, 2500);
					//获取用户输入的用户id
					userid = $("#userid").val();
					$("#tittle").text("聊天室:" + userid);
					//这个链接地址对应了springboot跑的端口和上面标注的访问的接口
					var socketUrl = "ws://localhost:8081/login_connect/" + userid;
					socket = new WebSocket(socketUrl);
					//下面几个方法对应了服务端的几个方法
					//连接成功
					socket.onopen = function() {
                        //连接成功后获取最新在线用户列表
						getonlineusers();
					};
					//发生了错误事件
					socket.onerror = function() {
						console.log("websocket发生了错误");
					}
					//收到消息
					socket.onmessage = function(msg) {
						console.log(msg.data);
						var message = JSON.parse(msg.data);
                        //接收到的消息为new_user类型
                        //刷新用户列表然后提示用户上线
						if (message.type == "new_user") {
							getonlineusers();
							nowusercount++;
							$("#nowusercount").text(nowusercount);
							sendservermessage("用户‘" + message.message + "’上线")
						}
                        //接收到chatroom类型的消息直接展示在聊天室
						if (message.type == "tochatroom") {
							addothermessage(message.userid, message.message);
						}
                        //接收到offline的消息提示离线然后刷新用户列表
						if (message.type == "offline") {
							sendservermessage("用户‘" + message.message + "’离线");
							getonlineusers();
						}
					};
					//关闭连接后运行
					socket.onclose = function() {
						console.log("websocket已关闭");
					};
				}
			},
			error: function() {}
		});
	} else {
		$("#tip").text("请输入用户名");
		setTimeout(function() {
			$("#tip").text("");
		}, 2500);
	}
}
//发送数据给服务器
function sendMessage() {
	if ($("#sendmessage").val() != "") {
        //输入框中输入的消息类型为tochatroom
		socket.send('{"type":"tochatroom","message":"' + $("#sendmessage").val() +
			'"}');
		addusermessage(userid, $("#sendmessage").val());
		$("#sendmessage").val("");
	}else{
		$("#sendbutton").text("不能为空");
		setTimeout(function() {
			$("#sendbutton").text("发送消息");
		}, 1500);
	}

}
//将别人的消息渲染页面
function addothermessage(sender, message) {
	$("#messages").html($("#messages").html() + '<dt class="sender_id">' +
		sender + '</dt><dd class="sender_message">' + message + '</dd>')
}
//将自己的消息渲染到页面
function addusermessage(userid, message) {
	$("#messages").html($("#messages").html() + '<dt class="user_id">' +
		userid + '</dt><dd class="user_message">' + message + '</dd>')
}
//将系统的消息渲染到页面
function sendservermessage(message) {
	$("#messages").html($("#messages").html() + '<dd class="server_message">' + message + '</dd>')
}

前端除js代码我都写好了注释,代码写的很简单,所以阅读起来难度也不大相信上手还是会很快的。

运行效果如下:

b7d28fefe8264e958de83928d6455916.png

 6ac0257495d345959eb9e7dffe38e7c1.png

第三阶段源码 

链接:https://pan.baidu.com/s/11UemzHvQCSOVYWfsMrJ3mg 
提取码:1234 
–来自百度网盘超级会员V2的分享

这次做的聊天室因为没有用到数据库,所以不能进行登录,消息保存操作,也没有实现加好友、删好友、私聊等功能,但是最基础的几个功能都已经实现了,如果是从阶段1开始设计的话,可以将这个项目复杂化,加上所有需要的功能,但是总的来说,会socket,和基础的数据库,那么上述功能都可以实现。

原文地址:https://blog.csdn.net/loveisbunny/article/details/126919477

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。

如若转载,请注明出处:http://www.7code.cn/show_9363.html

如若内容造成侵权/违法违规/事实不符,请联系代码007邮箱:suwngjj01@126.com进行投诉反馈,一经查实,立即删除

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注