windows使用socket.io实现群聊

安装Node.js

根据自己的操作系统,去Node.js官网下载安装即可。如果成功安装。在命令行输入node -vnpm -v应该能看到相应的版本号。


搭建WebSocket服务端

在本地开发环境,可以换成本地ip地址,或者使用一个虚拟域名指向本地ip。

进入项目根目录建立一个 package.json的文件,添加内容

{
  "name": "realtime-server",
  "version": "0.0.1",
  "description": "my first realtime server",
  "dependencies": {}
}
接下来使用npm命令安装express和socket.io

在cmd中执行

npm install --save express
npm install --save socket.io
安装成功后,应该可以看到工作目录下生成了一个名为 node_modules 的文件夹,里面分别是 express socket.io ,接下来可以开始编写服务端的代码了,新建一个文件: index.js
var app = require('express')();
var http = require('http').Server(app);
var io = require('socket.io')(http);

app.get('/', function(req, res){
	res.send('<h1>Welcome Realtime Server</h1>');
});

http.listen(3000, function(){
	console.log('listening on *:3000');
});

命令行运行node index.js,如果一切顺利,你应该会看到返回的listening on *:3000字样,这说明服务已经成功搭建了。此时浏览器中打开http://localhost:3000应该可以看到正常的欢迎页面。

如果你想要让服务运行在线上服务器,并且可以通过域名访问的话,可以使用Nginx做代理,在nginx.conf中添加如下配置,然后将域名(比如:zou
.com)解析到服务器IP即可。

server
{
  listen       80;
  server_name  realtime.plhwin.com;
  location / {
    proxy_pass http://127.0.0.1:3000;
  }
}
完成以上步骤,http:// zou.com:3000的后端服务就正常搭建了。

服务端代码实现

前面讲到的index.js运行在服务端,之前的代码只是一个简单的WebServer欢迎内容,让我们把WebSocket服务端完整的实现代码加入进去,整个服务端就可以处理客户端的请求了。完整的index.js代码如下:

var app = require('express')();
var http = require('http').Server(app);
var io = require('socket.io')(http);

app.get('/', function(req, res){
	res.send('<h1>Welcome Realtime Server</h1>');
});

//在线用户
var onlineUsers = {};
//当前在线人数
var onlineCount = 0;

io.on('connection', function(socket){
	console.log('a user connected');
	
	//监听新用户加入
	socket.on('login', function(obj){
		//将新加入用户的唯一标识当作socket的名称,后面退出的时候会用到
		socket.name = obj.userid;
		
		//检查在线列表,如果不在里面就加入
		if(!onlineUsers.hasOwnProperty(obj.userid)) {
			onlineUsers[obj.userid] = obj.username;
			//在线人数+1
			onlineCount++;
		}
		
		//向所有客户端广播用户加入
		io.emit('login', {onlineUsers:onlineUsers, onlineCount:onlineCount, user:obj});
		console.log(obj.username+'加入了聊天室');
	});
	
	//监听用户退出
	socket.on('disconnect', function(){
		//将退出的用户从在线列表中删除
		if(onlineUsers.hasOwnProperty(socket.name)) {
			//退出用户的信息
			var obj = {userid:socket.name, username:onlineUsers[socket.name]};
			
			//删除
			delete onlineUsers[socket.name];
			//在线人数-1
			onlineCount--;
			
			//向所有客户端广播用户退出
			io.emit('logout', {onlineUsers:onlineUsers, onlineCount:onlineCount, user:obj});
			console.log(obj.username+'退出了聊天室');
		}
	});
	
	//监听用户发布聊天内容
	socket.on('message', function(obj){
		//向所有客户端广播发布的消息
		io.emit('message', obj);
		console.log(obj.username+'说:'+obj.content);
	});
  
});

http.listen(3000, function(){
	console.log('listening on *:3000');
});

客户端代码实现

在该目录下 建立一个index.html

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <meta name="format-detection" content="telephone=no"/>
        <meta name="format-detection" content="email=no"/>
<meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=0" name="viewport">
        <title>多人聊天室</title>
        <link rel="stylesheet" type="text/css" href="./style.css" />
        <!--[if lt IE 8]><script src="./json3.min.js"></script><![endif]-->
        <script src="http://自己的ip或者域名:3000/socket.io/socket.io.js"></script>
    </head>
    <body>
        <div id="loginbox">
            <div style="width:260px;margin:200px auto;">
                请先输入你在聊天室的昵称
                <br/>
                <br/>
                <input type="text" style="width:180px;" placeholder="请输入用户名" id="username" name="username" />
				<input type="button" style="width:50px;" value="提交" οnclick="CHAT.usernameSubmit();"/>
            </div>
        </div>
        <div id="chatbox" style="display:none;">
            <div style="background:#3d3d3d;height: 28px; width: 100%;font-size:12px;">
                <div style="line-height: 28px;color:#fff;">
                    <span style="text-align:left;margin-left:10px;">Websocket多人聊天室</span>
                    <span style="float:right; margin-right:10px;"><span id="showusername"></span> | 
					<a href="javascript:;" οnclick="CHAT.logout()" style="color:#fff;">退出</a></span>
                </div>
            </div>
            <div id="doc">
                <div id="chat">
                    <div id="message" class="message">
<div id="onlinecount" style="background:#EFEFF4; font-size:12px; margin-top:10px; margin-left:10px; color:#666;">
</div>
                    </div>
                    <div class="input-box">
                        <div class="input">
<input type="text" maxlength="140" placeholder="请输入聊天内容,按Ctrl提交" id="content" name="content">
                        </div>
                        <div class="action">
                            <button type="button" id="mjr_send" οnclick="CHAT.submit();">提交</button>
                        </div>
                    </div>
                </div>
            </div>
        </div>
        <script type="text/javascript" src="./client.js"></script>
    </body>
</html>


上面的html内容本身没有什么好说的,我们主要看看里面的4个文件请求:
1、 realtime.plhwin.com:3000/socket.io/socket.io.js
2、 style.css

3、client.js

第1个JS是Socket.IO提供的客户端JS文件,在前面安装服务端的步骤中,当npm安装完socket.io并搭建起WebServer后,这个JS文件就可以正常访问了。

第2个style.css文件没什么好说的,就是样式文件而已。

第4个 client.js 是完整的客户端的业务逻辑实现代码,它的内容如下:

