Java+Netty+WebRTC、语音、视频、屏幕共享【聊天室设计实践】
创始人
2024-03-29 01:13:29
0

背景

本文使用webtrc实现了一个简单的语音视频聊天室、支持多人音视频聊天、屏幕共享。

环境配置

音视频功能需要在有Https协议的域名下才能获取到设备信息,

测试环境搭建Https服务参考Windows下Nginx配置SSL实现Https访问(包含openssl证书生成)_殷长庆的博客-CSDN博客

正式环境可以申请一个免费的证书 

复杂网络环境下需要自己搭建turnserver,网络上搜索大多是使用coturn来搭建turn服务 

turn默认监听端口3478,可以使用webrtc.github.io 测试服务是否可用

本文在局域网内测试,不必要部署turn,使用的谷歌的stun:stun.l.google.com:19302

webrtc参考文章

WebRTC技术简介 - 知乎 (zhihu.com)

实现 

服务端 

服务端使用netty构建一个websocket服务,用来完成为音视频传递ICE信息等工作。 

maven配置

4.0.0com.luck.cccc-im1.0-SNAPSHOTcc-imhttp://maven.apache.org${env.JAVA_HOME}UTF-81.8io.nettynetty-all4.1.74.Finalcn.hutoolhutool-all5.5.7maven-compiler-plugin1.81.8maven-assembly-plugin3.0.0com.luck.im.ServerStartjar-with-dependenciesmake-assemblypackagesingle

 JAVA代码

 聊天室服务

