栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

SpringBoot +WebSocket实现简单聊天室功能实例

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

SpringBoot +WebSocket实现简单聊天室功能实例

SpringBoot +WebSocket实现简单聊天室功能实例)
  • 一、代码来源
  • 二、依赖下载
  • 三、数据库准备(sql)
    • 数据库建表并插入sql
  • 四、resources文件配置
    • application.yml 配置
    • log4j2xml配置
    • 由mybatis-x生成 mapper下的 StaffMapper.xml 文件
  • 五、主文件
    • 项目结构图
    • ----补充 mybatis-plus + mybatis-x 用法
    • domain目录下
      • 由mybatis-x 生成的 Staff
      • 自个儿创建的Message
      • 自个儿创建的User
    • mapper目录下
      • 由mybatis-x 生成的StaffMapper
    • service目录下
      • 由mybatis-x 生成的StaffService 接口
      • WebSocketService
      • 由mybatis-x生成的Service实现类 impl的StaffServiceImpl
    • controller目录下
      • ChatController
      • LoginController
    • config目录下
      • WebSocketConfig
    • main 主入口
      • JohnsonApplication
  • 六、前端渲染文件(暂时还是copy)
    • 前端结构图
    • static目录下
    • templates目录下
      • chatroom
      • fail
      • login
  • 七、学习总结
    • 1 核心 websocket使用
    • 2 设计线程安全方法
  • 八、相关报错
        • 错误一 引入mybatis-plus依赖错误

一、代码来源

从gittee中找到的一个示例

代码链接

二、依赖下载


    4.0.0
    com.websocket_chat
    johnson
    0.0.1-SNAPSHOT
    johnson
    Demo project for Spring Boot

    
        1.8
        UTF-8
        UTF-8
        2.3.7.RELEASE
    

    
        
            org.springframework.boot
            spring-boot-starter-thymeleaf
        
        
            org.springframework.boot
            spring-boot-starter-websocket
        

        
            org.springframework.boot
            spring-boot-starter-test
            test
            
                
                    org.junit.vintage
                    junit-vintage-engine
                
            
        

        
            org.springframework.boot
            spring-boot-starter
            
                
                    org.springframework.boot
                    spring-boot-starter-logging
                
            
        

        
            org.springframework.boot
            spring-boot-starter-web
        

        
            mysql
            mysql-connector-java
        

        
            org.mybatis.spring.boot
            mybatis-spring-boot-starter
            2.1.4
        

        // springboot 使用这个 mybatisplus
        
            com.baomidou
            mybatis-plus-boot-starter
            3.4.2
        

        
            org.projectlombok
            lombok
        

        
            org.apache.poi
            poi
            3.17
        

        
            com.alibaba
            druid
            1.1.20
        

        
            com.alibaba
            fastjson
            1.2.73
        

        
            commons-net
            commons-net
            3.1
        
        
            org.springframework.boot
            spring-boot-starter-log4j2
        
        
            junit
            junit
            test
        


    

    
        
            
                org.springframework.boot
                spring-boot-dependencies
                ${spring-boot.version}
                pom
                import
            
        
    

    
        
            
                org.apache.maven.plugins
                maven-compiler-plugin
                3.8.1
                
                    1.8
                    1.8
                    UTF-8
                
            
            
                org.springframework.boot
                spring-boot-maven-plugin
                2.3.7.RELEASE
                
                    com.websocket_chat.johnson.JohnsonApplication
                
                
                    
                        repackage
                        
                            repackage
                        
                    
                
            
        
    



