这几天由于要做一个解决方案,才来学XMPP方案,Android+Smack+Openfire方案,其实这已经是很成熟的方案了。但因为没做过,所以有必要总结一下:
第一,PC的问题,很容易,也很快。只要解决防火墙的问题就可以解决了。示例代码如下:
1、GlobalHelper.java类
import org.jivesoftware.smack.Connection; import org.jivesoftware.smack.ConnectionConfiguration; import org.jivesoftware.smack.PacketCollector; import org.jivesoftware.smack.XMPPConnection; import org.jivesoftware.smack.XMPPException; import org.jivesoftware.smack.packet.IQ; import org.jivesoftware.smack.packet.Registration; public class GlobalHelper { private static Connection con; private static String Service_Name="www.example.com"; public static Connection CreateConnection() { // Create the configuration for this new connection XMPPConnection.DEBUG_ENABLED=false; ConnectionConfiguration config = new ConnectionConfiguration("172.168.1.100", 5222); config.setCompressionEnabled(false); config.setSASLAuthenticationEnabled(false); System.out.println("T1"); if(con==null||con.isConnected()==false) { try { con=new XMPPConnection(config); System.out.println("T2"); con.connect(); System.out.println("T3"); return con; } catch(XMPPException e) { System.out.println("T4"); System.out.println(e.getMessage()); e.printStackTrace(); return null; } } return con; } }
2、testopenfire.java类
import java.io.File; import java.util.Collection; import org.jivesoftware.smack.Connection; import org.jivesoftware.smack.RosterEntry; import org.jivesoftware.smack.XMPPException; import org.jivesoftware.smack.packet.Message; import org.jivesoftware.smack.packet.Presence; import org.jivesoftware.smackx.filetransfer.FileTransfer; import org.jivesoftware.smackx.filetransfer.FileTransferManager; import org.jivesoftware.smackx.filetransfer.OutgoingFileTransfer; public class testopenfire { public static void main(String[] args) throws Exception { // TODO Auto-generated method stub String account="test"; String password="test"; String retStr=Register(account, password); if(retStr=="0") System.out.println("fail"); else System.out.println("success"); getFriends(GlobalHelper.CreateConnection()); sendMessage("test1@example.com","我的测试数据",GlobalHelper.CreateConnection()); SendFile(GlobalHelper.CreateConnection(),"test1@example.com"); } public static String Register(String account,String password) { Connection conn=GlobalHelper.CreateConnection(); if(conn==null) return "0"; if(conn.isConnected()==false) return "0"; try { System.out.println( "连接成功"); conn.login(account, password); System.out.println("登录成功"); return "1"; } catch(XMPPException e) { System.out.println("登录失败:"+e.getMessage()); return "0"; } } public static boolean sendMessage(String JID,String body,Connection connection) { System.out.println("发送消息开始。。。。。"); Message newmsg=new Message(); newmsg.setTo(JID); newmsg.setBody(body); newmsg.setType(Message.Type.normal); try { connection.sendPacket(newmsg); System.out.println("发送消息结束。。。。。成功!"); return true; } catch(Exception e) { System.out.println("发送消息结束。。。。。失败!"); return false; } } public static void getFriends(Connection connection) { System.out.println("获取好友列表"); Collection<RosterEntry> rosters=connection.getRoster().getEntries(); for(RosterEntry rosterEntry:rosters) { System.out.println("name:"+rosterEntry.getName()+",jid:"+rosterEntry.getUser()); } } public static void SendFile(Connection connection,String User) throws Exception { System.out.println("进入发送文件方法。。。"+User); Presence pre=connection.getRoster().getPresence(User); System.out.println(pre); if(pre.getType()!=Presence.Type.unavailable) { FileTransferManager manager=new FileTransferManager(connection); OutgoingFileTransfer transfer=manager.createOutgoingFileTransfer(pre.getFrom()); transfer.sendFile(new File("test.png"), "图片"); while(!transfer.isDone()) { if(transfer.getStatus()==FileTransfer.Status.in_progress) { System.out.println(transfer.getStatus()); System.out.println(transfer.getProgress()); System.out.println(transfer.isDone()); } } } } } 以上是PC上的问题,如果连不上,基本都是防火墙的事。只要连不上,把防火墙关了就行了。
第二,就是Android手机或模拟器的问题了。这个问题经常出现如下三类错误,但网上又找不到,问又没解决方案。下面就我碰到的提出解决方案:
前提条件:
PC(或笔记本,无线或有线都可以,全试过)->路由<-手机(无线)
具体方案:
1、PC防火墙的问题,关闭就行了。
2、解决网络是否通的问题。在手机或模拟器上安装FPing ,解决Ping我PC地址是否通的问题,下载Easy Telnet解决端口5222(默认)是否通的问题。如果不通就得检查一下哪里出了问题。另外得从PC解决Ping我的手机的问题。获取手机IP的问题有如下几种方法:
1)自己写一个程序,获取本地IP。
2)网上随便下载一个获取本地IP的软件,N多,非常容易。
3)从路由上获取IP
以上这些都比较容易从PC Ping手机IP。通过以上的方法解决手机和电脑网络没问题。
3、以上确认完后,然后就是Android的问题了。
1)确认你的程序没问题,不同Smack版本,写法都是对应版本的写法,最简单的办法都是下载版本文档中都有一个对应的示例代码,各种参数设置是正确的。
2)第1)种情况都没问题的情况下,出现如下错误:
XMPPError connecting to 172.168.1.100:5222.: remote-server-error(502) XMPPError connecting to 172.168.1.100:5222. -- caused by: java.net.SocketException: socket failed: EACCES (Permission denied)
这个问题的解决方案,在AndroidManifest.xml中加入如下代码就行了:
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/> <uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
前两条语句最重要。
3)就是解决Android的Bug问题(这是我个人的理解,已经N年了Google还是没解决这个问题),出现的问题如下:
java.lang.IllegalArgumentException: Can't initialize the configured debugger!
这个问题之前我有关在讲网络编程时好象讲过。只要将如下的语句加进来就可以了。具体如下:
StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder().detectDiskReads().detectDiskWrites().detectNetwork().penaltyLog().build()); StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder().detectLeakedSqlLiteObjects().detectLeakedClosableObjects() .penaltyLog().penaltyDeath().build());
这个问题已经不是问题了,做Android网络编程的人(游戏、网购等的)非常熟悉。但对于初学者,没有愿意帮介绍。我今天总结一下,给大家发出来。我查过一天,翻墙等都没解决这个问题。没办法,一直在试,当快抢劫信心时,发现了问题所在。
全部原码如下,不通再找我:
package com.example.xmppclient; import org.jivesoftware.smack.Connection; import org.jivesoftware.smack.ConnectionConfiguration; import org.jivesoftware.smack.XMPPConnection; import org.jivesoftware.smack.XMPPException; import android.app.Activity; import android.content.Intent; import android.os.Bundle; import android.os.StrictMode; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.EditText; import android.widget.Toast; public class LoginActivity extends Activity { private Button btnLogin; private Button btnExit; private EditText txtUserID; private EditText txtPWD; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder() .detectDiskReads().detectDiskWrites().detectNetwork() .penaltyLog().build()); StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder() .detectLeakedSqlLiteObjects().detectLeakedClosableObjects() .penaltyLog().penaltyDeath().build()); setContentView(R.layout.login); btnLogin=(Button)findViewById(R.id.btnLogin); btnExit=(Button)findViewById(R.id.btnExit); txtUserID=(EditText)findViewById(R.id.txtUserID); txtPWD=(EditText)findViewById(R.id.txtPWD); btnLogin.setOnClickListener(new OnClickListener(){ public void onClick(View v) { //XMPPConnection.DEBUG_ENABLED=true; try { ConnectionConfiguration config = new ConnectionConfiguration("172.168.1.100", 5222); config.setCompressionEnabled(false); config.setSASLAuthenticationEnabled(false); Connection conn2 = new XMPPConnection(config); conn2.connect(); String account=txtUserID.getText().toString(); String password=txtPWD.getText().toString(); conn2.login(account, password); //Toast.makeText(getApplicationContext(),"登录成功", 5000).show(); Intent intent=new Intent(LoginActivity.this,MainActivity.class); startActivity(intent); } catch(XMPPException e) { Toast.makeText(getApplicationContext(), e.getMessage(), 5000).show(); } catch(Exception ex) { Toast.makeText(getApplicationContext(), ex.getMessage(), 5000).show(); } }}); btnExit.setOnClickListener(new OnClickListener(){ public void onClick(View v) { LoginActivity.this.finish(); }}); } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.login, menu); return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { // Handle action bar item clicks here. The action bar will // automatically handle clicks on the Home/Up button, so long // as you specify a parent activity in AndroidManifest.xml. int id = item.getItemId(); if (id == R.id.action_settings) { return true; } return super.onOptionsItemSelected(item); } }
XML文件如下:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/LinearLayout1" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" tools:context="com.example.xmppclient.LoginActivity" > <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" > <TextView android:id="@+id/textView1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="TextView" /> <EditText android:id="@+id/txtUserID" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="1" android:text="test" android:ems="10" > <requestFocus /> </EditText> </LinearLayout> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" > <TextView android:id="@+id/textView2" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="TextView" /> <EditText android:id="@+id/txtPWD" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="1" android:ems="10" android:text="test" android:inputType="textPassword" /> </LinearLayout> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" > <Button android:id="@+id/btnLogin" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Login" /> <Button android:id="@+id/btnExit" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Exit" /> </LinearLayout> </LinearLayout>