XMPP协议简介

XMPP协议(Extensible Messaging and PresenceProtocol,可扩展消息处理现场协议)是一种基于XML的协议,目的是为了解决及时通信标准而提出来的,最早是在Jabber上实现的。它继承了在XML环境中灵活的发展性。因此,基于XMPP的应用具有超强的可扩展性。并且XML很易穿过防火墙,所以用XMPP构建的应用不易受到防火墙的阻碍。利用XMPP作为通用的传输机制,不同组织内的不同应用都可以进行有效的通信。

这篇文章有基本的介绍,http://blog.csdn.net/xutaozero21/article/details/4873439 IM

Instant Messenger,及时通信软件,就是大家使用的QQ、MSN Messenger和Gtalk等等。其中Gtalk 就是基于XMPP 协议的一个实现,其他的则不是。当前IM 几乎作为每个上网者必然使用的工具,在国外的大型企业中有一些企业级的IM应用,但是其商业价值还没完全发挥出来。设想既然XMPP 协议是一个公开的协议,那么每个企业都可以利用它来开发适合本身企业工作,提高自身生产效率的IM;甚至,你还可以在网络游戏中集成这种通信软件,不但让你可以边游戏边聊天,也可以开发出适合游戏本身的IM 应用,比如说一些游戏关键场景提醒功能,团队语音交流等等都可以基于IM来实现。

本文主要讲解在android使用xmpp协议进行即时通信,所涉及3个主要的东西,它们是openfire、smack和spark,这个三个东东结合起来就是完整的xmpp IM实现,这里简单介绍一下这3个东东在下文的作用:

openfire主要是作为服务器,负责管理客户端的通信连接,以及提供客户端一些通信信息和连接信息。

Smack主要是xmpp协议的实现,提供了一套很好的api,所以下面操作xmpp都是通过使用smack的api来实现,当然因为是在android里,所以使用的是asmack这个包,里面方法跟smack包差不多。

Spark 是IM客户端的实现,其实就是使用了smack 的api实现的。

下图展示了三者之间的关系:(很明显这个图是偷别人的,具体是哪里我忘了,因为资料都是复制到文档后慢慢研究看的)

从图上可以了解到,client 端和server端都可以通过插件的方式来进行扩展,smack是二者传递数据的媒介。

配置openfire服务器

具体步骤请移步:http://javatech.blog.163.com/blog/static/1766322992010111725339587/

配置成功如果以后ip地址变了,那肯定又是开不了,解决办法请移步:http://blog.csdn.net/HappySheepherder/article/details/4707124

配置成功后,在服务器创建一个简单的用户来测试,然后安装spark,设置好服务器的ip与端口,使用刚才创建的用户登录,登录OK说明服务器成功搭建。

Android IM功能(因为是测试demo,因此界面超级简陋,代码都是给出重要的一部分,剩余的可以在最后下面项目查看)

配置要求

android 2.2、 asmack-jse.jar、myeclipse 连接服务器

在打开软件后会开始初始化,完成与openfire服务器的连接,设置一些配置

[代码]java代码:

static { XMPPConnection.DEBUG_ENABLED = true; final ConnectionConfiguration connectionConfig = new ConnectionConfiguration( host, 5222, ""); // Google talk // ConnectionConfiguration connectionConfig = new // ConnectionConfiguration( // "talk.google.com", 5222, "gmail.com"); // connectionConfig.setSASLAuthenticationEnabled(false); ActivityMain.connection = new XMPPConnection(connectionConfig); ActivityMain.connection.DEBUG_ENABLED = true; ProviderManager pm = ProviderManager.getInstance(); configure(pm); }

注册模块

注册有两种方法:一种是用createAccount ,不过我测试了一下发现不能创建用户,具体原因不详,下面介绍第二种。

如上图:注册成功后服务器将多了ggg用户。

具体实现如下:

[代码]java代码:

Registration reg = new Registration(); reg.setType(IQ.Type.SET); reg.setTo(ConnectionSingleton.getInstance().getServiceName()); reg.setUsername(username.getText().toString()); reg.setPassword(password.getText().toString()); reg.addAttribute("android", "geolo_createUser_android"); System.out.println("reg:" + reg); PacketFilter filter = new AndFilter(new PacketIDFilter(reg .getPacketID()), new PacketTypeFilter(IQ.class)); PacketCollector collector = ConnectionSingleton.getInstance() .createPacketCollector(filter); ConnectionSingleton.getInstance().sendPacket(reg); result = (IQ) collector.nextResult(SmackConfiguration .getPacketReplyTimeout()); // Stop queuing results collector.cancel(); if (result == null) { Toast.makeText(getApplicationContext(), "服, Toast.LENGTH_SHORT).show(); } else if (result.getType() == IQ.Type.ERROR) { if (result.getError().toString().equalsIgnoreCase( "conflict(409)")) { Toast.makeText(getApplicationContext(), "这, Toast.LENGTH_SHORT).show(); } else { Toast.makeText(getApplicationContext(), "注, Toast.LENGTH_SHORT).show(); } } else if (result.getType() == IQ.Type.RESULT) { Toast.makeText(getApplicationContext(), "恭, Toast.LENGTH_SHORT).show(); }

使用注册类,设置好注册的用户名密码和一些属性字段,直接设置包过滤,根据这个过滤创建一个结果集合,发送注册信息包,等待获取结果,剩余就是判断结果内容.

登录模块

登录比较简单

[代码]java代码:

ConnectionSingleton.getInstance().connect();// connect String account = etUsername.getText().toString(); String password = etPassword.getText().toString(); // 保存用户和密码ActivityLogin.util.saveString(ACCOUNT_KEY, account); ActivityLogin.util.saveString(PASSWORD_KEY, password); ConnectionSingleton.getInstance().login(account, password);// login // login success System.out.println("login success"); ActivityLogin.mCurrentAccount = account; System.out.println(ConnectionSingleton.getInstance() .getUser()); // 登录成功后发现在线状态Presence presence = new Presence(Presence.Type.available); ConnectionSingleton.getInstance().sendPacket(presence); // 开始主界面Intent intent = new Intent(ActivityLogin.this, ActivityMain.class); startActivity(intent);

获取联系人模块(ActivityMain 主界面)

获取联系人并将相关信息保存到一个list数组里,最后通知listview更新界面

[代码]java代码:

roster = ActivityMain.connection.getRoster();  public void updateRoster() { Collection<RosterEntry> entries = roster.getEntries(); for (RosterEntry entry : entries) { System.out.print(entry.getName() + " - " + entry.getUser() + " - " + entry.getType() + " - " + entry.getGroups().size()); Presence presence = roster.getPresence(entry.getUser()); System.out.println(" - " + presence.getStatus() + " - " + presence.getFrom()); User user = new User(); user.setName(entry.getName()); user.setUser(entry.getUser()); user.setType(entry.getType()); user.setSize(entry.getGroups().size()); user.setStatus(presence.getStatus()); user.setFrom(presence.getFrom()); userinfos.add(user); } rosterAdapter.notifyDataSetChanged(); } 

单人聊天模块

第一次修改:

在主界面点击选择一个用户,进入聊天Activity,ActivityChat先获取传过来的用户,创建聊天类并对该用户设置消息监听

[代码]java代码:

ChatManager chatmanager = ConnectionSingleton.getInstance() .getChatManager(); // get user Intent intent = getIntent(); String user = intent.getStringExtra("user"); System.out.println("user:" + user); // new a session newChat = chatmanager.createChat(user, null); // 监听聊天消息 chatmanager.addChatListener(new ChatManagerListenerEx()); // send message try { newChat.sendMessage("im bird man"); } catch (XMPPException e) { // TODO Auto-generated catch block e.printStackTrace(); }

监听类

[代码]java代码:

public class ChatManagerListenerEx implements ChatManagerListener { @Override public void chatCreated(Chat chat, boolean arg1) { // TODO Auto-generated method stub chat.addMessageListener(ml); } } public class MessageListenerEx implements MessageListener { @Override public void processMessage(Chat arg0, Message message) { String result = message.getFrom() + ":" + message.getBody(); System.out.println(result); android.os.Message msg = new android.os.Message(); msg.what = 0; Bundle bd = new Bundle(); bd.putString("msg", result); msg.setData(bd); handler.sendMessage(msg); } }

所获取到的消息都是通过handler来更新UI

[代码]java代码:

public Handler handler = new Handler() { @Override public void handleMessage(android.os.Message msg) { switch (msg.what) { case 0: { String result = msg.getData().getString("msg"); record.setText(record.getText() + "\n" + result); } break; default: break; } } };

aaa跟bbb 的聊天

第二次修改:

第一次的测试,发现如果多个人之间都成为好友,那么他们之间的聊天就出现了接收不到的信息,当然在跟spark测试时,spark可以收到android端的信息,不过android客户端是收到这个信息,不过却没有显示出来,具体原因还不太清楚。因此在第二次修改我改成监听所有聊天信息包,然后再分析包的归属,分发到对应的聊天窗口。

这里就是监听到包后打印的消息,打印出了jid和消息内容

[代码]java代码:

public class XmppMessageManager implements ChatManagerListener { private XMPPConnection _connection; private ChatManager manager = null; public void initialize(XMPPConnection connection) { _connection = connection; manager = _connection.getChatManager(); manager.addChatListener(this); } @Override public void chatCreated(Chat chat, boolean arg1) { // TODO Auto-generated method stub chat.addMessageListener(new MessageListener() { public void processMessage(Chat newchat, Message message) { // 若是聊天窗口存在,将消息转往目前窗口 // 若窗口不存在,创建新的窗口 System.out .println(message.getFrom() + ":" + message.getBody()); if (!ActivityMain.chats.containsKey(message.getFrom())) { ActivityMain.chats.put(message.getFrom(), newchat); } else { } } }); } }

主要就是重写了ChatManagerListener类的监听,分发处理暂时没有想好怎么写。接着在后台启动service就可以开始监听,行了第一次修改那些可以去掉了^0^。

多人聊天模块

也是在主界面的菜单进入ActivityMultiChat,该界面显示所创建的房间,点击就跳转到ActivityMultiRoom 。

获取所有房间比较简单,只需执行下面这段代码

[代码]java代码:

hostrooms = MultiUserChat.getHostedRooms(ActivityMain.connection,

"conference.zhanghaitao-pc");

跳转到后获取要加入的房间的jid,并创建监听。

[代码]java代码:

jid = getIntent().getStringExtra("jid");

  //后面服务名称必需是创建房间的那个服务 String multiUserRoom = jid; try { muc = new MultiUserChat(ActivityMain.connection, multiUserRoom); // 创建聊天室,进入房间后的nickname muc.join(ActivityLogin.mCurrentAccount); Log.v(TAG, "join success"); } catch (XMPPException e) { // TODO Auto-generated catch block e.printStackTrace(); } ChatPacketListener chatListener = new ChatPacketListener(muc); muc.addMessageListener(chatListener);

监听大概的流程跟单人聊天差不多,都是handler来操作。不过多人聊天是重写了PacketListener。具体如下(不过该方法是监听房间的信息,也就是说显示的是以房间为名字的消息):

[代码]java代码:

class ChatPacketListener implements PacketListener { private String _number; private Date _lastDate; private MultiUserChat _muc; private String _roomName; public ChatPacketListener(MultiUserChat muc) { _number = "0"; _lastDate = new Date(0); _muc = muc; _roomName = muc.getRoom(); } @Override public void processPacket(Packet packet) { Message message = (Message) packet; String from = message.getFrom(); if (message.getBody() != null) { DelayInformation inf = (DelayInformation) message.getExtension( "x", "jabber:x:delay"); Date sentDate; if (inf != null) { sentDate = inf.getStamp(); } else { sentDate = new Date(); } Log.w(TAG, "Receive old message: date=" + sentDate.toLocaleString() + " ; message=" + message.getBody()); android.os.Message msg = new android.os.Message(); msg.what = RECEIVE; Bundle bd = new Bundle(); bd.putString("from", from); bd.putString("body", message.getBody()); msg.setData(bd); handler.sendMessage(msg); } } }

下载模块

在主界面对着用户名长按,进入下载activity。进入activityFileTransfer,点击传输按钮即可将文件传输给之前选择的用户,当然这里做得比较简单,并没有拒绝功能,一旦发现有文件就接受。

[代码]java代码:

FileTransferManager transfer = new FileTransferManager( ActivityMain.connection); String destination = user; OutgoingFileTransfer out = transfer .createOutgoingFileTransfer(destination + "/Smack");

那用户是如何监听到有文件并且接受呢?在进入主界面的时候就已经开始了一个service(fileListenerService),该服务创建文件的监听类(XmppFileManager),监听类主要继承FileTransferListener 重写里面的fileTransferRequest方法。

[代码]java代码:

File saveTo; // set answerTo for replies and send() answerTo = request.getRequestor(); if (!Environment.MEDIA_MOUNTED.equals(Environment .getExternalStorageState())) { send("External Media not mounted read/write"); return; } else if (!landingDir.isDirectory()) { send("The directory " + landingDir.getAbsolutePath() + " is not a directory"); return; } saveTo = new File(landingDir, request.getFileName()); if (saveTo.exists()) { send("The file " + saveTo.getAbsolutePath() + " already exists"); // delete saveTo.delete(); // return; } IncomingFileTransfer transfer = request.accept(); send("File transfer: " + saveTo.getName() + " - " + request.getFileSize() / 1024 + " KB"); try { transfer.recieveFile(saveTo); send("File transfer: " + saveTo.getName() + " - " + transfer.getStatus()); double percents = 0.0; while (!transfer.isDone()) { if (transfer.getStatus().equals(Status.in_progress)) { percents = ((int) (transfer.getProgress() * 10000)) / 100.0; send("File transfer: " + saveTo.getName() + " - " + percents + "%"); } else if (transfer.getStatus().equals(Status.error)) { send(returnAndLogError(transfer)); return; } Thread.sleep(1000); } if (transfer.getStatus().equals(Status.complete)) { send("File transfer complete. File saved as " + saveTo.getAbsolutePath()); } else { send(returnAndLogError(transfer)); } } catch (Exception ex) { String message = "Cannot receive the file because an error occured during the process." + ex; Log.e(TAG, message, ex); send(message); }

网上说文件的传输遇到几个比较重要的问题,我也查看了很多资料(国内的基本是寥寥无几,对此我感到挺无奈的,只能看国外,这样每次我的英语水平都提高了太无奈了^0^)

android asmack 注册 登陆 聊天 多人聊天室 文件传输的更多相关文章

  1. Python3 网络通信 网络聊天室 文件传输

    Python3 网络通信 网络聊天室 文件传输 功能描述 该项目将实现一个文字和文件传输的客户端和服务器程序通信应用程序.它将传输和接收视频文件. 文本消息必须通过TCP与服务器通信,而客户端自己用U ...

  2. openfire Android 学习(四)----单人聊天和多人聊天(发送消息、接收消息)

    一.单人聊天 1)发送消息: 首先要获取一个聊天窗口,getConnection()为获取连接connection的方法,调用getFriendChat()获取 [java] view plainco ...

  3. java 通过TCP\UDP 协议实现多人聊天,点对点,文件传送-----分服务器端和客户端

    java 通过TCP\UDP 协议实现多人聊天,点对点,文件传送-----分服务器端和客户端 启动界面如下图: 首先启动服务器: 客户端登陆,登陆成功后为: 默认发送是全部用户,是多人发送. 当在边列 ...

  4. SignalR 实现Web多人聊天室

      ASP .NET SignalR 是一个ASP .NET 下的类库,可以在ASP .NET 的Web项目中实现实时通信.什么是实时通信的Web呢?就是让客户端(Web页面)和服务器端可以互相通知消 ...

  5. Openfire 单人聊天和多人聊天(发送消息、接收消息)

    Openfire 单人聊天和多人聊天(发送消息.接收消息) 一.单人聊天 1)发送消息: 首先要获取一个聊天窗口,getConnection()为获取连接connection的方法,调用getFrie ...

  6. Asp.net MVC + Signalr 实现多人聊天室

    Asp.net SignalR 简介: 首先简单介绍一下Signalr ,我也是刚接触,觉得挺好玩的,然后写了一个多人聊天室. Asp.net SignalR 是为Asp.net 开发人员提供的一个库 ...

  7. Python实现网络多人聊天室

    网络多人聊天室 文件结构: chatroom ├── client.py  # 客户端代码 ├── language.py  # 语言文件 ├── server.py  # 服务端代码 └── set ...

  8. Apache MiNa 实现多人聊天室

    Apache MiNa 实现多人聊天室 开发环境: System:Windows JavaSDK:1.6 IDE:eclipse.MyEclipse 6.6 开发依赖库: Jdk1.4+.mina-c ...

  9. Android基于XMPP Smack openfire 开发的聊天室

    Android基于XMPP Smack openfire 开发的聊天室(一)[会议服务.聊天室列表.加入] http://blog.csdn.net/lnb333666/article/details ...

随机推荐

  1. js鼠标,键盘,坐标轴事件

    鼠标按下事件,左键是0,滑轮是1,右键2 document.getElementById("box").onmousedown =function(e) { if (e.butto ...

  2. 洛谷P1930 亚瑟王的宫殿 Camelot

    P1930 亚瑟王的宫殿 Camelot 19通过 53提交 题目提供者JOHNKRAM 标签USACO 难度提高+/省选- 提交  讨论  题解 最新讨论 暂时没有讨论 题目描述 很久以前,亚瑟王和 ...

  3. mac 下安装nginx

    1,mac下的依赖: pcre-8.38.tar.gz nginx-1.4.7.tar.gz 2,解压pcre:进入器解压目录. EddydeMacBook-Pro:~ eddy$ cd /Users ...

  4. 操作笔记:linux下查看端口被占用

    [root@iZ945sgm0ugZ /]# lsof -i:8080 COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME java 1192 jet ...

  5. Code Sign error: No unexpired provisioning profiles found that contain any of the keychain's signing certificates

    最近离职了,刚好在离职之际有人叫我帮做个项目,简直了,没有mac电脑,没有真ji设备,简直了.接项目那哥们,暂且叫做J,大哥说我给你想办法,then,给借了个mac pro.刚拿到电脑真是喜出望外啊, ...

  6. MULTIBYTETOWIDECHAR的与WIDECHARTOMULTIBYTE的参数详解及相互转换

    第一个就是宽字符到多字节字符转换函数,函数原型如下: int WideCharToMultiByte( UINT CodePage, DWORD dwFlags, LPCWSTR lpWideChar ...

  7. PAT1069. The Black Hole of Numbers

    //这是到水题,之前因为四位数的原因一直不能A,看了别人的程序,才明白,不够四位的时候没考虑到,坑啊.....脸打肿 #include<cstdio>#include<algorit ...

  8. 监控系统 - check_mk_agent

    系统级监控 cpu (system, user) memory (cache, buffer, use)(MB) load (cpu core) diskspace (used, inode)(GB) ...

  9. 【MySQL】查询使用临时表

    MySQL查询产生临时表的分析 官网说明的地址:http://dev.mysql.com/doc/refman/5.5/en/internal-temporary-tables.html 参考:htt ...

  10. WeChat 6.3 wipe deleted chat messages as well as LINE 5.3 and above

    Let me show you the WeChat version first. It is 6.3. What will happen to WeChat deleted chat message ...