nodjs+soket.io+webRTC实现聊天室实录

在家闲着没事,就把之前写的web聊天室分享一下吧,其实早就想发了,但懒惰使我无力,而且没啥动力,哈哈,觉得对自己有帮助的朋友点个赞噢,菜鸡需要鼓励!

额,思路不太清晰,建议先下载源代码看看:CSDN资源百度云下载 (提取码: mr43)

一、HTML界面实现

首先要说一下这个界面我是引用的别人模板,我是在这个模板的基础上进行增删改的,但惭愧的是我已经不知道是从哪位大佬哪里copy的了,找了半天也没找着,知道的网友可以跟我说说,我注明一下。

1. 效果图

在这里插入图片描述

2. 整体代码

<html>
<head>
	<title>chat UI</title>
	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
	<link href="/css/chatroom.css" rel="stylesheet">
	<link rel="stylesheet" type="text/css" href="/css/jquery.dialogbox.css">

	<script>
		var user_name="";
		do user_name=prompt("请输入你的昵称:");
		while (user_name==""||user_name==undefined||user_name==null);
	</script>
</head>
<script>
</script>
<body class="box">
<div id="btn-dialogBox"></div>
<div id="auto-dialogBox"></div>
<div class="video_bar">
	<canvas id="pic_drawer" hidden></canvas>
	<video id="video_me" autoplay hidden></video>
	<video id="video_p2p" autoplay hidden></video>
	<ul id="video_list">
		<!--	视频窗口放置处	-->
<!--		<li>-->
<!--			<video id="video_p2p1" autoplay></video>-->
<!--			<span>猪猪侠</span>-->
<!--		</li>-->
<!--		<li>-->
<!--			<video id="video_p2p2" autoplay></video>-->
<!--			<span>猪猪侠</span>-->
<!--		</li>-->
	</ul>

</div>
<div class="container">
	<div class="chatbox">
		<div class="chatleft">
			<div class="top">
				<i class="fas fa-bars" style="font-size: 1.4em"></i>
<!--				<input type="text" placeholder="search" style="width: 140px; height: 36px; margin-left: 25px;">-->
<!--				<button class="searchbtn">搜</button>-->
				在线人数:<span id="user_num" style="color: red">0</span>&nbsp&nbsp昵称:<span id="user_name" style="color: green"></span>
			</div>
			<div class="center">
				<ul id="multi_chat_option" style="background-color: #d7dede;"><img ><span>群聊天室</span></ul>
				<ul id="user_list">
					<!--	用户列表				-->
<!--					<li><img><span>小兰</span></li>-->
<!--					<li><img><span>三大哈</span></li>-->
				</ul>
			</div>
		</div>
		<div class="chatright">
			<div class="top">
				<div class="top_chatting">
					<img src="/img/user_log.png">
					<span id="chat_user_name" style="margin-left: 20px;">聊天大厅</span>
				</div>
				<i class="fas fa-ellipsis-v" style="font-size: 1.4em; position: absolute; right: 20px; color: gray;"></i>
			</div>
			<div class="center" id="chat_list_scroll">
				<ul id="chat_list">
					<!--	聊天内容-->
<!--					<li class="msgleft"><div id="xiaolan"><img>小兰是憨憨j</div><p>This message on the left!</p></li>-->
<!--					<li class="msgright"><p>This message on the right!s a long message! This is a long message! This is a long left m</p><img></li>-->
<!--					<li class="msgleft"><div><img>小红</div><p>This is a long message! This is a long message! This is a long left message!</p></li><li class="msgright"><p>This  on the right!</p><img></li>-->
<!--					<li class="msgleft"><div><img>猪大侠</div><p>This is a long message! This is a long message! This is a long left message!</p></li>-->
				</ul>
			</div>
			<div class="footer">
				<div class="more_btn">
<!--					<img title="表情" id="emojiBtn" src="/img/emoji.png">-->
					<img title="图片上传" id="uploadBtn" onclick="picClick()" src="/img/picture.png">
					<ul class="btn_choose">
						<input type="file" id="image_file_sel" accept = 'image/*' style="width: 70px">
						<li id="quitPicBt" style="display: none"><button onclick="quitPicClick()">结束拍照</button></li>
						<li><button onclick="catPicClick()">拍照上传</button></li>
					</ul>
