Smack 结合 Openfire服务器,建立IM通信,发送聊天消息

53 篇文章 0 订阅
49 篇文章 0 订阅

在文章开始,请你了解和熟悉openfire方面的相关知识,这样对你理解下面代码以及下面代码的用途有很好的了解。同时,你可能需要安装一个简单的CS聊天工具,来测试你的代码是否成功的在openfire服务器上建立会话链接,并成功的向在线用户发送聊天消息。

必须了解:http://www.cnblogs.com/hoojo/archive/2012/05/17/2506769.html

http://www.cnblogs.com/hoojo/archive/2012/05/13/2498151.html (非windows 系统)

可选:http://www.cnblogs.com/hoojo/archive/2012/05/17/2506845.html

http://www.cnblogs.com/hoojo/archive/2012/06/18/2553975.html

聊天软件Spark,用于测试聊天消息发送是否成功,下载地址:http://www.igniterealtime.org/downloads/download-landing.jsp?file=spark/spark_2_6_3.exe

然后你需要添加smack相关的jar包

smack.jar
smackx.jar

jar包下载地址:http://www.igniterealtime.org/downloads/download-landing.jsp?file=smack/smack_3_2_2.zip

代码中还用到了junit,junit jar下载地址:http://ebr.springsource.com/repository/app/bundle/version/download?name=com.springsource.org.junit&version=4.8.2&type=binary