(function () {
	var d = document,
	w = window,
	p = parseInt,
	dd = d.documentElement,
	db = d.body,
	dc = d.compatMode == 'CSS1Compat',
	dx = dc ? dd: db,
	ec = encodeURIComponent;
	
	
	w.CHAT = {
		msgObj:d.getElementById("message"),
		screenheight:w.innerHeight ? w.innerHeight : dx.clientHeight,
		username:null,
		userid:null,
		socket:null,
		//让浏览器滚动条保持在最低部
		scrollToBottom:function(){
			w.scrollTo(0, this.msgObj.clientHeight);
		},
		//退出,本例只是一个简单的刷新
		logout:function(){
			//this.socket.disconnect();
			location.reload();
		},
		//提交聊天消息内容
		submit:function(){
			var content = d.getElementById("content").value;
			if(content != ''){
				var obj = {
					userid: this.userid,
					username: this.username,
					content: content
				};
				this.socket.emit('message', obj);
				d.getElementById("content").value = '';
			}
			return false;
		},
		genUid:function(){
			return new Date().getTime()+""+Math.floor(Math.random()*899+100);
		},
		//更新系统消息,本例中在用户加入、退出的时候调用
		updateSysMsg:function(o, action){
			//当前在线用户列表
			var onlineUsers = o.onlineUsers;
			//当前在线人数
			var onlineCount = o.onlineCount;
			//新加入用户的信息
			var user = o.user;
				
			//更新在线人数
			var userhtml = '';
			var separator = '';
			for(key in onlineUsers) {
		        if(onlineUsers.hasOwnProperty(key)){
					userhtml += separator+onlineUsers[key];
					separator = '、';
				}
		    }
			d.getElementById("onlinecount").innerHTML = '当前共有 '+onlineCount+' 人在线,在线列表:'+userhtml;
			
			//添加系统消息
			var html = '';
			html += '<div class="msg-system">';
			html += user.username;
			html += (action == 'login') ? ' 加入了聊天室' : ' 退出了聊天室';
			html += '</div>';
			var section = d.createElement('section');
			section.className = 'system J-mjrlinkWrap J-cutMsg';
			section.innerHTML = html;
			this.msgObj.appendChild(section);	
			this.scrollToBottom();
		},
		//第一个界面用户提交用户名
		usernameSubmit:function(){
			var username = d.getElementById("username").value;
			if(username != ""){
				d.getElementById("username").value = '';
				d.getElementById("loginbox").style.display = 'none';
				d.getElementById("chatbox").style.display = 'block';
				this.init(username);
			}
			return false;
		},
		init:function(username){
			/*
			客户端根据时间和随机数生成uid,这样使得聊天室用户名称可以重复。
			实际项目中,如果是需要用户登录,那么直接采用用户的uid来做标识就可以
			*/
			this.userid = this.genUid();
			this.username = username;
			
			d.getElementById("showusername").innerHTML = this.username;
			this.msgObj.style.minHeight = (this.screenheight - db.clientHeight + this.msgObj.clientHeight) + "px";
			this.scrollToBottom();
			
			//连接websocket后端服务器
			this.socket = io.connect('ws://自己的ip或者域名:3000');
			
			//告诉服务器端有用户登录
			this.socket.emit('login', {userid:this.userid, username:this.username});
			
			//监听新用户登录
			this.socket.on('login', function(o){
				CHAT.updateSysMsg(o, 'login');	
			});
			
			//监听用户退出
			this.socket.on('logout', function(o){
				CHAT.updateSysMsg(o, 'logout');
			});
			
			//监听消息发送
			this.socket.on('message', function(obj){
				var isme = (obj.userid == CHAT.userid) ? true : false;
				var contentDiv = '<div>'+obj.content+'</div>';
				var usernameDiv = '<span>'+obj.username+'</span>';
				
				var section = d.createElement('section');
				if(isme){
					section.className = 'user';
					section.innerHTML = contentDiv + usernameDiv;
				} else {
					section.className = 'service';
					section.innerHTML = usernameDiv + contentDiv;
				}
				CHAT.msgObj.appendChild(section);
				CHAT.scrollToBottom();	
			});

		}
	};
	//通过“回车”提交用户名
	d.getElementById("username").onkeydown = function(e) {
		e = e || event;
		if (e.keyCode === 13) {
			CHAT.usernameSubmit();
		}
	};
	//通过“回车”提交信息
	d.getElementById("content").onkeydown = function(e) {
		e = e || event;
		if (e.keyCode === 13) {
			CHAT.submit();
		}
	};
})();

style.css

 @media (max-device-width: 480px) and (orientation: portrait) {
  @-ms-viewport{
    width:320px;
  }
}

*, *:before, *:after {
  -webkit-box-sizing:border-box;
  box-sizing:border-box;
  -webkit-text-size-adjust:none;
  -ms-text-size-adjust:none;
  text-size-adjust:none;
  -webkit-tap-highlight-color:rgba(0, 0, 0, 0);
}

body, div, dl, dt, dd, ul, ol, li, h1, h2, h3,
h4, h5, h6, pre, code, form, field set, legend,
input, textarea, p, blockquote, th, td {
  margin:0;
  padding:0;
	-webkit-appearance:none;
}

table {
  border-collapse:collapse;
  border-spacing:0;
}

fieldset, img {
  border:0;
}

address, caption, cite, code,
dfn, em, strong, th, var {
  font-style:normal;
  font-weight:normal;
}

li {
  list-style:none;
}

caption, th {
  text-align:left;
}

h1, h2, h3, h4, h5, h6 {
  font-size:100%;
  font-weight:normal;
}

