精华内容
下载资源
问答
  • C# WebSocket Server

    2021-09-02 17:05:27
    C# 实现的 WebSocket在Web 应用 服务端如何开启WebSocketServer 需要nuget 安装 Fleck 在Web 应用 服务端如何开启WebSocketServer 基本思路就是在 Application_Start 中开启一个后台运行的WebSocketServer 如下: ...

    需要nuget 安装 Fleck

    在Web 应用 服务端如何开启WebSocketServer

    基本思路就是在 Application_Start 中开启一个后台运行的WebSocketServer 如下:
    在这里插入图片描述

     		static Thread socketThread;
            /// <summary>
            /// 启动
            /// </summary>
            protected void Application_Start()
            {
                AreaRegistration.RegisterAllAreas();
                GlobalConfiguration.Configure(WebApiConfig.Register);
                FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
                RouteConfig.RegisterRoutes(RouteTable.Routes);
                InitialConfig();
                //启动Socket服务端
                socketThread = new Thread(new ThreadStart(this.StartWebSocketServer));
                socketThread.IsBackground = true;//设置为后台线程
                socketThread.Start();
                WriteLog("Application_Start");
            }
    

    这个 server 具体的代码如下:
    在这里插入图片描述

    		private static WebSocketServer server;  
            List<IWebSocketConnection> allSockets = new List<IWebSocketConnection>();
            private void StartWebSocketServer()
            {
                try
                {
                    string IP = WebConfigurationManager.AppSettings["SocketIP"];
                    string Port = WebConfigurationManager.AppSettings["SocketPort"];
                    if (!commonService.CheckPortEnable(IP, int.Parse(Port)))
                    {
                        WriteLog("port Useful");
                        server = new WebSocketServer($"ws://{IP}:{Port}");
                        server.Start(socket =>
                        {
                            socket.OnOpen = () =>
                            {
                                Console.WriteLine("Open!");
                                allSockets.Add(socket);
                                //根据客户端ID保存所有连接过来的客户端,便于向指定服务端推送消息
                                if (!CommonService.ClientHashtablePool.ContainsKey($"{socket.ConnectionInfo.ClientIpAddress}"))
                                    CommonService.ClientHashtablePool.Add($"{socket.ConnectionInfo.ClientIpAddress}", socket);
                                
                            };
                            socket.OnClose = () =>
                            {
                                Console.WriteLine("Close!");
                                allSockets.Remove(socket);
                                //移除客户端
                                if (CommonService.ClientHashtablePool.ContainsKey($"{socket.ConnectionInfo.ClientIpAddress}"))
                                    CommonService.ClientHashtablePool.Remove($"{socket.ConnectionInfo.ClientIpAddress}");
                            };
                            socket.OnMessage = message =>
                            {
                                Console.WriteLine(message);
                                allSockets.ToList().ForEach(s => s.Send("Echo: nice to meet you!" + message));
                            };
                        });
                    }
                    else
                    {
                        WriteLog("port 占用");
                    }
                }
                catch (Exception ex)
                {
                    WriteLog(ex.Message);
                }
            }
    

    这样就启动了一个server

    CheckPortEnable 是检查端口是否占用的方法,如果让webSocket监听一个被占用的端口会引发异常;

    /// <summary>
            /// 检测IP 端口是否通
            /// </summary>
            /// <param name="_ip"></param>
            /// <param name="_port"></param>
            /// <returns></returns>
            public bool CheckPortEnable(string _ip, int _port)
            {
                //将IP和端口替换成为你要检测的
                string ipAddress = _ip;
                int portNum = _port;
    
                bool _portEnable = false;
                try
                {
                    using (Socket sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp))
                    {
                        IAsyncResult connResult = sock.BeginConnect(ipAddress, portNum, null, null);
                        connResult.AsyncWaitHandle.WaitOne(500, true);  //等待0.5秒
                        if (connResult.IsCompleted)
                        {
                            _portEnable = true;
                        }
                        sock.Close();
                    }
                }
                catch (SocketException e)
                {
                    _portEnable = false;
                }
                return _portEnable;
            }
    
    展开全文
  • redis解决websocket在分布式场景下session共享问题 在显示项目中遇到了一个问题,需要使用到websocket与小程序建立长链接。由于项目是负载均衡的,存在项目部署在多台机器上。这样就会存在一个问题,当一次请求负载...

    redis解决websocket在分布式场景下session共享问题

    在显示项目中遇到了一个问题,需要使用到websocket与小程序建立长链接。由于项目是负载均衡的,存在项目部署在多台机器上。这样就会存在一个问题,当一次请求负载到第一台服务器时,socketsession在第一台服务器线程上,第二次请求,负载到第二台服务器上,需要通过id查找当前用户的session时,是查找不到的。
    在这里插入图片描述

    • 可以看到,由于websocket的session并没有实现序列化接口。所以无法将session序列化到redis中。

    • web的中的httpsession 主要是通过下面的两个管理器实现序列化的。

      org.apache.catalina.session.StandardManager
    
      org.apache.catalina.session.PersistentManager
    

    StandardManager是Tomcat默认使用的,在web应用程序关闭时,对内存中的所有HttpSession对象进行持久化,把他们保存到文件系统中。默认的存储文件为

    <tomcat安装目录>/work/Catalina/<主机名>/<应用程序名>/sessions.ser

    PersistentManager比StandardManager更为灵活,只要某个设备提供了实现org.apache.catalina.Store接口的驱动类,PersistentManager就可以将HttpSession对象保存到该设备。
    在这里插入图片描述
    所以spring-session-redis 解决分布场景下的session共享就是将session序列化到redis中间件中,使用filter 加装饰器模式解决分布式场景httpsession 共享问题。

    解决方案

    1. 使用消息中间件解决websocket session共享问题。
    2. 使用redis的发布订阅模式解决

    本文使用方式二

    • 使用StringRedisTemplate的convertAndSend方法向指定频道发送指定消息:
    		this.execute((connection) -> {
                connection.publish(rawChannel, rawMessage);
                return null;
            }, true);
    

    redis的命令publish channel message

    • 添加一个监听的容器以及一个监听器适配器
    	@Bean
        RedisMessageListenerContainer container(RedisConnectionFactory connectionFactory, MessageListenerAdapter listenerAdapter)
        {
            RedisMessageListenerContainer container = new RedisMessageListenerContainer();
            container.setConnectionFactory(connectionFactory);
            // 可以添加多个 messageListener,配置不同的交换机
            container.addMessageListener(listenerAdapter, new PatternTopic(Constants.REDIS_CHANNEL));// 订阅最新消息频道
            return container;
        }
    
        @Bean
        MessageListenerAdapter listenerAdapter(RedisReceiver receiver)
        {
            // 消息监听适配器
            return new MessageListenerAdapter(receiver, "onMessage");
        }
    
    • 添加消息接收器
    /**
     * 消息监听对象,接收订阅消息
     */
    @Component
    public class RedisReceiver implements MessageListener {
        Logger log = LoggerFactory.getLogger(this.getClass());
    
        @Autowired
        private WebSocketServer webSocketServer;
    
    
        /**
         * 处理接收到的订阅消息
         */
        @Override
        public void onMessage(Message message, byte[] pattern)
        {
            String channel = new String(message.getChannel());// 订阅的频道名称
            String msg = "";
            try
            {
                msg = new String(message.getBody(), Constants.UTF8);//注意与发布消息编码一致,否则会乱码
                if (!StringUtils.isEmpty(msg)){
                    if (Constants.REDIS_CHANNEL.endsWith(channel))// 最新消息
                    {
                        JSONObject jsonObject = JSON.parseObject(msg);
                        webSocketServer.sendMessageByWayBillId(
                                Long.parseLong(jsonObject.get(Constants.REDIS_MESSAGE_KEY).toString())
                                ,jsonObject.get(Constants.REDIS_MESSAGE_VALUE).toString());
                    }else{
                        //TODO 其他订阅的消息处理
                    }
    
                }else{
                    log.info("消息内容为空,不处理。");
                }
            }
            catch (Exception e)
            {
                log.error("处理消息异常:"+e.toString());
                e.printStackTrace();
            }
        }
    }
    
    • websocket的配置类
    /**
     * @description: websocket的配置类
     * @dateTime: 2021/6/16 15:43
     */
    @Configuration
    @EnableWebSocket
    public class WebSocketConfiguration {
    
        @Bean
        public ServerEndpointExporter serverEndpointExporter() {
            return new ServerEndpointExporter();
        }
    }
    
    
    • 添加websocket的服务组件
    @ServerEndpoint("/websocket/{id}")
    @Component
    public class WebSocketServer {
    
        private static final long sessionTimeout = 600000;
    
        private static final Logger log = LoggerFactory.getLogger(WebSocketServer.class);
    
        /**
         * 当前在线连接数
         */
        private static AtomicInteger onlineCount = new AtomicInteger(0);
    
        /**
         * 用来存放每个客户端对应的 WebSocketServer 对象
         */
        private static ConcurrentHashMap<Long, WebSocketServer> webSocketMap = new ConcurrentHashMap<>();
    
        /**
         * 与某个客户端的连接会话,需要通过它来给客户端发送数据
         */
        private Session session;
    
        /**
         * 接收 id
         */
        private Long id;
    
    
        @Autowired
        private StringRedisTemplate template;
        /**
         * 连接建立成功调用的方法
         */
        @OnOpen
        public void onOpen(Session session, @PathParam("id") Long id) {
            session.setMaxIdleTimeout(sessionTimeout);
            this.session = session;
            this.id = id;
            if (webSocketMap.containsKey(id)) {
                webSocketMap.remove(id);
                webSocketMap.put(id, this);
            } else {
                webSocketMap.put(id, this);
                addOnlineCount();
            }
            log.info("编号id:" + id + "连接,当前在线数为:" + getOnlineCount());
            try {
                sendMessage("连接成功!");
            } catch (IOException e) {
                log.error("编号id:" + id + ",网络异常!!!!!!");
            }
        }
    
        /**
         * 连接关闭调用的方法
         */
        @OnClose
        public void onClose() {
            if (webSocketMap.containsKey(id)) {
                webSocketMap.remove(id);
                subOnlineCount();
            }
            log.info("编号id:" + id + "退出,当前在线数为:" + getOnlineCount());
        }
    
        /**
         * 收到客户端消息后调用的方法
         *
         * @param message 客户端发送过来的消息
         */
        @OnMessage
        public void onMessage(String message, Session session) {
    
            log.info("编号id消息:" + id + ",报文:" + message);
        }
    
        /**
         * 发生错误时调用
         *
         * @param session
         * @param error
         */
        @OnError
        public void onError(Session session, Throwable error) {
            log.error("编号id错误:" + this.id + ",原因:" + error.getMessage());
            error.printStackTrace();
        }
        
    
        /**
         * @description:  分布式  使用redis 去发布消息
         * @dateTime: 2021/6/17 10:31
         */
        public void sendMessage(@NotNull String key,String message) {
            String newMessge= null;
            try {
                newMessge = new String(message.getBytes(Constants.UTF8), Constants.UTF8);
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
            Map<String,String> map = new HashMap<String, String>();
            map.put(Constants.REDIS_MESSAGE_KEY, key);
            map.put(Constants.REDIS_MESSAGE_VALUE, newMessge);
            template.convertAndSend(Constants.REDIS_CHANNEL, JSON.toJSONString(map));
        }
    
        /**
         * @description: 单机使用  外部接口通过指定的客户id向该客户推送消息。
         * @dateTime: 2021/6/16 17:49
         */
        public void sendMessageByWayBillId(@NotNull Long key, String message) {
            WebSocketServer webSocketServer = webSocketMap.get(key);
            if (!StringUtils.isEmpty(webSocketServer)) {
                try {
                    webSocketServer.sendMessage(message);
                    log.info("编号id为:"+key+"发送消息:"+message);
                } catch (IOException e) {
                    e.printStackTrace();
                    log.error("编号id为:"+key+"发送消息失败");
                }
            }
            log.error("编号id号为:"+key+"未连接");
        }
        /**
         * 实现服务器主动推送
         */
        public void sendMessage(String message) throws IOException {
            this.session.getBasicRemote().sendText(message);
        }
    
        public static synchronized AtomicInteger getOnlineCount() {
            return onlineCount;
        }
    
        public static synchronized void addOnlineCount() {
            WebSocketServer.onlineCount.getAndIncrement();
        }
    
        public static synchronized void subOnlineCount() {
            WebSocketServer.onlineCount.getAndDecrement();
        }
    }
    
    
    • 项目结构

    在这里插入图片描述

    • 将该项目使用三个端口号启动三个服务
      在这里插入图片描述

    • 使用下面的这个网站进行演示。
      http://www.easyswoole.com/wstool.html
      在这里插入图片描述
      启动两个页面网址分别是:

    • ws://127.0.0.1:8081/websocket/456

    • ws://127.0.0.1:8082/websocket/456

    使用postman给http://localhost:8080/socket/456 发送请求

    在这里插入图片描述
    可以看到,我们给8080服务发送的消息,我们订阅的8081和8082 服务可以也可以使用该编号进行消息的推送。

    • 使用8082服务发送这个消息格式{"KEY":456,"VALUE":"aaaa"} 的消息。其他的服务也会收到这个信息。

    在这里插入图片描述

    • 以上就是使用redis的发布订阅解决websocket 的分布式session 问题。

    码云地址是: https://gitee.com/jack_whh/dcs-websocket-session

    展开全文
  • } /** * 广播消息 */ public void sendAllMessage(String message) { for (WebSocketServer webSocket : webSockets) { log.info("【websocket消息】广播消息:" + message); try { webSocket.session....

    前言

    在网站项目开发中,我们经常会有在线客服的功能,通过网站开启一个聊天室,进行客服解答功能,本节我们使用websocket实现一个简单的网站在线客服聊天功能,效果如下:

    正文

    • 后端引入websocket的pom依赖
    <dependency>
    	<groupId>org.springframework.boot</groupId>
    	<artifactId>spring-boot-starter-websocket</artifactId>
    </dependency>

    •  后端初始化注入websocket的ServerEndpointExporter端点
    package com.yundi.atp.platform.websocket;
    
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.socket.server.standard.ServerEndpointExporter;
    /**
     * @Author: yanp
     * @Description:
     * @Date: 2021/9/15 15:27
     * @Version: 1.0.0
     */
    @Configuration
    public class WebSocketConfig {
        /**
         * 注入ServerEndpointExporter,
         * 这个bean会自动注册使用了@ServerEndpoint注解声明的Websocket endpoint
         */
        @Bean
        public ServerEndpointExporter serverEndpointExporter() {
            return new ServerEndpointExporter();
        }
    }
    

    •  后端创建一个websocket服务器,用于前后端通信,发送消息
    package com.yundi.atp.platform.websocket;
    
    import com.alibaba.fastjson.JSON;
    import com.yundi.atp.platform.module.test.entity.ChatMsg;
    import com.yundi.atp.platform.module.test.service.ChatMsgService;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Component;
    
    import javax.websocket.OnClose;
    import javax.websocket.OnMessage;
    import javax.websocket.OnOpen;
    import javax.websocket.Session;
    import javax.websocket.server.PathParam;
    import javax.websocket.server.ServerEndpoint;
    import java.util.*;
    import java.util.concurrent.ConcurrentHashMap;
    import java.util.concurrent.CopyOnWriteArraySet;
    
    /**
     * @Author: yanp
     * @Description: WebSocket服务端
     * @Date: 2021/9/15 15:27
     * @Version: 1.0.0
     */
    @Slf4j
    @Component
    @ServerEndpoint("/websocket/chat/{userName}")
    public class WebSocketServer {
        private Session session;
    
        private static CopyOnWriteArraySet<WebSocketServer> webSockets = new CopyOnWriteArraySet<>();
    
        private static Map<String, Session> sessionPool = new ConcurrentHashMap<>();
    
        private static ChatMsgService chatMsgService;
    
        private static String SUPER_ADMIN = "super_admin";
    
        @Autowired
        public void setWebSocketServer(ChatMsgService chatMsgService) {
            WebSocketServer.chatMsgService = chatMsgService;
        }
    
        @OnOpen
        public void onOpen(Session session, @PathParam(value = "userName") String userName) {
            List<String> onlineList = new ArrayList<>();
            this.session = session;
            webSockets.add(this);
            sessionPool.put(userName, session);
            log.info("【websocket消息】有新的连接,总数为:" + webSockets.size());
            Iterator<Map.Entry<String, Session>> iterator = sessionPool.entrySet().iterator();
            while (iterator.hasNext()) {
                onlineList.add(iterator.next().getKey());
            }
            onlineList.remove(SUPER_ADMIN);
            Map<String, Object> map = new HashMap<>(16);
            map.put("key", 1);
            map.put("onlineList", onlineList);
            map.put("userList", chatMsgService.getUserList());
            this.sendOneMessage(SUPER_ADMIN, JSON.toJSONString(map));
        }
    
        @OnClose
        public void onClose(@PathParam(value = "userName") String userName) {
            webSockets.remove(this);
            sessionPool.remove(userName);
            log.info("【websocket消息】连接断开,总数为:" + webSockets.size());
            List<String> onlineList = new ArrayList<>();
            Iterator<Map.Entry<String, Session>> iterator = sessionPool.entrySet().iterator();
            while (iterator.hasNext()) {
                onlineList.add(iterator.next().getKey());
            }
            onlineList.remove(SUPER_ADMIN);
            Map<String, Object> map = new HashMap<>(16);
            map.put("key", 2);
            map.put("onlineList", onlineList);
            map.put("userList", chatMsgService.getUserList());
            this.sendOneMessage(SUPER_ADMIN, JSON.toJSONString(map));
        }
    
        @OnMessage
        public void onMessage(String message) {
            ChatMsg chatMsg = JSON.parseObject(message, ChatMsg.class);
            chatMsgService.save(chatMsg);
            Map<String, Object> map = new HashMap<>(16);
            map.put("key", 3);
            map.put("data", chatMsg);
            this.sendOneMessage(chatMsg.getSender(), JSON.toJSONString(map));
            this.sendOneMessage(chatMsg.getReceiver(), JSON.toJSONString(map));
        }
    
        /**
         * 广播消息
         */
        public void sendAllMessage(String message) {
            for (WebSocketServer webSocket : webSockets) {
                log.info("【websocket消息】广播消息:" + message);
                try {
                    webSocket.session.getAsyncRemote().sendText(message);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    
        /**
         * 单点消息
         *
         * @param userName
         * @param message
         */
        public void sendOneMessage(String userName, String message) {
            log.info("【websocket消息】单点消息:" + message);
            Session session = sessionPool.get(userName);
            if (session != null) {
                try {
                    session.getAsyncRemote().sendText(message);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    
    }
    

    • 后端聊天室功能控制层接口
    package com.yundi.atp.platform.module.test.controller;
    
    
    import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
    import com.yundi.atp.platform.common.Result;
    import com.yundi.atp.platform.module.test.entity.ChatMsg;
    import com.yundi.atp.platform.module.test.service.ChatMsgService;
    import io.swagger.annotations.Api;
    import io.swagger.annotations.ApiOperation;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.PathVariable;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    import javax.servlet.http.HttpServletRequest;
    import java.net.InetAddress;
    import java.net.UnknownHostException;
    import java.util.Comparator;
    import java.util.List;
    import java.util.stream.Collectors;
    
    /**
     * <p>
     * 前端控制器
     * </p>
     *
     * @author yanp
     * @since 2021-09-17
     */
    @Api(tags = "聊天室接口")
    @RestController
    @RequestMapping("/test/chatMsg")
    public class ChatMsgController {
        @Autowired
        private ChatMsgService chatMsgService;
    
        @ApiOperation(value = "获取聊天室地址")
        @GetMapping(value = "/getWebSocketAddress/{username}")
        public Result getWebSocketAddress(HttpServletRequest request, @PathVariable(value = "username") String username) throws UnknownHostException {
            String address = "ws://" + InetAddress.getLocalHost().getHostAddress() + ":" + request.getServerPort() + request.getContextPath() + "/websocket/chat/" + username;
            return Result.success(address);
        }
    
        @ApiOperation(value = "获取历史聊天记录")
        @GetMapping(value = "/getHistoryChat/{username}")
        public Result getWebSocketAddress(@PathVariable(value = "username") String username) {
            List<ChatMsg> list = chatMsgService.list(new QueryWrapper<ChatMsg>()
                    .and(wrapper -> wrapper.eq("sender", username).or().eq("receiver", username))
                    .orderByDesc("create_time"));
            List<ChatMsg> collect = list.stream().sorted(Comparator.comparing(ChatMsg::getCreateTime)).collect(Collectors.toList());
            return Result.success(collect);
        }
    
        @ApiOperation(value = "获取用户列表")
        @GetMapping(value = "/getUserList")
        public Result getUserList() {
            List<String> userList = chatMsgService.getUserList();
            return Result.success(userList);
        }
    
    }
    
    

    •  后端聊天室功能业务层
    package com.yundi.atp.platform.module.test.service.impl;
    
    import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
    import com.yundi.atp.platform.module.test.entity.ChatMsg;
    import com.yundi.atp.platform.module.test.mapper.ChatMsgMapper;
    import com.yundi.atp.platform.module.test.service.ChatMsgService;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    
    import java.util.List;
    
    /**
     * <p>
     * 服务实现类
     * </p>
     *
     * @author yanp
     * @since 2021-09-17
     */
    @Service
    public class ChatMsgServiceImpl extends ServiceImpl<ChatMsgMapper, ChatMsg> implements ChatMsgService {
        @Autowired
        private ChatMsgMapper chatMsgMapper;
    
        @Override
        public List<String> getUserList() {
            List<String> userList = chatMsgMapper.getUserList();
            return userList;
        }
    }
    

    •   后端聊天室功能持久层
    package com.yundi.atp.platform.module.test.mapper;
    
    import com.baomidou.mybatisplus.core.mapper.BaseMapper;
    import com.yundi.atp.platform.module.test.entity.ChatMsg;
    
    import java.util.List;
    
    /**
     * <p>
     * Mapper 接口
     * </p>
     *
     * @author yanp
     * @since 2021-09-17
     */
    public interface ChatMsgMapper extends BaseMapper<ChatMsg> {
        /**
         * 用户列表
         * @return
         */
        List<String> getUserList();
    
    }
    

    •  后端聊天室功能持久层Mapper
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    <mapper namespace="com.yundi.atp.platform.module.test.mapper.ChatMsgMapper">
    
        <select id="getUserList" resultType="java.lang.String">
            SELECT sender FROM `chat_msg` where sender != 'super_admin' GROUP BY sender
        </select>
    </mapper>
    

    •   前端聊天室功能页面
    <template>
      <div class="container">
        <el-card class="box-card">
          <div slot="header">
            <el-row type="flex">
              <el-col :span="1" style="margin: 15px 10px;">
                <img alt="ATP客服" src="@/assets/logo.png" style="width:40px;height:40px;"/>
              </el-col>
              <el-col :span="3" style="line-height: 74px;margin-left: 10px;">
                <span style="display: inline-block;color: white;">ATP客服</span>
              </el-col>
              <el-col :span="20" v-if="username==='super_admin'">
                <h5 style="color: #83ccd2;padding: 0;text-align: right;margin: 50px 20px 0 0;">当前在线人数:{{ online }}</h5>
              </el-col>
              <el-col :span="20" v-else>
                <h5 style="color: #83ccd2;padding: 0 0 2px 0;text-align: right;margin: 50px 20px 0 0;font-size: 18px;">
                  {{ username }}</h5>
              </el-col>
            </el-row>
          </div>
          <div class="content" ref="content">
            <el-row type="flex">
              <el-col :span="6" style="background: #eee;min-height: 600px;" v-if="username==='super_admin'">
                <el-tabs v-model="activeName" @tab-click="handleClick" style="position: fixed;width: 190px;margin: 0 2px;">
                  <el-tab-pane label="在线用户" name="online">
                    <div v-for="item in friend" :key="item" @click="switchUser(item)" :class="item===active?'mark':''">
                      <el-badge :is-dot=msgNotify.includes(item) class="item" type="success">
                        <li style="list-style-type:none;padding: 5px 8px;cursor: pointer;"
                            class="active">
                          {{ item }}
                        </li>
                      </el-badge>
                      <el-divider></el-divider>
                    </div>
                  </el-tab-pane>
                  <el-tab-pane label="全部用户" name="all">
                    <div v-for="item in userList" :key="item" @click="switchUser(item)" :class="item===active?'mark':''">
                      <el-badge :is-dot=msgNotify.includes(item) class="item" type="success">
                        <li style="list-style-type:none;padding: 5px 8px;cursor: pointer;"
                            :class="friend.includes(item)?'active':''">
                          {{ item }}
                        </li>
                      </el-badge>
                      <el-divider></el-divider>
                    </div>
                  </el-tab-pane>
                </el-tabs>
              </el-col>
              <el-col :span="18" v-if="username==='super_admin'">
                <div v-for="item in chatMsgList">
                  <el-row type="flex" style="margin-bottom: 20px;" v-if="username===item.sender">
                    <el-col :span="2">
                      <img alt="ATP客服" src="@/assets/logo.png" style="width:30px;height:30px;margin: 10px 0px 0px 20px;"/>
                    </el-col>
                    <el-col :span="22">
                      <el-row type="flex" style="margin-top: 10px;margin-left: 5px;opacity: 0.2;">
                        <el-col :span="7"><span style="padding-left: 20px;">{{ item.sender }}</span></el-col>
                        <el-col :span="7"><span>{{ item.createTime | dataFormat('yyyy-MM-dd HH:mm') }}</span></el-col>
                      </el-row>
                      <el-row>
                        <el-col :span="14" style="margin-left: 8px;margin-top: 5px;">
                          <el-card style="padding: 8px 5px;">
                            {{ item.msg }}
                          </el-card>
                        </el-col>
                      </el-row>
                    </el-col>
                  </el-row>
                  <el-row type="flex" style="margin-bottom: 20px;" v-else justify="end">
                    <el-col :span="22">
                      <el-row type="flex" style="margin-top: 10px;margin-right: 5px;opacity: 0.2;" justify="end">
                        <el-col :span="6"><span>{{ item.sender }}</span></el-col>
                        <el-col :span="7"><span>{{ item.createTime | dataFormat('yyyy-MM-dd HH:mm') }}</span></el-col>
                      </el-row>
                      <el-row type="flex" justify="end" style="margin-right: 8px;margin-top: 5px;">
                        <el-col :span="14" style="margin-right: 8px;">
                          <el-card style="padding: 8px 5px;">
                            {{ item.msg }}
                          </el-card>
                        </el-col>
                      </el-row>
                    </el-col>
                    <el-col :span="2">
                      <el-avatar shape="square" size="medium" style="float: right;margin: 10px 20px 0px 0px;">客户</el-avatar>
                    </el-col>
                  </el-row>
                </div>
              </el-col>
              <el-col :span="24" v-else>
                <div v-for="item in chatMsgList">
                  <el-row type="flex" style="margin-bottom: 20px;" v-if="username===item.sender">
                    <el-col :span="2">
                      <el-avatar shape="square" size="medium" style="float: right;margin: 10px 20px 0px 0px;">客户</el-avatar>
                    </el-col>
                    <el-col :span="22">
                      <el-row type="flex" style="margin-top: 10px;opacity: 0.2;margin-left: 20px;">
                        <el-col :span="7"><span style="padding-left: 5px;">{{ item.sender }}</span></el-col>
                        <el-col :span="7"><span>{{ item.createTime | dataFormat('yyyy-MM-dd HH:mm') }}</span></el-col>
                      </el-row>
                      <el-row>
                        <el-col :span="14">
                          <el-card style="padding: 8px 5px;">
                            {{ item.msg }}
                          </el-card>
                        </el-col>
                      </el-row>
                    </el-col>
                  </el-row>
                  <el-row type="flex" style="margin-bottom: 20px;" v-else justify="end">
                    <el-col :span="22">
                      <el-row type="flex" style="margin-top: 10px;margin-right: 5px;opacity: 0.2;" justify="end">
                        <el-col :span="6"><span>{{ item.sender }}</span></el-col>
                        <el-col :span="7"><span>{{ item.createTime | dataFormat('yyyy-MM-dd HH:mm') }}</span></el-col>
                      </el-row>
                      <el-row type="flex" justify="end" style="margin-top: 5px;">
                        <el-col :span="14">
                          <el-card style="padding: 8px 5px;">
                            {{ item.msg }}
                          </el-card>
                        </el-col>
                      </el-row>
                    </el-col>
                    <el-col :span="2">
                      <img alt="ATP客服" src="@/assets/logo.png" style="width:30px;height:30px;margin: 10px 0px 0px 20px;"/>
                    </el-col>
                  </el-row>
                </div>
              </el-col>
            </el-row>
          </div>
          <div class="operate" v-if="username==='super_admin'">
            <el-input
                type="textarea"
                :autosize="{ minRows: 3, maxRows: 3}"
                placeholder="您好!这里是ATP客服部,我是客服小美,很高兴为您服务!"
                v-model="msg">
            </el-input>
            <el-button type="success" size="mini" style="float: right;margin-top: 5px;" @click="sendMsg"
                       :disabled="!(msg && active)">
              发送
            </el-button>
          </div>
          <div class="operate" v-else>
            <el-input
                type="textarea"
                :autosize="{ minRows: 3, maxRows: 3}"
                placeholder="您好!这里是ATP客服部,我是客服小美,很高兴为您服务!"
                v-model="msg">
            </el-input>
            <el-button type="success" size="mini" style="float: right;margin-top: 5px;" @click="sendMsg" :disabled="!msg">
              发送
            </el-button>
          </div>
        </el-card>
      </div>
    </template>
    
    <script>
    export default {
      name: "ClientChat",
      data() {
        return {
          msg: '',
          chatMsgList: [],
          username: sessionStorage.getItem("username"),
          friend: [],
          online: 0,
          active: '',
          receiver: 'super_admin',
          userList: [],
          activeName: 'online',
          msgNotify:[],
        }
      },
      created() {
        this.getWebSocketAddress();
      },
      methods: {
        //tab切换
        handleClick(tab, event) {
          const _this = this;
          if (tab.name === 'online') {
            if (!_this.active) {
              if (_this.online > 0) {
                _this.active = _this.friend[0];
                _this.activeName = 'online';
                _this.receiver = _this.active;
                _this.getHistoryChat(_this.receiver);
              } else {
                if (_this.userList.length > 0) {
                  _this.active = _this.userList[0];
                  _this.activeName = 'all';
                  _this.receiver = _this.active;
                  _this.getHistoryChat(_this.receiver);
                }
              }
            }
          }
          if (tab.name === 'all') {
            if (!_this.active) {
              if (_this.online > 0) {
                _this.active = _this.friend[0];
                _this.activeName = 'online';
                _this.receiver = _this.active;
                _this.getHistoryChat(_this.receiver);
              } else {
                if (_this.userList.length > 0) {
                  _this.active = _this.userList[0];
                  _this.activeName = 'all';
                  _this.receiver = _this.active;
                  _this.getHistoryChat(_this.receiver);
                }
              }
            }
          }
        },
        //切换用户
        switchUser(data) {
          if (this.active === data) {
            return;
          }
          this.active = data;
          this.receiver = data;
          //获取历史聊天记录
          this.getHistoryChat(this.receiver);
          this.msgNotify = this.msgNotify.filter(item => item != this.active);
        },
        //获取历史聊天记录
        getHistoryChat(data) {
          this.$http.get('/test/chatMsg/getHistoryChat/' + data).then(res => {
            if (res.data.code === 1) {
              this.chatMsgList = res.data.data;
              this.flushScroll();
            } else {
              this.$message.warning(res.data.msg);
            }
          }).catch(error => {
            this.$message.error(error);
          });
        },
        //获取websocket地址
        getWebSocketAddress() {
          this.$http.get('/test/chatMsg/getWebSocketAddress/' + this.username).then(res => {
            if (res.data.code === 1) {
              if ('WebSocket' in window) {
                this.websocket = new WebSocket(res.data.data);
                this.initWebSocket();
                if (this.username != 'super_admin') {
                  this.getHistoryChat(this.username);
                }
              } else {
                this.$message.warning('当前浏览器不支持websocket创建!');
              }
            } else {
              this.$message.warning(res.data.msg);
            }
          }).catch(error => {
            this.$message.error(error);
          });
        },
        //初始化websocket
        initWebSocket() {
          const _this = this;
          _this.websocket.onerror = function (event) {
            _this.$message.error('服务端连接错误!');
          }
          _this.websocket.onopen = function (event) {
            _this.$message.success("连接成功!");
          }
          _this.websocket.onmessage = function (event) {
            let res = JSON.parse(event.data);
            if (res.key === 1) {
              _this.userList = res.userList;
              _this.friend = res.onlineList;
              _this.online = _this.friend.length;
              if (!_this.active) {
                if (_this.online > 0) {
                  _this.active = _this.friend[0];
                  _this.activeName = 'online';
                  _this.receiver = _this.active;
                  _this.getHistoryChat(_this.receiver);
                } else {
                  if (_this.userList.length > 0) {
                    _this.active = _this.userList[0];
                    _this.activeName = 'all';
                    _this.receiver = _this.active;
                    _this.getHistoryChat(_this.receiver);
                  }
                }
              }
            }
            if (res.key === 2) {
              _this.userList = res.userList;
              _this.friend = res.onlineList;
              _this.online = _this.friend.length;
              if (!_this.active) {
                if (_this.online > 0) {
                  _this.active = _this.friend[0];
                  _this.activeName = 'online';
                  _this.receiver = _this.active;
                  _this.getHistoryChat(_this.receiver);
                } else {
                  if (_this.userList.length > 0) {
                    _this.active = _this.userList[0];
                    _this.activeName = 'all';
                    _this.receiver = _this.active;
                    _this.getHistoryChat(_this.receiver);
                  }
                }
              }
            }
            if (res.key === 3) {
              if (_this.username === res.data.sender) {
                _this.chatMsgList.push(res.data);
                _this.flushScroll();
              } else {
                if (res.data.sender === 'super_admin') {
                  _this.chatMsgList.push(res.data);
                  _this.flushScroll();
                } else {
                  if (res.data.sender === _this.active) {
                    _this.chatMsgList.push(res.data);
                    _this.flushScroll();
                  } else {
                    //发送其它用户处理
                    _this.msgNotify.push(res.data.sender);
                  }
                }
              }
            }
          }
          _this.websocket.onclose = function (event) {
            _this.$message.warning('服务端已关闭!');
          }
        },
        //发送消息
        sendMsg() {
          if (this.msg.trim().length === 0) {
            this.$message.warning('不能发送空消息!');
            return;
          }
          let chatMsg = {};
          chatMsg.msg = this.msg;
          chatMsg.sender = this.username;
          chatMsg.createTime = new Date();
          chatMsg.receiver = this.receiver;
          this.websocket.send(JSON.stringify(chatMsg));
          this.msg = '';
          this.flushScroll();
        },
        //刷新滚动条
        flushScroll() {
          let content = this.$refs.content;
          setTimeout(() => {
            content.scrollTop = content.scrollHeight;
          }, 100);
        },
      }
    }
    </script>
    
    <style scoped lang="scss">
    .container {
      padding-top: 50px;
    
      .box-card {
        margin: auto;
        width: 800px;
        height: 800px;
        max-height: 900px;
    
        ::v-deep .el-card__header {
          background: #867ba9 !important;
          border-bottom: none;
          padding: 0;
        }
    
        ::v-deep .el-card__body {
          padding: 0px !important;
          position: relative;
    
          .content {
            height: 600px;
            background: #ddd;
            overflow-y: auto;
    
            .el-divider--horizontal {
              margin: 0;
            }
    
            .active {
              color: #0080ff;
            }
    
            .mark {
              background: #deb068;
            }
    
            .item {
              margin-top: 10px;
              margin-right: 10px;
            }
          }
    
          .operate {
            padding: 5px 15px;
          }
        }
      }
    }
    </style>

    •  聊天室页面效果

    结语

    本节内容到这里就结束了,我们下期见。。。

    展开全文
  • #删除连接,从集合中删除连接对象itemdef deleteconnection(item):global connectionlistdel connectionlist['connection'+item]#WebSocket服务器对象()class WebSocketServer(object):def __init__(self):self....

    #删除连接,从集合中删除连接对象item

    def deleteconnection(item):

    global connectionlist

    del connectionlist['connection'+item]

    #WebSocket服务器对象()

    class WebSocketServer(object):

    def __init__(self):

    self.socket = None

    self.i = 0

    #开启操作

    def begin(self):

    self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

    ip = '10.1.80.83'

    port = 8080

    self.socket.bind((ip,port))

    self.socket.listen(50)

    #全局连接集合

    global connectionlist

    while True:

    #服务器响应请求,返回连接客户的信息(连接fd,客户地址)

    connection, address = self.socket.accept()

    #根据连接的客户信息,创建WebSocket对象(本质为一个线程)

    #sockfd,index,用户名,地址

    newSocket = WebSocket(connection,self.i,address[0],address)

    #线程启动

    newSocket.start()

    #更新连接的集合(hash表的对应关系)-name->sockfd

    connectionlist['connection'+str(self.i)]=connection

    self.i += 1

    if __name__ == "__main__":

    server = WebSocketServer()

    server.begin()

    说明

    在实例化一个WebSocketServer对象后,调用对象的begin方法,将启用WebSocket服务端程序,一旦接受到客户端的访问请求,则通过实例化一个WebSocket对象,并调用对象的start方法来处理客户端请求,并同时将客户端的socket connection信息保存在字典中供后续使用,后面一个篇幅会详细介绍WebSocket类。

    展开全文
  • WebSocketServer

    2021-08-10 01:38:58
    WebSocketServer接口说明一、服务说明用于通过WebSocket发送消息二、WebSocket设置2.1 初始化WebSocket连接1)连接WebSocket连接地址:wss://vvuc.egoonet.com:8081/msgserver2)通过WebSocket发送初始化消息发送的...
  • 原因是因为spring对象的创建都是以单例模式创建的,但是websocket的创建是spring启动是创建一次,然后每一个用户连接,都会创建一次webscket对象,所以当你启动项目时,你想要注入的对象已经注入进去,但是当用户...
  • QML:WebSocketServer

    2021-07-03 23:48:00
    一、属性 1、accept : bool:设置为 true 以在服务器... 二、信号 1、clientConnected(WebSocket webSocket) 当客户端连接到此服务器时会发出此信号。 参数是新创建的 WebSocket。对应的处理程序是 onClientConnected。
  • python-websocket-server

    2021-04-26 18:32:59
    Websocket ServerA minimal Websockets Server in Python with no external dependencies.Python2 and Python3 supportClean simple APIMultiple clientsNo dependenciesNotice that this implementation does not s...
  • SpringBoot 基于注解实现的WebSocketServer

    千次阅读 2021-12-10 11:51:21
    基于原生的WebSocket服务端 支持SpringBoot
  • 本篇文章带领大家如何在 SpringBoot 中整合 tio-websocket-server 搭建一个自己的 websocket 服务器 1、引入 maven 依赖 <!-- tio-websocket --> <dependency> <groupId>org.t-io</groupId>...
  • 解决方案: static OpLogService opLogService; static UserService userService;... WebSocketServer.opLogService = opLogService; } @Autowired public void setUserService(UserService userServic
  • WebSocket 最大特点就是,服务器可以主动向客户端推送信息,客户端也可以主动向服务器发送信息 var ws = new WebSocket('wss://echo.websocket.org'); // 发送 ws.onopen = function (evt) { console.log('...
  • 用laravel-echo-server实现实时消息通知 一、实时消息 以印象视频为例,如聊天消息和点赞评论通知等,都是通过laravel提供的消息通知系统,将相关的notification对象存储到数据库中,然后提供一些api或者gql接口给...
  • SpringBoot基于注册实现的Websocket Server
  • 本文将向您阐述一个简单而快速的在Windows Server 2019中使用Powershell安装和启动IIS的方法,本方法也适用于Windows Server 2012、Windows Server 2016或Windows 10/8/7,包括如何使用命令去重启IIS 或获取服务状态...
  • tio-websocket-server 中对用户发送消息的方法如下: Tio.sendToUser(channelContext.tioConfig, userId, wsResponse); channelContext.tioConfig,指整个 websocket 的配置信息,可以通过 ChannelContext....
  • Python | WebSocketServer

    2021-01-14 22:57:10
    '''Host a multiplayer sever via WebSocket protocol...
  • 这个错误,相信你已经找了,是因为spring代理的问题,这里我要是说下我这边的情况, 首先感谢这位博主,SpringBoot 集成 WebSocket 遇到的问题_华阳余文乐的博客-CSDN博客 他分析是没问题,就是AOP;但是我要说的,...
  • nested exception is java.lang.IllegalStateException: javax.websocket.server.ServerContainer not available 导致启动单元测试失败,遇到这类错误,要从末尾往前看,往往末尾的一个报错是根本原因。 这里可以...
  • websocket: the client is not using the websocket protocol: 'upgrade' token not found in 'Connection' head 当反向代理或负载均衡未正确传递WebSocket请求时,就会出现此问题。 解决方案 Nginx加上以下解析 ...
  • from websocket_server import WebsocketServer class TestWebsocketServer(WebsocketServer): """ 继承自WebsocketServer的测试 """ # 重载接口,当有新连接进入时的回调函数 def new_client(self, client, ...
  • socketserver:socketserver可用于实现并发通信。socketserver 模块简化了编写网络服务程序的任务...Python把网络服务抽象成两个主要的类,一个是 Server 类,用于处理连接相关的网络操作,另外一个则是 RequestHan...
  • C++ websocket client/server library

    千次阅读 2021-11-10 13:42:02
    In this tutorial we will modify this program to perform tasks and retrieve data from a remote server over a WebSocket connection. Build clang++ step1.cpp Code so far note A code snapshot for each ...
  • websocket 可以实现的东西有很多,,,可以广播式的无差别推送,,,也可以实现点对点的沟通,,,网页版的聊天对话 以下是 在客户端方面的配置 $(document).ready(function () { chinaTv(); }); var reconnect = 0...
  • tomcat加载war包 出现javax.websocket.server.ServerContainer not available 详细描述: springboot项目打了war包,发现在tomcat8.5.32下运行不起来,报下面的异常,经测试,在tomcat8.5.51及以上版本就可以运行...
  • 我正在尝试创建一个Java WebSocket服务器,没有什么真正复杂的,只是一个小丑陋的小服务器,使用标准定义的api从浏览器尝试websockets.我一直在阅读协议的规范,这很简单.我做了握手,显然它正在工作.我用Java做的是:...
  • WebSocket服务器因某些原因无法正常工作(WebSocket server not working for some reasons)我尝试使用ws创建一个非常简单的服务器,当我运行服务器node index.js并且我在我的浏览器中午餐localhost:8080时,我的...
  • 将/etc/ssh/sshd_config 中的 #AllowTcpForwarding yes 改为 AllowTcpForwarding yes
  • Swoole创建WebSocket服务器没反应,创建http Server可以,也没有任何错误提示;...代码就是这个:$server = new swoole_websocket_server("0.0.0.0", 9501);$server->on('open', function (swoole_websock...
  • ESP32 Web Server (WebSocket) 实现多控件多引脚调节(PWM) 本教程将展示如何构建一个ESP32 web服务器,该服务器可以显示带有多个滑块的网页。滑块控制不同PWM通道的占空比来控制多个led的亮度。也可以代替led,你...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 71,683
精华内容 28,673
关键字:

serverwebsocket