专栏首页品茗ITSocket、Http、WebSocket?强大的Netty几行语句就帮你实现!

Socket、Http、WebSocket?强大的Netty几行语句就帮你实现!

Socket、Http、WebSocket?强大的Netty几行语句就帮你实现!

一、概述

Netty是目前最流行的由JBOSS提供的一个Java开源框架NIO框架,Netty提供异步的、事件驱动的网络应用程序框架和工具,用以快速开发高性能、高可靠性的网络服务器和客户端程序。

相比JDK原生NIO,Netty提供了相对十分简单易用的API,非常适合网络编程。Netty是完全基于NIO实现的,所以Netty是异步的。

Mina同样也是一款优秀的NIO框架,而且跟Netty是出自同一个人之手,但是Netty要晚一点,优点更多一些,想了解更多可以直接搜索mina和netty比较。

使用Netty,我们可以作为Socket服务器,也可以用来做Http服务器,同时也可以支持WebSocket,这里,我们将这三种方式都详细介绍一下。

**如果大家正在寻找一个java的学习环境,或者在开发中遇到困难,可以<a

href="https://jq.qq.com/?_wv=1027&k=52sgH1J"

target="_blank">

加入我们的java学习圈,点击即可加入

</a>

,共同学习,节约学习时间,减少很多在学习中遇到的难题。**

二、依赖Jar包

只需要引入Netty的jar包,为了方便json转换,我这里引入fastjson。

<dependency>
	<groupId>io.netty</groupId>
	<artifactId>netty-all</artifactId>
	<version>4.1.17.Final</version>
</dependency>
<dependency>
	<groupId>com.alibaba</groupId>
	<artifactId>fastjson</artifactId>
	<version>1.2.40</version>
</dependency>

三、通用的监听配置

不论是Socket、http还是websocket,都是基于tcp的,因此,netty需要配置EventLoopGroup做监听。

package cn.pomit.springwork.nettynew.server;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
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 javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;

public abstract class NettyServiceTemplate {
	static private EventLoopGroup bossGroup = new NioEventLoopGroup();
	static private EventLoopGroup workerGroup = new NioEventLoopGroup();

	abstract protected ChannelHandler[] createHandlers();

	abstract public int getPort();

	abstract public String getName();

	@PostConstruct
	public void start() throws Exception {
		ServerBootstrap b = new ServerBootstrap();
		b.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class)
				.childHandler(new ChannelInitializer<SocketChannel>() {
					@Override
					public void initChannel(SocketChannel ch) throws Exception {
						ChannelHandler[] handlers = createHandlers();
						for (ChannelHandler handler : handlers) {
							ch.pipeline().addLast(handler);
						}
					}
				}).option(ChannelOption.SO_BACKLOG, 128).option(ChannelOption.SO_REUSEADDR, true)
				.childOption(ChannelOption.SO_KEEPALIVE, true).childOption(ChannelOption.SO_REUSEADDR, true);

		ChannelFuture cf = b.bind(getPort()).await();
		// cf.channel().closeFuture().await();
		if (!cf.isSuccess()) {
			System.out.println("无法绑定端口:" + getPort());
			throw new Exception("无法绑定端口:" + getPort());
		}

		System.out.println("服务[{" + getName() + "}]启动完毕,监听端口[{" + getPort() + "}]");
	}

	@PreDestroy
	public void stop() {
		bossGroup.shutdownGracefully().syncUninterruptibly();
		workerGroup.shutdownGracefully().syncUninterruptibly();
		System.out.println("服务[{" + getName() + "}]关闭。");
	}
}

这里的配置,都是netty的常用配置:

  • option和childOption是配置连接属性的。
  • ChannelHandler是必须的,这里通过抽象方法,由子类负责配置。

启动的时候,new一个子类,调用start方法即可。

四、Netty的Socket监听

有了上面的NettyServiceTemplate,我们可以用几行代码构建一个tcp服务器。实现父类的抽象方法createHandlers,传递ChannelHandler数组。

下面的ChannelHandler数组包含:

  1. 换行分割解码器
  2. 字符串解码器
  3. 字符串编码器
  4. 自定义处理器,写自己逻辑用。

StringTcpServer :

package cn.pomit.springwork.nettynew.server.tcp;