q:before,
q:after {
  content:"";
}

abbr, acronym {
  border:0;
  font-variant:normal;
}

sup {
  vertical-align:text-top;
}

sub {
  vertical-align:text-bottom;
}

input, textarea, select {
  font-family:inherit;
  font-size:inherit;
  font-weight:inherit;
}

input:password {
  ime-mode:disabled;
}

*:focus {
  outline:none;
}

ul {
  margin:0;
	list-style:none;
}

a{
  color:#2982e5;
  text-decoration:none;
}

a:active {
  color:#f60;
}

.smaller {
  font-size:.8em
}

body {
  background-color:#efeff4;
}

#doc {
  height: 100%;
  line-height:1.4;
}

#chat-robot {
  padding:4px;
  padding-bottom:0;
  border:1px solid #7bb0e2;
  background-color:#e6f5fc;
}

#mask {
  display:table;
  position:absolute;
  top:0;
  height:100%;
  width:100%;
  background-color:#fff;
  z-index:100;
}

#mask div {
  display:table-cell;
  height:100%;
  vertical-align:middle;
  text-align:center;
}
/*guide mask begin*/
.guide-mask,.guide-mask .mask-top{
  position: absolute;
  height: 100%;
  width: 100%;
}
.guide-mask{
  top:0;
  left:0;
	z-index: 90;
}
.guide-mask .mask-top{
	bottom: 44px;
	background: rgba(0,0,0,.7);
	z-index: 90;
}
.guide-mask .mask-bottom{
	position: absolute;
	bottom: 0;
	width: 100%;
	height: 44px;
	background:rgba(255, 255, 255, 0);
	z-index: 90;
}
.mask-top .iknow-btn{
	position: absolute;
	width: 256px;
	height: 213px;
	bottom:0;
	left:50%;
	margin-left:-128px;
	background:url('https://i.alipayobjects.com/i/ecmng/png/201404/2NcJmE3ldt.png') no-repeat;
	background-size: 100%;
	z-index: 91;
}
/*guide mask end*/

/*notice begin*/
.notice-wrap-out{
 /*min-height:41px;  */
 max-height: 56px;
}
.notice-wrap{
  position:fixed;
  top:0;
  display:-webkit-box;
  width:100%;
  background:rgba(255, 255, 255, .95);
  /*line-height:41px;
  padding-left:10px;*/
  padding:10px 0;
  font-size: 13px;
  z-index:89;
}
.delNotice-wrap{
  padding:0 10px 0 10px;
  /*padding:13px 10px;*/
}
.icon-notice,.icon-delNotice{
  width:15px;
  height:15px;
  background:url('https://i.alipayobjects.com/i/ecmng/png/201403/2MRvjlV1Vd.png') no-repeat 0 0;
  background-size:100%;

}
.text-notice{
  -webkit-box-flex:1;
  margin-left:10px;
  /*white-space:nowrap;*/
  /*text-overflow:ellipsis;*/
  overflow: hidden;
}
.icon-delNotice{
  background-position: 0 -20px;
}
.icon-notice{
  margin-top:13px;
}
/*notice end*/
.action button {
	border:none;
	border-radius:30px;
	color:#8c8c8c;
	height:28px;
	width:68px;
	text-align:center;
	background:#fbfbfb;
	box-shadow:0 0 1px #000,0 2px 2px #eee;
}

.action button:active,
.action button[disabled] {
  color:#fff;
  background-color:#0044cc;
}

.action button:focus {
  outline:thin dotted #333;
  outline:5px auto -webkit-focus-ring-color;
  outline-offset:-2px;
}

.action button[disabled] {
  cursor:default;
  background-color:#ccc;
  background-image:none;
  border-color:#ccc;
  box-shadow:none;
}

.fn-hide {
  display:none;
}

.message {
  border-bottom:0;
  background-color:#efeff4;
  /*min-height:300px;*/
  padding-bottom: 100px;
}
.message section a{
  /*white-space:nowrap*/
}

