XMPP 服务器 Openfire 的 Emoji 支持问题(进行部分修改)

openfire+asmack中文api+androidDemo 一套即时通讯demo 立即下载

当前最新版3.9.3已经可以支持Emoji 

----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------


在为领航信息开发 eMessage 支持的时候,我们曾使用著名的开源 XMPP 服务器软件 Openfire。但在使用中遇到了几个问题,并通过修改源代码将这些问题解决掉了。接下来的几篇文章,我会介绍一下这些问题并讲述是如何解决掉的。

先介绍一下背景。XMPP 是一个开放的即时通讯协议,非常不错,有很多开源软件实现了 XMPP 协议,Openfire 算是实现得比较全的,而且安装配置比较容易。其他比较流行的开源 XMPP 服务器还有 Tigase 和 ejabberd。我们现在已经切换到了 ejabberd 上,毕竟 WhatsApp 最初也使用的是 ejabberd 嘛,呵呵。读者如果有兴趣,我也可以跟大家讲讲这几个 XMPP 服务器的区别和潜在问题。

问题描述

Emoji 现在基本已经成为一种工业事实标准,最早在日本流行,由日本的 DoKoMo 等运营商支持,后来苹果在 iOS 中支持了这一技术,最终变得全世界流行起来。Emoji 使用了一些 UNICODE 字符集中尚未定义的码位来表示一个个的表情图案。和一般的字体不同,应用程序在遇到这些字符的时候,需要使用位图来显示对应的表情图标,而不是直接使用字体中定义的字型来显示(当然,也可以用字体来显示 Emoji 字符,比如某些 Linux 控制台就可以显示部分 Emoji 字符)。前者可以是彩色的,而后者只能是某个特定的颜色。所以,要显示 Emoji 就要在应用程序中做扩展,在输出这些特定字符的时候做特殊处理,将其用对应的表情位图显示出来。让客户端应用支持 Emoji 并不是很难的工作,Android 的短信应用不支持 Emoji,但支持预先定义的特定字符序列来表示特定的表情,比如将“:)”显示成笑脸。处理这种字符序列的方法和处理 Emoji 表情的方法本质上一样的。

在使用 Openfire 作为 XMPP 服务器,将表示 Emoji 的 UNICODE 字符发送给其他用户的时候,就会出现问题。问题的原因在于 Emoji 使用的 UNICODE 字符集码位尚未被 UNICODE 标准化组织标准化,而 Openfire 会将 Emoji 字符看成是不符合标准的字符而直接忽略掉或者干脆断开客户端的连接。因此,要解决这个问题,其实相当容易,通过搜索引擎可以很快找到了解决方案。

解决方案