import cn.pomit.springwork.nettynew.handler.tcp.StringTcpServerHandler;
import cn.pomit.springwork.nettynew.server.NettyServiceTemplate;
import io.netty.channel.ChannelHandler;
import io.netty.handler.codec.DelimiterBasedFrameDecoder;
import io.netty.handler.codec.Delimiters;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;

public class StringTcpServer extends NettyServiceTemplate {
	private int port = 8088;
	private String name = "String Server";

	public StringTcpServer(int port) {
		this.port = port;
	}

	@Override
	protected ChannelHandler[] createHandlers() {
		return new ChannelHandler[] { 
				new DelimiterBasedFrameDecoder(8192, Delimiters.lineDelimiter()),
				new StringDecoder(), 
				new StringEncoder(),
				new StringTcpServerHandler() };
	}

	@Override
	public int getPort() {
		return port;
	}

	@Override
	public String getName() {
		return name;
	}

	public void setPort(int port) {
		this.port = port;
	}

	public void setName(String name) {
		this.name = name;
	}

}

这样就是一个tcp服务器了,StringTcpServerHandler特别简单,打印并返回:

package cn.pomit.springwork.nettynew.handler.tcp;

import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;

public class StringTcpServerHandler extends SimpleChannelInboundHandler<String> {
	String charset = "UTF-8";

	@Override
	protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
		System.out.println("内容:" + msg);
		ctx.writeAndFlush("返回内容:" + msg);
	}
}

五、Netty的Http监听

Netty的http,稍微复杂点,复杂只是相对于返回值的控制,cookie的控制,session的控制,因为netty只负责帮你解析了http的所有信息,并没有像sevlet容器那样还给你控制session了啥的。

我这里只用netty做json数据转换,用来提供rest服务,如果想了解更多,可以看下我用netty实现的一个类似Spring容器的工具:https://gitee.com/ffch/teaboot, 这个工具解析html、rest等,提供了简单的session控制、security控制。代码属于早期内容,尚待维护。

5.1 Http服务器

这个服务器是专门面向rest服务的。有了上面的NettyServiceTemplate,我们可以用几行代码构建一个http服务器。实现父类的抽象方法createHandlers,传递ChannelHandler数组。

ChannelHandler数组中:

  1. HttpResponseEncoder是netty自己的响应编码器,报文级别
  2. HttpRequestDecoder是netty自己的请求解码器,报文级别
  3. HttpObjectAggregator是完全的解析Http POST请求用的。
  4. HttpMsgResponseEncoder是自定义的响应编码器,为的是对响应做简单的控制。应用级别
  5. HttpMsgRequestDecoder是自定义的请求解码器,是对http请求转换为json。应用级别
  6. JsonHttpServerHandler是自定义逻辑处理器,写自己业务用。

JsonHttpServer :

package cn.pomit.springwork.nettynew.server.http;

import cn.pomit.springwork.nettynew.coder.http.HttpMsgRequestDecoder;
import cn.pomit.springwork.nettynew.coder.http.HttpMsgResponseEncoder;
import cn.pomit.springwork.nettynew.handler.http.JsonHttpServerHandler;
import cn.pomit.springwork.nettynew.server.NettyServiceTemplate;
import io.netty.channel.ChannelHandler;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpRequestDecoder;
import io.netty.handler.codec.http.HttpResponseEncoder;

public class JsonHttpServer extends NettyServiceTemplate {
	int port = 8888;
	String name = "Json Server";
	private String charset = "UTF-8";
	private int timeout = 60;
	
	public JsonHttpServer(int port) {
		this.port = port;
	}

	@Override
	protected ChannelHandler[] createHandlers() {
		return new ChannelHandler[] { 
				new HttpResponseEncoder(), 
				new HttpRequestDecoder(),
				new HttpObjectAggregator(1048576), 
				new HttpMsgResponseEncoder(charset, timeout),
				new HttpMsgRequestDecoder(charset), 
				new JsonHttpServerHandler() };
	}

	@Override
	public int getPort() {
		return port;
	}

	@Override
	public String getName() {
		return name;
	}

}

5.2 自定义的请求解码器

请求解码器就是转换下数据,变成字符串。

HttpMsgRequestDecoder:

package cn.pomit.springwork.nettynew.coder.http;

import java.nio.charset.Charset;
import java.util.List;

import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToMessageDecoder;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpObject;