下面开始代码部分

  1. package com.hoo.smack;
  2. import java.util.Collection;
  3. import java.util.Iterator;
  4. import javax.net.SocketFactory;
  5. import org.jivesoftware.smack.AccountManager;
  6. import org.jivesoftware.smack.Chat;
  7. import org.jivesoftware.smack.ChatManager;
  8. import org.jivesoftware.smack.Connection;
  9. import org.jivesoftware.smack.ConnectionConfiguration;
  10. import org.jivesoftware.smack.MessageListener;
  11. import org.jivesoftware.smack.Roster;
  12. import org.jivesoftware.smack.RosterEntry;
  13. import org.jivesoftware.smack.XMPPConnection;
  14. import org.jivesoftware.smack.XMPPException;
  15. import org.jivesoftware.smack.packet.Message;
  16. import org.jivesoftware.smack.packet.Presence;
  17. import org.jivesoftware.smack.packet.Session;
  18. import org.jivesoftware.smack.packet.Message.Type;
  19. import org.junit.After;
  20. import org.junit.Before;
  21. import org.junit.Test;
  22. /**
  23. * <b>function:</b> 利用Smack框架完成 XMPP 协议通信
  24. * @author hoojo
  25. * @createDate 2012-5-22 上午10:28:18
  26. * @file ConnectionServerTest.java
  27. * @package com.hoo.smack.conn
  28. * @project jwchat
  29. * @blog http://blog.csdn.net/IBM_hoojo
  30. * @email hoojo_@126.com
  31. * @version 1.0
  32. */
  33. public class SmackXMPPTest {
  34. private Connection connection;
  35. private ConnectionConfiguration config;
  36. /** openfire服务器address */
  37. private final static String server = "192.168.8.32";
  38. private final void fail(Object o) {
  39. if (o != null) {
  40. System.out.println(o);
  41. }
  42. }
  43. private final void fail(Object o, Object... args) {
  44. if (o != null && args != null && args.length > 0) {
  45. String s = o.toString();
  46. for (int i = 0; i < args.length; i++) {
  47. String item = args[i] == null ? "" : args[i].toString();
  48. if (s.contains("{" + i + "}")) {
  49. s = s.replace("{" + i + "}", item);
  50. } else {
  51. s += " " + item;
  52. }
  53. }
  54. System.out.println(s);
  55. }
  56. }
  57. /**
  58. * <b>function:</b> 初始Smack对openfire服务器链接的基本配置
  59. * @author hoojo
  60. * @createDate 2012-6-25 下午04:06:42
  61. */
  62. @Before
  63. public void init() {
  64. try {
  65. //connection = new XMPPConnection(server);
  66. //connection.connect();
  67. /** 5222是openfire服务器默认的通信端口,你可以登录http://192.168.8.32:9090/到管理员控制台查看客户端到服务器端口 */
  68. config = new ConnectionConfiguration(server, 5222);
  69. /** 是否启用压缩 */
  70. config.setCompressionEnabled(true);
  71. /** 是否启用安全验证 */
  72. config.setSASLAuthenticationEnabled(true);
  73. /** 是否启用调试 */
  74. config.setDebuggerEnabled(false);
  75. //config.setReconnectionAllowed(true);
  76. //config.setRosterLoadedAtLogin(true);
  77. /** 创建connection链接 */
  78. connection = new XMPPConnection(config);
  79. /** 建立连接 */
  80. connection.connect();
  81. } catch (XMPPException e) {
  82. e.printStackTrace();
  83. }
  84. fail(connection);
  85. fail(connection.getConnectionID());
  86. }
  87. @After
  88. public void destory() {
  89. if (connection != null) {
  90. connection.disconnect();
  91. connection = null;
  92. }
  93. }
  94. /**
  95. * <b>function:</b> ConnectionConfiguration 的基本配置相关信息
  96. * @author hoojo
  97. * @createDate 2012-6-25 下午04:11:25
  98. */
  99. @Test
  100. public void testConfig() {
  101. fail("PKCS11Library: " + config.getPKCS11Library());
  102. fail("ServiceName: {0}", config.getServiceName());
  103. // ssl证书密码
  104. fail("TruststorePassword: {0}", config.getTruststorePassword());
  105. fail("TruststorePath: {0}", config.getTruststorePath());
  106. fail("TruststoreType: {0}", config.getTruststoreType());
  107. SocketFactory socketFactory = config.getSocketFactory();
  108. fail("SocketFactory: {0}", socketFactory);
  109. /*try {
  110. fail("createSocket: {0}", socketFactory.createSocket("localhost", 3333));
  111. } catch (IOException e) {
  112. e.printStackTrace();
  113. }*/
  114. }
  115. /**
  116. * <b>function:</b> Connection 基本方法信息
  117. * @author hoojo
  118. * @createDate 2012-6-25 下午04:12:04
  119. */
  120. @Test
  121. public void testConnection() {
  122. /** 用户管理 */
  123. AccountManager accountManager = connection.getAccountManager();
  124. for (String attr : accountManager.getAccountAttributes()) {
  125. fail("AccountAttribute: {0}", attr);
  126. }
  127. fail("AccountInstructions: {0}", accountManager.getAccountInstructions());
  128. /** 是否链接 */
  129. fail("isConnected:", connection.isConnected());
  130. fail("isAnonymous:", connection.isAnonymous());
  131. /** 是否有权限 */
  132. fail("isAuthenticated:", connection.isAuthenticated());
  133. fail("isSecureConnection:", connection.isSecureConnection());
  134. /** 是否使用压缩 */
  135. fail("isUsingCompression:", connection.isUsingCompression());
  136. }
  137. /**
  138. * <b>function:</b> 用户管理器
  139. * @author hoojo
  140. * @createDate 2012-6-25 下午04:22:31
  141. */
  142. @Test
  143. public void testAccountManager() {
  144. AccountManager accountManager = connection.getAccountManager();
  145. for (String attr : accountManager.getAccountAttributes()) {
  146. fail("AccountAttribute: {0}", attr);
  147. }
  148. fail("AccountInstructions: {0}", accountManager.getAccountInstructions());
  149. fail("supportsAccountCreation: {0}", accountManager.supportsAccountCreation());
  150. try {
  151. /** 创建一个用户boy,密码为boy;你可以在管理员控制台页面http://192.168.8.32:9090/user-summary.jsp查看用户/组的相关信息,来查看是否成功创建用户 */
  152. accountManager.createAccount("boy", "boy");
  153. /** 修改密码 */
  154. accountManager.changePassword("abc");
  155. } catch (XMPPException e) {
  156. e.printStackTrace();
  157. }
  158. }
  159. @Test
  160. public void testUser() {
  161. try {
  162. /** 用户登陆,用户名、密码 */
  163. connection.login("hoojo", "hoojo");
  164. } catch (XMPPException e) {
  165. e.printStackTrace();
  166. }
  167. /** 获取当前登陆用户 */
  168. fail("User:", connection.getUser());
  169. /** 所有用户组 */
  170. Roster roster = connection.getRoster();
  171. /** 好友用户组,你可以用Spark添加用户好友,这样这里就可以查询到相关的数据 */
  172. Collection<RosterEntry> rosterEntiry = roster.getEntries();
  173. Iterator<RosterEntry> iter = rosterEntiry.iterator();
  174. while (iter.hasNext()) {
  175. RosterEntry entry = iter.next();
  176. fail("Groups: {0}, Name: {1}, Status: {2}, Type: {3}, User: {4}", entry.getGroups(), entry.getName(), entry.getStatus(), entry.getType(), entry);
  177. }
  178. fail("-------------------------------");
  179. /** 未处理、验证好友,添加过的好友,没有得到对方同意 */
  180. Collection<RosterEntry> unfiledEntries = roster.getUnfiledEntries();
  181. iter = unfiledEntries.iterator();
  182. while (iter.hasNext()) {
  183. RosterEntry entry = iter.next();
  184. fail("Groups: {0}, Name: {1}, Status: {2}, Type: {3}, User: {4}", entry.getGroups(), entry.getName(), entry.getStatus(), entry.getType(), entry);
  185. }
  186. }
  187. @Test
  188. @SuppressWarnings("static-access")
  189. public void testPacket() {
  190. try {
  191. connection.login("hoojo", "hoojo");
  192. } catch (XMPPException e) {
  193. e.printStackTrace();
  194. }
  195. //Packet packet = new Data(new DataPacketExtension("jojo@" + server, 2, "this is a message"));
  196. //connection.sendPacket(packet);
  197. /** 更改用户状态,available=true表示在线,false表示离线,status状态签名;当你登陆后,在Spark客户端软件中就可以看到你登陆的状态 */
  198. Presence presence = new Presence(Presence.Type.available);
  199. presence.setStatus("Q我吧");
  200. connection.sendPacket(presence);
  201. Session session = new Session();
  202. String sessid = session.nextID();
  203. connection.sendPacket(session);
  204. /**
  205. * 向jojo@192.168.8.32 发送聊天消息,此时你需要用Spark软件登陆jojo这个用户,
  206. * 这样代码就可以向jojo这个用户发送聊天消息,Spark登陆的jojo用户就可以接收到消息
  207. **/
  208. /** Type.chat 表示聊天,groupchat多人聊天,error错误,headline在线用户; */
  209. Message message = new Message("jojo@" + server, Type.chat);
  210. //Message message = new Message(sessid, Type.chat);
  211. message.setBody("h!~ jojo, I'am is hoojo!");
  212. connection.sendPacket(message);
  213. try {
  214. Thread.sleep(1);
  215. } catch (InterruptedException e) {
  216. e.printStackTrace();
  217. }
  218. }
  219. /**
  220. * <b>function:</b> 测试聊天消息管理类
  221. * @author hoojo
  222. * @createDate 2012-6-25 下午05:03:23
  223. */
  224. @Test
  225. public void testChatManager() {
  226. /** 设置状态 */
  227. try {
  228. connection.login("hoojo", "hoojo");
  229. } catch (XMPPException e) {
  230. e.printStackTrace();
  231. }
  232. /** 设置状态 */
  233. Presence presence = new Presence(Presence.Type.available);
  234. presence.setStatus("Q我吧");
  235. connection.sendPacket(presence);
  236. /** 获取当前登陆用户的聊天管理器 */
  237. ChatManager chatManager = connection.getChatManager();
  238. /** 为指定用户创建一个chat,MyMessageListeners用于监听对方发过来的消息 */
  239. Chat chat = chatManager.createChat("jojo@" + server, new MyMessageListeners());
  240. try {
  241. /** 发送消息 */
  242. chat.sendMessage("h!~ jojo……");
  243. /** 用message对象发送消息 */
  244. Message message = new Message();
  245. message.setBody("message");
  246. message.setProperty("color", "red");
  247. chat.sendMessage(message);
  248. } catch (XMPPException e) {
  249. e.printStackTrace();
  250. }
  251. try {
  252. Thread.sleep(1000 * 1000);
  253. } catch (InterruptedException e) {
  254. e.printStackTrace();
  255. }
  256. }
  257. /**
  258. * <b>function:</b> 消息监听器,用户监听对方发送的消息,也可以想对方发送消息
  259. * @author hoojo
  260. * @createDate 2012-6-25 下午05:05:31
  261. * @file SmackXMPPTest.java
  262. * @package com.hoo.smack
  263. * @project jwchat
  264. * @blog http://blog.csdn.net/IBM_hoojo
  265. * @email hoojo_@126.com
  266. * @version 1.0
  267. */
  268. class MyMessageListeners implements MessageListener {
  269. public void processMessage(Chat chat, Message message) {
  270. try {
  271. /** 发送消息 */
  272. chat.sendMessage("dingding……" + message.getBody());
  273. } catch (XMPPException e) {
  274. e.printStackTrace();
  275. }
  276. /** 接收消息 */
  277. fail("From: {0}, To: {1}, Type: {2}, Sub: {3}", message.getFrom(), message.getTo(), message.getType(), message.toXML());
  278. /*Collection<Body> bodys = message.getBodies();
  279. for (Body body : bodys) {
  280. fail("bodies[{0}]", body.getMessage());
  281. }
  282. //fail(message.getLanguage());
  283. //fail(message.getThread());
  284. //fail(message.getXmlns());*/
  285. fail("body: ", message.getBody());
  286. }
  287. }
  288. }
