【Openfire】网页版单对单聊天实例

网上部分关于Openfire的代码,只实现了,从单一应用与Openfire已经封装好的Spark客户端交互的功能,并没有实现一个应用内,多个用户交互的功能。

下面编写一个Openfire网页版单对单聊天的实例,用Smack的api与Struts2实现。仅包含两在线用户聊天部分。

如下图,开两个浏览器,模拟两个用户,实现网页版的Openfire聊天。

具体文件结构如下:


除了补充了一个fastjson-1.1.24.jar实现Java的容器类到Json字符串的转换,详见《【Java】读取网页中的内容》(点击打开链接),其余关于Openfire的用户处理部分在《【Openfire】网页版的用户注册、登录、修改密码》(点击打开链接)一文中已经详细介绍,这次工程是在其上面开发的。在struts.xml新增了部分关于聊天的Ajax Action,更新之后如下:

<?xml version="1.0" encoding="UTF-8"?>  
<!DOCTYPE struts PUBLIC    
      "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"    
      "http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
	<package name="test" extends="struts-default">
		<action name="register" class="test.user" method="register">
			<result name="success" type="redirect">/index.jsp</result>
		</action>
		<action name="login" class="test.user" method="login">
			<result name="success" type="redirect">/chat.jsp</result>
			<result name="input" type="redirect">/index.jsp</result>
		</action>
		<action name="logout" class="test.user" method="logout">
			<result name="success" type="redirect">/index.jsp</result>
		</action>
		<action name="modify_password" class="test.user" method="modify_password">
			<result name="success" type="redirect">/index.jsp</result>
		</action>
		
		<action name="init" class="test.chat" method="init" />
		<action name="load_msg" class="test.chat" method="load_msg" />
		<action name="submit_msg" class="test.chat" method="submit_msg" />
	</package>
</struts>   

具体改动如下:

基本上已经可以通过Struts2的Ajax Action略窥一二,网页聊天主要实现三个事情,一个是初始化init,一个是让前台javascript的定时器setInterval定时执行的,载入信息的Action,还有一个是用户点击按钮的发送信息的Action。

这些Action全写在chat.java这个文件里面,具体代码如下,详情请留意注释:

package test;

import java.io.IOException;
import java.io.PrintWriter;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.struts2.ServletActionContext;
import org.jivesoftware.smack.Chat;
import org.jivesoftware.smack.ChatManager;
import org.jivesoftware.smack.ChatManagerListener;
import org.jivesoftware.smack.Connection;
import org.jivesoftware.smack.MessageListener;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.packet.Message;

import tool.DB;

import com.alibaba.fastjson.JSON;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionSupport;

@SuppressWarnings({ "unchecked", "rawtypes", "serial" })
public class chat extends ActionSupport {
	
	private String msg;//用户要发送的信息
	private String to_user;//用户选择的收信人

	private Map session = ActionContext.getContext().getSession();//Session

	//将1970年到现在的long时间记法,转换成真实时间,人看的时间。
	private String get_realTime(long systime) {
		return new SimpleDateFormat("hh:mm:ss").format(new Date(systime))
				.toString();
	}

	//聊天系统的初始化
	public void init() throws IOException {
		PrintWriter printWriter = ServletActionContext.getResponse()
				.getWriter();//用于打印Ajax

		//新建一个名为message_list存信息的ArrayList放入session
		ArrayList<Message> message_list = new ArrayList<Message>();
		session.put("message_list", message_list);

		//Openfire的关键步骤,为当前的连接,新建聊天信息接受的接听器。
		Connection connection = (Connection) session.get("connection");
		ChatManager chatmanager = connection.getChatManager();
		chatmanager.addChatListener(new ChatManagerListener() {
			@Override
			public void chatCreated(Chat chat, boolean arg1) {
				//对Openfire的消息处理进行重写
				chat.addMessageListener(new MessageListener() {
					//要求这个监听器,收到信息,通通放入session中的message_list
					public void processMessage(Chat arg0,
							Message message_receive) {
						if (message_receive.getBody() != null) {
							ArrayList<Message> message_list = (ArrayList<Message>) session
									.get("message_list");
							//由于Smack的api是没有对Openfire中Time属性的处理
							//因此必须自己再处理一下
							message_receive.setProperty("time",
									System.currentTimeMillis());
							message_list.add(message_receive);
							session.put("message_list", message_list);
						}
					}
				});
			}
		});//这样重写了监听器之后,就可以避免原来Openfire必须指定接听哪个用户发来的信息的情况
		//任意用户给当前登录用户发来信息,都会放入session中,一会儿载入信息的load_msg()仅仅需要对session进行处理

		//载入Openfire中所有用户列表,剔除当前用户,和在初始化Openfire服务器时设定系统总管理用户admin
		DB db = DB.getInstance();
		List<Object[]> userlist = db
				.getBySql("select username from ofuser where username !='"
						+ session.get("username") + "' and username!='admin'");
		String json = JSON.toJSONString(userlist);

		//将用户列表打印给前台
		printWriter.print(json);
		printWriter.flush();
		printWriter.close();
	}