public class HttpMsgRequestDecoder extends MessageToMessageDecoder<HttpObject>{
	private String charset;

	public HttpMsgRequestDecoder(String charset) {
		super();
		this.charset = charset;
	}

	@Override
	protected void decode(ChannelHandlerContext ctx, HttpObject in,
			List<Object> out) throws Exception {
		FullHttpRequest request = (FullHttpRequest) in;

		ByteBuf buf = request.content();
		String jsonStr = buf.toString(Charset.forName(charset));
		out.add(jsonStr);
	}
}

5.3 自定义的响应编码器

响应编码器中,定义了连接的配置信息,http头信息、内容类型等。

HttpMsgResponseEncoder:

package cn.pomit.springwork.nettynew.coder.http;

import java.util.List;

import cn.pomit.springwork.nettynew.model.http.HttpResponseMsg;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToMessageEncoder;
import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.HttpHeaderNames;
import io.netty.handler.codec.http.HttpHeaderValues;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpVersion;

public class HttpMsgResponseEncoder extends MessageToMessageEncoder<HttpResponseMsg> {
	private String charset;
	private int timeout;

	public HttpMsgResponseEncoder(String charset, int timeout) {
		super();
		this.charset = charset;
		this.timeout = timeout;
	}

	@Override
	protected void encode(ChannelHandlerContext ctx, HttpResponseMsg message, List<Object> out) {
		try {
			DefaultFullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.valueOf(message.getResCode()),
					Unpooled.wrappedBuffer(message.getMessage().getBytes(charset)));
			response.headers().set(HttpHeaderNames.CONTENT_TYPE, message.getResType()+";charset=" + charset);
			response.headers().set(HttpHeaderNames.CONTENT_LENGTH, response.content().readableBytes());

			// 强制keep-alive
			response.headers().set(HttpHeaderNames.CONNECTION, HttpHeaderValues.KEEP_ALIVE);
			response.headers().set("Keep-Alive", "timeout=" + timeout);

			out.add(response);
		} catch (Exception e) {
			e.printStackTrace();
		}

	}
}

5.4 业务处理器

这里的业务处理器很简单,就打印返回。

JsonHttpServerHandler:

package cn.pomit.springwork.nettynew.handler.http;

import cn.pomit.springwork.nettynew.model.http.HttpResponseMsg;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;

public class JsonHttpServerHandler extends SimpleChannelInboundHandler<String> {
	String charset = "UTF-8";

	@Override
	protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
		System.out.println("post内容:" + msg);
		HttpResponseMsg hrm = new HttpResponseMsg();
    	hrm.setResType(HttpResponseMsg.ResType.JSON.getValue());
    	hrm.setResCode(HttpResponseMsg.ResCode.OK.getValue());
    	hrm.setMessage(msg);
    	ctx.writeAndFlush(hrm);
	}

}

六、Netty的WebSocket监听

Netty的WebSocket,相对于http还稍微简单点。

Netty对WebSocket做了完全控制,你需要做的只是对WebSocket的用户进行控制,能根据用户找到相应的通道即可。

6.1 WebSocket服务器

有了上面的NettyServiceTemplate,我们可以用几行代码构建一个WebSocket服务器。实现父类的抽象方法createHandlers,传递ChannelHandler数组。

ChannelHandler数组中:

  1. HttpServerCodec是netty自己的http解码器,报文级别
  2. ChunkedWriteHandler是用于大数据的分区传输。
  3. HttpObjectAggregator是完全的解析Http消息体请求用的。
  4. WebSocketServerProtocolHandler是配置websocket的监听地址。
  5. WebSocketServerHandler是自定义逻辑处理器,写自己业务用。

WebSocketServer :

package cn.pomit.springwork.nettynew.server.websocket;

import cn.pomit.springwork.nettynew.handler.websocket.WebSocketServerHandler;
import cn.pomit.springwork.nettynew.server.NettyServiceTemplate;
import io.netty.channel.ChannelHandler;
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;

public class WebSocketServer extends NettyServiceTemplate {
	int port = 9999;
	String name = "WebSocket";
	
	public WebSocketServer(int port) {
		this.port = port;
	}

	@Override
	protected ChannelHandler[] createHandlers() {
		return new ChannelHandler[] { 
				new HttpServerCodec(), 
				new ChunkedWriteHandler(),
				new HttpObjectAggregator(1048576), 
				new WebSocketServerProtocolHandler("/ws"),
				new WebSocketServerHandler() };
	}