package com.hoo.smack;

import java.util.Collection;
import java.util.Iterator;
import javax.net.SocketFactory;
import org.jivesoftware.smack.AccountManager;
import org.jivesoftware.smack.Chat;
import org.jivesoftware.smack.ChatManager;
import org.jivesoftware.smack.Connection;
import org.jivesoftware.smack.ConnectionConfiguration;
import org.jivesoftware.smack.MessageListener;
import org.jivesoftware.smack.Roster;
import org.jivesoftware.smack.RosterEntry;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.packet.Presence;
import org.jivesoftware.smack.packet.Session;
import org.jivesoftware.smack.packet.Message.Type;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

/**
 * <b>function:</b> 利用Smack框架完成 XMPP 协议通信
 * @author hoojo
 * @createDate 2012-5-22 上午10:28:18
 * @file ConnectionServerTest.java
 * @package com.hoo.smack.conn
 * @project jwchat
 * @blog http://blog.csdn.net/IBM_hoojo
 * @email hoojo_@126.com
 * @version 1.0
 */
public class SmackXMPPTest {

	private Connection connection;
	private ConnectionConfiguration config;
	/** openfire服务器address */
	private final static String server = "192.168.8.32";
	
	private final void fail(Object o) {
		if (o != null) {
			System.out.println(o);
		}
	}
	
