一天时间用OpenFire打造自己的IM聊天工具

Home / Android MrLee 2015-1-7 43877

Openfire 采用Java开发,开源的实时协作(RTC)服务器基于XMPP(Jabber)协议。Openfire安装和使用都非常简单,并利用Web进行管理。单台服务器可支持上万并发用户。
好友界面

device-2015-01-07-130047    


功能界面

device-2015-01-07-115755    


聊天界面

device-2015-01-07-115905    


服务端可以用本服务器测试,地址:www.it72.com。如果想自己搭可在本博客其它XMPP文章找到相关搭建资料。
实现了用户注册,登录,添加好友,聊天核心功能。可正常聊天!
核心通讯代码:

package com.lee.xqq.control;
import java.io.ByteArrayInputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import org.jivesoftware.smack.Connection;
import org.jivesoftware.smack.ConnectionConfiguration;
import org.jivesoftware.smack.ConnectionCreationListener;
import org.jivesoftware.smack.ConnectionListener;
import org.jivesoftware.smack.PacketCollector;
import org.jivesoftware.smack.PacketListener;
import org.jivesoftware.smack.Roster;
import org.jivesoftware.smack.RosterEntry;
import org.jivesoftware.smack.RosterGroup;
import org.jivesoftware.smack.SmackConfiguration;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.filter.AndFilter;
import org.jivesoftware.smack.filter.PacketFilter;
import org.jivesoftware.smack.filter.PacketIDFilter;
import org.jivesoftware.smack.filter.PacketTypeFilter;
import org.jivesoftware.smack.packet.IQ;
import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.packet.Packet;
import org.jivesoftware.smack.packet.Presence;
import org.jivesoftware.smack.packet.Presence.Type;
import org.jivesoftware.smack.packet.Registration;
import org.jivesoftware.smack.provider.PrivacyProvider;
import org.jivesoftware.smack.provider.ProviderManager;
import org.jivesoftware.smack.util.StringUtils;
import org.jivesoftware.smackx.Form;
import org.jivesoftware.smackx.GroupChatInvitation;
import org.jivesoftware.smackx.PrivateDataManager;
import org.jivesoftware.smackx.ReportedData;
import org.jivesoftware.smackx.ReportedData.Row;
import org.jivesoftware.smackx.bytestreams.socks5.provider.BytestreamsProvider;
import org.jivesoftware.smackx.packet.ChatStateExtension;
import org.jivesoftware.smackx.packet.LastActivity;
import org.jivesoftware.smackx.packet.OfflineMessageInfo;
import org.jivesoftware.smackx.packet.OfflineMessageRequest;
import org.jivesoftware.smackx.packet.SharedGroupsInfo;
import org.jivesoftware.smackx.packet.VCard;
import org.jivesoftware.smackx.provider.AdHocCommandDataProvider;
import org.jivesoftware.smackx.provider.DataFormProvider;
import org.jivesoftware.smackx.provider.DelayInformationProvider;
import org.jivesoftware.smackx.provider.DiscoverInfoProvider;
import org.jivesoftware.smackx.provider.DiscoverItemsProvider;
import org.jivesoftware.smackx.provider.MUCAdminProvider;
import org.jivesoftware.smackx.provider.MUCOwnerProvider;
import org.jivesoftware.smackx.provider.MUCUserProvider;
import org.jivesoftware.smackx.provider.MessageEventProvider;
import org.jivesoftware.smackx.provider.MultipleAddressesProvider;
import org.jivesoftware.smackx.provider.RosterExchangeProvider;
import org.jivesoftware.smackx.provider.StreamInitiationProvider;
import org.jivesoftware.smackx.provider.VCardProvider;
import org.jivesoftware.smackx.provider.XHTMLExtensionProvider;
import org.jivesoftware.smackx.search.UserSearch;
import org.jivesoftware.smackx.search.UserSearchManager;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import com.lee.xqq.ChatActivity;
import com.lee.xqq.MainActivity;
import com.lee.xqq.SplashActivity;
import com.lee.xqq.base.BroadSender;
import com.lee.xqq.base.MLog;
import com.lee.xqq.item.XUserItem;
import com.lee.xqq.net.HttpResult;
import com.lee.xqq.net.HttpUtil;
import com.lee.xqq.util.CachedThreadPool;
/**
 * 所有的数据控制,逻辑可以在这里实现
 * 
 */
public class DataUtils implements ConnectionListener,
		ConnectionCreationListener, PacketListener {
	private static DataUtils instance;
	private Connection connection;
	private static String host;
	private static int port;
	private String user;
	private DataUtils() {
	}
	public void setHost(String host, int port) {
		DataUtils.host = host;
		DataUtils.port = port;
	}
	public static DataUtils getInstance() {
		if (instance == null)
			instance = new DataUtils();
		return instance;
	}
	public Connection getConnection() {
		return connection;
	}
	public String getUser() {
		return user;
	}
	public void requestBaidu() {
		CachedThreadPool.execute(new Runnable() {
			@Override
			public void run() {
				// TODO Auto-generated method stub
				HttpUtil httpUtil = HttpUtil.requestText(
						"http://www.baidu.com", "GET");
				HttpResult result = httpUtil.getResponse();
				if (result.getCode() == 200) {
					// 向界面发送接收广播
				}
			}
		});
	}
	public void initXMPP() {
		CachedThreadPool.execute(new Runnable() {
			@Override
			public void run() {
				// TODO Auto-generated method stub
				connectXmppServer();
			}
		});
	}
	private void connectXmppServer() {
		ConnectionConfiguration config = new ConnectionConfiguration(host, port);
		/** 是否启用安全验证 */
		config.setSASLAuthenticationEnabled(false);
		config.setReconnectionAllowed(true);// 断线重连
		/** 是否启用调试 */
		// config.setDebuggerEnabled(true);
		/** 创建connection链接 */
		try {
			Connection.addConnectionCreationListener(this);
			configure(ProviderManager.getInstance());
			connection = new XMPPConnection(config);
			/** 建立连接 */
			connection.connect();
			connection.addConnectionListener(this);
			PacketTypeFilter filter = new PacketTypeFilter(Packet.class);
			connection.addPacketListener(this, filter);
		} catch (XMPPException e) {
			e.printStackTrace();
			MLog.makeText("服务器连接失败");
		}
	}
	/**
	 * Android读不到/META-INF下的配置文件,需要手工配置。
	 * 
	 * @param pm
	 */
	private void configure(ProviderManager pm) {
		// Private Data Storage
		pm.addIQProvider("query", "jabber:iq:private",
				new PrivateDataManager.PrivateDataIQProvider());
		// Time
		try {
			pm.addIQProvider("query", "jabber:iq:time",
					Class.forName("org.jivesoftware.smackx.packet.Time"));
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
		// Roster Exchange
		pm.addExtensionProvider("x", "jabber:x:roster",
				new RosterExchangeProvider());
		// Message Events
		pm.addExtensionProvider("x", "jabber:x:event",
				new MessageEventProvider());
		// Chat State
		pm.addExtensionProvider("active",
				"http://jabber.org/protocol/chatstates",
				new ChatStateExtension.Provider());
		pm.addExtensionProvider("composing",
				"http://jabber.org/protocol/chatstates",
				new ChatStateExtension.Provider());
		pm.addExtensionProvider("paused",
				"http://jabber.org/protocol/chatstates",
				new ChatStateExtension.Provider());
		pm.addExtensionProvider("inactive",
				"http://jabber.org/protocol/chatstates",
				new ChatStateExtension.Provider());
		pm.addExtensionProvider("gone",
				"http://jabber.org/protocol/chatstates",
				new ChatStateExtension.Provider());
		// XHTML
		pm.addExtensionProvider("html", "http://jabber.org/protocol/xhtml-im",
				new XHTMLExtensionProvider());
		// Group Chat Invitations
		pm.addExtensionProvider("x", "jabber:x:conference",
				new GroupChatInvitation.Provider());
		// Service Discovery # Items
		pm.addIQProvider("query", "http://jabber.org/protocol/disco#items",
				new DiscoverItemsProvider());
		// Service Discovery # Info
		pm.addIQProvider("query", "http://jabber.org/protocol/disco#info",
				new DiscoverInfoProvider());
		// Data Forms
		pm.addExtensionProvider("x", "jabber:x:data", new DataFormProvider());
		// MUC User
		pm.addExtensionProvider("x", "http://jabber.org/protocol/muc#user",
				new MUCUserProvider());
		// MUC Admin
		pm.addIQProvider("query", "http://jabber.org/protocol/muc#admin",
				new MUCAdminProvider());
		// MUC Owner
		pm.addIQProvider("query", "http://jabber.org/protocol/muc#owner",
				new MUCOwnerProvider());
		// Delayed Delivery
		pm.addExtensionProvider("x", "jabber:x:delay",
				new DelayInformationProvider());
		// Version
		try {
			pm.addIQProvider("query", "jabber:iq:version",
					Class.forName("org.jivesoftware.smackx.packet.Version"));
		} catch (ClassNotFoundException e) {
			// Not sure what's happening here.
			e.printStackTrace();
		}
		// VCard
		pm.addIQProvider("vCard", "vcard-temp", new VCardProvider());
		// Offline Message Requests
		pm.addIQProvider("offline", "http://jabber.org/protocol/offline",
				new OfflineMessageRequest.Provider());
		// Offline Message Indicator
		pm.addExtensionProvider("offline",
				"http://jabber.org/protocol/offline",
				new OfflineMessageInfo.Provider());
		// Last Activity
		pm.addIQProvider("query", "jabber:iq:last", new LastActivity.Provider());
		// User Search
		pm.addIQProvider("query", "jabber:iq:search", new UserSearch.Provider());
		// SharedGroupsInfo
		pm.addIQProvider("sharedgroup",
				"http://www.jivesoftware.org/protocol/sharedgroup",
				new SharedGroupsInfo.Provider());
		// JEP-33: Extended Stanza Addressing
		pm.addExtensionProvider("addresses",
				"http://jabber.org/protocol/address",
				new MultipleAddressesProvider());
		// FileTransfer
		pm.addIQProvider("si", "http://jabber.org/protocol/si",
				new StreamInitiationProvider());
		pm.addIQProvider("query", "http://jabber.org/protocol/bytestreams",
				new BytestreamsProvider());
		// Privacy
		pm.addIQProvider("query", "jabber:iq:privacy", new PrivacyProvider());
		pm.addIQProvider("command", "http://jabber.org/protocol/commands",
				new AdHocCommandDataProvider());
		pm.addExtensionProvider("malformed-action",
				"http://jabber.org/protocol/commands",
				new AdHocCommandDataProvider.MalformedActionError());
		pm.addExtensionProvider("bad-locale",
				"http://jabber.org/protocol/commands",
				new AdHocCommandDataProvider.BadLocaleError());
		pm.addExtensionProvider("bad-payload",
				"http://jabber.org/protocol/commands",
				new AdHocCommandDataProvider.BadPayloadError());
		pm.addExtensionProvider("bad-sessionid",
				"http://jabber.org/protocol/commands",
				new AdHocCommandDataProvider.BadSessionIDError());
		pm.addExtensionProvider("session-expired",
				"http://jabber.org/protocol/commands",
				new AdHocCommandDataProvider.SessionExpiredError());
	}
	public boolean register(String account, String password) {
		Registration reg = new Registration();
		reg.setType(IQ.Type.SET);
		reg.setTo(connection.getServiceName());
		reg.setUsername(account);// 注意这里createAccount注册时,参数是username,不是jid,是“@”前面的部分。
		reg.setPassword(password);
		reg.addAttribute("android", "geolo_createUser_android");// 这边addAttribute不能为空,否则出错。所以做个标志是android手机创建的吧!!!!!
		PacketFilter filter = new AndFilter(new PacketIDFilter(
				reg.getPacketID()), new PacketTypeFilter(IQ.class));
		PacketCollector collector = connection.createPacketCollector(filter);
		connection.sendPacket(reg);
		IQ result = (IQ) collector.nextResult(SmackConfiguration
				.getPacketReplyTimeout());
		if (result == null) {
			MLog.makeText("连接超时");
		} else if (result.getType() == IQ.Type.RESULT) {
			MLog.makeText("注册成功");
			return true;
		} else {
			MLog.makeText(result.getError().toString());
		}
		return false;
	}
	public boolean login(String userName, String userPwd) {
		try {
			connection.login(userName, userPwd);
			user = userName + "@" + connection.getServiceName();
			return true;
		} catch (Exception e) {
			e.printStackTrace();
		}
		MLog.makeText("登录失败");
		return false;
	}
	public void changePassword(String pwd) {
		try {
			connection.getAccountManager().changePassword(pwd);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	/**
	 * 更改用户状态
	 */
	public void setPresence(int code) {
		Presence presence = null;
		switch (code) {
		case 0:
			presence = new Presence(Presence.Type.available);
			connection.sendPacket(presence);
			MLog.makeText("设置在线");
			break;
		case 1:
			presence = new Presence(Presence.Type.available);
			presence.setMode(Presence.Mode.chat);
			connection.sendPacket(presence);
			MLog.makeText("设置Q我吧");
			break;
		case 2:
			presence = new Presence(Presence.Type.available);
			presence.setMode(Presence.Mode.dnd);
			connection.sendPacket(presence);
			MLog.makeText("设置忙碌");
			break;
		case 3:
			presence = new Presence(Presence.Type.available);
			presence.setMode(Presence.Mode.away);
			connection.sendPacket(presence);
			MLog.makeText("设置离开");
			break;
		case 4:
			Roster roster = connection.getRoster();
			Collection<RosterEntry> entries = roster.getEntries();
			for (RosterEntry entry : entries) {
				presence = new Presence(Presence.Type.unavailable);
				presence.setPacketID(Packet.ID_NOT_AVAILABLE);
				presence.setFrom(connection.getUser());
				presence.setTo(entry.getUser());
				connection.sendPacket(presence);
			}
			// 向同一用户的其他客户端发送隐身状态
			presence = new Presence(Presence.Type.unavailable);
			presence.setPacketID(Packet.ID_NOT_AVAILABLE);
			presence.setFrom(connection.getUser());
			presence.setTo(StringUtils.parseBareAddress(connection.getUser()));
			connection.sendPacket(presence);
			MLog.makeText("设置隐身");
			break;
		case 5:
			presence = new Presence(Presence.Type.unavailable);
			connection.sendPacket(presence);
			MLog.makeText("设置离线");
			break;
		default:
			break;
		}
	}
	public void logoutAccount() {
		try {
			connection.getAccountManager().deleteAccount();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	public Collection<RosterGroup> getGroups() {
		return connection.getRoster().getGroups();
	}
	public List<RosterEntry> getEntriesByGroup(String groupName) {
		List<RosterEntry> Entrieslist = new ArrayList<RosterEntry>();
		RosterGroup rosterGroup = connection.getRoster().getGroup(groupName);
		Collection<RosterEntry> rosterEntry = rosterGroup.getEntries();
		Iterator<RosterEntry> i = rosterEntry.iterator();
		while (i.hasNext()) {
			Entrieslist.add(i.next());
		}
		return Entrieslist;
	}
	public Drawable getUserImage(String user) {
		ByteArrayInputStream bais = null;
		try {
			VCard vcard = new VCard();
			// 加入这句代码,解决No VCard for
			ProviderManager.getInstance().addIQProvider("vCard", "vcard-temp",
					new org.jivesoftware.smackx.provider.VCardProvider());
			if (user == "" || user == null || user.trim().length() <= 0) {
				return null;
			}
			vcard.load(connection, user + "@" + connection.getServiceName());
			if (vcard == null || vcard.getAvatar() == null)
				return null;
			bais = new ByteArrayInputStream(vcard.getAvatar());
			BitmapDrawable drawable = new BitmapDrawable(bais);
			bais.close();
			return drawable;
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}
	public List<RosterEntry> getAllEntries() {
		List<RosterEntry> Entrieslist = new ArrayList<RosterEntry>();
		Collection<RosterEntry> rosterEntry = connection.getRoster()
				.getEntries();
		Iterator<RosterEntry> i = rosterEntry.iterator();
		while (i.hasNext()) {
			Entrieslist.add(i.next());
		}
		return Entrieslist;
	}
	public boolean addGroup(String groupName) {
		try {
			connection.getRoster().createGroup(groupName);
			MLog.makeText("创建成功");
			return true;
		} catch (Exception e) {
			e.printStackTrace();
		}
		return false;
	}
	/**
	 * 添加好友 无分组
	 * 
	 * @param userName
	 * @param name
	 * @return
	 */
	public boolean addUser(String userName, String name) {
		try {
			String account = userName + "@" + connection.getServiceName();
			connection.getRoster().createEntry(account, name, null);
			return true;
		} catch (Exception e) {
			e.printStackTrace();
		}
		return false;
	}
	public boolean addUserWithName(String userName, String name,
			Presence.Type type) {
		return addUserWithName(userName, name, XMsgConst.X_DEFAULT_GROUP, type);
	}
	/**
	 * 添加好友 有分组
	 * 
	 * @param userName
	 * @param name
	 * @param groupName
	 * @return
	 */
	public boolean addUserWithName(String userName, String name,
			String groupName, Presence.Type type) {
		try {
			Presence subscription = new Presence(type);
			subscription.setTo(userName);
			userName += "@" + connection.getServiceName();
			connection.sendPacket(subscription);
			if (type == Type.subscribed)
				connection.getRoster().createEntry(userName, name,
						new String[] { groupName });
			return true;
		} catch (Exception e) {
			e.printStackTrace();
			return false;
		}
	}
	public boolean removeUser(String userName) {
		try {
			RosterEntry entry = null;
			if (userName.contains("@"))
				entry = connection.getRoster().getEntry(userName);
			else
				entry = connection.getRoster().getEntry(
						userName + "@" + connection.getServiceName());
			if (entry == null)
				entry = connection.getRoster().getEntry(userName);
			connection.getRoster().removeEntry(entry);
			return true;
		} catch (Exception e) {
			e.printStackTrace();
		}
		return false;
	}
	public List<XUserItem> searchUsers(String userName) {
		List<XUserItem> results = new ArrayList<XUserItem>();
		try {
			UserSearchManager usm = new UserSearchManager(connection);
			String serverDomain = "search." + connection.getServiceName();
			Form searchForm = usm.getSearchForm(serverDomain);
			Form answerForm = searchForm.createAnswerForm();
			answerForm.setAnswer("Username", true);
			answerForm.setAnswer("search", userName);
			ReportedData data = usm.getSearchResults(answerForm, serverDomain);
			Iterator<Row> it = data.getRows();
			Row row = null;
			XUserItem user = null;
			while (it.hasNext()) {
				row = it.next();
				String uName = row.getValues("Username").next().toString();
				String Name = row.getValues("Name").next().toString();
				String Email = row.getValues("Email").next().toString();
				user = new XUserItem(uName, Name, Email);
				results.add(user);
			}
			return results;
		} catch (XMPPException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return results;
	}
	public void sendMessage(String toJID, String message) {
		// TODO Auto-generated method stub
		final Message newMessage = new Message(toJID, Message.Type.chat);
		newMessage.setBody(message);
		connection.sendPacket(newMessage);
	}
	@Override
	public void connectionClosed() {
		// TODO Auto-generated method stub
		MLog.makeText("连接已关闭");
	}
	@Override
	public void connectionClosedOnError(Exception arg0) {
		// TODO Auto-generated method stub
		MLog.makeText("连接关闭异常");
		arg0.printStackTrace();
	}
	@Override
	public void reconnectingIn(int arg0) {
		// TODO Auto-generated method stub
		MLog.makeText("reconnectingIn:" + arg0);
	}
	@Override
	public void reconnectionFailed(Exception arg0) {
		// TODO Auto-generated method stub
		MLog.makeText("连接失败");
		arg0.printStackTrace();
	}
	@Override
	public void reconnectionSuccessful() {
		// TODO Auto-generated method stub
		MLog.makeText("重连成功");
	}
	@Override
	public void connectionCreated(Connection arg0) {
		// TODO Auto-generated method stub
		BroadSender.sendBroadcast(SplashActivity.class);
	}
	@Override
	public void processPacket(Packet packet) {
		// TODO Auto-generated method stub
		if (packet instanceof Message) {
			Message msg = (Message) packet;
			CacheUtils.getInstance().addMessage(msg);
			BroadSender.sendBroadcast(ChatActivity.class, "action",
					XMsgConst.X_PACKET_TYPE_MESSAGE);
		} else if (packet instanceof Presence) {
			Presence presence = (Presence) packet;
			String from = presence.getFrom();// 发送方
			String to = presence.getTo();// 接收方
			Type type = presence.getType();
			int typeValue = 0;
			if (type.equals(Presence.Type.subscribe)) {
				// 好友申请
				typeValue = XMsgConst.X_PRESENCE_SUBSCRIBE;
			} else if (type.equals(Presence.Type.subscribed)) {
				// 同意添加好友
				typeValue = XMsgConst.X_PRESENCE_SUBSCRIBED;
			} else if (type.equals(Presence.Type.unsubscribe)) {
				// 拒绝添加好友
				typeValue = XMsgConst.X_PRESENCE_UNSUBSCRIBE;
			} else if (type.equals(Presence.Type.unsubscribed)) {
				// 删除好友
				typeValue = XMsgConst.X_PRESENCE_UNSUBSCRIBED;
			} else if (type.equals(Presence.Type.unavailable)) {
				// 好友下线
				typeValue = XMsgConst.X_PRESENCE_UNAVAILABLE;
				from = processFrom(from);
				CacheUtils.getInstance().putPresence(from, presence);
			} else {
				// 好友上线
				typeValue = XMsgConst.X_PRESENCE_ONLINE;
				from = processFrom(from);
				CacheUtils.getInstance().putPresence(from, presence);
			}
			String[] params = new String[] { "action",
					XMsgConst.X_PACKET_TYPE_PRESENCE, "type",
					String.valueOf(typeValue), "from", from, "to", to };
			BroadSender.sendBroadcast(MainActivity.class, params);
		}
	}
	/**
	 * 去掉多余的服务器字符串
	 * 
	 * @param from
	 * @return
	 */
	private String processFrom(String from) {
		int endPos = from.lastIndexOf("/");
		if (endPos != -1)
			from = from.substring(0, endPos);
		return from;
	}
}


点击下载

本文链接:https://www.it72.com/636.htm

推荐阅读
最新回复 (25)
  • 太需要了,找了半天!终于找到最新可用的了!
  • anofapaqaciqo 2015-3-6
    引用 3
    喜欢
  • pcctxdy 2015-11-10
    引用 4
    楼主大哥,我想知道在哪个地方接受的消息,看见了回复一下,跪求了QQ138521351 谢谢哥
  • gghggh 2015-11-10
    引用 5
    代码里面不是已经很明显了么?590行代码!
     public void processPacket(Packet packet)
  • dailongli 2015-11-11
    引用 6
    哥,这个不是接收是否同意加好友吗,我的意思是,在哪里获得的我跟对方聊天的信息,对方发给我的信息我要如何获取呢
  • SINA1988289085 2015-11-11
    引用 7
    是这个里面,这个里面有分类你没看到么?
    if (packet instanceof Message)
    这句就表示是消息数据,然后放到缓存队列里面。
    else if (packet instanceof Presence)
    这个才是状态消息!
  • rabbitly520 2015-11-11
    引用 8
    不好意思哥,我瞎了。。。。
  • ucefisehubex 2015-11-11
    引用 9
    哥,在问一个,这个msg就是他发过来的信息吗?,还有,他的user怎么获取
  • RamiroAngas609 2015-11-11
    引用 10
    是的,消息。你还是仔细看下源码的。源码我编译可以成功运行的。 然后也很简单的几个类。一会儿就能看完了。
  • ihahulaxoyuk 2015-12-3
    引用 11
    楼主你好,我这个openfire为什么接收消息的时候进入不了if (packet instanceof Message) {这个判断里面而是进入 else if (packet instanceof Presence) {这个请求判断里面,跪求,真的很急!
  • cipo2008 2015-12-4
    引用 12
    那不就得了,你发的是Presence消息,也就是状态消息。并不是Message消息。
  • wyl 2016-4-5
    引用 13
    大哥,你是在太屌了吧,一天时间搭出这样牛逼的APP。。。我简直是蝼蚁般的存在。。膜拜,太特么神话了
  • Hector44C8666249 2016-11-25
    引用 14
    开始连接服务器失败。、。
  • aimsky0411 2016-11-26
    引用 15
    为什么在手机运行不行?
  • 电脑和手机处于同一个局域网就行了,ip地址为你电脑本机无限局域网ip地址,不要弄错。
  • andyching 2016-12-4
    引用 17
    楼主你好,请问一下你是如何保持用户登录状态的?我使用差不多同样的业务功能代码实现以后没办法实现用户登录状态保存,难道要轮询那个登录方法吗?还是api有另外的接口?
  • kelegames 2016-12-5
    引用 18
    这个具体你可能需要去查阅官方的文档了,我仅仅是实现了最基本的功能。理论上肯定是发心跳包,基本上IM都是这种形式嘛!
  • adora340 2016-12-6
    引用 19
    恩恩,这个问题暂时已经解决了,有一个新的问题就是我现在不知道为什么,两个用户A和B聊天,A发送消息以后马上会收到一条B的消息,但是消息内容是空的,发送人也是B,感觉上像是一条回执消息,但是从头至尾B一条消息也收不到。也不知道是哪里出了问题
  • cordmm001 2016-12-9
    引用 20
    唉,还是自己不细心,上一个问题是因为发送消息时用户名后面没有跟上主机名导致的。
  • qwe123 2017-3-23
    引用 21
    楼主大哥,为什么一直服务器连接失败,求解答,感激不尽,qq:776998428,谢谢
  • winnersj 2017-3-26
    引用 22
    如果你连不是我的很正常。因为我服务器已经关闭了这个服务。
  • nofly 2017-4-5
    引用 23
    大哥我手机和电脑同一个局域网为什么初始化界面还是一直显示连接服务器,登录不上呀
  • usiqogimev 2017-4-5
    引用 24
    楼主大哥,为什么我一直停留在初始化界面,一直在连接服务器进不去呢?看到求回复谢谢
  • 我的服务端已经关了,你自己搭建一个服务端吧!
  • go770567 2017-4-6
    引用 26
    要怎么搭建呢,有相关的文章吗?
返回