	@Override
	public int getPort() {
		return port;
	}

	@Override
	public String getName() {
		return name;
	}

}

6.2 WebSocket的聊天室逻辑

下面是用websocket做聊天室的逻辑:

  • 使用MessageDTO 做消息的传递实体;
  • WebSocketUser存储了每个连接上来的WebSocket用户,保存对应段分Channel。
  • 前端要求填入用户后,模拟登录,并返回用户列表。前端根据用户列表选择人发送信息。
  • 根据MessageDTO中的发送人和接收人,找到对应的Channel并发送消息。

WebSocketServerHandler:

package cn.pomit.springwork.nettynew.handler.websocket;

import java.util.List;

import com.alibaba.fastjson.JSONObject;

import cn.pomit.springwork.nettynew.model.websocket.MessageDTO;
import cn.pomit.springwork.nettynew.model.websocket.WebSocketUser;
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 WebSocketServerHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {

	@Override
	protected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame msg) throws Exception {
		MessageDTO messageDTO = JSONObject.parseObject(msg.text(), MessageDTO.class);
		if (messageDTO.getMessageType().equals(MessageDTO.Type.TYPE_NEW.getMessageType())) {
			WebSocketUser.add(messageDTO.getFromUserName(), ctx.channel());
			messageDTO.setTargetUserName(messageDTO.getFromUserName());
			messageDTO.setFromUserName("SYSTEM");
			messageDTO.setMessage(JSONObject.toJSONString(WebSocketUser.getUserList()));
			ctx.channel().writeAndFlush(new TextWebSocketFrame(JSONObject.toJSONString(messageDTO)));
		} else {
			List<Channel> webUsers = WebSocketUser.getSessionByUserName(messageDTO.getTargetUserName());
			if (webUsers == null || webUsers.size() == 0) {
				System.out.print("发送给" + messageDTO.getTargetUserName() + ",当前无session");
				MessageDTO messageDTOError = new MessageDTO();
				messageDTOError.setFromUserName("SYSTEM");
				messageDTOError.setTargetUserName(messageDTO.getFromUserName());
				messageDTOError.setMessageType(MessageDTO.Type.TYPE_ERROR.getMessageType());
				messageDTOError.setMessage("发送失败!");
				ctx.channel().writeAndFlush(new TextWebSocketFrame(JSONObject.toJSONString(messageDTOError)));
				return;
			}
			System.out.print("发送给" + messageDTO.getTargetUserName() + ",当前session个数为:" + webUsers.size());

			for (int i = 0; i < webUsers.size(); i++) {
				Channel session = webUsers.get(i);
				if (!session.isOpen()) {
					WebSocketUser.removeWebSocketSession(messageDTO.getTargetUserName(), session);
				}

				session.writeAndFlush(new TextWebSocketFrame(JSONObject.toJSONString(messageDTO)));
			}
		}

	}

	@Override
	public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
		System.out.println("用户:" + ctx.channel().id().asLongText() + "上线");
	}

	@Override
	public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
		System.out.println("用户下线: " + ctx.channel().id().asLongText());
	}

	@Override
	public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
		ctx.channel().close();
	}
}

6.3 用户信息保存

用一个并发map保存所有用户和对应的Channel。

WebSocketUser:

package cn.pomit.springwork.nettynew.model.websocket;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

import io.netty.channel.Channel;

public class WebSocketUser {
	private static Map<String, List<Channel>> userNameWebsession = new ConcurrentHashMap<>();

	public static void add(String userName, Channel webSocketSession) {
		userNameWebsession.computeIfAbsent(userName, v -> new ArrayList<Channel>()).add(webSocketSession);
	}

	/**
	 * 根据昵称拿WebSocketSession
	 * 
	 * @param nickName
	 * @return
	 */
	public static List<Channel> getSessionByUserName(String userName) {
		return userNameWebsession.get(userName);
	}

	/**
	 * 移除失效的WebSocketSession
	 * 
	 * @param webSocketSession
	 */
	public static void removeWebSocketSession(String userName, Channel webSocketSession) {
		if (webSocketSession == null)
			return;
		List<Channel> webSessoin = userNameWebsession.get(userName);
		if (webSessoin == null || webSessoin.isEmpty())
			return;
		webSessoin.remove(webSocketSession);
	}