	private final void fail(Object o, Object... args) {
		if (o != null && args != null && args.length > 0) {
			String s = o.toString();
			for (int i = 0; i < args.length; i++) {
				String item = args[i] == null ? "" : args[i].toString();
				if (s.contains("{" + i + "}")) {
					s = s.replace("{" + i + "}", item);
				} else {
					s += " " + item;
				}
			}
			System.out.println(s);
		}
	}
	
	/**
	 * <b>function:</b> 初始Smack对openfire服务器链接的基本配置
	 * @author hoojo
	 * @createDate 2012-6-25 下午04:06:42
	 */
	@Before
	public void init() {
		try {
			//connection = new XMPPConnection(server);
			//connection.connect();
			
            /** 5222是openfire服务器默认的通信端口,你可以登录http://192.168.8.32:9090/到管理员控制台查看客户端到服务器端口 */
			config = new ConnectionConfiguration(server, 5222);
			
			/** 是否启用压缩 */ 
			config.setCompressionEnabled(true);
			/** 是否启用安全验证 */
			config.setSASLAuthenticationEnabled(true);
			/** 是否启用调试 */
			config.setDebuggerEnabled(false);
			//config.setReconnectionAllowed(true);
			//config.setRosterLoadedAtLogin(true);
			
			/** 创建connection链接 */
			connection = new XMPPConnection(config);
			/** 建立连接 */
			connection.connect();
		} catch (XMPPException e) {
			e.printStackTrace();
		}
		fail(connection);
		fail(connection.getConnectionID());
	}
	
