微信网页版仿写
聊天思路
在一个窗口发送消息,确保其他建立了连接的窗口能够接收到这个消息。
1.和好友聊天:提供sendUid和receiveUid,对当前登录用户id判断是否和该receiveUid一致。
2.和群聊聊天,提供sendUid和reveiveGroupId,对当前登录用户id判断是否在该群聊中。
WebSocket部分
这里的代码差不多是整个项目中最核心的了,参考的how2j上的教程websokcet系列教程
一.后端WebSocket
Chat类
通过该类接收客户端的数据,发送数据给客户端
package cn.itcast.webSocket;
import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
/**
* @ServerEndpoint 注解是一个类层次的注解,它的功能主要是将目前的类定义成一个websocket服务器端,
* 注解的值将被用于监听用户连接的终端访问URL地址,客户端可以通过这个URL来连接到WebSocket服务器端
*/
@ServerEndpoint("/ws/chat")
public class Chat {
// (解决多个连接,只有1个连接消息有效的问题)
boolean flag;
//与某个客户端的连接会话,需要通过它来给客户端发送数据
private Session session;
// 客户端发送过来的消息
private String message;
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
@OnOpen
public void onOpen(Session session){
this.session = session;
ServerManger.add(this);
}
// 发给客户端的消息
public void sendMessage(String message) throws IOException {
this.session.getBasicRemote().sendText(message);
}
@OnClose
public void onClose(){
ServerManger.remove(this);
//System.out.println("关闭连接");
}
@OnMessage
public void onMessage(String message, Session session) {
System.out.println("来自客户端的消息:" + message);
flag=true;
this.message=message;
}
@OnError
public void onError(Session session, Throwable error){
System.out.println("发生错误");
error.printStackTrace();
}
}
ServerManager类
这个类就是浏览器发起一个连接请求就会创建一个Chat对象,将这些对象添加到线程安全的集合servers中。
package cn.itcast.webSocket;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
public class ServerManager {
private static Collection<Chat> servers = Collections.synchronizedCollection(new ArrayList<Chat>());
public static void add(Chat chat){
servers.add(chat);
//System.out.println("有连接加入! 当前总连接数是:"+ servers.size());
}
public static void remove(Chat server){
servers.remove(server);
// System.out.println("有连接退出! 当前总连接数是:"+ servers.size());
}
public static void broadcast(String msg){
for (Chat chat : servers) {
try {
chat.sendMessage(msg);
} catch (IOException e) {
}
}
}
public static Collection<Chat> getServers(){
return servers;
}
}
Send
这个类会处理客户端发送过来的数据,需要确保广播(broadcast方法)最后过来的,run方法会一直运行,客户端没有新消息过来的话,也就不需要再发送消息给全部的客户端。
package cn.itcast.webSocket;
import javax.servlet.ServletConfig;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import java.util.Random;
@WebServlet(name="Send",urlPatterns = "/Send",loadOnStartup=1) //标记为Servlet不是为了其被访问,而是为了便于伴随Tomcat一起启动
public class Send extends HttpServlet implements Runnable {
public void init(ServletConfig config){
startup();
}
public void startup(){
new Thread(this).start();
}
@Override
public void run() {
while(true) {
// 广播当前连接数目
// ServerManger.broadcast("{number:"+ServerManger.getServers().size()+"}");
int duration = new Random().nextInt(1000);
try {
Thread.sleep(duration);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
String msg=" ";
// 广播最后输入的,保证对每个客户端的信息都是一致的,不能在broadcast内部传入各自的信息
for(Chat chat: ServerManager.getServers()){
//判断chat的消息是否有值,不然每次建立新连接,就重置为null了
if(chat.getMessage()!=null&&chat.flag==true)
msg=chat.getMessage();
chat.flag=false; // 新增标志位,每次发消息过来设置为true
// System.out.println("客户端消息:"+msg);
// System.out.println("结束");
}
MyMessage my=MyMessage.getInstance();
// 如果没有新消息发送过来,就不要发送了
if(my.getMessage()==msg)
msg="";
if(msg!="") {
// System.out.println("临时保存消息:"+my.getMessage());
// System.out.println("发送消息:"+msg);
my.setMessage(msg);
// 确认发送消息,更新message
ServerManager.broadcast(msg);
}
}
}
}
MyMessage类
保存最后从客户端发送过来的数据
package cn.itcast.webSocket;
public class MyMessage {
private String message;
private MyMessage(){
}
private static MyMessage myMessage=new MyMessage();
public static MyMessage getInstance(){
return myMessage;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
二.前端WebSocket
js代码
// 创建WebSocket连接
var local="ws://localhost:8888/ws/chat";
var web="ws://101.201.124.20:8080/ws/chat";
var sockeet=new WebSocket(local);
// 连接
sockeet.onopen=function () {
}
// 发生错误
sockeet.onerror=function () {
}
//监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接,防止连接还没断开就关闭窗口,server端会抛异常。
window.onbeforeunload = function () {
closeWebSocket();
}
//关闭WebSocket连接
function closeWebSocket() {
sockeet.close();
}
// 客户端接收浏览器的消息(用null判断空)
sockeet.onmessage=function (result) {
}
var msg={"time":time,"name":name,"value":value,"sendUid":sendUid,"receiveUid":receiveUid,"headImg":headImg};
sockeet.send(JSON.stringify(msg));
主要页面展示
1.注册登录页面
仿写的知乎前端,那个短信验证码想做真实的,但后面想起来的时候没时间去弄了。
2.和好友聊天
3.群聊
4.添加好友
仿写的qq添加好友
5.群聊管理
也是仿写的qq群聊管理页面
6.更换头像(用户和群聊)
7.查看好友个人信息
源码下载:
无法访问github
找到hosts文件(C:\Windows\System32\drivers\etc下)
最后一行添加:140.82.112.3 github.com
不行的话可以换个能用的ip,我反正就这样弄的。出现提示私密链接,就注释掉github
或者直接翻墙?
项目地址
http://101.201.124.20:8080/
总结
这个项目是去年12月开始写的,那个时候刚学完SSM框架没多久,了解到WebSocket协议之后就想着看能不能自己动手写一个网页版的聊天系统出来。
算是自己的第一个独立思考完成的项目了,还有很多待完善之处,用的技术也比较老了,没有用流行的SpringBoot框架,自己还没学到。还有前端页面用的是jsp,其实我用起来和用html差别不大,应该要用html的。前端就是html,css,JavaScript最原生的东西,页面切换这里做的有点差,前端基本没用到多少框架的东西,都是看着知乎登录页面和微信客户端页面还有QQ那些添加好友和群聊管理页面一行一行代码敲出来的。
一直想加上发送文件,图片的功能,我想的是在消息类再加个字段判断消息类型,还有聊天窗口里的时间提示,但是现在已经在准备考研了,就没时间去弄了,微信客户端的功能我感觉还是比较多的,所以就只把一些最核心的功能实现了。