	public static Set<String> getUserList() {
		return userNameWebsession.keySet();
	}
}

七、过程中用到的其他实体、启动类及页面

7.1 启动类

TestApp:

package cn.pomit.springwork.nettynew;

import cn.pomit.springwork.nettynew.server.http.JsonHttpServer;
import cn.pomit.springwork.nettynew.server.tcp.StringTcpServer;
import cn.pomit.springwork.nettynew.server.websocket.WebSocketServer;

public class TestApp {
	
	public static void main(String args[]) throws ClassNotFoundException, InstantiationException, IllegalAccessException{		
		StringTcpServer stringTcpServerTest = new StringTcpServer(8088);
		JsonHttpServer jsonHttpServer = new JsonHttpServer(8880);
		WebSocketServer webSocketServer = new WebSocketServer(9999);
		try {
			stringTcpServerTest.start();
			jsonHttpServer.start();
			webSocketServer.start();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

7.2 Http响应实体

HttpResponseMsg:

package cn.pomit.springwork.nettynew.model.http;

public class HttpResponseMsg {
	public enum ResType {  
		HTML("text/html"),
	    JSON("application/json"),
	    JS("application/javascript"),
	    PNG("image/png"),
	    JPG("image/jpg");
	    String value = null;
	    ResType(String value) {
	        this.value = value;
	    }
	    public String getValue() {
	        return value;
	    }
	}  
	
	public enum ResCode {  
		NOT_FOUND(404),
	    OK(200),
	    INTERNAL_ERROR(500);
	    int value = 200;
	    ResCode(int value) {
	        this.value = value;
	    }
	    public int getValue() {
	        return value;
	    }
	}  
	public int resCode;
	
	public String resType;
	
	public String message;

	public int getResCode() {
		return resCode;
	}

	public void setResCode(int resCode) {
		this.resCode = resCode;
	}

	public String getResType() {
		return resType;
	}

	public void setResType(String resType) {
		this.resType = resType;
	}

	public String getMessage() {
		return message;
	}

	public void setMessage(String message) {
		this.message = message;
	}
	
}

7.3 WebSocket传递实体

MessageDTO:

package cn.pomit.springwork.nettynew.model.websocket;

public class MessageDTO {
	private String fromUserName;
	private String targetUserName;
	private String message;
	private String messageType;

	public String getFromUserName() {
		return fromUserName;
	}

	public void setFromUserName(String fromUserName) {
		this.fromUserName = fromUserName;
	}

	public String getTargetUserName() {
		return targetUserName;
	}

	public void setTargetUserName(String targetUserName) {
		this.targetUserName = targetUserName;
	}

	public String getMessage() {
		return message;
	}

	public void setMessage(String message) {
		this.message = message;
	}

	public String getMessageType() {
		return messageType;
	}

	public void setMessageType(String messageType) {
		this.messageType = messageType;
	}

	public static enum Type {
		TYPE_NEW("0000"), TYPE_TEXT("1000"), TYPE_BYTE("1001"), TYPE_ERROR("1111");
		private String messageType;

		Type(String messageType) {
			this.messageType = messageType;
		}

		public String getMessageType() {
			return messageType;
		}

		public void setMessageType(String messageType) {
			this.messageType = messageType;
		}

	}
}

7.4 WebSocket前端页面

<!DOCTYPE html>
<html>

<head>
   <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
    <meta name="viewport" content="width=device-width, initial-scale=1"/>
    <title>品茗IT-WebSocket测试</title>

    <!-- CSS  -->
    <link href="https://lib.baomitu.com/material-design-icons/3.0.1/iconfont/material-icons.min.css" rel="stylesheet">
    <link href="https://lib.baomitu.com/materialize/0.100.2/css/materialize.min.css" type="text/css" rel="stylesheet" media="screen,projection"/>

  <style>
body { text-align:left; margin:0; font:normal 12px Verdana, Arial;
background:#FFEEFF } form { margin:0; font:normal 12px Verdana,
Arial; } table,input { font:normal 12px Verdana, Arial; }
a:link,a:visited{ text-decoration:none; color:#333333; } a:hover{
text-decoration:none; color:#FF6600 } #main { width:400px;
position:absolute; left:600px; top:100px; background:#EFEFFF;
text-align:left; filter:Alpha(opacity=90) } #ChatHead {
text-align:right; padding:3px; border:1px solid #003399;
background:#DCDCFF; font-size:20px; color:#3366FF; cursor:move; }
#ChatHead a:link,#ChatHead a:visited, { font-size:14px;
font-weight:bold; padding:0 3px } #ChatBody { border:1px solid
#003399; border-top:none; padding:2px; } #ChatContent {
height:200px; padding:6px; overflow-y:scroll; word-break: break-all
}#ChatBtn { border-top:1px solid #003399; padding:2px }
		