	@After
	public void destory() {
		if (connection != null) {
			connection.disconnect();
			connection = null;
		}
	}
	
	/**
	 * <b>function:</b> ConnectionConfiguration 的基本配置相关信息
	 * @author hoojo
	 * @createDate 2012-6-25 下午04:11:25
	 */
	@Test
	public void testConfig() {
		fail("PKCS11Library: " + config.getPKCS11Library());
		fail("ServiceName: {0}", config.getServiceName());
		// ssl证书密码
		fail("TruststorePassword: {0}", config.getTruststorePassword());
		fail("TruststorePath: {0}", config.getTruststorePath());
		fail("TruststoreType: {0}", config.getTruststoreType());
		
		SocketFactory socketFactory = config.getSocketFactory();
		fail("SocketFactory: {0}", socketFactory);
		/*try {
			fail("createSocket: {0}", socketFactory.createSocket("localhost", 3333));
		} catch (IOException e) {
			e.printStackTrace();
		}*/
	}
	
	/**
	 * <b>function:</b> Connection 基本方法信息
	 * @author hoojo
	 * @createDate 2012-6-25 下午04:12:04
	 */
	@Test
	public void testConnection() {
		/** 用户管理 */
		AccountManager accountManager = connection.getAccountManager();
		for (String attr : accountManager.getAccountAttributes()) {
			fail("AccountAttribute: {0}", attr);
		}
		fail("AccountInstructions: {0}", accountManager.getAccountInstructions());
		/** 是否链接 */
		fail("isConnected:", connection.isConnected());
		fail("isAnonymous:", connection.isAnonymous());
		/** 是否有权限 */
		fail("isAuthenticated:", connection.isAuthenticated());
		fail("isSecureConnection:", connection.isSecureConnection());
		/** 是否使用压缩 */
		fail("isUsingCompression:", connection.isUsingCompression());
	}
	
	/**
	 * <b>function:</b> 用户管理器
	 * @author hoojo
	 * @createDate 2012-6-25 下午04:22:31
	 */
	@Test
	public void testAccountManager() {
		AccountManager accountManager = connection.getAccountManager();
		for (String attr : accountManager.getAccountAttributes()) {
			fail("AccountAttribute: {0}", attr);
		}
		fail("AccountInstructions: {0}", accountManager.getAccountInstructions());
		
		fail("supportsAccountCreation: {0}", accountManager.supportsAccountCreation());
		try {
            /** 创建一个用户boy,密码为boy;你可以在管理员控制台页面http://192.168.8.32:9090/user-summary.jsp查看用户/组的相关信息,来查看是否成功创建用户 */
			accountManager.createAccount("boy", "boy");
			/** 修改密码 */
			accountManager.changePassword("abc");
		} catch (XMPPException e) {
			e.printStackTrace();
		}
	}
	
	@Test
	public void testUser() {
		try {
			/** 用户登陆,用户名、密码 */
			connection.login("hoojo", "hoojo");
		} catch (XMPPException e) {
			e.printStackTrace();
		}
		/** 获取当前登陆用户 */
		fail("User:", connection.getUser());
		
		/** 所有用户组 */
		Roster roster = connection.getRoster();
		
        /** 好友用户组,你可以用Spark添加用户好友,这样这里就可以查询到相关的数据 */
		Collection<RosterEntry> rosterEntiry = roster.getEntries();
		Iterator<RosterEntry> iter = rosterEntiry.iterator();
		while (iter.hasNext()) {
			RosterEntry entry = iter.next();
			fail("Groups: {0}, Name: {1}, Status: {2}, Type: {3}, User: {4}", entry.getGroups(), entry.getName(), entry.getStatus(), entry.getType(), entry);
		}
		
		fail("-------------------------------");
        /** 未处理、验证好友,添加过的好友,没有得到对方同意 */
		Collection<RosterEntry> unfiledEntries = roster.getUnfiledEntries();
		iter = unfiledEntries.iterator();
		while (iter.hasNext()) {
			RosterEntry entry = iter.next();
			fail("Groups: {0}, Name: {1}, Status: {2}, Type: {3}, User: {4}", entry.getGroups(), entry.getName(), entry.getStatus(), entry.getType(), entry);
		}
	}
	
	@Test
	@SuppressWarnings("static-access")
	public void testPacket() {
		try {
			connection.login("hoojo", "hoojo");
		} catch (XMPPException e) {
			e.printStackTrace();
		}
		
		//Packet packet = new Data(new DataPacketExtension("jojo@" + server, 2, "this is a message"));
		//connection.sendPacket(packet);
		
        /** 更改用户状态,available=true表示在线,false表示离线,status状态签名;当你登陆后,在Spark客户端软件中就可以看到你登陆的状态 */
		Presence presence = new Presence(Presence.Type.available);
        presence.setStatus("Q我吧");
		connection.sendPacket(presence);
		
		Session session = new Session();
		String sessid = session.nextID();
		connection.sendPacket(session);
		/** 
		 * 向jojo@192.168.8.32 发送聊天消息,此时你需要用Spark软件登陆jojo这个用户,
		 * 这样代码就可以向jojo这个用户发送聊天消息,Spark登陆的jojo用户就可以接收到消息 
		 **/
        /** Type.chat 表示聊天,groupchat多人聊天,error错误,headline在线用户; */
		Message message = new Message("jojo@" + server, Type.chat);
		//Message message = new Message(sessid, Type.chat);
		message.setBody("h!~ jojo, I'am is hoojo!");
		connection.sendPacket(message);
		
		try {
			Thread.sleep(1);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * <b>function:</b> 测试聊天消息管理类
	 * @author hoojo
	 * @createDate 2012-6-25 下午05:03:23
	 */
	@Test
	public void testChatManager() {
		/** 设置状态 */
		try {
			connection.login("hoojo", "hoojo");
		} catch (XMPPException e) {
			e.printStackTrace();
		}
		
		/** 设置状态 */
		Presence presence = new Presence(Presence.Type.available);
        presence.setStatus("Q我吧");
		connection.sendPacket(presence);
		
		/** 获取当前登陆用户的聊天管理器 */
		ChatManager chatManager = connection.getChatManager();
		/** 为指定用户创建一个chat,MyMessageListeners用于监听对方发过来的消息  */
		Chat chat = chatManager.createChat("jojo@" + server, new MyMessageListeners());
		try {
			/** 发送消息 */
			chat.sendMessage("h!~ jojo……");
			
			/** 用message对象发送消息 */
			Message message = new Message();
			message.setBody("message");
			message.setProperty("color", "red");
			chat.sendMessage(message);
		} catch (XMPPException e) {
			e.printStackTrace();
		}
		try {
			Thread.sleep(1000 * 1000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * <b>function:</b> 消息监听器,用户监听对方发送的消息,也可以想对方发送消息
	 * @author hoojo
	 * @createDate 2012-6-25 下午05:05:31
	 * @file SmackXMPPTest.java
	 * @package com.hoo.smack
	 * @project jwchat
	 * @blog http://blog.csdn.net/IBM_hoojo
	 * @email hoojo_@126.com
	 * @version 1.0
	 */
	class MyMessageListeners implements MessageListener {
		public void processMessage(Chat chat, Message message) {
			try {
				/** 发送消息 */
				chat.sendMessage("dingding……" + message.getBody());
			} catch (XMPPException e) {
				e.printStackTrace();
			}
			/** 接收消息 */
			fail("From: {0}, To: {1}, Type: {2}, Sub: {3}", message.getFrom(), message.getTo(), message.getType(), message.toXML());
			/*Collection<Body> bodys =  message.getBodies();
			for (Body body : bodys) {
				fail("bodies[{0}]", body.getMessage());
			}
			//fail(message.getLanguage());
			//fail(message.getThread());
			//fail(message.getXmlns());*/
			fail("body: ", message.getBody());
		}
	}
}

好了,这些都是smack的基本功能,还有更多的东西需要研究,下次有机会再分享!

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值