package com.example.netty.chat.server;
import com.example.netty.chat.protocol.IMDecoder;
import com.example.netty.chat.protocol.IMEncoder;
import com.example.netty.chat.server.handler.HttpHandler;
import com.example.netty.chat.server.handler.SocketHandler;
import com.example.netty.chat.server.handler.WebSocktHandler;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler;
import io.netty.handler.stream.ChunkedWriteHandler;
import lombok.extern.slf4j.Slf4j;
import java.io.IOException;
@Slf4j
public class ChatServer {
private int port = 8080;
public void start() {
//主
EventLoopGroup bossGroup = new NioEventLoopGroup();
//工作
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class).option(ChannelOption.SO_BACKLOG, 1024)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
/** 解析自定义协议 */
pipeline.addLast(new IMDecoder());
pipeline.addLast(new IMEncoder());
pipeline.addLast(new SocketHandler());
/** 解析Http请求 */
pipeline.addLast(new HttpServerCodec());
//主要是将同一个http请求或响应的多个消息对象变成一个 fullHttpRequest完整的消息对象
pipeline.addLast(new HttpObjectAggregator(64 * 1024));
//主要用于处理大数据流,比如一个1G大小的文件如果你直接传输肯定会撑暴jvm内存的 ,加上这个handler我们就不用考虑这个问题了
pipeline.addLast(new ChunkedWriteHandler());
pipeline.addLast(new HttpHandler());
/** 解析WebSocket请求 */
pipeline.addLast(new WebSocketServerProtocolHandler("/im"));
pipeline.addLast(new WebSocktHandler());
}
});
ChannelFuture f = b.bind(this.port).sync();
log.info("服务已启动,监听端口" + this.port);
f.channel().closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
workerGroup.shutdownGracefully();
bossGroup.shutdownGracefully();
}
}
public static void main(String[] args) throws IOException {
new ChatServer().start();
}
}
package com.example.netty.chat.server.handler;
import com.example.netty.chat.processor.MsgProcessor;
import org.apache.log4j.Logger;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
public class WebSocktHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {
private static Logger LOG = Logger.getLogger(WebSocktHandler.class);
private MsgProcessor processor = new MsgProcessor();
@Override
protected void channelRead0(ChannelHandlerContext ctx,TextWebSocketFrame msg) throws Exception {
System.out.println("---channelRead0"+msg.text());
processor.sendMsg(ctx.channel(), msg.text());
LOG.info("---channelRead0"+msg.text());
}
@Override
public void handlerAdded(ChannelHandlerContext ctx) throws Exception { // (2)
Channel client = ctx.channel();
String addr = processor.getAddress(client);
LOG.info("WebSocket Client:" + addr + "加入");
}
@Override
public void handlerRemoved(ChannelHandlerContext ctx) throws Exception { // (3)
Channel client = ctx.channel();
processor.logout(client);
LOG.info("WebSocket Client:" + processor.getNickName(client) + "离开");
}
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception { // (5)
Channel client = ctx.channel();
String addr = processor.getAddress(client);
LOG.info("WebSocket Client:" + addr + "上线");
}
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception { // (6)
Channel client = ctx.channel();
String addr = processor.getAddress(client);
LOG.info("WebSocket Client:" + addr + "掉线");
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)
throws Exception {
Channel client = ctx.channel();
String addr = processor.getAddress(client);
LOG.info("WebSocket Client:" + addr + "异常");
// 当出现异常就关闭连接
cause.printStackTrace();
ctx.close();
}
}
package com.example.netty.chat.server.handler;
import io.netty.channel.*;
import io.netty.handler.codec.http.*;
import lombok.extern.slf4j.Slf4j;
import java.io.File;
import java.io.RandomAccessFile;
import java.net.URL;
@Slf4j
public class HttpHandler extends SimpleChannelInboundHandler<FullHttpRequest> {
//获取class路径
private URL baseURL = HttpHandler.class.getProtectionDomain().getCodeSource().getLocation();
private final String webroot = "static";
private File getResource(String fileName) throws Exception{
String path = baseURL.toURI() + webroot + "/" + fileName;
path = !path.contains("file:") ? path : path.substring(5);
path = path.replaceAll("//", "/");
return new File(path);
}
@Override
public void channelRead0(ChannelHandlerContext ctx, FullHttpRequest request) throws Exception {
String uri = request.getUri();
RandomAccessFile file = null;
try{
String page = uri.equals("/") ? "chat.html" : uri;
file = new RandomAccessFile(getResource(page), "r");
}catch(Exception e){
ctx.fireChannelRead(request.retain());
return;
}
HttpResponse response = new DefaultHttpResponse(request.getProtocolVersion(), HttpResponseStatus.OK);
String contextType = "text/html;";
if(uri.endsWith(".css")){
contextType = "text/css;";
}else if(uri.endsWith(".js")){
contextType = "text/javascript;";
}else if(uri.toLowerCase().matches("(jpg|png|gif)$")){
String ext = uri.substring(uri.lastIndexOf("."));
contextType = "image/" + ext;
}
response.headers().set(HttpHeaders.Names.CONTENT_TYPE, contextType + "charset=utf-8;");
boolean keepAlive = HttpHeaders.isKeepAlive(request);
if (keepAlive) {
response.headers().set(HttpHeaders.Names.CONTENT_LENGTH, file.length());
response.headers().set(HttpHeaders.Names.CONNECTION, HttpHeaders.Values.KEEP_ALIVE);
}
ctx.write(response);
ctx.write(new DefaultFileRegion(file.getChannel(), 0, file.length()));
// ctx.write(new ChunkedNioFile(file.getChannel()));
ChannelFuture future = ctx.writeAndFlush(LastHttpContent.EMPTY_LAST_CONTENT);
if (!keepAlive) {
future.addListener(ChannelFutureListener.CLOSE);
}
file.close();
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)
throws Exception {
Channel client = ctx.channel();
log.info("Client:"+client.remoteAddress()+"异常");
// 当出现异常就关闭连接
cause.printStackTrace();
ctx.close();
}
}
package com.example.netty.chat.server.handler;
import com.example.netty.chat.processor.MsgProcessor;
import com.example.netty.chat.protocol.IMMessage;
import org.apache.log4j.Logger;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
public class SocketHandler extends SimpleChannelInboundHandler<IMMessage>{
private static Logger LOG = Logger.getLogger(SocketHandler.class);
private MsgProcessor processor = new MsgProcessor();
@Override
protected void channelRead0(ChannelHandlerContext ctx, IMMessage msg) throws Exception {
processor.sendMsg(ctx.channel(), msg);
}
@Override
public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
LOG.info(" 服务端 SocketHandler 创建...");
super.handlerAdded(ctx);
}
@Override
public void handlerRemoved(ChannelHandlerContext ctx) throws Exception { // (3)
Channel client = ctx.channel();
processor.logout(client);
LOG.info("Socket Client:" + processor.getNickName(client) + "离开");
}
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
LOG.info("channelInactive");
super.channelInactive(ctx);
}
/**
* tcp链路建立成功后调用
*/
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
LOG.info("Socket Client: 有客户端连接:"+ processor.getAddress(ctx.channel()));
}
/**
* 异常处理
*/
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
LOG.info("Socket Client: 与客户端断开连接:"+cause.getMessage());
cause.printStackTrace();
ctx.close();
}
}
package com.example.netty.chat.protocol;
/**
* 自定义IM协议,Instant Messaging Protocol即时通信协议
*
*/
public enum IMP {
/** 系统消息 */
SYSTEM("SYSTEM"),
/** 登录指令 */
LOGIN("LOGIN"),
/** 登出指令 */
LOGOUT("LOGOUT"),
/** 聊天消息 */
CHAT("CHAT"),
/** 送鲜花 */
FLOWER("FLOWER");
private String name;
public static boolean isIMP(String content){
return content.matches("^\\[(SYSTEM|LOGIN|LOGIN|CHAT)\\]");
}
IMP(String name){
this.name = name;
}
public String getName(){
return this.name;
}
public String toString(){
return this.name;
}
}
package com.example.netty.chat.protocol;
import org.msgpack.annotation.Message;
/**
* 自定义消息实体类
*
*/
@Message
public class IMMessage{
private String addr; //IP地址及端口
private String cmd; //命令类型[LOGIN]或者[SYSTEM]或者[LOGOUT]
private long time; //命令发送时间
private int online; //当前在线人数
private String sender; //发送人
private String receiver; //接收人
private String content; //消息内容
public IMMessage(){}
public IMMessage(String cmd,long time,int online,String content){
this.cmd = cmd;
this.time = time;
this.online = online;
this.content = content;
}
public IMMessage(String cmd,long time,String sender){
this.cmd = cmd;
this.time = time;
this.sender = sender;
}
public IMMessage(String cmd,long time,String sender,String content){
this.cmd = cmd;
this.time = time;
this.sender = sender;
this.content = content;
}
public String getCmd() {
return cmd;
}
public void setCmd(String cmd) {
this.cmd = cmd;
}
public long getTime() {
return time;
}
public void setTime(long time) {
this.time = time;
}
public int getOnline() {
return online;
}
public void setOnline(int online) {
this.online = online;
}
public String getSender() {
return sender;
}
public void setSender(String sender) {
this.sender = sender;
}
public String getReceiver() {
return receiver;
}
public void setReceiver(String receiver) {
this.receiver = receiver;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public String getAddr() {
return addr;
}
public void setAddr(String addr) {
this.addr = addr;
}
}
package com.example.netty.chat.protocol;
import org.msgpack.MessagePack;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToByteEncoder;
/**
* 自定义IM协议的编码器
*/
public class IMEncoder extends MessageToByteEncoder<IMMessage> {
@Override
protected void encode(ChannelHandlerContext ctx, IMMessage msg, ByteBuf out)
throws Exception {
out.writeBytes(new MessagePack().write(msg));
}
public String encode(IMMessage msg){
if(null == msg){ return ""; }
String prex = "[" + msg.getCmd() + "]" + "[" + msg.getTime() + "]";
if(IMP.LOGIN.getName().equals(msg.getCmd()) ||
IMP.CHAT.getName().equals(msg.getCmd()) ||
IMP.FLOWER.getName().equals(msg.getCmd())){
prex += ("[" + msg.getSender() + "]");
}else if(IMP.SYSTEM.getName().equals(msg.getCmd())){
prex += ("[" + msg.getOnline() + "]");
}
if(!(null == msg.getContent() || "".equals(msg.getContent()))){
prex += (" - " + msg.getContent());
}
return prex;
}
}
package com.example.netty.chat.protocol;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.msgpack.MessagePack;
import org.msgpack.MessageTypeException;
/**
* 自定义IM协议的编码器
*/
public class IMDecoder extends ByteToMessageDecoder {
//解析IM写一下请求内容的正则
private Pattern pattern = Pattern.compile("^\\[(.*)\\](\\s\\-\\s(.*))?");
@Override
protected void decode(ChannelHandlerContext ctx, ByteBuf in,List<Object> out) throws Exception {
try{
//先获取可读字节数
final int length = in.readableBytes();
final byte[] array = new byte[length];
String content = new String(array,in.readerIndex(),length);
//空消息不解析
if(!(null == content || "".equals(content.trim()))){
if(!IMP.isIMP(content)){
ctx.channel().pipeline().remove(this);
return;
}
}
in.getBytes(in.readerIndex(), array, 0, length);
out.add(new MessagePack().read(array,IMMessage.class));
in.clear();
}catch(MessageTypeException e){
ctx.channel().pipeline().remove(this);
}
}
/**
* 字符串解析成自定义即时通信协议
* @param msg
* @return
*/
public IMMessage decode(String msg){
if(null == msg || "".equals(msg.trim())){ return null; }
try{
Matcher m = pattern.matcher(msg);
String header = "";
String content = "";
if(m.matches()){
header = m.group(1);
content = m.group(3);
}
String [] heards = header.split("\\]\\[");
long time = 0;
try{ time = Long.parseLong(heards[1]); } catch(Exception e){}
String nickName = heards[2];
//昵称最多十个字
nickName = nickName.length() < 10 ? nickName : nickName.substring(0, 9);
if(msg.startsWith("[" + IMP.LOGIN.getName() + "]")){
return new IMMessage(heards[0],time,nickName);
}else if(msg.startsWith("[" + IMP.CHAT.getName() + "]")){
return new IMMessage(heards[0],time,nickName,content);
}else if(msg.startsWith("[" + IMP.FLOWER.getName() + "]")){
return new IMMessage(heards[0],time,nickName);
}else{
return null;
}
}catch(Exception e){
e.printStackTrace();
return null;
}
}
}
package com.example.netty.chat.processor;
import com.alibaba.fastjson.JSONObject;
import com.example.netty.chat.protocol.IMDecoder;
import com.example.netty.chat.protocol.IMEncoder;
import com.example.netty.chat.protocol.IMMessage;
import com.example.netty.chat.protocol.IMP;
import io.netty.channel.Channel;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.netty.util.AttributeKey;
import io.netty.util.concurrent.GlobalEventExecutor;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class MsgProcessor {
//记录在线用户
private static ChannelGroup onlineUsers = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
//定义一些扩展属性
private final AttributeKey<String> NICK_NAME = AttributeKey.valueOf("nickName");
private final AttributeKey<String> IP_ADDR = AttributeKey.valueOf("ipAddr");
private final AttributeKey<JSONObject> ATTRS = AttributeKey.valueOf("attrs");
//自定义解码器
private IMDecoder decoder = new IMDecoder();
//自定义编码器
private IMEncoder encoder = new IMEncoder();
/**
* 获取用户昵称
* @param client
* @return
*/
public String getNickName(Channel client){
return client.attr(NICK_NAME).get();
}
/**
* 获取用户远程IP地址
* @param client
* @return
*/
public String getAddress(Channel client){
return client.remoteAddress().toString().replaceFirst("/","");
}
/**
* 获取扩展属性
* @param client
* @return
*/
public JSONObject getAttrs(Channel client){
try{
return client.attr(ATTRS).get();
}catch(Exception e){
return null;
}
}
/**
* 获取扩展属性
* @param client
* @return
*/
private void setAttrs(Channel client,String key,Object value){
try{
JSONObject json = client.attr(ATTRS).get();
json.put(key, value);
client.attr(ATTRS).set(json);
}catch(Exception e){
JSONObject json = new JSONObject();
json.put(key, value);
client.attr(ATTRS).set(json);
}
}
/**
* 登出通知
* @param client
*/
public void logout(Channel client){
//如果nickName为null,没有遵从聊天协议的连接,表示未非法登录
if(getNickName(client) == null){ return; }
for (Channel channel : onlineUsers) {
IMMessage request = new IMMessage(IMP.SYSTEM.getName(), sysTime(), onlineUsers.size(), getNickName(client) + "离开");
String content = encoder.encode(request);
channel.writeAndFlush(new TextWebSocketFrame(content));
}
onlineUsers.remove(client);
}
/**
* 发送消息
* @param client
* @param msg
*/
public void sendMsg(Channel client,IMMessage msg){
sendMsg(client,encoder.encode(msg));
}
/**
* 发送消息
* @param client
* @param msg
*/
public void sendMsg(Channel client,String msg){
IMMessage request = decoder.decode(msg);
if(null == request){ return; }
String addr = getAddress(client);
if(request.getCmd().equals(IMP.LOGIN.getName())){
client.attr(NICK_NAME).getAndSet(request.getSender());
client.attr(IP_ADDR).getAndSet(addr);
onlineUsers.add(client);
for (Channel channel : onlineUsers) {
if(channel != client){
request = new IMMessage(IMP.SYSTEM.getName(), sysTime(), onlineUsers.size(), getNickName(client) + "加入");
}else{
request = new IMMessage(IMP.SYSTEM.getName(), sysTime(), onlineUsers.size(), "已与服务器建立连接!");
}
String content = encoder.encode(request);
log.info("-----sendMsg");
channel.writeAndFlush(new TextWebSocketFrame(content));
}
}else if(request.getCmd().equals(IMP.CHAT.getName())){
for (Channel channel : onlineUsers) {
if (channel == client) {
request.setSender("you");
}else{
request.setSender(getNickName(client));
}
request.setTime(sysTime());
String content = encoder.encode(request);
channel.writeAndFlush(new TextWebSocketFrame(content));
}
}else if(request.getCmd().equals(IMP.FLOWER.getName())){
JSONObject attrs = getAttrs(client);
long currTime = sysTime();
if(null != attrs){
long lastTime = attrs.getLongValue("lastFlowerTime");
//60秒之内不允许重复刷鲜花
int secends = 10;
long sub = currTime - lastTime;
if(sub < 1000 * secends){
request.setSender("you");
request.setCmd(IMP.SYSTEM.getName());
request.setContent("您送鲜花太频繁," + (secends - Math.round(sub / 1000)) + "秒后再试");
String content = encoder.encode(request);
client.writeAndFlush(new TextWebSocketFrame(content));
return;
}
}
//正常送花
for (Channel channel : onlineUsers) {
if (channel == client) {
request.setSender("you");
request.setContent("你给大家送了一波鲜花雨");
setAttrs(client, "lastFlowerTime", currTime);
}else{
request.setSender(getNickName(client));
request.setContent(getNickName(client) + "送来一波鲜花雨");
}
request.setTime(sysTime());
String content = encoder.encode(request);
channel.writeAndFlush(new TextWebSocketFrame(content));
}
}
}
/**
* 获取系统时间
* @return
*/
private Long sysTime(){
return System.currentTimeMillis();
}
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<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="css/style.css" />
<script type="text/javascript" src="/js/lib/jquery.min.js"></script>
<script type="text/javascript" src="/js/lib/jquery.snowfall.js"></script>
<script type="text/javascript" src="/js/chat.util.js"></script>
</head>
<body>
<div id="loginbox">
<div style="width:300px;margin:200px auto;">
欢迎进入咕泡学院WebSocket聊天室
<br/>
<br/>
<input type="text" style="width:180px;" placeholder="进入前,请先输入昵称" id="nickname" name="nickname" />
<input type="button" style="width:50px;" value="进入" onclick="CHAT.login();" />
<div id="error-msg" style="color:red;"></div>
</div>
</div>
<div id="chatbox" style="display: none;">
<div style="background:#3d3d3d;height: 28px; width: 100%;font-size:12px;position: fixed;top: 0px;z-index: 999;">
<div style="line-height: 28px;color:#fff;">
<span style="text-align:left;margin-left:10px;">咕泡学院聊天室</span>
<span style="float:right; margin-right:10px;">
<span>当前在线<span id="onlinecount">0</span>人</span> |
<span id="shownikcname">匿名</span> |
<a href="javascript:;" onclick="CHAT.logout()" style="color:#fff;">退出</a>
</span>
</div>
</div>
<div id="doc">
<div id="chat">
<div id="message" class="message">
<div id="onlinemsg" style="background:#EFEFF4; font-size:12px; margin-top:40px; margin-left:10px; color:#666;">
</div>
</div>
<form onsubmit="return false;">
<div class="tool-box">
<div class="face-box" id="face-box"></div>
<span class="face" onclick="CHAT.openFace()" title="选择表情"></span>
<!--
<span class="img" id="tool-img-btn" title="发送图片"></span>
<span class="file" id="tool-file-btn" title="上传文件"></span>
-->
<span class="flower" onclick="CHAT.sendFlower()" title="送鲜花"></span>
</div>
<div class="input-box">
<div class="input" contenteditable="true" id="send-message"></div>
<div class="action">
<input class="button" type="button" id="mjr_send" onclick="CHAT.sendText()" value="发送"/>
</div>
</div>
<div class="copyright">咕泡学院©版权所有</div>
</form>
</div>
</div>
</div>
</body>
</html>
Date.prototype.format = function(format){
var o = {
"M+" : this.getMonth()+1, //月
"d+" : this.getDate(), //日
"h+" : this.getHours(), //时
"m+" : this.getMinutes(), //分
"s+" : this.getSeconds(), //秒
"q+" : Math.floor((this.getMonth()+3)/3), //刻
"S" : this.getMilliseconds() //毫秒
}
if(/(y+)/.test(format)) {
format = format.replace(RegExp.$1, (this.getFullYear()+"").substr(4 - RegExp.$1.length));
}
for(var k in o) {
if(new RegExp("("+ k +")").test(format)) {
format = format.replace(RegExp.$1, RegExp.$1.length==1 ? o[k] : ("00"+ o[k]).substr((""+ o[k]).length));
}
}
return format;
};
var getStr1 =function(url) {
var domain = url.split('/'); //以“/”进行分割
if( domain[2] ) {
domain = domain[2];
} else {
domain = ''; //如果url不正确就取空
}
return domain;
};
$(document).ready(function(){
var host = getStr1(location.href);
window.CHAT = {
//保存服务器端WebSocket的请求地址
serverAddr:"ws://" + host + "im",
//保存用户输入的昵称
nickname:null,
//保存浏览器socket对象
socket:null,
//将滚动条设置到最顶部,以便能看到最新的消息
scrollToBottom:function(){
window.scrollTo(0, $("#onlinemsg")[0].scrollHeight);
},
//登录到聊天室
login:function(){
console.log("---login");
$("#error-msg").empty();
var _reg = /^\S{1,10}$/;
var nickname = $("#nickname").val();
if (nickname != "") {
if (!(_reg.test($.trim(nickname)))) {
console.log("-----------昵称长度必须在10个字以内");
$('#error-msg').html("昵称长度必须在10个字以内");
return false;
}
$("#nickname").val('');
$("#loginbox").hide();
$("#chatbox").show();
this.init(nickname);
}else{
$('#error-msg').html("先输入昵称才能进入聊天室");
return false;
}
return false;
},
//退出登录
logout:function(){
location.reload();
},
//清空聊天记录
clear:function(){
CHAT.box.innerHTML = "";
},
//发送聊天消息
sendText:function() {
var message = $("#send-message");
//去掉空格
if(message.html().replace(/\s/ig,"") == ""){ return; }
if (!window.WebSocket) { return; }
if (CHAT.socket.readyState == WebSocket.OPEN) {
var msg = ("[CHAT][" + new Date().getTime() + "]" + "[" + CHAT.nickname + "] - " + message.html().replace(/\n/ig,"<br/>"));
CHAT.socket.send(msg);
message.empty();
message.focus();
} else {
alert("与服务器连接失败.");
}
},
//发送鲜花
sendFlower:function(){
if (!window.WebSocket) { return; }
if (CHAT.socket.readyState == WebSocket.OPEN) {
var message = ("[FLOWER][" + new Date().getTime() + "]" + "[" + CHAT.nickname + "]");
CHAT.socket.send(message);
$("#send-message").focus();
} else {
alert("与服务器连接失败.");
}
},
//选择表情
selectFace:function(img){
var faceBox = $("#face-box");
faceBox.hide();
faceBox.removeClass("open");
var i = '<img src="' + img + '" />';
$("#send-message").html($("#send-message").html() + i);
$("#send-message").focus();
},
//打开表情弹窗
openFace:function(e){
var faceBox = $("#face-box");
if(faceBox.hasClass("open")){
faceBox.hide();
faceBox.removeClass("open");
return;
}
faceBox.addClass("open");
faceBox.show();
var box = '';
for(var i = 1;i <= 130; i ++){
var img = '/images/face/' + i + '.gif';
box += '<span class="face-item" onclick="CHAT.selectFace(\'' + img + '\');">';
box += '<img src="' + img + '"/>';
box += '</span>';
}
faceBox.html(box);
},
//初始化聊天组件
init:function(nickname){
console.log("----init");
var message = $("#send-message");
//自动获取焦点
message.focus();
//按回车键自动发送
message.keydown(function(e){
if ((e.ctrlKey && e.which == 13) || e.which == 10) {
CHAT.sendText();
}
});
CHAT.nickname = nickname;
$("#shownikcname").html(nickname);
//添加系统提示
var addSystemTip = function(c){
var html = "";
html += '<div class="msg-system">';
html += c;
html += '</div>';
var section = document.createElement('section');
section.className = 'system J-mjrlinkWrap J-cutMsg';
section.innerHTML = html;
$("#onlinemsg").append(section);
};
//将消息添加到聊天面板
var appendToPanel = function(message){
var regx = /^\[(.*)\](\s\-\s(.*))?/g;
var group = '',label = "",content = "",cmd="",time=0,name="";
while(group = regx.exec(message)){
label = group[1];
content = group[3];
}
var labelArr = label.split("][");
cmd = labelArr[0];
time = labelArr[1];
name = labelArr[2];
if(cmd == "SYSTEM"){
var total = labelArr[2];
$("#onlinecount").html("" + total);
addSystemTip(content);
}else if(cmd == "CHAT"){
var date = new Date(parseInt(time));
addSystemTip('<span class="time-label">' + date.format("hh:mm:ss") + '</span>');
var isme = (name == "you") ? true : false;
var contentDiv = '<div>' + content + '</div>';
var usernameDiv = '<span>' + name + '</span>';
var section = document.createElement('section');
if (isme) {
section.className = 'user';
section.innerHTML = contentDiv + usernameDiv;
} else {
section.className = 'service';
section.innerHTML = usernameDiv + contentDiv;
}
$("#onlinemsg").append(section);
}else if(cmd == "FLOWER"){
addSystemTip(content);
//鲜花特效
$(document).snowfall('clear');
$(document).snowfall({
image:"/images/face/50.gif",
flakeCount:60,
minSize:20,
maxSize:40
});
window.flowerTimer = window.setTimeout(function(){
$(document).snowfall('clear');
window.clearTimeout(flowerTimer);
},5000);
}
//有新的消息过来以后,自动切到最底部
CHAT.scrollToBottom();
};
if (!window.WebSocket) {
window.WebSocket = window.MozWebSocket;
}
if (window.WebSocket) {
CHAT.socket = new WebSocket(CHAT.serverAddr);
// 收消息
CHAT.socket.onmessage = function(e) {
console.log("----收消息");
appendToPanel(e.data);
};
CHAT.socket.onopen = function(e) {
CHAT.socket.send("[LOGIN][" + new Date().getTime() +"][" + nickname + "]");
};
CHAT.socket.onclose = function(e) {
appendToPanel("[SYSTEM][" + new Date().getTime() + "][0] - 服务器关闭,暂不能聊天!");
};
} else {
alert("你的浏览器不支持 WebSocket!");
}
}
};
});