  </style>
</head>
<script type="text/javascript">
var ws = null;
var curUser=null;
var chatUser = null;
var imgName = null;
var fileImgSize = 0;
window.onbeforeunload = function()
{
	disconnect(ws);
}

function gs(d) {
	var t = document.getElementById(d);
	if (t) {
		return t.style;
	} else {
		return null;
	}
}
function gs2(d, a) {
	if (d.currentStyle) {
		var curVal = d.currentStyle[a]
	} else {
		var curVal = document.defaultView
				.getComputedStyle(d, null)[a]
	}
	return curVal;
}
function ChatHidden() {
	gs("ChatBody").display = "none";
}
function ChatShow() {
	gs("ChatBody").display = "";
}
function ChatClose() {
	gs("main").display = "none";
}
function ChatNew(userId) {
	gs("main").display = "";
	chatUser = userId;
	$("#ChatUsers").html(chatUser);
	$('.emotion').qqFace({

		id : 'facebox', 

		assign:'saytext', 

		path: './img/arclist/'	//表情存放的路径

	});
}
function ChatClear(obj) {
	$("#ChatContent").html("");
}

function ChatRead() {
	if(document.getElementById(chatUser)){
		document.getElementById(chatUser).setAttribute('src', './img/users.png');
	}
}

function ChatSend(obj) {
	var o = obj.ChatValue;
	var msg = replace_em(o.value);
	if (o.value.length > 0) {
		$("#ChatContent").append(
				"<p align=\"right\"><strong>" + curUser + "(我) :</strong>" + msg
						+ "</p>");
		var number = $("#ChatContent").scrollTop();
    	number += 16;
    	$("#ChatContent").scrollTop(number);
    	if(ws!=null){
    		var json={"fromUserName":curUser,"targetUserName":chatUser,"message":o.value,"messageType":"1000"};
    		// encodeURI(o.value)
    		console.log(json);
    		ws.send(JSON.stringify(json));
    	}
		o.value = '';
	}
	
	var img = obj.ChatFile;
	if (img.value.length > 0){
		$("#ChatContent").append(
				"<p align=\"right\"><strong>" + nickName + "(我) :</strong>" + img.value
						+ "</p><br/>");

		imgName = nickName+'(我)';
		fileImgSize = img.files.length;
		//alert(fileImgSize);
		$.ajaxFileUpload({
			//处理文件上传操作的服务器端地址(可以传参数,已亲测可用)
			url:'im/fileUpload?userId='+muserId,
			secureuri:true,                       //是否启用安全提交,默认为false 
			fileElementId:'ChatFile',           //文件选择框的id属性
			dataType:'text',                       //服务器返回的格式,可以是json或xml等
			success:function(data, status){        //服务器响应成功时的处理函数
				//$("#ChatContent").append("<p align=\"right\">" + data + "</p><br/>");
			},
			error:function(data, status, e){ //服务器响应失败时的处理函数
				$("#ChatContent").append('<p align=\"right\">图片上传失败,请重试!!</p><br/>');
				imgName = msgUser;
			}
		});
	}
}
	if (document.getElementById) {
		(function() {
			if (window.opera) {
				document.write("<input type='hidden' id='Q' value=' '>");
			}

			var n = 500;
			var dragok = false;
			var y, x, d, dy, dx;

			function move(e) {
				if (!e)
					e = window.event;
				if (dragok) {
					d.style.left = dx + e.clientX - x + "px";
					d.style.top = dy + e.clientY - y + "px";
					return false;
				}
			}

			function down(e) {
				if (!e)
					e = window.event;
				var temp = (typeof e.target != "undefined") ? e.target
						: e.srcElement;
				if (temp.tagName != "HTML" | "BODY"
						&& temp.className != "dragclass") {
					temp = (typeof temp.parentNode != "undefined") ? temp.parentNode
							: temp.parentElement;
				}
				if ('TR' == temp.tagName) {
					temp = (typeof temp.parentNode != "undefined") ? temp.parentNode
							: temp.parentElement;
					temp = (typeof temp.parentNode != "undefined") ? temp.parentNode
							: temp.parentElement;
					temp = (typeof temp.parentNode != "undefined") ? temp.parentNode
							: temp.parentElement;
				}

				if (temp.className == "dragclass") {
					if (window.opera) {
						document.getElementById("Q").focus();
					}
					dragok = true;
					temp.style.zIndex = n++;
					d = temp;
					dx = parseInt(gs2(temp, "left")) | 0;
					dy = parseInt(gs2(temp, "top")) | 0;
					x = e.clientX;
					y = e.clientY;
					document.onmousemove = move;
					return false;
				}
			}

			function up() {
				dragok = false;
				document.onmousemove = null;
			}

			document.onmousedown = down;
			document.onmouseup = up;

		})();
	}

</script>
<body>
<div id="main" class="dragclass" onclick="ChatRead()" style="left: 400px; top: 200px;">
		<div id="ChatUsers" style="width:100px; padding:3px; font-size:15px;float:left; display:inline"></div>
		<div id="ChatHead">
			<a href="#" onclick="ChatHidden();">-</a> <a href="#"
				onclick="ChatShow();">+</a> <a href="#" onclick="ChatClose();">x</a>
		</div>
		<div id="ChatBody">
			<div id="ChatContent"></div>
			<div id="ChatBtn">
				<form action="" name="chat" method="post">
					<textarea name="ChatValue" id="saytext" rows="3" style="width: 350px"></textarea>
					<input name="Submit" type="button" value="发送"
						onclick="ChatSend(this.form);" />
					<input name="ClearMsg" type="button" value="清空记录"
						onclick="ChatClear(this.form);" />
					<input type="button" class="emotion" value="表情">
					<input id="ChatFile" type="file" name="myfiles"  multiple>   
				</form>
			</div>
		</div>
</div>
<div id="modalAddUser" class="modal modal-fixed-footer" style="max-width:400px;max-height:400px">
    <div class="modal-content">
      <h4>生成用户名</h4>
      	<div class="row center">
	     	<input class="browser-default searchInput" placeholder="请输入用户名" style="margin-top:50px;margin-left:20px;max-width:300px" id="catoryAddText" type="text" >
	     	
