`
天高云淡000
  • 浏览: 54998 次
  • 性别: Icon_minigender_1
  • 来自: 郑州
社区版块
存档分类
最新评论

简单的基于XMPP协议的即时通信的实现

阅读更多
学java有四个多月的时间了。目前研究到了通信这方面,也是java比较核心的一方面。和以往的学习方法一样,做了个简陋的即时通信来提高理解。

下面一步步的分析即时通信系统的实现。

[list]
  • 首先。也是最终重要的一步定通信协议。那么何为通信协议?我理解就是通信消息的格式,试想一下如果没有通信协议,所有的通信软件都能相互通信,那么QQ能与飞信用户聊天,魔兽世界能登录地下城。。~~~因此协议是重要的一步,而且是需要详细紧密考虑的一步,因为它涉及到通信系统的稳定性和可扩展性。。
  • 下面是xmpp协议的基本格式:
  •        <MyQQ><type>类型</type><body消息></body></MyQQ>
  •       当然协议是自定义的,只要符合这种格式,内容自定义了。
  • 由于目前做的只是简陋的通信系统,我的协议也是相当的简单:
  • 引用
    服务器发送协议
  • 登录确认<MyQQ><type>loginConfirm</type><consequence>1/0</consequence>
  • <ID>帐号</ID><password>密码</password><name>用户名</name></MyQQ>
  • 1代表登录成功 0代表帐号或密码错误
  • 注册确认<MyQQ><type>registerConfirm</type><consequence>1/0</consequence>
  • </MyQQ> 1代表注册成功 0代表帐号已存在注册失败
  • 发送在线客户作为好友<MyQQ><type>friendlist</type><IDS>
  • 帐号,帐号,帐号</IDS><names>用户名,用户名,用户名</names></MyQQ>
  • 客户端发送协议
  • 登录请求<MyQQ><type>login</type><ID>帐号</ID><password>密码</password></MyQQ>  服务器收到后分析发送确认消息
  • 注册请求<MyQQ><type>register</type><ID>帐号</ID><password>密码</password><name>用户名</name></MyQQ>  同上
  • 发送消息<MyQQ><type>chat</type><senderID>发送者帐号</senderID><recieverID>接受者帐号</recieverID><senderName>发送者用户名</senderName><recieverName>接受接用户名</recieverName><message>消息内容</message></MyQQ>  服务器分析对象转发
  • 发送震动窗体<MyQQ><type>shock</type><senderID>发送者帐号</senderID><recieverID>接受者帐号</recieverID><senderName>发送者用户名</senderName><recieverName>接受接用户名</recieverName></MyQQ>   服务器分析对象转发
  • 在协议中我们实现了登录,注册,登录验证,注册验证,发送好友列表,发送聊天消息,发送窗体震动的基本功能。下面我就根据这些简单协议来实现基本功能。
  • 然后,就是服务器和客户端的建立。
  • [*]
    /**
    [*]	 * 建立配置服务器的方法
    [*]	 * @param port 端口号
    [*]	 */
    [*]	public void setUp(int port){
    [*]		
    [*]		try {
    [*]			serverSocket = new ServerSocket(port);
    [*]			System.out.println("服务器已建立");
    [*]		} catch (IOException e) {
    [*]			// TODO Auto-generated catch block
    [*]			e.printStackTrace();
    [*]			System.out.println("端口已占用");
    [*]		}
    [*]	}
    [*]	/**
    [*]	 * 等待客户端接入的方法
    [*]	 */
    [*]	public void waitForClient(){
    [*]		try {
    [*]			sSocket = serverSocket.accept();
    [*]			System.out.println("有客户端进入");
    [*]			ServerClient serverClient = new ServerClient(sSocket);
    [*]		} catch (IOException e) {
    [*]			// TODO Auto-generated catch block
    [*]			e.printStackTrace();
    [*]		}
    [*]	}
  • 我想这点真的有些多此一举,不过要把这个简陋通信系统讲清楚还是提一下。
  • 这里值得注意的是sSocket = serverSocket.accept();这段代码。这句代码使服务器阻塞直到有客户机接入为止。这里有必要提一下通信中另一个常见的阻塞语句——ins.read(),从网络流中读取数据,一直阻塞知道流中有数据可以读取为止。
  • 再次,就是对接收到消息的分析也是通信中最麻烦最核心的内容。这里我们首先要做的是要分析,当服务器或客户端读取到哪个字节后算是读完了一整条消息(我认为这是XMPP协议的一个缺陷,当读完一整条消息后进行分析才不易出错。而当传输大文件是这是相当不明智的,而要转用字节流协议)。我们可以这样分析,每当服务器或客户端读取一个字节时,我们就把所有读到的字节转成字符串,看字符串是否是以</MyQQ>结尾(当然如果你自定义协议的末尾不是</MyQQ>就以自己的为结尾)。
  • [*]
    // 没有读到结束一直读取字节转为字符串
    [*]				while (!messageRead.endsWith("</MyQQ>")) {
    [*]					try {
    [*]						byteList.add((byte) read());
    [*]					} catch (IOException e1) {
    [*]						// TODO Auto-generated catch block
    [*]						// 读取出现异常关闭客户机对应的服务器
    [*]						e1.printStackTrace();
    [*]						close();
    [*]
    [*]						break;
    [*]					}
    [*]					messageRead = new String(listToArray(byteList));
    [*]									}
    [*]				System.out.println("服务器读到消息末尾跑出了循环" + messageRead);
    [*]				// 跑出循环服务器分析发送或转发数据 之后字符串赋值""
    [*]				MessageAnalyse.messageType(this);
  • 引用
    其中byteList是一个字节队列用来存放读到的字节,listToArray字节队列转成字节数组的方法,messageType是分析消息类型的方法
  • 之后要进行的就是要分析消息类型,消息的发送者,消息的接收者……为了得到这些内容,可以利用String的一个方法split(String regex, int limit) 来拆分字符串。
  • [*]
    /**
    [*]	 * 分割字符串的方法
    [*]	 * 
    [*]	 * @param message
    [*]	 *            要分割的字符串
    [*]	 * @param reference
    [*]	 *            分割的间隔字符串
    [*]	 * @return 返回分割出的内容
    [*]	 */
    [*]	public static String divideMessage(String message, String reference) {
    [*]		message = (message.split("<" + reference + ">", 0))[1];
    [*]		message = (message.split("</" + reference + ">", 0))[0];
    [*]		System.out.println("分割字符串的到的内容是" + message);
    [*]		return message;
    [*]
    [*]	}
  • 引用
    我把他们封装成了一个易于我使用的方法。
  • 经过对字符串的分析,系统就能知道,这是一条什么消息,是谁发来的消息,消息该发给谁……
  • 其四,就是客户端接收到消息后的响应,这个可以根据自己的设计来响应消息。比如好友上下线消息(界面上的好友列表),比如收到聊天消息(显示到界面)……由于这些不是通信系统中的主要内容这里就不做介绍。
  • 引用
    总之一句话,东西是死的,人是活的。程序变成什么样是自己设计的,关键是有思路。
  • 最后,是我在通信是遇到的问题。
  • [list]
  • [*]1、乱码问题 这一点在我的博客 http://ml5858258-sina-com.iteye.com/blog/958747有介绍。
  • [*]2、通信的的异常 这一点在我的博客 http://ml5858258-sina-com.iteye.com/blog/943799
  • [*]3、内存泄漏。导致此原因是由于,客户端关闭,而服务器端还在死循环的读取。不停地抛出socket reset的异常。看过问题2博客大家都会知道这是个什么异常。解决方法如下:
  • [*][*]
    try {
    [*][*]						byteList.add((byte) read());
    [*][*]					} catch (IOException e1) {
    [*][*]						// TODO Auto-generated catch block
    [*][*]						// 读取出现异常关闭客户机对应的服务器
    [*][*]						e1.printStackTrace();
    [*][*]						close();
    [*][*]											}
  • [*]
    引用
    close()是我自定的关闭连接的方法。这样就不会死循环抛异常了。
  • [/list]
  • [/list]由于界面过于丑陋,这里就不贴图了。等到完成的项目出炉后在来发表。

    下面是简陋通信的源码供广大爱好者们一共学习。
    分享到:
    评论
    1 楼 yanhua_it 2013-10-08  
    为二位

    相关推荐

    Global site tag (gtag.js) - Google Analytics