世纪佳缘网页聊天室

项目简介

本项目是基于WebSocket和Servlet实现的网页聊天室。使用到的技术包括:MVC编程思想、WebSocket、Servlet、MySQL、DruidDataSource、Json、Junit、Lombok、Maven

功能实现

1、通过浏览器访问Tomcat服务器,显示注册、登录、聊天界面,实现聊天室的可视化功能

2、使用MySQL数据库存储用户信息,通过与数据库交互来实现用户登录、注册等功能

3、使用WebSocket协议来实现客户端与服务端之间数据的双向交互,以此完成聊天功能

4、通过遍历在线用户列表,广播发送消息,实现群聊、提醒某用户上线下线的功能

思路

1.注册实现

世纪佳缘网页聊天室_第1张图片

2.登录实现

世纪佳缘网页聊天室_第2张图片

3.私聊实现

世纪佳缘网页聊天室_第3张图片

4.群聊实现

世纪佳缘网页聊天室_第4张图片

后端设计

  • 工具类
package com.bittech.java7.chatroom.utils;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;

import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;

/**
 * @Description:封装基础的工具方法,如加载配置文件、json序列化等
 */
public class CommUtils {
    private static final Gson gson = new GsonBuilder().create();
    private CommUtils(){}

    /**
     * 根据指定的文件名加载配置文件
     * @param fileName 配置文件名
     * @return
     */
    public static Properties loadProperties(String fileName) {
        Properties properties = new Properties();
        // 获取当前配置文件夹下的文件输入流
        InputStream in = CommUtils.class.getClassLoader()
                .getResourceAsStream(fileName);
        // 加载配置文件中的所有内容
        try {
            properties.load(in);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return properties;
    }

    public static String object2Json(Object obj) {
        return gson.toJson(obj);
    }

    public static Object json2Object(String jsonStr,Class objClass) {
        return gson.fromJson(jsonStr,objClass);
    }

    public static boolean strIsNull(String str) {
        return str == null || str.equals("");
    }
}

  • DAO层
AcountDao类:
package com.bittech.java7.chatroom.dao;

import com.bittech.java7.chatroom.entity.User;
import org.apache.commons.codec.digest.DigestUtils;

import java.sql.*;

/**
 * @Description:关于用户模块的dao层
 */
public class AccountDao extends BaseDao{

    // 用户登录 select
    public User userLogin(String userName,String password) {
        Connection connection = null;
        PreparedStatement statement = null;
        ResultSet resultSet = null;
        User user = null;
        try {
            connection = getConnection();
            String sql = "SELECT * FROM user WHERE username = ? AND " +
                    " password = ?";
            statement = connection.prepareStatement(sql);
            statement.setString(1,userName);
            statement.setString(2, DigestUtils.md5Hex(password));
            resultSet = statement.executeQuery();
            if (resultSet.next()) {
                user = getUserInfo(resultSet);
            }
        }catch (Exception e) {
            System.err.println("查询用户信息出错");
            e.printStackTrace();
        }finally {
            closeResources(connection,statement,resultSet);
        }
        return user;
    }

    // 用户注册 insert
    public boolean userRegister(User user) {
        String userName = user.getUserName();
        String password = user.getPassword();
        Connection connection = null;
        PreparedStatement statement = null;
        boolean isSuccess = false;
        try {
            connection = getConnection();
            String sql = "INSERT INTO user(username, password)" +
                    " VALUES(?,?) ";
            statement = connection.prepareStatement(sql,Statement.RETURN_GENERATED_KEYS);
            statement.setString(1,userName);
            statement.setString(2,DigestUtils.md5Hex(password));
            isSuccess = (statement.executeUpdate() == 1);
        }catch (Exception e) {
            System.err.println("用户注册失败");
            e.printStackTrace();
        }finally {
            closeResources(connection,statement);
        }
        return isSuccess;
    }

    // 将数据表信息封装到User类中
    public User getUserInfo(ResultSet resultSet) throws SQLException {
        User user = new User();
        user.setId(resultSet.getInt("id"));
        user.setUserName(resultSet.getString("username"));
        user.setPassword(resultSet.getString("password"));
        return user;
    }

}

BaseDao类:
package com.bittech.java7.chatroom.dao;

import com.alibaba.druid.pool.DruidDataSourceFactory;
import com.bittech.java7.chatroom.utils.CommUtils;

import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;

/**
 * @Description:封装基础操作,数据源、获取连接、关闭资源
 */
public class BaseDao {
    private static DataSource dataSource;
    static {
        Properties properties = CommUtils.
                loadProperties("datasource.properties");
        try {
            dataSource = DruidDataSourceFactory.createDataSource(properties);
        } catch (Exception e) {
            System.err.println("数据源加载失败");
        }
    }

    // 获取数据库连接
    protected Connection getConnection() {
        try {
            return dataSource.getConnection();
        } catch (SQLException e) {
            System.err.println("获取连接失败");
        }
        return null;
    }