	//载入用户信息
	public void load_msg() throws IOException, XMPPException {
		ServletActionContext.getResponse().setContentType(
				"text/html;charset=UTF-8");//由于用户发送的信息可能有中文,所以必须要先设置编码
		PrintWriter printWriter = ServletActionContext.getResponse()
				.getWriter();

		//对session中的message_list进行处理构造JSON
		ArrayList<Message> message_list = (ArrayList<Message>) session
				.get("message_list");
		
		ArrayList<HashMap<String, String>> message_json = new ArrayList<HashMap<String, String>>();//这个message_json会被fastjson-1.1.24.jar转化成json字符串
		for (int i = 0; i < message_list.size(); i++) {
			Message message = message_list.get(i);

			HashMap<String, String> one_message = new HashMap<String, String>();
			
			String from = message.getFrom().split("@")[0];//由于Openfire会自动在发信人后面带上一堆服务器、设备参数,因此,做此划分
			String time = get_realTime((long) message.getProperty("time"));//将long时间转现实时间输出到前台
			one_message.put("from", from);
			one_message.put("time", time);
			one_message.put("body", message.getBody());

			message_json.add(one_message);//每一条消息都这样处理
		}

		//打印到前台
		String json = JSON.toJSONString(message_json);
		printWriter.print(json);
		printWriter.flush();
		printWriter.close();
	}

	// 信息发送
	public void submit_msg() throws IOException, XMPPException {
		PrintWriter printWriter = ServletActionContext.getResponse()
				.getWriter();

		Connection connection = (Connection) session.get("connection");//取连接
		//按Openfire要求的格式构造发信、收信人
		String from = (String) session.get("username") + "@"
				+ connection.getServiceName();
		String to = to_user + "@" + connection.getServiceName();

		//发信息核心代码
		ChatManager chatmanager = connection.getChatManager();
		Chat chat = chatmanager.createChat(to, null);//新建一个会话,指定收信人,不对收信人所回复的信息进行监听
		//因为在初始化的时候,已经对此连接收到信息都进行了监听,无须重复监听
		chat.sendMessage(msg);//对此会话进行发送
		//但同样要将发送的信息记录在session中的message_list里面,用户打印,这才符合现时主流的聊天工具一问一答的方式
		ArrayList<Message> message_list = (ArrayList<Message>) session
				.get("message_list");
		Message message_send = new Message();
		message_send.setFrom(from);
		message_send.setTo(to);
		message_send.setBody(msg);
		message_send.setProperty("time", System.currentTimeMillis());
		message_list.add(message_send);
		session.put("message_list", message_list);

		//其实,消息发送,是没有任何东西交互给前台的,但必须打印一个空信息,才能保证ajax过程不出错。
		printWriter.print("");
		printWriter.flush();
		printWriter.close();
	}

	public String getMsg() {
		return msg;
	}

	public void setMsg(String msg) {
		this.msg = msg;
	}

	public String getTo_user() {
		return to_user;
	}

	public void setTo_user(String to_user) {
		this.to_user = to_user;
	}

}

最后,配合前台的chat.jsp一切就大功告成,HTML布局部分不重要就表格里面的几行和一个按钮。关键是javascript部分:

<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
<%@ page isELIgnored="false"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<script src="jquery-1.8.3.js" type="text/javascript"></script>
<title>聊天</title>
</head>
<body>
	<table border="1" width="100%">
		<tr>
			<td colspan="2" width="100%">
				欢迎,${sessionScope.username},<a	href="logout">登出</a>
			</td>
		</tr>
		<tr height="150px" valign="top">
			<td id="msg_list"></td>
		</tr>
		<tr>
			<td>
				<textarea id="msg" style="width: 100%; height: 50px"></textarea>
				<select id="user_list"></select>
				<button οnclick="submit_msg()">发送!</button>
			</td>
		</tr>
	</table>

</body>
<script type="text/javascript">

	function init(){
		$.ajax({
			type : "post",
			url : "init",
			dataType : "json",
			success : function(data) {
				for(var i=0;i<data.length;i++){
					$("#user_list").append("<option value='"+data[i]+"'>"+data[i]+"</option>");
				}
			},
			error : function() {
				alert("出错了");
			}
		});
		setInterval("load_msg()",2000);
	}

	function load_msg(){
		var msg="";
		$.ajax({
			type : "post",
			url : "load_msg",
			dataType : "json",
			success : function(data) {
				for(var i=0;i<data.length;i++){
					msg=msg+
						"<p sytle='text-align:center'>"+data[i].time+"</p>"+
						"<p>"+data[i].from+":"+data[i].body+"</p>";
					$("#msg_list").html(msg);
				}
			},
			error : function() {
				alert("出错了");
			}
		});
	}
	
	function submit_msg() {
		var msg = $("#msg").val();
		var to_user = $("#user_list").val();
		$("#msg").val("");
		$.ajax({
			type : "post",
			url : "submit_msg",
			dataType : "text",
			data : {
				msg : msg,
				to_user : to_user
			},
			success : function(data) {
			},
			error : function() {
				alert("出错了");
			}
		});
	}
	
	init();	
	
</script>
</html>

要求进入这个页面就马上执行init()的代码,载入用户列表的同时,同时触发读取信息的定时器,每2秒一次执行load_msg()里面的所有内容。

将发送信息的按钮的点击事件,绑定于submit_msg()事件与后台进行交互。

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值