.message:after {
  display:block;
  clear:both;
  height:0;
  content:"\0020";
}
section,div.robot {
  padding:10px;
  clear:both;
}

div.robot,section.robot, section.service,section.top-message {
  float:left;
}

div.robot>div,
section.robot>div,
section.service>div,
section.top-message>div{
  max-width: 225px;
  display: inline-block;
  position:relative;
  padding:12px;
  margin:0 20px 0 10px;
  border-radius:10px;
  background-color:#fff;
  box-shadow: inset 0 0 1px #fff;
  vertical-align: top;
}
section.robot>div{
  word-break: break-word;
}
/*
div.robot>div,
section.robot>div{
    margin: 0 11px 0 45px;
} */
div.robot>div:before,
section.robot>div:before,
section.service>div:before,
section.top-message>div:before,
.system .windowContent:before{
  content:"";
  position: absolute;
  left: -5px;
  top: 4px;
  width: 0;
  height: 0;
  border-top: solid transparent;
  border-right: 7px solid #fff;
  border-bottom: 4px solid transparent;
}
/*
div.robot>div:after,
section.robot>div:after,
section.service>div:after,
section.top-message>div:after,
.system .windowContent:after {
  border-color:rgba(227, 236, 255, 0);
  border-right-color:#c5e4fa;
  border-width:8px;
  top:15px;
  margin-top:-8px;
}

div.robot>div:before,
section.robot>div:before,
section.service>div:before,
section.top-message>div:before,
.system .windowContent:before {
  border-color:rgba(165, 181, 213, 0);
  border-right-color:#fff;
  border-width:9px;
  top:15px;
  margin-top:-9px;
}
  */
section.user {
  float:right;
  text-align:right;
}
section.user>div {
  max-width: 225px;
  position:relative;
  display: inline-block;
  padding:8px;
  margin:0 10px 0 12px;
  text-align:left;
  border-radius:10px;
  color:#fff;
  box-shadow: inset 0 0 1px #007aff;
  background-color:#007aff;
  vertical-align: top;
  word-break: break-all;
}
 /*
section.user>div:after,
section.user>div:before {
  left:100%;
  border:solid transparent;
  content:" ";
  height:0;
  width:0;
  position:absolute;
  pointer-events:none;
}

section.user>div:after {
  border-color:rgba(255, 255, 255, 0);
  border-left-color:#e3e3e3;
  border-width:8px;
  top:15px;
  margin-top:-8px;
}
  */
section.user>div:before {
    content:"";
    position: absolute;
    right: -5px;
    top: 4px;
    width: 0;
    height: 0;
    border-top: solid transparent;
    border-left: 7px solid #007aff;
    border-bottom: 4px solid transparent;
}

header {
  font-weight:700;
  color:#4d4d4d;
}
.welcome em{
 color:#2982e5;
}
.welcome {
  color:#000;
  font-size:16px;
  margin-bottom: 0;
}

.welcome span, .user>span {
  font-weight:700;
  color:#f60;
}

.user>span {

  font-weight:normal;
	font-size:14px;
	color:#f60;
	 max-width: 60px;
    height: 36px;
	overflow: hidden;
	float: right;
}

.robot>span, .service>span {

  color:#006e9c;
  font-weight:700;
}
.service>span{
    max-width: 60px;
    height: 36px;
	/*
    background: url("https://i.alipayobjects.com/i/ecmng/png/201404/2O35FCjByn.png");
	color: transparent;
	background-size: 100%;
	*/
	font-weight:normal;
	font-size:14px;
	color:#999;
    overflow: hidden;
    float: left;
}

.service>time{
    display: none;
}
.top-message article{
    color: #ff6600;
}
.top-message article .init-temp{
    color: #000;
}
.top-message article .topHeader{
    color: #ff6600;
}
.top-message ul{
    padding: 0;
}
.top-message ul,.top-message li{
    list-style: none;
}
.robot>span ,.top-message>span{
    float: left;
}
.service>time{
    display: none;
}
.robot>span ,.top-message>span{
    float: left;
}
.system {
  text-align:center;
  color:#999;
  font-size:.8em;
}
.system .windowimg{
    float: left;
}
.system .windowContent{
    position: relative;
    border: solid 1px #7bb2d9;
    border-radius: 5px;
    background-color: #c5e4fa;
    padding: 8px;
    margin: 0 12px 0 45px;
    text-align: left;
    color: #000;
    font-size: 14px;
}
time {
  color:#909090;
  font-size:.8em;
}

