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

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

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

[list]
[*][b]首先[/b]。也是最终重要的一步定通信协议。那么何为通信协议?我理解就是通信消息的格式,试想一下如果没有通信协议,所有的通信软件都能相互通信,那么QQ能与飞信用户聊天,魔兽世界能登录地下城。。~~~因此协议是重要的一步,而且是需要详细紧密考虑的一步,因为它涉及到通信系统的稳定性和可扩展性。。
[*]下面是xmpp协议的基本格式:
[*] <MyQQ><type>类型</type><body消息></body></MyQQ>
[*] 当然协议是自定义的,只要符合这种格式,内容自定义了。
[*]由于目前做的只是简陋的通信系统,我的协议也是相当的简单:
[*][quote]服务器发送协议
[*]登录确认<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> 服务器分析对象转发[/quote]在协议中我们实现了登录,注册,登录验证,注册验证,发送好友列表,发送聊天消息,发送窗体震动的基本功能。下面我就根据这些简单协议来实现基本功能。
[*]
[*][b]然后[/b],就是服务器和客户端的建立。
[*]
/**
[*] * 建立配置服务器的方法
[*] * @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(),从网络流中读取数据,一直阻塞知道流中有数据可以读取为止。
[*]
[*][b]再次[/b],就是对接收到消息的分析也是通信中最麻烦最核心的内容。这里我们首先要做的是要分析,当服务器或客户端读取到哪个字节后算是读完了一整条消息(我认为这是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);

[*][quote]其中byteList是一个字节队列用来存放读到的字节,listToArray字节队列转成字节数组的方法,messageType是分析消息类型的方法[/quote]
[*]之后要进行的就是要分析消息类型,消息的发送者,消息的接收者……为了得到这些内容,可以利用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;
[*]
[*] }

[*][quote]我把他们封装成了一个易于我使用的方法。[/quote]
[*]经过对字符串的分析,系统就能知道,这是一条什么消息,是谁发来的消息,消息该发给谁……
[*]
[*][b]其四[/b],就是客户端接收到消息后的响应,这个可以根据自己的设计来响应消息。比如好友上下线消息(界面上的好友列表),比如收到聊天消息(显示到界面)……由于这些不是通信系统中的主要内容这里就不做介绍。
[*][quote]总之一句话,东西是死的,人是活的。程序变成什么样是自己设计的,关键是有思路。[/quote]
[*]
[*]
[*]
[*][b]最后[/b],是我在通信是遇到的问题。
[*][list]
[*][*]1、乱码问题 这一点在我的博客 [url]http://ml5858258-sina-com.iteye.com/blog/958747[/url]有介绍。
[*][*]2、通信的的异常 这一点在我的博客 [url]http://ml5858258-sina-com.iteye.com/blog/943799[/url]
[*][*]3、内存泄漏。导致此原因是由于,客户端关闭,而服务器端还在死循环的读取。不停地抛出socket reset的异常。看过问题2博客大家都会知道这是个什么异常。解决方法如下:
[*][*]
try {
[*][*] byteList.add((byte) read());
[*][*] } catch (IOException e1) {
[*][*] // TODO Auto-generated catch block
[*][*] // 读取出现异常关闭客户机对应的服务器
[*][*] e1.printStackTrace();
[*][*] close();
[*][*] }

[*][*][quote]close()是我自定的关闭连接的方法。这样就不会死循环抛异常了。[/quote]
[*][/list]
[/list]由于界面过于丑陋,这里就不贴图了。等到完成的项目出炉后在来发表。

下面是简陋通信的源码供广大爱好者们一共学习。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值