package com.luck.im;import java.util.List;import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.MessageToMessageCodec;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler;public class ChatSocket {private static EventLoopGroup bossGroup = new NioEventLoopGroup();private static EventLoopGroup workerGroup = new NioEventLoopGroup();private static ChannelFuture channelFuture;/*** 启动服务代理* * @throws Exception*/public static void startServer() throws Exception {try {ServerBootstrap b = new ServerBootstrap();b.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class).childHandler(new ChannelInitializer() {@Overridepublic void initChannel(SocketChannel ch) throws Exception {ChannelPipeline pipeline = ch.pipeline();pipeline.addLast(new HttpServerCodec());pipeline.addLast(new WebSocketServerProtocolHandler("/myim", null, true, Integer.MAX_VALUE, false));pipeline.addLast(new MessageToMessageCodec() {@Overrideprotected void decode(ChannelHandlerContext ctx, TextWebSocketFrame frame,List list) throws Exception {list.add(frame.text());}@Overrideprotected void encode(ChannelHandlerContext ctx, String msg, List list)throws Exception {list.add(new TextWebSocketFrame(msg));}});pipeline.addLast(new ChatHandler());}});channelFuture = b.bind(8321).sync();channelFuture.channel().closeFuture().sync();} finally {shutdown();// 服务器已关闭}}public static void shutdown() {if (channelFuture != null) {channelFuture.channel().close().syncUninterruptibly();}if ((bossGroup != null) && (!bossGroup.isShutdown())) {bossGroup.shutdownGracefully();}if ((workerGroup != null) && (!workerGroup.isShutdown())) {workerGroup.shutdownGracefully();}}} 

聊天室业务 

package com.luck.im;import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.util.AttributeKey;
import io.netty.util.internal.StringUtil;public class ChatHandler extends SimpleChannelInboundHandler {/** 用户集合 */private static Map umap = new ConcurrentHashMap<>();/** channel绑定自己的用户ID */public static final AttributeKey UID = AttributeKey.newInstance("uid");@Overridepublic void channelRead0(ChannelHandlerContext ctx, String msg) {JSONObject parseObj = JSONUtil.parseObj(msg);Integer type = parseObj.getInt("t");String uid = parseObj.getStr("uid");String tid = parseObj.getStr("tid");switch (type) {case 0:// 心跳break;case 1:// 用户加入聊天室umap.put(uid, ctx.channel());ctx.channel().attr(UID).set(uid);umap.forEach((x, y) -> {if (!x.equals(uid)) {JSONObject json = new JSONObject();json.set("t", 2);json.set("uid", uid);json.set("type", "join");y.writeAndFlush(json.toString());}});break;case 2:Channel uc = umap.get(tid);if (null != uc) {uc.writeAndFlush(msg);}break;case 9:// 用户退出聊天室umap.remove(uid);leave(ctx, uid);ctx.close();break;default:break;}}@Overridepublic void channelInactive(ChannelHandlerContext ctx) throws Exception {String uid = ctx.channel().attr(UID).get();if (StringUtil.isNullOrEmpty(uid)) {super.channelInactive(ctx);return;}ctx.channel().attr(UID).set(null);umap.remove(uid);leave(ctx, uid);super.channelInactive(ctx);}/*** 用户退出* * @param ctx* @param uid*/private void leave(ChannelHandlerContext ctx, String uid) {umap.forEach((x, y) -> {if (!x.equals(uid)) {// 把数据转到用户服务JSONObject json = new JSONObject();json.set("t", 9);json.set("uid", uid);y.writeAndFlush(json.toString());}});}@Overridepublic void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {cause.printStackTrace();ctx.close();}
}

启动类

package com.luck.im;public class ServerStart {public static void main(String[] args) throws Exception {// 启动聊天室ChatSocket.startServer();}
}

前端

网页主要使用了adapter-latest.js,下载地址webrtc.github.io

github访问不了可以用webrtc/adapter-latest.js-Javascript文档类资源-CSDN文库 

index.html 



聊天室




Nginx配置

上面的index.html文件放到D盘根目录下了,然后配置一下websocket

    server {listen       443 ssl;server_name    mytest.com;ssl_certificate      lee/lee.crt;ssl_certificate_key  lee/lee.key;ssl_session_cache    shared:SSL:1m;ssl_session_timeout  5m;ssl_ciphers  HIGH:!aNULL:!MD5;ssl_prefer_server_ciphers  on;location / {root   d:/;index  index.html index.htm index.php;}location /myim {proxy_pass http://127.0.0.1:8321/myim;}}

运行 

java启动

java -jar cc-im.jar

网页访问

https://127.0.0.1/index.html

相关内容

热门资讯

北京放宽购房门槛,优化住房信贷...   为更好满足居民刚性和多样化改善性住房需求,北京进一步优化调整房地产相关政策。   12月24日,...
刘百奇:政策与市场双轮驱动——... 12月28日,由创业黑马主办的“第17届创业家年会”在北京举办,年会主题为“智业革命 —— 跨越断层...
原创 郑... 自从被曝与前夫张恒在国外合开代孕机构之后,郑爽突然又活跃了起来,舆论都过去了,以郑爽名义成立的几个账...
成都警方通报燃爆事件:段某因纠... 2025年12月28日,成都市公安局高新区分局发布警情通报: 来源:成都公安
原创 从... 古代传统婚姻讲究“聘则为妻,奔则为妾”,这句话出自《礼记.内则》,将妻和妾的关系区分的很明白。娶妻不...
成都警方:一男子因纠纷引发燃爆... 12月28日,成都市公安局高新区分局发布警情通报: 12月28日下午,我区南三环路四段一汽车销售服务...
高新区一4S店发生燃爆致1死4... 12月28日,成都市公安局高新公安分局发布警情通报称,2025年12月28日下午,高新区南三环路四段...
吉利起诉欣旺达索赔23亿,最“... 文 | 超聚焦 辛辛苦苦干两年,结果到头还赔钱。 12月26日,欣旺达发布公告称,子公司欣旺达动力...
向上·2025湖湘经济关键词盘... 三湘都市报·新湖南客户端 全媒体记者 龙思言 “守住消费券发放时间,买了就是省钱!”近日,已有身孕的...
宁波这类车淘汰更新,补贴政策调... 此前 宁波市生态环境局等5部门联合印发了 《宁波市国四排放标准 非营运中、重型货车提前淘汰 及新能源...