	 	</div>
	 	<div class="row center">				          		
     		<a class="waves-effect waves-light btn" id="userAddBtn" style="color:white;"><i class="material-icons" style="font-size:1.1rem">添用户</i></a>
     	</div>
    </div>
    <div class="modal-footer">
      <a href="#!" class=" modal-action modal-close waves-effect waves-green btn-flat">关闭</a>
    </div>
</div>
<div align="left" style="margin-top: 50px;margin-left: 20px;">
	<p>欢迎您,
		<span id="userName">匿名用户</span>
	</p>
	<a id="addUser" class="btn waves-effect waves-light white cyan-text" style="border-radius: 40px;">添加用户</a>
	<p id="content"></p>
</div>
<script src="https://lib.baomitu.com/jquery/3.3.0/jquery.min.js"></script>
<script src="https://lib.baomitu.com/materialize/0.100.2/js/materialize.min.js"></script>
<script src="./js/websocket.js"></script>
<script src="./js/ajaxfileupload.js"></script>
<script src="./js/jquery-browser.js"></script>
<script src="./js/jquery.qqFace.js"></script>
<script>
function getUser(){
	$.ajax({
		type : "get",
		url : "../im/user",
		dataType : "json",
		data : {} ,
		success : function(data) {
			if(data.errorCode == "0000"){
				$("#userName").html(data.data);
				curUser = data.data;
			}
		},
		error : function(XMLHttpRequest, textStatus, errorThrown) {
			alert(errorThrown);
		}
	});
}	
function addUser(userName){
	$.ajax({
		type : "post",
		url : "../im/setUser",
		dataType : "json",
		data : {"userName":userName} ,
		success : function(data) {
			if(data.errorCode == "0000"){
				$("#userName").html(userName);
				curUser = data.data;
			}
		},
		error : function(XMLHttpRequest, textStatus, errorThrown) {
			alert(errorThrown);
		}
	});
}
function userList(){
	$.ajax({
		type : "get",
		url : "../im/userList",
		dataType : "json",
		data : {} ,
		success : function(data) {
			if(data.errorCode == "0000"){
				var content = "";
				for(var i =0;i<data.data.length;i++){
					var userId = data.data[i];
					content += "<img src=\"./img/msgget.gif\" id=\""
						+ userId
						+ "\" alt=\"\" style=\"cursor: pointer\" width='40px' "
						+ "onclick=\"ChatNew('"+userId+"')\" />"
						+ userId
						+ "<br><br>";
				}
		    	$("#content").append(content);
			}
		},
		error : function(XMLHttpRequest, textStatus, errorThrown) {
			alert(errorThrown);
		}
	});
}

window.onbeforeunload = function(){
	disconnect(ws);
}
$(function () {
	$('.modal').modal({
		dismissible: true, // 点击模态外面模态消失关闭
		opacity: 0.1, // 相对于背景的不透明度
		in_duration: 300, // 显示特效的时间
		out_duration: 200, // 消失特效时间
		starting_top: '80%', // 启动时的样式属性
		ending_top: '20%', // 结束时的样式属性
		ready: function(modal, trigger) { // 模态加载完成触发事件
			
		},
		complete: function() { 
			
		} // 关闭时触发的事件
	});
	getUser();
	$("#addUser").click(function() {
    	$('#modalAddUser').modal('open');	
	});
	
	$("#userAddBtn").click(function() {
		var catory = $('#catoryAddText').val();
		addUser(catory);
	});
	userList();
	if (ws == null) {
		var url = getUrl();
	    //alert("url:"+url);  
	    if (!url) {  
	        return;  
	    }  
		console.log(url);
	    ws = new WebSocket(url);  
		connect(ws);
		ChatClose();
	}
});
</script>
</body>

</html>

js略大了点,不贴了,直接加群找我要吧

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Spring环境下使用Netty写Socket和Http详解

