基于socketio 写一个聊天室

一、前端

import React from 'react';
import io from 'socket.io-client';
import { List, InputItem, Button, Text, SearchBar } from 'antd-mobile';
import style from './home.css';
import longzhu from '../../assets/images/longzhu.png';
import lubanqihao from '../../assets/images/lubanqihao.png';
import lvsheng1 from '../../assets/images/lvsheng1.jpg';

let socket;

const serverUri = 'http://175.24.121.152:10246';
const sendEvent = 'chat message';
const receiveEvent = 'chat message';

class ChatRoom extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      texts: [],
    };
  }

  componentDidMount() {
    this.connect(1101);
  }

  connect = (uid) => {
    socket = io.connect(serverUri, {
      'force new connection': true,
      query: 'UID=' + uid,
    });
    let this_ = this;
    socket.on('connect', function () {
      console.log('连接成功');
      //如果发送字符串send("hello Server"); *需要修改服务端接收类型
      this.send({
        name: 'client',
        message: 'hello Server',
      });
    });
    socket.on(receiveEvent, function (data) {
      this_.setState({ texts: [...this_.state.texts, data.text] });
      console.log(data, this_.state.texts);
    });

    socket.on('disconnect', function () {
      console.log('连接断开');
    });
  };

  send(data) {
    socket.emit(sendEvent, data);
  }

  render() {
    return (
      <div className={style.chatRoom}>
        <textarea
          rows={32}
          id="sendMessage"
          onFocus={()=>{
            setTimeout(function(){
              let input = document.getElementById('sendMessage');
              input.style.top = '350px';
            },400);
          }}
          onBlur={()=>{
            setTimeout(function(){
              let input = document.getElementById('sendMessage');
              input.style.top = '580px';
            },400);
          }}
          onChange={() => {
            let input = document.getElementById('sendMessage');
            const value = input.value;
            if (value.toString().charAt(value.length - 1) === '\n') {
              console.log('回车', value);
              this.send({ text: value.toString().trim() });
              input.value = '';
            }
          }}
        />
        <button onClick={() => {}}>发送</button>
        <ul>
          {this.state.texts.map((item, index) =>
            index % 2 === 0 ? (
              <div
                style={{ margin: '15px 50px 10px 20px', position: 'relative' }}
              >
                <li className={style.li1}>{item}</li>
                <img src={longzhu} />
              </div>
            ) : (
              <div
                style={{ margin: '15px 50px 10px 20px', position: 'relative' }}
              >
                <li
                  className={style.li2}
                  style={{ backgroundColor: '#fa541c' }}
                >
                  {item}
                </li>
                <img src={lubanqihao} />
              </div>
            ),
          )}
        </ul>
      </div>
    );
  }
}

export default ChatRoom;


.chatRoom{
  margin: 20px auto 0;
  text-align: center;
}

.chatRoom ul{
  display: flex;
  list-style: none;
  flex-direction: column;
  align-items: right;
}

.chatRoom li{
  background: #20B2AA;color:#fff;
  text-align: left;line-height:20px;
  display: inline-block;
  padding: 5px;
  position: relative;
  float: right;
  border-radius:10px 10px 6px 10px;
}

.chatRoom button{
  visibility: hidden;
}

.chatRoom .li1::after{
  content: '';
  display: block;
  width:0;height:0;
  border:solid 16px;
  border-top:20px;
  border-color:transparent transparent transparent #20B2AA;
  position: absolute;
  top:8px;right:-30px;
}

.chatRoom .li2::after{
  content: '';
  display: block;
  width:0;height:0;
  border:solid 16px;
  border-top:20px;
  border-color:transparent transparent transparent #fa541c;
  position: absolute;
  top:8px;right:-30px;
}

.chatRoom img{
  width:30px;
  height: 30px;
  position: absolute;
  bottom: 10px;
  right:-46px;
  top: -3px;
  /*border: 0.5px solid gray;*/
  padding: 2px;
}

.chatRoom textarea{
  position: fixed;
  height: 30px;
  padding: 5px;
  width:90%;
  border: 2px solid darkgreen;
  border-radius: 5px;
  top:580px;
  left:18px;
  resize: none;
  z-index: 2;
}

二、后端

        <dependency>
            <groupId>com.corundumstudio.socketio</groupId>
            <artifactId>netty-socketio</artifactId>
            <version>1.7.7</version>
        </dependency>
# netty socket io setting
#============================================================================
# host在本地测试可以设置为localhost或者本机IP,在Linux服务器跑可换成服务器IP
socketio:
  host: 0.0.0.0	#监听的ip
  port: 10246		#监听端口
  # 设置最大每帧处理数据的长度,防止他人利用大数据来攻击服务器
  maxFramePayloadLength: 1048576
  # 设置http交互最大内容长度
  maxHttpContentLength: 1048576
  # socket连接数大小(如只监听一个端口boss线程组为1即可)
  bossCount: 1
  workCount: 100
  allowCustomRequests: true
  # 协议升级超时时间(毫秒),默认10秒。HTTP握手升级为ws协议超时时间
  upgradeTimeout: 1000000
  # Ping消息超时时间(毫秒),默认60秒,这个时间间隔内没有接收到心跳消息就会发送超时事件
  pingTimeout: 6000000
  # Ping消息间隔(毫秒),默认25秒。客户端向服务器发送一条心跳消息间隔
  pingInterval: 25000