.feedback {
  position:relative;
  margin:8px 0;
  padding:4px 8px;
  border:1px solid #fad58c;
  background-color:#fffeeb;
}

.feedback .arrow {
  position:absolute;
  display:block;
  width:10px;
  height:10px;
  font-size:0;
  background:#fffeeb;
  border-left:1px solid #fad58c;
  border-top:1px solid #fad58c;
  top:-7px;
  left:16px;
  -webkit-transform:rotate(45deg);
  -ms-transform:rotate(45deg);
  -moz-transform:rotate(45deg);
  -o-transform:rotate(45deg);
  transform:rotate(45deg);
}

.input-box {
  display:-webkit-box;
  display:-ms-flexbox;
  display:-moz-box;
  position:fixed;
  bottom: 0;
  padding:8px 10px;
  width:100%;
  box-shadow:0 0 1px #000;
  background-color:#fff;
  height: 100px;
}

.input-box .input {
  -webkit-box-flex:1;
  -ms-flex:1;
  -moz-box-flex:1;
  background-color:#fff;
   -webkit-box-flex:1;
  /*height: 28px;*/
  line-height:25px;
}

.input input {
	margin:0;
	float: left;
	padding:0 6px;
	height:28px;
	width:93%;
	font-size:14px;
	background-color: #fbfbfb;
	border:none;
	-webkit-border-radius:15px;
	border-radius:15px;
	color:#000;
	box-shadow:0 0 1px #000;
}

.input input[disabled] {
  background-color:#eee;
}

.input-box button {
  font-size: 15px;
  /*margin-left:11px;*/
 float: right;
}

section.robot>.robot-comment {
  padding: 0;
}
.robot-comment .robot-comment-title{
    padding: 8px;
}
.service-comment button, .robot-comment button {
  padding:2px 12px;
  margin:0 8px;
}
.robot-comment button{
    margin: 0;
}
section.service-comment>div{
    padding: 0;
    padding-top: 10px;
}
.message .robot .btn-robot{
    padding: 0;
}
.btn-robot .btn-title{
    padding: 8px;
}
.suggest {
  border:1px solid #afafaf;
  border-bottom:0 none;
  background-color: rgba(255,255,255, .95);
  width:100%;
}

.suggest ul {
  margin:0;
  padding:0;
  list-style:none;
}

.suggest li {
  height: 44px;
  line-height: 44px;
  padding-left: 10px;
  border-bottom:1px solid #afafaf;
  font-size: 17px;
  overflow: hidden;
  text-overflow:ellipsis;
  white-space: nowrap;
}

.ui-tab-wrap {
  padding:5px 10px;
  background-color:#e4e3e8;
}

.ui-tab {
  height:30px;
  line-height:26px;
  border-radius:5px;
  -webkit-border-radius:5px;
  -o-border-radius:5px;
  -ms-border-radius:5px;
  font-size:14px;
}

.ui-tab-item {
  display:inline-block;
  width:49%;
  text-align:center;
  text-decoration:none;
}

.ui-tab-item-right {
    float:right;
    background: #ebebeb;
    border: 1px solid #bababa;
    border-radius: 0 5px 5px 0;
    color: #999;
}
.ui-tab .ui-tab-item-right:active,.ui-tab .ui-tab-item-right:visited,.ui-tab .ui-tab-item-right:link{
    color: #999;
}

.ui-tab-item-active {
    float: right;
    background-color:#6a89c4;
    background-color: #2059b0;
    border: 1px solid #2059b0;
    border-radius: 5px 0 0 5px;
}