在 Openfire 3.8.2 版本源代码术中,修改 openfire_src/src/java/org/jivesoftware/openfire/net 目录下的 MXParser.java 文件的最后一个函数:
[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1.      /** 
  2.       * Makes sure that each individual character is a valid XML character. 
  3.       *  
  4.       * Note that when MXParser is being modified to handle multibyte chars correctly, this method needs to change (as 
  5.       * then, there are more codepoints to check). 
  6.       */  
  7.  @Override  
  8.  protected char more() throws IOException, XmlPullParserException {  
  9.      final char codePoint  = super.more(); // note - this does NOT return a codepoint now, but simply a (single byte) char  
  10. er!  
  11.              if ((codePoint == 0x0) ||  // 0x0 is not allowed, but flash clients insist on sending this as the very first   
  12. racter of a stream. We should stop allowing this codepoint after the first byte has been parsed.  
  13.                              (codePoint == 0x9) ||  
  14.                              (codePoint == 0xA) ||  
  15.                              (codePoint == 0xD) ||  
  16.                              ((codePoint >= 0x20) && (codePoint <= 0xD7FF)) ||  
  17.                              ((codePoint >= 0xE000) && (codePoint <= 0xFFFD))) {  
  18.                       return codePoint;  
  19.              throw new XmlPullParserException("Illegal XML character: " + Integer.parseInt(codePoint+"", 16));  
  20.  }  
先看看这个函数的作用。如注释所言,这个函数用来判定特定字符是否是一个合法的 XML 字符。XML 一般要求按照 UTF8 编码的方式存储和传输字符,在程序处理时,会直接转换成 UNICODE 的 UCS 形式,这样便于程序做处理。

在 Linux 控制台上运行 $ man utf8 命令,你可以迅速知悉 UNICODE 码位范围以及和 UTF-8 编码之间的对应关系:
[plain]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. The following byte sequences are used to represent a character.  The sequence to be used depends on the  UCS  code  
  2. number of the character:  
  3.   
  4. 0x00000000 - 0x0000007F:  
  5.     0xxxxxxx  
  6.   
  7. 0x00000080 - 0x000007FF:  
  8.     110xxxxx 10xxxxxx  
  9.   
  10. 0x00000800 - 0x0000FFFF:  
  11.     1110xxxx 10xxxxxx 10xxxxxx  
  12.   
  13. 0x00010000 - 0x001FFFFF:  
  14.     11110xxx 10xxxxxx 10xxxxxx 10xxxxxx  
  15.   
  16. 0x00200000 - 0x03FFFFFF:  
  17.     111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx  
  18.   
  19. 0x04000000 - 0x7FFFFFFF:  
  20.     1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx  

从上面的对应关系就可以知悉,UNICODE 可能的码位(code point 或者 code number)最大可以到 0x7FFFFFFF!而 Openfire 判断是否为合法 XML 字符的范围只到 0xFFFD!显然,不能显示正确处理 Emoji 字符的原因是 Openfire 将用来表示 Emoji 字符的那些码位范围给当成非法 XML 字符了。一种比较简单粗暴的修改办法是:
[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1.     /** 
  2.      * Makes sure that each individual character is a valid XML character. 
  3.      *  
  4.      * Note that when MXParser is being modified to handle multibyte chars correctly, this method needs to change (as 
  5.      * then, there are more codepoints to check). 
  6.      */  
  7. @Override  
  8. protected char more() throws IOException, XmlPullParserException {  
  9.     final char codePoint  = super.more(); // note - this does NOT return a codepoint now, but simply a (single byte) char  
  10. r!  
  11.             if ((codePoint == 0x0) ||  // 0x0 is not allowed, but flash clients insist on sending this as the very first   
  12. acter of a stream. We should stop allowing this codepoint after the first byte has been parsed.  
  13.                             (codePoint == 0x9) ||  
  14.                             (codePoint == 0xA) ||  
  15.                             (codePoint == 0xD) ||  
  16.                             ((codePoint >= 0x20) && (codePoint <= 0xFFFD)) ||  
  17.                             ((codePoint >= 0x10000) && (codePoint <= 0x10FFFF))) {  
  18.                     return codePoint;  
  19.             }  
  20.   
  21.             throw new XmlPullParserException("Illegal XML character: " + Integer.parseInt(codePoint+"", 16));  
  22. }  
但马上有高手指出,这个方法太粗暴了,可能带来一些安全隐患(参见: http://community.igniterealtime.org/thread/48846),所以更加正确的方法是:



@Override
	protected char more() throws IOException, XmlPullParserException {


		final char codePoint = super.more(); // note - this does NOT return a codepoint now, but simply a (double byte) character!


		boolean validCodepoint = false;


		boolean isLowSurrogate = Character.isLowSurrogate(codePoint);


		if ((codePoint == 0x0)|| // 0x0 is not allowed, but flash clients insist on sending this as the very first character of a stream. We should stop allowing this codepoint after the first byte has been parsed.
		    (codePoint == 0x9) ||
			(codePoint == 0xA) ||
			(codePoint == 0xD)|| 
			((codePoint >= 0x20) && (codePoint <= 0xD7FF)) ||
			((codePoint >= 0xE000) && (codePoint <= 0xFFFD))) {
			validCodepoint = true;
		}


		else if (highSurrogateSeen) {
			if (isLowSurrogate) {


				validCodepoint = true;
			} else {


				throw new XmlPullParserException(
						"High surrogate followed by non low surrogate '0x"
								+ String.format("%x", (int) codePoint) + "'");


			}
		}


		else if (isLowSurrogate) {
			throw new XmlPullParserException("Low surrogate '0x "+ String.format("%x", (int) codePoint)+ " without preceeding high surrogate");
		}


		else if (Character.isHighSurrogate(codePoint)) {
			highSurrogateSeen = true;// Return here so that highSurrogateSeen is not reset
			return codePoint;
		}


		// Always reset high surrogate seen
		highSurrogateSeen = false;
		if (validCodepoint)
			return codePoint;


		throw new XmlPullParserException("Illegal XML character '0x"+ String.format("%x", (int) codePoint) + "'");


	}



有了上述修改,你的 Openfire 服务器就可以正确处理 Emoji 了。Openfire 最新的版本是 3.9.1,估计已经修改掉这个问题了吧,但本人未确认。

在 MySQL 中存储 Emoji 字符

没想到,为了在 MySQL 数据库中保存 Emoji 字符,需要使用 MySQL 5.5 以上版本引入的 utf8mb4 的字符集。原来 MySQL 的 utf8 字符集只支持编码为一个、两个字节或者三个字节的情形,也就是 UNICODE  UCS 编码范围为 0 到 0xFFFD 这种情形。要支持超过这个范围的 UTF8 编码字符,就需要使用 MySQL 5.5 中引入的 utf8mb4 字符集。从名字中可以看出,这个字符集专门用来支持单个 UNICODE 的 UTF8 编码长度达到四个字节的情形。当然,超过也许也是可以的。至于为什么不能直接用 utf8 编码来兼容这些字符,我就不知道了,也许是历史原因吧。

大家可以用“mysql utf8mb4”为关键词搜索一下就知道如何设置/配置 mysql 来支持这个字符集了。但是,要让 openfire 能够和 mysql 正确打交道,还需要升级一下 openfire 使用的 JAVA mysql 数据库连接器(connnector),要升级到最新的版本。否则,如果使用老的 mysql 数据库连接器,会出现无法理解 utf8mb4 字符集的情形。

吐槽一下,我实在是不能理解为什么 MySQL 要引入 utf8mb4 这个字符集,难不成将来还需要引入 utf8mb6、utf8mb8 这样的字符集不成?哪位大侠可以帮我解答这个疑惑?

后记

俺是不太喜欢 JAVA 语言的,至今未能使用 JAVA 语言完整编写过一个程序,这实在是本人二十多年码农生涯的一大遗憾,但打打补丁这事儿还是可以做做的。下一篇文章给大家介绍一个针对 Openfire 服务器在集群环境下处理 SOCKS5 代理的问题,就修改了几行代码,但解决了一个大问题。

敬请期待。

android openfire xmpp smack即时通讯,包括:聊天发生文字,表情,图片,语音等 android openfire xmpp smack即时通讯,包括:聊天发生文字,表情,图片,语音。添加删除好友,消息提醒,群聊,邀请好友进入房间,聊天记录保存等等。界面美观。最后说明:此demo是在网上一个叫EIM企信通的demo基础上进行完善修改得来的成果,添加了好多功能,大家有兴趣的配置好openfire设置好IP就可以使用,完美运行,欢迎大家下载,不理解的留言问我,本人QQ:375642003 立即下载

相关推荐

交错并联图腾柱PFC变换器仿真设计(Simulink仿真实现)

内容概要:本文档围绕“交错并联图腾柱PFC变换器”的Simulink仿真实现展开,系统阐述了该高效率功率因数校正(PFC)拓扑的建模、控制策略设计与仿真验证全过程。作为适用于中大功率场合的先进PFC结构,交错并联图腾柱通过降低输入电流纹波、提高功率密度和系统效率,展现出显著优势。文档详细介绍了系统总体架构、主电路参数设计、双闭环控制策略(包括电压外环与电流内环)、PWM调制技术、软启动机制以及仿真结果分析等内容,帮助读者掌握从理论分析到仿真实践的完整开发流程。同时,文档还引入与其他主流变换器拓扑(如LLC、DAB、Vienna整流器等)的对比分析与协同应用场景探讨,体现了其在现代电力电子系统集成化发展中的重要地位。; 适合人群:具备电力电子技术基础知识、熟悉Simulink/Matlab仿真环境,从事电源系统设计、新能源变换器研发等相关工作的研究生、工程师及科研人员(建议具有1年以上相关实践经验)。; 使用场景及目标:① 深入理解交错并联图腾柱PFC变换器的工作原理与高性能控制方法;② 掌握基于Simulink的PFC系统建模与闭环控制器设计流程;③ 通过仿真数据分析系统动态响应与稳态性能,优化控制参数;④ 为后续硬件原型开发提供可靠的理论依据与前期验证手段。; 阅读建议:建议结合文档配套的Simulink仿真模型进行逐步操作,重点聚焦于双闭环控制结构设计与PI参数整定环节,深入理解各模块间的耦合关系。同时,可参考文中提及的Vienna、LLC等其他典型拓扑,开展横向对比研究,全面把握不同类型PFC架构的技术特点与适用边界。对于初学者,建议先夯实Buck/Boost电路基础与PWM控制原理,再进入本主题的深入学习。

openfire+mysql存入emoji表情失败,表情变成问号

最近在做即时聊天的Android端和openfire服务器开发(...),在做emoji表情开发时,在线表情是可以正常收发的,而离线表情在接收后,会被转成问号!因为在线消息是不存入数据库中的,而离线消息在接收方接收前,会被添加到openfire提供的ofOffline这张表中,那这问题就一定出在数据库编码格式上啦! 其实,在建表的时候,我是考虑到emoji这个字符问题的,因此数据库创建时的字符集...

superyu1992的专栏 1509

火山引擎实时对话AI实战:5分钟搞定WebRTC+大模型+ASR/TTS全链路集成

本文详细介绍了如何利用火山引擎的实时对话AI解决方案,快速集成WebRTC、大模型、ASR和TTS服务,构建低延迟、高保真的实时语音对话应用。通过实战代码示例,从环境准备、音频流管理到全链路串联与性能优化,为开发者提供了一套端到端的集成指南,适用于智能客服、在线教育等互动场景。

weixin_29051245的博客 1129

JDK源码解析(4)——Byte、Character

一、Byte Byte与Integer基本大同小异,也会在加载时生成-128到127的初始值,所以不多赘述。 二、Character Character会在加载时生成127+1的初始值,所以不多赘述。 isValidCodePoint // 判断指定的代码点是否为从 0x0000 到 0x10FFFF 范围之内的有效 Unicode 代码点值。 public static boolean isValidCodePoint(int codePoint) { // Optimized for.

Leeand的博客 435

浅谈Mike 3D中制作垂向水温分层边界

本次以水温分层为例进行讲解,可应用于水库的水温分层模拟中

zhuzhujiayoua的博客 1415

Unity VR 零基础开发之 Pico4 MR

2、弹出弹窗后,点击Android选项,然后再点击Switch Platform按钮切换成安卓平台。3、切换完成后Android选项后面会显示unity图标。

十画的博客 5106

XMPP 服务器 Openfire 的 Emoji 支持问题

在为领航信息开发 eMessage 支持的时候,我们曾使用著名的开源 XMPP 服务器软件 Openfire。但在使用中遇到了几个问题,并通过修改源代码将这些问题解决掉了。接下来的几篇文章,我会介绍一些这些问题并讲述是如何解决掉的。 先介绍一下背景。XMPP 是一个开放的即时通讯协议,非常不错,有很多开源软件实现了 XMPP 协议,Openfire 算是实现得比较全的。其他比较流行的还有 Tig

u014542833的专栏 3138

Rust 与 Golang 深度对决:从语法到应用场景的全方位解析

Rust 定义函数以fn关键字开头,参数需明确指定类型,返回值类型紧跟在箭头->之后。// 定义函数add,接收两个i32类型参数a和b,返回i32类型结果,功能为两数相加a + bRust 和 Golang 各有千秋,在不同领域展现独特优势。Rust 凭借强大的内存安全与高性能,在系统编程、网络编程以及对内存安全和性能要求高的场景中表现突出;Golang 则以简洁语法和出色并发性能,在云计算、分布式系统、Web 开发等领域占据一席之地。

muyouking 1770

OPENFIRE支持EMOJI表情

用openfire搭建XMPP服务器时,客户端一发送emoji表情文字就会掉线,错误日志片段如下   2013.05.21 12:57:44  org.jivesoftware.openfire.nio.ConnectionHandler – Closing connection due to error while    processing message: 这里

renfengmei技术博客 1909

OData 请求里的 Nextlink 实战:在 ABAP Client Proxy 中优雅处理长列表分页

本文介绍了OData v4标准中的@odata.nextLink分页机制及其在SAP ABAP中的实践应用。重点解析了该机制通过服务端提供的完整URL实现分页获取,客户端只需循环处理直到该字段消失的特点。文章提供了清晰的ABAP代码模板,展示如何通过Remote Client Proxy实现分页数据获取,并强调了仅支持OData v4和远程消费的限制条件。通过HR员工数据聚合的实际案例,说明了流式分页处理的最佳实践,包括服务端过滤、稳定排序和内存控制等关键技术要点,为ABAP开发者提供了可直接落地的解决方案

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 272

【DRAM存储器五十一】LPDDR5介绍--DFE与Pre-Emphasis

简述LPDDR5的DFE和预加重应用设置

highman110的博客 707

解决openfire中发送某些特殊字符会断开xmpp连接的问题

在openfire中,如果发送某些特殊的字符(例如一些表情符合),会断开xmpp的连接,经查,是由以下的代码问题引起的: src\java\org\jivesoftware\openfire\net\MXParser.java protected char more() throws IOException, XmlPullParserException { fi

曾健生的专栏 5403

《AgentScope-Java 深入浅出教程》第2章 环境搭建与第一个智能体

创建// 【导入说明】/*** 时间相关工具类* 工具类是普通的 Java 类,通过注解声明为工具。* 智能体可以根据用户需求自动调用这些工具。*/// 【工具定义】获取当前时间@Tool(name = "get_current_time", // 工具名称:LLM 看到的名称description = "获取指定时区的当前时间" // 工具描述:帮助 LLM 理解何时使用// 【参数定义】// @ToolParam 的 name 属性是必需的!

步子哥的博客 417

OPENFIRE支持EMOJI

org.jivesoftware.openfire.nio.ConnectionHandler – Closing connection due to error while processing message: 这里是 emoji表情 java.lang.NumberFormatException: For input string: “?” at java.lang.Numb

renfengmei技术博客 1138

Outlook Express 6 (OE6) for Win7/Win8/Win10

英文版 Outlook Express6.0, 兼容Win7/Win10/WinXP,解压就可用。

迈瑞BS系列全自动生化分析仪LIS协议接口手册(内含HL7和ASTM)

迈瑞BS系列全自动生化分析仪LIS协议接口手册,内含HL7和ASTM)

OPENFIRE支持EMOJI(OPENFIRE 3.8.1版本)

  用openfire搭建XMPP服务器时,客户端一发送emoji表情文字就会掉线,错误日志片段如下   2013.05.21 12:57:44 org.jivesoftware.openfire.nio.ConnectionHandler – Closing connection due to error while    processing message: &lt;messa...

linyu19872008的专栏 193

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

Android禁止发送表情图片

话不多说,直接上代码 public static boolean containsEmoji(String source) { int len = source.length(); for (int i = 0; i char codePoint = source.charAt(i); if (!isEmojiCharacter(codePoint)) { // 如果不能匹配,则该字符

灿烂的西瓜皮 631

android 蓝牙通讯demo

android 下蓝牙通讯demo,实现了发现蓝牙,使能蓝牙,通过蓝牙发送消息。

emoji表情处理的坑

现在比较流行的几种emoji表情的处理方式:1.该数据库编码格式,2.通过第三方插件emoji-java 但是我这边不能该数据库格式,但是使用emoji-java插件发现,有很多最新的字符不支持(这是比较大的坑) 最后找到了编码格式转换存储的方式: /** 将emoji表情符号转换可读表情符号 */ public static String decodeJava(String text) ...

aguai0的专栏 2032

关于MYSQL保存emoji表情及openfire处理办法

在开发移动应用的时候,用户在输入了emoji表情后,系统异常,我是做服务器端的,发现错误如下: java.sql.SQLException: Incorrect string value: '\xF0\x9F\x92\x94' for column 'name' at row 1 at com.mysql.jdbc.SQLError.createSQLException(SQLError.j

fly_yuge的博客 932

[转]关于 the char 'ox1e' after '' is not a valid xml character

说明:项目里webservice借口上传数据出异常 日志打印报错类似于标题那种。开始的解决方法是JS正则去掉非汉字、数字、英文、标点符号。后来上网查了下找到下述解决方法。 --------------------分割线--------------------------------- 以下内容转自:http://hi.baidu.com/godtrip/item/90c1a4eec422eb0...

weixin_30751947的博客 689

使用Strophe连接xmpp,轻松构建web即时聊天工具

使用Strophe连接xmpp轻松构建web即时聊天工具

牛儿吃草的博客 2万+

iOS中Emoji表情的判断

问题 服务器端不支持Emoji表情,因此客户端在上传用户输入时,不能包含Emoji表情。 解决方案 在客户端发送请求前,判断用户输入中是否含有表情,如果含有表情,则提示用户重新输入。这个过程关键是如何判断字符串中是否含有Emoji表情。要判断是否含有Emoji表情,必须先了解什么是Emoji。 Emoji 是一套起源于日本的12x12像素表情符号,由栗田穣崇(Shigeta

IT_ZGC的博客 1008
上一篇: mysql导入导出数据,表结构等
下一篇: javamail linux 下报错javax.mail.MessagingException: 500 Error: bad syntax
ldwtill
博客等级 码龄18年 49粉丝 84原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值