SocketIOConfig

package com.example.readinglist.config;

import com.corundumstudio.socketio.SocketConfig;
import com.corundumstudio.socketio.SocketIOServer;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
class SocketIOConfig {

    @Value("${socketio.host}")
    private String host;

    @Value("${socketio.port}")
    private Integer port;

    @Value("${socketio.bossCount}")
    private int bossCount;

    @Value("${socketio.workCount}")
    private int workCount;

    @Value("${socketio.allowCustomRequests}")
    private boolean allowCustomRequests;

    @Value("${socketio.upgradeTimeout}")
    private int upgradeTimeout;

    @Value("${socketio.pingTimeout}")
    private int pingTimeout;

    @Value("${socketio.pingInterval}")
    private int pingInterval;

    /**
     * 以下配置在上面的application.properties中已经注明
     * @return
     */
    @Bean
    public SocketIOServer socketIOServer() {
        SocketConfig socketConfig = new SocketConfig();
        socketConfig.setTcpNoDelay(true);
        socketConfig.setSoLinger(0);
        com.corundumstudio.socketio.Configuration config = new com.corundumstudio.socketio.Configuration();
        config.setSocketConfig(socketConfig);
        config.setHostname(host);
        config.setPort(port);
        config.setBossThreads(bossCount);
        config.setWorkerThreads(workCount);
        config.setAllowCustomRequests(allowCustomRequests);
        config.setUpgradeTimeout(upgradeTimeout);
        config.setPingTimeout(pingTimeout);
        config.setPingInterval(pingInterval);
        return new SocketIOServer(config);
    }
}

SocketIoTest

package com.example.readinglist.config;

import com.alibaba.fastjson.JSONObject;
import com.corundumstudio.socketio.SocketIOClient;
import com.corundumstudio.socketio.SocketIOServer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

@Service
public class SocketIoTest {
    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    // 用来存已连接的客户端
    private static Map<String, SocketIOClient> clientMap = new ConcurrentHashMap<>();

    @Autowired
    private SocketIOServer socketIOServer;

    /**
     * Spring IoC容器创建之后,在加载SocketIOServiceImpl Bean之后启动
     * @throws Exception
     */
    @PostConstruct
    private void autoStartup() throws Exception {
        start();
    }

    /**
     * Spring IoC容器在销毁SocketIOServiceImpl Bean之前关闭,避免重启项目服务端口占用问题
     * @throws Exception
     */
    @PreDestroy
    private void autoStop() throws Exception  {
        stop();
    }

    public void start() {
        // 监听客户端连接
        socketIOServer.addConnectListener(client -> {
            String uid = getParamsByClient(client);
            if (uid != null) {
                clientMap.put(uid, client);
                logger.info("有新客户端连接UID:{}",uid);
            }
            // 给客户端发送一条信息 发送ClientReceive事件 需要客户端绑定此事件即可接收到消息
            JSONObject jsonObject = new JSONObject();
            jsonObject.put("text","hello client,connect the server successfully!");
            client.sendEvent("chat message",jsonObject);
        });

        // 监听客户端断开连接
        socketIOServer.addDisconnectListener(client -> {
            String uid = getParamsByClient(client);
            if (uid != null) {
                clientMap.remove(uid);
                client.disconnect();
            }
            logger.info("一条客户端连接中断");
        });

        // 处理自定义的事件,与连接监听类似
        // 此示例中测试的json收发 所以接收参数为JSONObject 如果是字符类型可以用String.class或者Object.class
        socketIOServer.addEventListener("chat message",JSONObject.class, (client, data, ackSender) -> {
            // TODO do something
            String uid = getParamsByClient(client);
            if (uid != null) {
                logger.info("接收到SID:{}发来的消息:{}",uid,data.toJSONString());
//                client.sendEvent("chat message",data);
                socketIOServer.getAllClients().forEach(cli-> cli.sendEvent("chat message",data));
            }
        });
        socketIOServer.start();
        logger.info("socket.io初始化服务完成");
    }

    public void stop() {
        if (socketIOServer != null) {
            socketIOServer.stop();
            socketIOServer = null;
        }
        logger.info("socket.io服务已关闭");
    }

    /**
     * 此方法为获取client连接中的参数,可根据需求更改
     * @param client
     * @return
     */
    private String getParamsByClient(SocketIOClient client) {
        // 从请求的连接中拿出参数(这里的sid必须是唯一标识)
        Map<String, List<String>> params = client.getHandshakeData().getUrlParams();
        List<String> list = params.get("UID");
        if (list != null && list.size() > 0) {
            return list.get(0);
        }
        return null;
    }
}

三、效果展示
在这里插入图片描述

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值