    Netty是目前最流行的由JBOSS提供的一个Java开源框架NIO框架,Netty提供异步的、事件驱动的网络应用程序框架和工具,用以快速开发高性能、高可靠性的...

    品茗IT
  • Consul-Proxy:使用netty实现快速服务注册(一)注册服务并提供服务

    Springcloud+consul作为微服务的注册已经见怪不怪了,试下也很流行,在我个人云服务器上,我也是这样做的。

    品茗IT
  • SpringBoot入门建站全系列(十一)Spring-security进行权限认证

    Spring 是一个非常流行和成功的 Java 应用开发框架。Spring Security 基于 Spring 框架,提供了一套 Web 应用安全性的完整解决...

    品茗IT
  • [设计模式]之十:组合模式

    将对象组合成树形结构以表示“部分-整体”的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。

    wOw
  • 重要性抽样方法实例分享

    经过matlab爱好者公众号连续不断的推送Monte Carlo方法,所以我们对其了解透彻了吗?NO!当然还得日日精进,大家经常使用的Monte Car...

    matlab爱好者
  • C语言入门:输入任意一个正数(奇数),判断是否为质数

    其实学编程关键是学习其思想,如果你精通了一门,再去学其他的时候也很容易上手。C不会过时的,尤其是在unix、linux操作平台上,学好C是必须的。

    诸葛青云
  • Python Celery初研究

    最近,换了一个工作环境去做研究,当然啦,新公司新作风,需要研究python并行分布式框架:Celery,不用多说,干呗。

    py3study
  • Huginn安装教程—建立你自己的IFTTT

    首先,Huginn是一款开源应用(开源赛高!!!),这是它的Github地址,有上万的star(经常混Github的人应该都知道,上万的star意味着这个开源应...

    huginn 中文网
  • 析构函数-复制构造函数-赋值操作符重载-默认构造函数<代码解析>

    通过下面primer中的一道习题,可以更深刻的了解,析构函数,复制构造函数,赋值操作符重载,默认构造函数的使用。 但是我的结果与primer习题解答里面的并不相...

    用户1154259
  • Maven生命周期

    在Maven出现之前,项目构建的生命周期就已经存在,软件开发人员每天都在对项目进行清理、编译、测试及部署。虽然大家都在不停地做构建工作,但公司和公司间、项目和项...

    栋先生

扫码关注云+社区

领取腾讯云代金券