当前位置: 首页 > 工具软件 > ejabberd > 使用案例 >

Android 记录smack+xxmp+ejabberd的坑,如何信任所有证书链接跟发送语音图片消息

莘绍元
2023-12-01

一.首先服务端,需要去(https://www.process-one.net/en/ejabberd/downloads/)官网下载ejabberd,然后就是简单的安装啦。安装过程中设置domain域名跟管理员账号密码。
二.安装跟启动完成之后,启动ejabberd服务,会进入一个后台管理页面,然后你就可以进行创建用户啦。
三.android端,我这边采用的是一个xxmp的第三方框架smack
首先你需要引入依赖

	
    def smackVersion = '4.1.9'
    implementation "org.igniterealtime.smack:smack-android-extensions:$smackVersion"
    implementation "org.igniterealtime.smack:smack-experimental:$smackVersion"
    implementation "org.igniterealtime.smack:smack-tcp:$smackVersion"

接着来了,开始链接

 XMPPTCPConnectionConfiguration configuration = null;
        try {
            configuration = XMPPTCPConnectionConfiguration.builder()
                    .setHost("你的域名")
//                    .setPort(port)
//                    .setResource("phone")//代表链接设备的一个标识
                    .setServiceName("你开始设置的服务器名也就是domain")
                    .setDebuggerEnabled(DEBUG)//是否开启debug
                    .setConnectTimeout(5000)
                    .setSendPresence(true)//不发在线状态 等UI处理完以后再发
                    .setSecurityMode(ConnectionConfiguration.SecurityMode.disable)//跳过安全认证
                    .setCompressionEnabled(false)//是否压缩发送
                    .build();
          connection = new XMPPTCPConnection(configuration);
          connection.connect();//开始链接

上面就是链接的代码,你以为这里就可以链接上去了吗?太天真了,如果是openfire应该就是没问题了,但是ejabberd是不行的,ejabberd默认是开启starttls,如果你用以上连接,那么会报下面这样一个错误

 org.jivesoftware.smack.SmackException$SecurityRequiredByServerException: SSL/TLS required by server but disabled in client

在这里有两个解决办法,
(1)直接去服务端ejabberd那里找到conf文件夹下面的ejabberd.yml文件把starttls_required = true改为false
(2)使用下面代码,信任所有证书

在设置Configuration的时候加上这两个配置就可以搞定啦
 XMPPTCPConnectionConfiguration.builder()
 					.setSecurityMode(ConnectionConfiguration.SecurityMode.require)
 					.setHostnameVerifier(new HostnameVerifier() {
                        	@Override
                       		 public boolean verify(String s, SSLSession sslSession) {
                            		return true;
                       		 }
                    	})
                 .setCustomSSLContext(sslContext())
public SSLContext sslContext() {
        TrustManager[] trustAllCerts = new TrustManager[]{new X509TrustManager() {
            public X509Certificate[] getAcceptedIssuers() {
                return new X509Certificate[0];
            }

            @Override
            public void checkClientTrusted(X509Certificate[] certs, String authType) {
            }

            @Override
            public void checkServerTrusted(X509Certificate[] certs, String authType) {
            }
        }};

        SSLContext sc = null;
        try {
            sc = SSLContext.getInstance("TLS");
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        // trustAllCerts信任所有的证书
        try {
            sc.init(null, trustAllCerts, new SecureRandom());
        } catch (KeyManagementException e) {
            e.printStackTrace();
        }
        return sc;
    }

四:链接完成之后,就可以开始操作啦,首先是登陆
登陆的话可以直接调用拿到的XMPPTCPConnection实例,或者直接在配置上面设置setUsernameAndPassword("username","password")
如果你上面有设置setUsernameAndPassword那么你就直接调用XMPPTCPConnection.login()没有的话则可以调用XMPPTCPConnection.login(“username”,"password","resource")
登陆完成之后,就是获取联系人啊,发送消息这些,这些我看网上有很多类似的博客跟文章了,我就不再赘述了
五:这里有两个注意的点,
(1)如果你发现你不能注册的话,你需要在ejabberd.yml上的trusted_network: allow: loopback改为trusted_network: allow: all
(2)接着就是ejabberd的搜索好友问题,如果你不能搜索好友的话,你需要在ejabberd.yml的mod_vcard:{}这里改为mod_vcard:{search:true},其次还有代码跟openfire不一样,ejabberd使用的代码是

 UserSearchManager userSearchManager = new UserSearchManager(connection);
        try {
            Form searchForm = userSearchManager.getSearchForm("vjud." + connection.getServiceName());//还有这里,openfire是serach,ejabberd是vjud
            Form answer = searchForm.createAnswerForm();
            answer.setAnswer("user", “user”)//主要是这里不一样
            ReportedData reportedData = userSearchManager.getSearchResults(answer, "vjud." + connection.getServiceName());
                ArrayList<String> columnnames = new ArrayList<>();
            for (ReportedData.Column column : reportedData.getColumns()) {
                columnnames.add(column.getLabel());
            }
            for (ReportedData.Row row : reportedData.getRows()) {
                if (!row.getValues(columnnames.get(0)).isEmpty()) {
                    String s = row.getValues(columnnames.get(0)).get(0).toString();
                    list.add(new ContactVo(s));
                }
            }

六:发送图片和发送语音消息
网上查资料说的都是按照下面代码去实现,调用XMPPTCPConnection的发送文件去发送的

 FileTransferManager fileTransferManager = FileTransferManager.getInstanceFor(connection);
        OutgoingFileTransfer fileTransfer = fileTransferManager
                .createOutgoingFileTransfer("jid");//这里的jid需要在后面加一个/resource否则会报错
 fileTransfer.sendFile("file", "这是一个标识"); //这是发送文件的
然后接收文件是下面代码
  FileTransferListener fileTransferListener = new FileTransferListener(){
   @Override
        public void fileTransferRequest(FileTransferRequest request) {
            IncomingFileTransfer accept = request.accept();            
            try {
                if (file != null) {
                    accept.recieveFile(file);
                }
                System.out.println("接收文件=====");
            } catch (IOException e) {
                e.printStackTrace();
            } catch (SmackException e) {
                e.printStackTrace();
            }
            }
  }
 FileTransferManager fileTransferManager = FileTransferManager.getInstanceFor(connection);
            fileTransferManager.addFileTransferListener(fileTransferListener);

这是我网上找到smack发送图片信息跟文件信息的一些代码,然后这个对我好像没用,我都是报503错误,这个估计是服务器需要做什么配置,希望有知道的可以告诉我一下。
我来说说我后面实现的思路,我这边后面的实现思路是这样子的,先将语音跟图片转成Base64的字符串然自定义element然后通过文本消息发送过去,最后在接收消息的那边将Base64字符串转成文件,下面我贴一下自定义element的代码

class ImageElement : ExtensionElement {
    var imageUrl: String? = null

    constructor(imageUrl: String?) {
        this.imageUrl = imageUrl
    }

    constructor() {}

    override fun getNamespace(): String {
        return nameSpace
    }

    override fun getElementName(): String {
        return Companion.elementName
    }

    override fun toXML(): CharSequence {
        return "<" +
                Companion.elementName +
                " xmlns=\"http://mangga.me/protocol/image\"" +
                " type=\"image/jpeg\"" +
                ">" +
                imageUrl +
                "</" +
                Companion.elementName +
                ">"
    }

    companion object {
        const val elementName = "image"
    }
}
public class ImageExtensionProvider extends ExtensionElementProvider {

    @Override
    public Element parse(XmlPullParser parser, int initialDepth) throws XmlPullParserException, IOException, SmackException {
        boolean done = false;
        ImageElement element = new ImageElement();
        int eventType = parser.next();
        String name = parser.getName();
        if (eventType == XmlPullParser.TEXT) {
            element.setImageUrl( parser.getText());
        }
        return element;
    }
}

然后在发送文本消息的时候加上这句话
 msg.addExtension(new ImageElement(“iamgeurl”));
 还有配置添加拓展信息加上
   ProviderManager.addExtensionProvider(ImageElement.elementName, ImageElement.nameSpace, new ImageExtensionProvider());
   最后在接收信息这里拿到字符串去转文件
    ImageElement element1 = msg.getExtension(ImageElement.elementName, ImageElement.nameSpace);
    element.getImageUrl就可以拿到base64的字符串了

上面就是我发送语音消息跟图片消息的具体实现,如果有错的,希望大佬能指出来,还有那个sendFile报503的问题也希望有人可以告诉我一下,文章到此就告一段落啦,有可以问的,也可以跟我一起继续探讨,有什么好建议我也会听取各位大考的意见的,因为android跟ejabberd的相关资料比较少,所以就记录一下,希望可以帮到你们。

 类似资料: