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

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

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

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

具体文件结构如下:


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

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <!DOCTYPE struts PUBLIC
  3. "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
  4. "http://struts.apache.org/dtds/struts-2.0.dtd">
  5. <struts>
  6. <package name="test" extends="struts-default">
  7. <action name="register" class="test.user" method="register">
  8. <result name="success" type="redirect">/index.jsp </result>
  9. </action>
  10. <action name="login" class="test.user" method="login">
  11. <result name="success" type="redirect">/chat.jsp </result>
  12. <result name="input" type="redirect">/index.jsp </result>
  13. </action>
  14. <action name="logout" class="test.user" method="logout">
  15. <result name="success" type="redirect">/index.jsp </result>
  16. </action>
  17. <action name="modify_password" class="test.user" method="modify_password">
  18. <result name="success" type="redirect">/index.jsp </result>
  19. </action>
  20. <action name="init" class="test.chat" method="init" />
  21. <action name="load_msg" class="test.chat" method="load_msg" />
  22. <action name="submit_msg" class="test.chat" method="submit_msg" />
  23. </package>
  24. </struts>

具体改动如下:

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

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

  1. package test;
  2. import java.io.IOException;
  3. import java.io.PrintWriter;
  4. import java.text.SimpleDateFormat;
  5. import java.util.ArrayList;
  6. import java.util.Date;
  7. import java.util.HashMap;
  8. import java.util.List;
  9. import java.util.Map;
  10. import org.apache.struts2.ServletActionContext;
  11. import org.jivesoftware.smack.Chat;
  12. import org.jivesoftware.smack.ChatManager;
  13. import org.jivesoftware.smack.ChatManagerListener;
  14. import org.jivesoftware.smack.Connection;
  15. import org.jivesoftware.smack.MessageListener;
  16. import org.jivesoftware.smack.XMPPException;
  17. import org.jivesoftware.smack.packet.Message;
  18. import tool.DB;
  19. import com.alibaba.fastjson.JSON;
  20. import com.opensymphony.xwork2.ActionContext;
  21. import com.opensymphony.xwork2.ActionSupport;
  22. @SuppressWarnings({ "unchecked", "rawtypes", "serial" })
  23. public class chat extends ActionSupport {
  24. private String msg; //用户要发送的信息
  25. private String to_user; //用户选择的收信人
  26. private Map session = ActionContext.getContext().getSession(); //Session
  27. //将1970年到现在的long时间记法,转换成真实时间,人看的时间。
  28. private String get_realTime(long systime) {
  29. return new SimpleDateFormat( "hh:mm:ss").format( new Date(systime))
  30. .toString();
  31. }
  32. //聊天系统的初始化
  33. public void init() throws IOException {
  34. PrintWriter printWriter = ServletActionContext.getResponse()
  35. .getWriter(); //用于打印Ajax
  36. //新建一个名为message_list存信息的ArrayList放入session
  37. ArrayList<Message> message_list = new ArrayList<Message>();
  38. session.put( "message_list", message_list);
  39. //Openfire的关键步骤,为当前的连接,新建聊天信息接受的接听器。
  40. Connection connection = (Connection) session.get( "connection");
  41. ChatManager chatmanager = connection.getChatManager();
  42. chatmanager.addChatListener( new ChatManagerListener() {
  43. @Override
  44. public void chatCreated(Chat chat, boolean arg1) {
  45. //对Openfire的消息处理进行重写
  46. chat.addMessageListener( new MessageListener() {
  47. //要求这个监听器,收到信息,通通放入session中的message_list
  48. public void processMessage(Chat arg0,
  49. Message message_receive) {
  50. if (message_receive.getBody() != null) {
  51. ArrayList<Message> message_list = (ArrayList<Message>) session
  52. .get( "message_list");
  53. //由于Smack的api是没有对Openfire中Time属性的处理
  54. //因此必须自己再处理一下
  55. message_receive.setProperty( "time",
  56. System.currentTimeMillis());
  57. message_list.add(message_receive);
  58. session.put( "message_list", message_list);
  59. }
  60. }
  61. });
  62. }
  63. }); //这样重写了监听器之后,就可以避免原来Openfire必须指定接听哪个用户发来的信息的情况
  64. //任意用户给当前登录用户发来信息,都会放入session中,一会儿载入信息的load_msg()仅仅需要对session进行处理
  65. //载入Openfire中所有用户列表,剔除当前用户,和在初始化Openfire服务器时设定系统总管理用户admin
  66. DB db = DB.getInstance();
  67. List<Object[]> userlist = db
  68. .getBySql( "select username from ofuser where username !='"
  69. + session.get( "username") + "' and username!='admin'");
  70. String json = JSON.toJSONString(userlist);
  71. //将用户列表打印给前台
  72. printWriter.print(json);
  73. printWriter.flush();
  74. printWriter.close();
  75. }
  76. //载入用户信息
  77. public void load_msg() throws IOException, XMPPException {
  78. ServletActionContext.getResponse().setContentType(
  79. "text/html;charset=UTF-8"); //由于用户发送的信息可能有中文,所以必须要先设置编码
  80. PrintWriter printWriter = ServletActionContext.getResponse()
  81. .getWriter();
  82. //对session中的message_list进行处理构造JSON
  83. ArrayList<Message> message_list = (ArrayList<Message>) session
  84. .get( "message_list");
  85. ArrayList<HashMap<String, String>> message_json = new ArrayList<HashMap<String, String>>(); //这个message_json会被fastjson-1.1.24.jar转化成json字符串
  86. for ( int i = 0; i < message_list.size(); i++) {
  87. Message message = message_list.get(i);
  88. HashMap<String, String> one_message = new HashMap<String, String>();
  89. String from = message.getFrom().split( "@")[ 0]; //由于Openfire会自动在发信人后面带上一堆服务器、设备参数,因此,做此划分
  90. String time = get_realTime(( long) message.getProperty( "time")); //将long时间转现实时间输出到前台
  91. one_message.put( "from", from);
  92. one_message.put( "time", time);
  93. one_message.put( "body", message.getBody());
  94. message_json.add(one_message); //每一条消息都这样处理
  95. }
  96. //打印到前台
  97. String json = JSON.toJSONString(message_json);
  98. printWriter.print(json);
  99. printWriter.flush();
  100. printWriter.close();
  101. }
  102. // 信息发送
  103. public void submit_msg() throws IOException, XMPPException {
  104. PrintWriter printWriter = ServletActionContext.getResponse()
  105. .getWriter();
  106. Connection connection = (Connection) session.get( "connection"); //取连接
  107. //按Openfire要求的格式构造发信、收信人
  108. String from = (String) session.get( "username") + "@"
  109. + connection.getServiceName();
  110. String to = to_user + "@" + connection.getServiceName();
  111. //发信息核心代码
  112. ChatManager chatmanager = connection.getChatManager();
  113. Chat chat = chatmanager.createChat(to, null); //新建一个会话,指定收信人,不对收信人所回复的信息进行监听
  114. //因为在初始化的时候,已经对此连接收到信息都进行了监听,无须重复监听
  115. chat.sendMessage(msg); //对此会话进行发送
  116. //但同样要将发送的信息记录在session中的message_list里面,用户打印,这才符合现时主流的聊天工具一问一答的方式
  117. ArrayList<Message> message_list = (ArrayList<Message>) session
  118. .get( "message_list");
  119. Message message_send = new Message();
  120. message_send.setFrom(from);
  121. message_send.setTo(to);
  122. message_send.setBody(msg);
  123. message_send.setProperty( "time", System.currentTimeMillis());
  124. message_list.add(message_send);
  125. session.put( "message_list", message_list);
  126. //其实,消息发送,是没有任何东西交互给前台的,但必须打印一个空信息,才能保证ajax过程不出错。
  127. printWriter.print( "");
  128. printWriter.flush();
  129. printWriter.close();
  130. }
  131. public String getMsg() {
  132. return msg;
  133. }
  134. public void setMsg(String msg) {
  135. this.msg = msg;
  136. }
  137. public String getTo_user() {
  138. return to_user;
  139. }
  140. public void setTo_user(String to_user) {
  141. this.to_user = to_user;
  142. }
  143. }

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

  1. <%@ page language="java" contentType="text/html; charset=UTF-8"
  2. pageEncoding= "UTF-8"%>
  3. <%@ page isELIgnored="false"%>
  4. <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
  5. <html>
  6. <head>
  7. <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
  8. <script src="jquery-1.8.3.js" type="text/javascript"> </script>
  9. <title>聊天 </title>
  10. </head>
  11. <body>
  12. <table border="1" width="100%">
  13. <tr>
  14. <td colspan="2" width="100%">
  15. 欢迎,${sessionScope.username}, <a href="logout">登出 </a>
  16. </td>
  17. </tr>
  18. <tr height="150px" valign="top">
  19. <td id="msg_list"> </td>
  20. </tr>
  21. <tr>
  22. <td>
  23. <textarea id="msg" style="width: 100%; height: 50px"> </textarea>
  24. <select id="user_list"> </select>
  25. <button onclick="submit_msg()">发送! </button>
  26. </td>
  27. </tr>
  28. </table>
  29. </body>
  30. <script type="text/javascript">
  31. function init(){
  32. $.ajax({
  33. type : "post",
  34. url : "init",
  35. dataType : "json",
  36. success : function(data) {
  37. for( var i= 0;i<data.length;i++){
  38. $( "#user_list").append( "<option value='"+data[i]+ "'>"+data[i]+ "</option>");
  39. }
  40. },
  41. error : function() {
  42. alert( "出错了");
  43. }
  44. });
  45. setInterval( "load_msg()", 2000);
  46. }
  47. function load_msg(){
  48. var msg= "";
  49. $.ajax({
  50. type : "post",
  51. url : "load_msg",
  52. dataType : "json",
  53. success : function(data) {
  54. for( var i= 0;i<data.length;i++){
  55. msg=msg+
  56. "<p sytle='text-align:center'>"+data[i].time+ "</p>"+
  57. "<p>"+data[i].from+ ":"+data[i].body+ "</p>";
  58. $( "#msg_list").html(msg);
  59. }
  60. },
  61. error : function() {
  62. alert( "出错了");
  63. }
  64. });
  65. }
  66. function submit_msg() {
  67. var msg = $( "#msg").val();
  68. var to_user = $( "#user_list").val();
  69. $( "#msg").val( "");
  70. $.ajax({
  71. type : "post",
  72. url : "submit_msg",
  73. dataType : "text",
  74. data : {
  75. msg : msg,
  76. to_user : to_user
  77. },
  78. success : function(data) {
  79. },
  80. error : function() {
  81. alert( "出错了");
  82. }
  83. });
  84. }
  85. init();
  86. </script>
  87. </html>

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

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值