.ui-tab-item,
.ui-tab-item:active,
.ui-tab-item:visited,
.ui-tab-item:link {
  color:#6a89c4
}

.ui-tab-item-active,
.ui-tab-item-active:active,
.ui-tab-item-active:visited,
.ui-tab-item-active:link {
  color:#fff;
}

.windowContent input{
    padding: 13px 10px 12px;
    width: 100%;
    display: block;
    border-radius: 8px;
    border: 1px solid #ccc;
    background: #fff;
}
.windowContent .revaliValue{
    margin-top: 8px;
}
.windowContent .error-p{
    color: #f60;
    margin-top: 5px;
}
.windowContent .button-p{
    margin-top: 15px;
}
.init-temp li a{
    color: #2982e5;
}
.recommend ul li,.recommend ul li a{
    color: #2982e5;
}

section.robot>.comment-btns,section.service>.comment-btns{
    padding: 0;
}
.comment-btns .comment-title{
    padding: 8px;
}
.comment-btns section{
    padding: 0;
    margin-bottom: 0;
    border-top:1px solid #7bb3db;
}
.btn-container{
    padding: 0;
    margin-bottom: 0;
    border-top:1px solid #7bb3db;
}
.service-comment button{
    padding: 2px 15px;
    height: 30px;
    background: none;
    border: none;
    border-right: 1px solid #7bb3db;
    margin: 0;
}
.service-comment button:last-child{
    border-right:none;
}
.btn-no{
  border-right: 1px solid #7bb3db;
}
.robot-comment .isClicked{
    color: #f60;
}
.robot-comment button img{
    margin-right: 10px;
}
.service-comment button span{
    width: 18px;
    height: 18px;
    display: inline-block;
    margin-right: 2px;
}
.service-comment button .btn-3{
    background: url("https://i.alipayobjects.com/e/201312/1eg8I2tUAn.png") no-repeat;
}

.service-comment button .btn-2{
    background: url("https://i.alipayobjects.com/e/201312/1egEIXEjv3.png") no-repeat;
}
.service-comment button .btn-1{
    background: url("https://i.alipayobjects.com/e/201312/1egEVvyvdN.png") no-repeat;
}
.robot-comment button{
  border: none;
  background: none;
  width:110px;
  height:35px;
  line-height:35px;
}
button.btn-no{
  border-radius:0 0 0 8px;
}
button.btn-yes{
  border-radius:0 0 8px 0;
}
.robot-comment button:last-child{
    border-left:  1px solid #7bb3db;
}
.robot-comment .isClicked,.service-comment .isClicked{
    background: #93c3e5;
}
button:active, button[disabled]{
    color: #999;
}