三、数据库准备(sql) 数据库建表并插入sql

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for staff
-- ----------------------------
DROp TABLE IF EXISTS `staff`;
CREATE TABLE `staff` (
  `staff_id` tinyint(3) unsigned NOT NULL AUTO_INCREMENT,
  `first_name` varchar(45) CHARACTER SET utf8 NOT NULL,
  `last_name` varchar(45) CHARACTER SET utf8 NOT NULL,
  `address_id` smallint(5) unsigned NOT NULL,
  `picture` blob,
  `email` varchar(50) CHARACTER SET utf8 DEFAULT NULL,
  `store_id` tinyint(3) unsigned NOT NULL,
  `active` tinyint(1) NOT NULL DEFAULT '1',
  `username` varchar(16) CHARACTER SET utf8 NOT NULL,
  `password` varchar(40) COLLATE utf8_bin DEFAULT NULL,
  `last_update` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`staff_id`) USING BTREE,
  KEY `idx_fk_store_id` (`store_id`) USING BTREE,
  KEY `idx_fk_address_id` (`address_id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8 COLLATE=utf8_bin ROW_FORMAT=DYNAMIC;

-- ----------------------------
-- Records of staff
-- ----------------------------
BEGIN;
INSERT INTO `staff` VALUES (1, 'Mike', 'Hillyer', 3, NULL, '', 1, 1, 'Mike', '81dc9bdb52d04dc20036dbd8313ed055', '2017-06-29 14:27:38');
INSERT INTO `staff` VALUES (2, 'Jon', 'Stephens', 4, NULL, '', 2, 1, 'Jon', '1234', '2017-06-29 14:27:38');
INSERT INTO `staff` VALUES (3, 'TOM', 'DSA', 3, NULL, 'DF', 1, 1, 'TOM', '1234', '2016-11-11 11:08:10');
INSERT INTO `staff` VALUES (4, 'John', 'son', 4, NULL, NULL, 1, 1, 'John', '1234', '2022-08-09 09:13:28');
COMMIT;

SET FOREIGN_KEY_CHECKS = 1;

四、resources文件配置 application.yml 配置
spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://192.168.1.128:3306/sakila?serverTimezone=Asia/Shanghai&useSSL=false&characterEncoding=utf8
    username: root
    password: root
mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl #开启SQL语句打印
server:
  port: 8080
log4j2xml配置


	
		
		./logs
	
	
		
	
	
	
		
			
		
		
		
		
			
			
			
				
			
			
			
		
		
		
		
			
                  
                
            
			
			
				
				
			
			
			
		
		
		
			
                  
                
            
			
			
				
			
			
		
		
		
			
                  
                
            
			
			
				
			
			
		
		
		
			
				
                
            
			
			
				
			
			
		
	

		
		
		
		
			
			
			
			
			
			
		
	

由mybatis-x生成 mapper下的 StaffMapper.xml 文件




    
            
            
            
            
            
            
            
            
            
            
    

    
        staff_id,first_name,last_name,
        address_id,email,store_id,
        active,username,password,
        last_update,picture
    
    
        select * from staff where staff_id =#{id}
    



五、主文件 项目结构图

----补充 mybatis-plus + mybatis-x 用法

大家可以参考 这篇文章

https://blog.csdn.net/qq_45134562/article/details/125185816

会生成 domain+mapper+service+ resource.mapper 目录和文件

domain目录下 由mybatis-x 生成的 Staff
@TableName(value ="staff")
@Data
public class Staff implements Serializable {
    
    @TableId(type = IdType.AUTO)
    private Long staffId;

    
    private String firstName;

    
    private String lastName;

    
    private Short addressId;

    
    private String email;

    
    private Integer storeId;

    
    private Boolean active;

    
    private String username;

    
    private String password;

    
    private Date lastUpdate;

    
    private byte[] picture;

    @TableField(exist = false)
    private static final long serialVersionUID = 1L;

    @Override
    public boolean equals(Object that) {
        if (this == that) {
            return true;
        }
        if (that == null) {
            return false;
        }
        if (getClass() != that.getClass()) {
            return false;
        }
        Staff other = (Staff) that;
        return (this.getStaffId() == null ? other.getStaffId() == null : this.getStaffId().equals(other.getStaffId()))
            && (this.getFirstName() == null ? other.getFirstName() == null : this.getFirstName().equals(other.getFirstName()))
            && (this.getLastName() == null ? other.getLastName() == null : this.getLastName().equals(other.getLastName()))
            && (this.getAddressId() == null ? other.getAddressId() == null : this.getAddressId().equals(other.getAddressId()))
            && (this.getEmail() == null ? other.getEmail() == null : this.getEmail().equals(other.getEmail()))
            && (this.getStoreId() == null ? other.getStoreId() == null : this.getStoreId().equals(other.getStoreId()))
            && (this.getActive() == null ? other.getActive() == null : this.getActive().equals(other.getActive()))
            && (this.getUsername() == null ? other.getUsername() == null : this.getUsername().equals(other.getUsername()))
            && (this.getPassword() == null ? other.getPassword() == null : this.getPassword().equals(other.getPassword()))
            && (this.getLastUpdate() == null ? other.getLastUpdate() == null : this.getLastUpdate().equals(other.getLastUpdate()))
            && (Arrays.equals(this.getPicture(), other.getPicture()));
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((getStaffId() == null) ? 0 : getStaffId().hashCode());
        result = prime * result + ((getFirstName() == null) ? 0 : getFirstName().hashCode());
        result = prime * result + ((getLastName() == null) ? 0 : getLastName().hashCode());
        result = prime * result + ((getAddressId() == null) ? 0 : getAddressId().hashCode());
        result = prime * result + ((getEmail() == null) ? 0 : getEmail().hashCode());
        result = prime * result + ((getStoreId() == null) ? 0 : getStoreId().hashCode());
        result = prime * result + ((getActive() == null) ? 0 : getActive().hashCode());
        result = prime * result + ((getUsername() == null) ? 0 : getUsername().hashCode());
        result = prime * result + ((getPassword() == null) ? 0 : getPassword().hashCode());
        result = prime * result + ((getLastUpdate() == null) ? 0 : getLastUpdate().hashCode());
        result = prime * result + (Arrays.hashCode(getPicture()));
        return result;
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append(getClass().getSimpleName());
        sb.append(" [");
        sb.append("Hash = ").append(hashCode());
        sb.append(", staffId=").append(staffId);
        sb.append(", firstName=").append(firstName);
        sb.append(", lastName=").append(lastName);
        sb.append(", addressId=").append(addressId);
        sb.append(", email=").append(email);
        sb.append(", storeId=").append(storeId);
        sb.append(", active=").append(active);
        sb.append(", username=").append(username);
        sb.append(", password=").append(password);
        sb.append(", lastUpdate=").append(lastUpdate);
        sb.append(", picture=").append(picture);
        sb.append(", serialVersionUID=").append(serialVersionUID);
        sb.append("]");
        return sb.toString();
    }
}
自个儿创建的Message
@Data
public class Message {

    private String from;

    private String to;

    private String text;
    @JSONField(format = "yyyy-MM-dd HH:mm:ss")
    private Date date;
}
自个儿创建的User
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    Long uid;
    String name;
}
mapper目录下 由mybatis-x 生成的StaffMapper
//@Component
public interface StaffMapper extends BaseMapper {
	
    Staff getPasswordByUsername(String name);

    Staff getUsernameByStaffId(long id);
}
service目录下 由mybatis-x 生成的StaffService 接口
public interface StaffService extends IService {

    String getPasswordByUsername(String name);

    String  getUsernameByStaffId(long id);

    Long getUidByName(String name);
}

WebSocketService
@ServerEndpoint("/webSocket/{username}")
@Component
public class WebSocketService {

    //静态变量,用来记录当前在线用户数,设计为线程安全
    private static AtomicInteger onlineNum = new AtomicInteger();
    //
    private static ConcurrentHashMap sessionPools =new ConcurrentHashMap<>();

    //发送消息
    public void sendMessage(Session session, String message) throws IOException {
        if(session!=null){
            synchronized (session){
                System.out.println("发送数据:"+message);
                session.getBasicRemote().sendText(message);
            }
        }
    }

    //给指定用户发送消息
    public void sendInfo(String username,String message){
        Session session = sessionPools.get(username);
        try {
            sendMessage(session,message);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    //群发消息
    public void broadCast(String message){
        for (Session session : sessionPools.values()) {
            try {
                sendMessage(session,message);
            } catch (IOException e) {
                e.printStackTrace();
                continue;
            }
        }
    }

    // 建立连接成功调用

    @OnOpen
    public void onOpen(Session session, @PathParam("username") String username){
        sessionPools.put(username,session);
        addOnlineNum();
        System.out.println(username+"加入聊天室,现在的人数是:"+onlineNum);
        Message message = new Message();
        message.setDate(new Date());
        message.setTo("0");
        message.setText(username);
        broadCast(JSON.toJSONString(message));
    }

    //关闭连接时调用
    @OnClose
    public void onClose(@PathParam("username") String username){
        sessionPools.remove(username);
        subOnlineNum();
        System.out.println(username+"退出聊天室,现在聊天人数是:"+onlineNum);
        Message message = new Message();
        message.setText(username);
        message.setDate(new Date());
        message.setTo("-2");
        broadCast(JSON.toJSONString(message));
    }

    //收到客户端信息后,根据接收人的username来群发或者私发
    // -1 为群发

    @OnMessage
    public void  onMessage(String message){
        System.out.println("服务器接收到消息:"+message);
        Message msg = JSON.parseObject(message,Message.class);
        msg.setDate(new Date());
        if(msg.getTo().equals("-1")){
            //群发
            broadCast(JSON.toJSONString(msg,true));
        }else {
            sendInfo(msg.getTo(), JSON.toJSONString(msg,true));
        }
    }

    @OnError
    public void onError(Session session, Throwable throwable){
        System.out.println("发生错误");
        throwable.printStackTrace();
    }




    //增加用户数
    public static void addOnlineNum(){
        onlineNum.incrementAndGet();
    }



    //减少用户数
    public static void subOnlineNum(){
        onlineNum.decrementAndGet();
    }

    public static AtomicInteger getOnlineNum(){
        return onlineNum;
    }

    public static ConcurrentHashMap getSessionPools(){
        return sessionPools;
    }
}

由mybatis-x生成的Service实现类 impl的StaffServiceImpl
 */
@Service
public class StaffServiceImpl extends ServiceImpl
    implements StaffService {
    @Autowired
    private StaffMapper staffMapper;


    @Override
    public String getPasswordByUsername(String name) {
        Staff staff = staffMapper.getPasswordByUsername(name);
        if(staff !=null)
        return staff.getPassword();
        else
        return null;
    }

    @Override
    public String getUsernameByStaffId(long id) {
        Staff staff = staffMapper.getUsernameByStaffId(id);
        if(staff !=null)
            return staff.getUsername();
        else
            return null;
    }

    @Override
    public Long getUidByName(String name) {
        Staff staff = staffMapper.getPasswordByUsername(name);
        if(staff !=null)
            return staff.getStaffId();
        else
            return null;
    }
}





controller目录下 ChatController
@Controller
public class ChatController {
    @Autowired
    private StaffService staffService;

    @RequestMapping("/onlineusers")
    @ResponseBody
    public Set onlineUser(@RequestParam("currentuser") String currentUser) {
        ConcurrentHashMap sessionPools = WebSocketService.getSessionPools();
        Set set = sessionPools.keySet();
        Iterator iterator = set.iterator();
        HashSet nameSet = new HashSet<>();
        while (iterator.hasNext()) {
            String next = iterator.next();
            if (!next.equals(currentUser)) {
                nameSet.add(next);
            }
        }
        return nameSet;
    }

    @RequestMapping("/getuid")
    @ResponseBody
    public User getUid(@RequestParam("username") String username){
        Long uid = staffService.getUidByName(username);
        return new User(uid,null);
    }

}

LoginController
@Controller
public class LoginController {

    @Autowired
    private StaffService staffService;

    @RequestMapping("/loginvalidate")
    public String loginvalidate(@RequestParam("username") String username, @RequestParam("password") String password, HttpSession httpSession){
        if(username==null){
            return "login";
        }
        String realPwd = staffService.getPasswordByUsername(username);
        if(realPwd!=null && realPwd.equals(password)){
            Long uid = staffService.getUidByName(username);
            System.out.println("登录成功:"+uid);
            httpSession.setAttribute("uid",uid);
            return "chatroom";
        }else {
            return "fail";
        }
    }

    @RequestMapping("/login")
    public String login(){
        return "login";
    }

    @RequestMapping("/https://blog.csdn.net/qq_34272900/article/details/logout")
    public String https://blog.csdn.net/qq_34272900/article/details/logout(){
        return "login";
    }

    @GetMapping("/currentuser")
    @ResponseBody
    public User currentUser(HttpSession httpSession){
        Long uid = (Long) httpSession.getAttribute("uid");
        String username = staffService.getUsernameByStaffId(uid);
        System.out.println("用户名:"+username);
        return new User(uid,username);
    }
}

config目录下 WebSocketConfig
@Configuration
public class WebSocketConfig {

    // 自动注册使用@ServerEndPoint 注解声明的websocket endpoint

    @Bean
    public ServerEndpointExporter serverEndpointExporter(){
        return new ServerEndpointExporter();
    }
}
main 主入口 JohnsonApplication
@SpringBootApplication
@MapperScan("com.websocket_chat.johnson.mapper")
public class JohnsonApplication {

    public static void main(String[] args) {
        SpringApplication.run(JohnsonApplication.class, args);
    }

}

六、前端渲染文件(暂时还是copy) 前端结构图

static目录下

存放的是前端资源,使用的是 bootstrap+jquery来美化前端。

templates目录下 chatroom


  
    聊天室
    




  
    

当前登录用户

当前在线的其他用户

群发系统广播



fail

login


  
    login
    



  
  
    

登录聊天室

七、学习总结 1 核心 websocket使用 2 设计线程安全方法 八、相关报错 错误一 引入mybatis-plus依赖错误

[ERROR] [18:13:57] org.apache.catalina.core.ContainerBase.[Tomcat].[localhost].[/].[dispatcherServlet] - Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.apache.ibatis.binding.BindingException: Invalid bound statement (not found): com.websocket_chat.johnson.mapper.StaffMapper.getPasswordByUsername] with root cause
org.apache.ibatis.binding.BindingException: Invalid bound statement (not found): com.websocket_chat.johnson.mapper.StaffMapper.getPasswordByUsername

原因:
开始时我用 如下依赖

        
            com.baomidou
            mybatis-plus
            3.4.2
        

需改为

        
            com.baomidou
            mybatis-plus-boot-starter
            3.4.2
        
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/1039276.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号