<!--					<img title="文件上传" id="fileBtn" src="/img/file.png">-->
					<img title="视频聊天" id="videoBtn" onclick="videoClick()" src="/img/video.png">
<!--					<img title="语音聊天" οnclick="testRun()" id="audioBtn" src="/img/audio.png">-->
				</div>
				<textarea id="inputMsg" onkeyup="onInputEvent(event.key);" maxlength="800" rows="5" cols="40" style="width: 100%; resize: none; border: none; " placeholder="请在此输入要发送的内容..."></textarea>
				<button class="sendbtn" onclick="sendMsg()">发送</button>
			</div>
		</div>
	</div>
</div>
<script src="/js/jquery-3.5.1.min.js"></script>
<script src="/js/jquery.dialogBox.js"></script>
<script src="/socket.io/socket.io.js"></script>
<script src="/js/socket_setting.js"></script>
<script src="/js/chatroom.js"></script>
<script src="/js/bbyrtc.js"></script>

</body>
</html>

3. 解释

这里我有引用很多文件,在head里是两个css文件:

	<!-- css布局文件-->
	<link href="/css/chatroom.css" rel="stylesheet">
	
	<!-- dialog插件的css文件-->
	<link rel="stylesheet" type="text/css" href="/css/jquery.dialogbox.css">

其中,‘chatroom.css’是我的界面的的布局,没有啥好说的,而‘jquery.dialogbox.css’则是我引入的一个插件,因为在后面的开发中,需要服务器与用户进行交互,这就要用到询问对话框,当然也可以使用DOM
Window 的 confirm() 函数,但那样的话,在有的浏览器上可能会不支持。就比如谷歌,在谷歌浏览器上使用
confirm(),它貌似会说这玩意必须是由用户引起的事件触发,服务器发送信息触发就报错。
因此我在网上搜了个dialog插件放了进去,瞬间就舒服了。css就不贴了,太长了,在资源里。

然后就是body里面用到的js文件了:

	<!-- jquery不必多说,js与jq的配合,舒服-->
	<script src="/js/jquery-3.5.1.min.js"></script>
	
	<!-- dialog插件主程序-->
	<script src="/js/jquery.dialogBox.js"></script>	
	
	<!-- 引用socket.io,必须的 -->
	<script src="/socket.io/socket.io.js"></script>
	
	<!-- 这里存放了我对socket事件的处理等等 -->
	<script src="/js/socket_setting.js"></script>
	
	<!-- 这个文件主要是界面事件的监听,如按钮,鼠标点击等-->
	<script src="/js/chatroom.js"></script>
	
	<!-- 实现RTC视频聊天的文件-->
	<script src="/js/bbyrtc.js"></script>

因为代码太多,我就堆js文件进行了分类,按大致情况将他们丢在三个不同文件里,也方便修改哈,具体内容在后面解读!

二、关键代码实现

四个js文件,一个css文件 加起来七八百行了,为避免影响阅读就不一一贴上了,这里例举几个关键代码块吧。

1. 特殊部分

(1) 图片按钮鼠标移动变色(css)

.more_btn img:hover{/*鼠标放上去*/
    filter:alpha(Opacity=40);
    -moz-opacity:0.4;
    opacity: 0.4;
}

效果演示:
图片按钮移动变色

(2)图片传输

function readFileAsDataURL(file,onload) {
    var reader = new FileReader();
    reader.readAsDataURL(file);
    reader.onload = onload;
    reader.onerror = function() {
        console.log('Unable to read ' + file.fileName);
    };
}
document.getElementById('image_file_sel').onchange=function(e){
    console.log('选择文件');
    readFileAsDataURL(this.files[0],function (event) {
        iosocket.emit('msg_to',{data: event.target.result,type:'img',room:my_room});
        addMsgPic(null,null,event.target.result);
    });
    $(".btn_choose").hide();
}