    // 关闭资源Statement ResultSet Connection
    protected void closeResources(Connection connection,
                                  Statement statement) {
        if (connection != null) {
            try {
                connection.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if (statement != null) {
            try {
                statement.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }

    protected void closeResources(Connection connection,
                                  Statement statement,
                                  ResultSet resultSet) {
        closeResources(connection,statement);
        if (resultSet != null) {
            try {
                resultSet.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}

  • entity层
package com.bittech.java7.chatroom.entity;

import lombok.Data;

import java.util.Map;

/**
 * @Description:后端发送给前端的信息实体
 */
@Data
public class Message2Client {
    // 聊天内容
    private String content;
    // 服务端登录的所有用户列表
    private Map names;

    public void setContent(String msg) {
        this.content = msg;
    }
    public void setContent(String userName,String msg) {
        this.content = userName + "说:" + msg;
    }
}

package com.bittech.java7.chatroom.entity;

import lombok.Data;

/**
 * @Description:前端发送给后端的信息实体类
 * 群聊:{"msg":"777","type":1}
 * 私聊:{"to":"0-","msg":"33333","type":2}
 */
@Data
public class MessageFromClient {
    // 聊天信息
    private String msg;
    // 聊天类别: 1表示群聊 2表示私聊
    private String type;
    // 私聊的对象SessionID
    private String to;
}

package com.bittech.java7.chatroom.entity;

import lombok.Data;


@Data
public class User {
    private Integer id;
    private String userName;
    private String password;
}

  • controller层
package com.bittech.java7.chatroom.controller;

import com.bittech.java7.chatroom.service.AccountService;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

/**
 * @Description:
 */
@WebServlet(urlPatterns = "/doRegister")
public class AccountController extends HttpServlet {
    private AccountService accountService = new AccountService();

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String userName = req.getParameter("username");
        String password = req.getParameter("password");
        resp.setContentType("text/html;charset=utf8");
        PrintWriter writer = resp.getWriter();
        if (accountService.userRegister(userName,password)) {
            // 用户注册成功
            // 弹框提示,返回登陆界面
            writer.println("");
        }else {
            // 弹框提示失败,保留原页面
            writer.println("");
        }
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req,resp);
    }
}

package com.bittech.java7.chatroom.controller;
import	java.util.HashMap;

import com.bittech.java7.chatroom.config.FreeMarkerListener;
import com.bittech.java7.chatroom.service.AccountService;
import com.bittech.java7.chatroom.utils.CommUtils;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Map;

/**
 * @Description:
 */
@WebServlet(urlPatterns = "/login")
public class LoginController extends HttpServlet {
    private AccountService accountService = new AccountService();
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String userName = req.getParameter("username");
        String password = req.getParameter("password");
        resp.setContentType("text/html;charset=utf8");
        PrintWriter out = resp.getWriter();
        if (CommUtils.strIsNull(userName) || CommUtils.strIsNull(password)) {
            // 登录失败,停留登录页面
            out.println("    ");
        }
        if (accountService.userLogin(userName,password)) {
            // 登录成功,跳转到聊天页面
            // 加载chat.ftl
            Template template = getTemplate(req,"/chat.ftl");
            Map map = new HashMap<>();
            map.put("username",userName);
            try {
                template.process(map, out);
            } catch (TemplateException e) {
                e.printStackTrace();
            }
        }else {
            // 登录失败,停留在登录页面
            out.println("    ");
        }
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req,resp);
    }

    private Template getTemplate(HttpServletRequest req,String fileName) {
        Configuration cfg = (Configuration)
                req.getServletContext().getAttribute(FreeMarkerListener.TEMPLATE_KEY);
        try {
            return cfg.getTemplate(fileName);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }
}

  • service层:
package com.bittech.java7.chatroom.service;

import com.bittech.java7.chatroom.dao.AccountDao;
import com.bittech.java7.chatroom.entity.User;

/**
 * @Description:
 */
public class AccountService {
    private AccountDao accountDao = new AccountDao();

    // 用户登陆
    public boolean userLogin(String userName,String password) {
        User user = accountDao.userLogin(userName,password);
        if (user == null) {
            return false;
        }
        return true;
    }

    // 用户注册
    public boolean userRegister(String userName,String password) {
        User user = new User();
        user.setUserName(userName);
        user.setPassword(password);
        return accountDao.userRegister(user);
    }
}

package com.bittech.java7.chatroom.service;

import com.bittech.java7.chatroom.entity.Message2Client;
import com.bittech.java7.chatroom.entity.MessageFromClient;
import com.bittech.java7.chatroom.utils.CommUtils;

import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArraySet;

/**
 * @Description:
 */
@ServerEndpoint("/websocket")
public class WebSocket {
    // 存储所有连接到后端的websocket
    private static CopyOnWriteArraySet clients =
            new CopyOnWriteArraySet<>();
    // 缓存所有的用户列表
    private static Map names = new ConcurrentHashMap<>();
    // 绑定当前websocket会话
    private Session session;
    // 当前客户端的用户名
    private String userName;

    @OnOpen
    public void onOpen(Session session) {
        this.session = session;
        // username=' + '${username}
        String userName = session.getQueryString().split("=")[1];
        this.userName = userName;
        // 将客户端聊天实体保存到clients
        clients.add(this);
        // 将当前用户以及SessionID保存到用户列表
        names.put(session.getId(),userName);
        System.out.println("有新的连接,SessionID为"+session.getId() +
                ",用户名为"+userName);
        // 发送给所有在线用户一个上线通知
        Message2Client message2Client = new Message2Client();
        message2Client.setContent(userName+"上线了!");
        message2Client.setNames(names);
        // 发送信息
        String jsonStr = CommUtils.object2Json(message2Client);
        for (WebSocket webSocket : clients) {
            webSocket.sendMsg(jsonStr);
        }
    }

    @OnError
    public void onError(Throwable e) {
        System.err.println("WebSocket连接失败!");
    }

    @OnMessage
    //群聊:{"msg":"777","type":1}
    //私聊:{"to":"0-","msg":"33333","type":2}
    public void onMessage(String msg) {
        // 将msg -> MessageFromClient
        MessageFromClient messageFromClient = (MessageFromClient) CommUtils
                .json2Object(msg,MessageFromClient.class);
        if (messageFromClient.getType().equals("1")) {
            // 群聊信息
            String content = messageFromClient.getMsg();
            Message2Client message2Client = new Message2Client();
            message2Client.setContent(content);
            message2Client.setNames(names);
            // 广播发送
            for (WebSocket webSocket : clients) {
                webSocket.sendMsg(CommUtils.object2Json(message2Client));
            }
        }else if (messageFromClient.getType().equals("2")) {
            // 私聊信息
            // {"to":"0-1-2-","msg":"33333","type":2}
            // 私聊内容
            String content = messageFromClient.getMsg();
            int toL = messageFromClient.getTo().length();
            String tos[] = messageFromClient.getTo()
                    .substring(0,toL-1).split("-");
            List lists = Arrays.asList(tos);
            // 给指定的SessionID发送信息
            for (WebSocket webSocket : clients) {
                if (lists.contains(webSocket.session.getId()) &&
                this.session.getId() != webSocket.session.getId()) {
                    // 发送私聊信息
                    Message2Client message2Client = new Message2Client();
                    message2Client.setContent(userName,content);
                    message2Client.setNames(names);
                    webSocket.sendMsg(CommUtils.object2Json(message2Client));
                }
            }
        }
    }

    @OnClose
    public void onClose() {
        // 将客户端聊天实体移除
        clients.remove(this);
        // 将当前用户以及SessionID保存到用户列表
        names.remove(session.getId());
        System.out.println("有连接下线了"+
                ",用户名为"+userName);
        // 发送给所有在线用户一个下线通知
        Message2Client message2Client = new Message2Client();
        message2Client.setContent(userName+"下线了!");
        message2Client.setNames(names);
        // 发送信息
        String jsonStr = CommUtils.object2Json(message2Client);
        for (WebSocket webSocket : clients) {
            webSocket.sendMsg(jsonStr);
        }
    }

    public void sendMsg(String msg) {
        try {
            this.session.getBasicRemote().sendText(msg);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

  • config层:
package com.bittech.java7.chatroom.config;
import	java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;

import freemarker.template.Configuration;

import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;

/**
 * @Description:
 */
@WebListener
public class FreeMarkerListener implements ServletContextListener {
    public static final String TEMPLATE_KEY = "_template_";
    @Override
    public void contextInitialized(ServletContextEvent sce) {
        // 配置版本
        Configuration cfg = new Configuration(Configuration.VERSION_2_3_0);
        // 配置加载ftl的路径
        try {
            cfg.setDirectoryForTemplateLoading(
                    new File("/Users/liuyiming/Documents/IDEA_Project/java7_chatroom_websocket/src/main/webapp"));
        } catch (IOException e) {
            e.printStackTrace();
        }
        // 配置页面编码
        cfg.setDefaultEncoding(StandardCharsets.UTF_8.displayName());
        sce.getServletContext().setAttribute(TEMPLATE_KEY,cfg);
    }

    @Override
    public void contextDestroyed(ServletContextEvent sce) {

    }
}

用户界面设计

  • chat.ftl



    
    
    java7_chatroom


    
    
    
    




${username}
  • index.html



    
    log-in
    
    


    
  • registration.html



    
    sign-up
    


    

创建账户

  • websocketTest.html



    
    websocket demo


    请输入要发送的信息:
    
    
    
收到服务端信息为:

你可能感兴趣的:(世纪佳缘项目)