.windowContent .button-p{
    margin-top: 15px;
    border-top: 1px solid #77b0d9;
}
.J-confirm{
    width: 117px;
    height: 30px;
    border: none;
    background: none;
    border-left: 1px solid #77b0d9;
}
.J-cancel{
    width: 124px;
    height: 30px;
    background: none;
    border: none;
}
.button-p button[disabled]{
    cursor: default;
    background-color: none;
    background-image: none;
    border-color: none;
    box-shadow: none;
}
.button-p button:active,.button-p button[disabled]{
   background-color: none;
}
.btnClicked{
    background: #93c3e5;
}
.user .avatar-img,.avatar-service{
  width: 34px;
  height: 34px;
  border-radius: 2px;
}
section article header{
	margin-top:10px;

}
section article li{
  width: 202px;
  padding:10px 0;
  list-style:none;
  border-top: solid 1px #afafaf;
  overflow: hidden;
  text-overflow:ellipsis;
  white-space: nowrap;
  color:#2982e5;
}
section article li:last-child{
  padding-bottom:0;
}
section,section a{
  font-size:16px;
}
.arrow-down,.arrow-up{
  width:10px;
  height:6px;
  background:url("https://i.alipayobjects.com/i/ecmng/png/201404/2O4b2MEDzR.png") no-repeat;
  background-size:100%;
  padding:5px 0;
  margin:5px auto 0;
}
.arrow-up{
  background:url("https://i.alipayobjects.com/i/ecmng/png/201404/2O4b2MEDzR.png") no-repeat 0 -16px;
  background-size:100%;
}
section .msg-system{
  color: #999;
  font-size: 13px;
}
section .msg-system a{
  font-size:13px;
}
section .msg-system a.isClicked{
  color:#999;
}
section.evaluateSec .answer{
  padding: 8px 12px 4px 12px;
}
.poll-loading{
  /*position: absolute;*/
  height: 24px;
  width: 24px;
  margin:0 auto;
  text-align: center;
  background: #ff9e3e;
  background:url('https://i.alipayobjects.com/i/ecmng/gif/201404/2VZeenZ86v.gif') no-repeat;
  background-size: 100%;
  z-index:86;
}
/**layer**/
  .layer{
    display: none;
    position: absolute;
    width: 275px;
    background: rgba(255, 255, 255, .95);
    border-radius: 7px;
    z-index: 120;
  }
  .layer .layer-hd{
    font-size: 16px;
    line-height: 41px;
    text-align: center;
  }
  .layer .layer-ct{
    padding:0 15px 15px 15px;
    text-align: center;
  }
  .layer .layer-ct input.tel-input{
    width: 230px;
    height: 30px;
    border: solid 1px #ccc;
    border-radius:5px;
    padding:0 4px;
  }
  .layer .layer-ft{
    border-top:solid 1px #afafaf;
    font-size: 16px;
    line-height: 41px;
    text-align: center;
  }
  .layer-ft button, .evaluateWrap button{
    border:none;
    background-color:#fff;
    opacity: .95;
    height: 44px;
    line-height: 44px;
    text-align: center;
    color:#007aff;
    width: 135px;
    font-size: 16px;
  }
.evaluateWrap button{
  width:85px;
  height:28px;
  line-height:24px;
}
  .layer .button-left, .evaluateWrap .button-cp{
    border-radius: 0 0 0 7px;
    border-right: solid 1px #afafaf;
  }
  .layer .button-right{
    border-radius: 0 0 7px 0;
  }
  .layer-mask{
    background-color: rgba(0,0,0,.6);
    position: absolute;
    top:0;
    z-index: 110;
  }
  .layer .err-msg{
    color:red;
    font-size: 12px;
    text-align: left;
    margin-top:5px;
    padding-left:5px;
  }
.evaluateWrap .title{
  border-bottom:solid 1px #afafaf;
  padding-bottom:8px;
  margin-bottom:5px;
  font-size: 16px;
  color: #000;
}
.bad-evaluate-wrap{
  width: 290px;
}
.bad-evaluate-wrap .layer-hd{
  border-bottom: solid 1px #afafaf;
}
.layer .evaluate-list li{
  line-height: 44px;
  border-bottom: solid 1px #afafaf;
}
.layer .evaluate-list li:after,.other-wrap:after{
 display:block;
  clear:both;
  height:0;
  content:"\0020";
}
.layer .evaluate-list li:last-child{
  border-bottom: none;
}
.layer .evaluate-list li label{
  float: left;
  width:250px;
  overflow: hidden;
}
.layer .evaluate-list li input{
  float: right;
  margin-top: 16px;
  vertical-align: bottom;
  -webkit-appearance:checkbox;
}
.layer .evaluate-list{
  padding:0 10px;
  text-align: left;
}
.text-wrap{
  border-top: solid 1px #afafaf;
}
.text-wrap textarea{
  padding: 4px;
  width: 265px;
  height: 44px;
  border: none;
}
.layer button[disabled],
.evaluateWrap button[disabled],
.evaluateWrap button.isClicked{
  color:#afafaf;
}
.history-date{
  border-radius: 3px;
  background: #afafaf;
  color: #fff;
  width: 100px;
  margin: 0 auto;
  height: 18px;
  padding: 0 4px;
  line-height: 20px;
}
            
            

最后大家 用cmd进入自己的工作目录开始监听端口

node index.js

这样就可以简单的实现了群聊





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值