这里嘛,是将选择的图片转化为DataURL,就是字符串形式,然后发送出去,不过这里与其它部分有点不同,readAsDataURL() 不会马上结束,js没有sleep()函数让我们等待,也不能等待,于是只能将发送代码进行封装,等readAsDataURL结束后自动调用,这里为了以后复用,我将函数当成变量传送(菜鸡不知专业术语,见谅)。DataURL是将图片镶入html的不错选择。

2. js界面部分

(1) 几种页面监听事件

1) 直接在html标签中声明onclick

<img title="视频聊天" id="videoBtn" onclick="videoClick()" src="/img/video.png">

2)JQ选择器.click()

//点击事件
$('#multi_chat_option').click(function () {
    if(chatting_user!='multi_chat_option'){
        $('#btn-dialogBox').dialogBox({//询问一下是否进入私聊
            hasBtn: true,hasClose:true, confirmValue:'确认',cancelValue: '算了',
            title: '通知', content: "你正在和"+user_list[chatting_user]+"私聊,确定退出嘛?",
            confirm:function () {
                iosocket.emit('chat_p2p',{what:'leave_out'});
            }
        });
    }
    // $
  1. JQ选择器.on()
//移动变色事件
$('#user_list').on('mouseenter','li',function () {
    if(this.id==chatting_user) return;
    this.style.backgroundColor='#d7dede';
});
$('#user_list').on('mouseout','li',function () {
    if(this.id==chatting_user) return;
    this.style.backgroundColor='white';
});

(2)dialog插件的引用

$('#btn-dialogBox').dialogBox({//询问一下是否进入私聊
   hasBtn: true,hasClose:true, confirmValue:'确认',cancelValue: '算了',
   title: '通知', content: "你正在和"+user_list[chatting_user]+"私聊,确定退出嘛?",
   confirm:function () {
       iosocket.emit('chat_p2p',{what:'leave_out'});
   }
});

(3)用户列表的显示与刷新

function addUserLi(id,name){
    let user_h='<li class="user_li" id="'+id+'">'+'<img/><span>'+name+'</span></li>';
    $('#user_list').append($(user_h));
}
iosocket.on('flash list', function(msgs) {//刷新用户列表,列表中id用socket.id
    $('#user_list').empty();//先清空
    user_list=msgs.list;
    my_room=msgs.room;
    delete msgs.list[iosocket.id];//删除自己的记录,如果有的话
    // addUserLi(iosocket.id,user_name);//顶部是群聊
    for(let key in msgs.list) addUserLi(key,msgs.list[key]);
    $('#user_num').text(msgs.num);
});
iosocket.on('news', function(msgs) {//用户变更
    if(msgs.what=="new") {//新用户加入聊天室
        if($('#' + msgs.id).length == 0){
            addUserLi(msgs.id, msgs.name);
            user_list[msgs.id]=msgs.name;
        }
    } else{//有用户退出聊天室
        $('#' + msgs.id).remove();
        if(isInVideo){
            let v=$('#video_' + msgs.id);
            if (v) {
                v.parent().remove();
                pcs[msgs.id].close();
                delete pcs[msgs.id];
            }
        }
        delete user_list[msgs.id];
    }
    $('#user_num').text(msgs.num);//更新人数显示
});

(4)聊天记录显示

function addMsgPic(id,name,src) {//显示接收到的图片
    $('#chat_list').append(
        (id==null)?  '<li class="msgright"><p><img class="msg_pic" src="'+src+'"></p><img></li>'
            :'<li class="msgleft"><div id='+id+'><img>'+name+'</div><p><img class="msg_pic" src="'+src+'"></p></li>'
    );
    let scl =$('#chat_list_scroll');
    scl.animate({scrollTop: scl.get(0).scrollHeight},500);
}
function addMsgRecord(id,name,msg) {//显示收到的消息
    let htext=(id==null)? '<li class="msgright"><p>'+msg+'</p><img></li>'
        : '<li class="msgleft"><div class="chatRecd_'+id+'"><img>'+name+'</div> <p>'+msg+'</p></li>';
    $('#chat_list').append(htext);
    let scl =$('#chat_list_scroll');
    scl.animate({scrollTop: scl.get(0).scrollHeight},500);//移动到最新消息
}

(4)新建视频聊天窗口

function createRemoteVideo(id) {
    let remoteVideo=document.getElementById('video_'+id);
    if (remoteVideo) return remoteVideo;
    video_list.append('<li><video autoplay id="video_'+id+'"></video><span>'+user_list[id]+'</span></li>');
    console.log('新建 '+user_list[id]+' 的视频窗口!');
    return remoteVideo=document.getElementById('video_'+id);
}

3. socket.io 主要代码

聊天室用socket.io是非常不错的选择,这里是对socket_setting.js的几个监听事件的解释
在这里插入图片描述

(1)connect 事件

    iosocket.emit('set nickname',user_name);
    iosocket.on(.....);
    ...

这是socket.io成功建立建立后触发的事件,既然建立连接成功,辣么就该初始化其它事件了,同时告诉服务器,我丫来了,记住俺的名字,user_name!

(2)webrtc 事件

在这里插入图片描述

这是用来进行视频聊天的,当用户收到一个sdp后,判断这个sdpoffer还是answer

  • 如果收到的是offer,初始化并发送answer
  • 如果收到的是answer,将sdp放在自己的RemoteDescription里,准备通话了要 如果收到的不是sdp而是 candidate 的话,辣么就直接将它交给ICE就成了。

(3)flash list 和 news 事件

这里前面说了,这是刷新用户列表用的。

(4)chat_p2p 事件
在这里插入图片描述

这个是私聊请求等互动事件的处理,没啥好说的。

(5)msg 事件

    iosocket.on('msg', function(msgs) {
        if(msgs.type=='img') addMsgPic(msgs.id,msgs.name,msgs.data);
        else addMsgRecord(msgs.id,msgs.name,msgs.msg);
    });

显示聊天记录用的。

(6) video_multi 事件

    iosocket.on('video_multi', function(data) {
        if(data.isNew){//有人新进入视频,直接向他发送offer
            console.log(user_list[data.id]+'加入群视频!');
            createRTCPeerConnection(data.id,createRemoteVideo(data.id),true);
        }else if(data.isQuit) {//有人退出
            console.log(user_list[data.id]+'退出群视频!');
            pcs[data.id].close();
            delete [data.id];
            $('#video_'+data.id).parent().remove();
        }else {//list,自己嘛什么都不干。
            console.log('已加入!');
            if(data.list.length<2)
                $('#btn-dialogBox').dialogBox({//询问一下
                    title: '通知', content: "暂时没有人加入群视频,请等待?",
                    hasClose:true, cancelValue:'哦哦'
                });
            isInVideo=true;
        }
    });

群聊时的视频聊天请求的处理,有人请求视频聊天,同意后会新建个RTCPeerConnection,就这样。

3. webRTC 主要代码

webRTC相关的代码并不多,毕竟我只实现了视频聊天,其它的什么优化视频啊什么的骚操作一个木有!
在这里插入图片描述

这里我将创建webRTC对象封装成了函数,调用这个函数就能获得一个PeerConnection,这个PeerConnection将储存在变量pcs[user_id]中,其中,pcs是一个数组,user_id是与自己建立PeerConnection连接的用户的socket连接的id,用于区分识别用户。
注:在创建 PeerConnection 时,要分情况,若自己是接收方的话,则不在这里添加视频流track。而是在添加了对方的offer信息后将视频流添的track加进去。如下图。

在这里插入图片描述

3. server 服务端主要代码

在这里插入图片描述

服务端主要就是信息转发,没啥好说的,这里我用的是https,至于怎么配置,嗯,在我之前发的另一篇文中。。。

三、总结

差不多就这样了,这此学习还是挺成功的,虽然有很多计划的功能都没有实现(皮一下)。毕竟在此之前我只是处于认得出这是html语言的阶段,哈哈,有的夸张了,不过也八九不离十就那样。学习期间很感谢助教大大的帮助,其它的,感谢网友分享的各种资源吧!如果本文对网友们的学习有帮助,点个赞呗,有错误也请指出(认真脸)。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值