如何实现一个IM
Client + Data Transfer Protocol + Server 搞定这三个部分就能实现一个IM
-
Client: 根据支持PC、浏览器、手机等特性来决定支持的语言来实现
-
Transfer Protocol: http/tcp(ip)/)websocket + xml/json 来实现
-
Server: 主要承担Client信息记录、连接管理、信息路由等职责
-
Getway: 网关是用来实现异构IM互通,主要用于协议转换和交互. 可选.
IM常用功能
- 即时通讯 - 在用户和在线朋友之间来回发送信息
- 聊天 - 创建用户与朋友的自定义聊天室
- 网页链接 - 共享用户喜爱的网址
- 支持图片 - 浏览朋友计算机中的图片
- 支持声音 - 给朋友播放音乐
- 支持文件传输 - 直接将文件发送给朋友,以便于共享
- 交谈 - 使用 Internet,而不是电话,与朋友们进行真正的交谈
- 影音串流内容 - 实时或准实时的股市行情或新闻
数据传输协议选择 - XMPP
我们简要说明一下XMPP协议
Xmpp协议 —— 简单说来就是基于XML的一种实时通信协议,包含打包、解包消息,一般使用tcp/ip通讯。
优缺点
- 优点: 扩展性好
- 缺点:XML冗余数据多,另外Xmpp协议定义的交互方式导致完成一次会话交互次数比较多,造成数据流量浪费;且不能修改二进制的问题,所以文件传输只能使用外部Http
JID(Jabber Identifier, JID)
Xmpp协议中定义的各种对象和实体(用户、Server、Gateway)都可以用唯一的JID标识,成为Jabber地址. 具体格式:
[ node "@" ] domain [ "/" resource ]
最常见的标识一个用户,比如:joseph@imserver/ipad .resource用来标识用户的特殊对象,比如位置或设备
XML数据结构
<stream>
--------------------------
<presence></presence>
--------------------------
<message></message>
--------------------------
<iq></iq>
--------------------------
<stream>
每一条线分割标识一次会话
要区分两个基本概念:XML流和XML节
-
XML流是整个网络交互的容器,包含了所有网络交互的XML元素,XMPP是以
开头, 结尾关闭流. 这是异步数据传输的一种标准方式. -
XML节是通过XML流传输的一种结构化信息单元,比如<presence/>、<message/>、<iq/>
-
比较特殊的是安全认证相关的元素,不被认为是XML节,主要用于协商传输层安全协议、简单认证与安全层协议和服务器回拨认证协议完成通信认证、加密等目的的数据交互
流元素属性
属性 | 初始方->接收方 | 接收方->初始方 |
---|---|---|
to | 接收方的主机名 | 忽略 |
from | 忽略 | 接收方的主机名 |
id | 忽略 | 会话键值 |
xml:lang | 缺省语言 | 缺省语言 |
version | 支持XMPP1.0 | 支持XMPP1.0 |
交互过程
SENT (0): <stream:stream xmlns='jabber:client' to='127.0.0.1' xmlns:stream='http://etherx.jabber.org/streams' version='1.0' xml:lang='en'>
RECV (0): <?xml version='1.0' encoding='UTF-8'?><stream:stream xmlns:stream="http://etherx.jabber.org/streams" xmlns="jabber:client" from="127.0.0.1" id="96fafe03" xml:lang="en" version="1.0"><stream:features><starttls xmlns="urn:ietf:params:xml:ns:xmpp-tls"></starttls><mechanisms xmlns="urn:ietf:params:xml:ns:xmpp-sasl"><mechanism>DIGEST-MD5</mechanism><mechanism>PLAIN</mechanism><mechanism>ANONYMOUS</mechanism><mechanism>CRAM-MD5</mechanism></mechanisms><compression xmlns="http://jabber.org/features/compress"><method>zlib</method></compression><auth xmlns="http://jabber.org/features/iq-auth"/><register xmlns="http://jabber.org/features/iq-register"/></stream:features>
SENT (0): <auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl' mechanism='DIGEST-MD5'>=</auth>
RECV (0): <challenge xmlns="urn:ietf:params:xml:ns:xmpp-sasl">cmVhbG09IjEyNy4wLjAuMSIsbm9uY2U9Illuc1Z4U2VyY2RSZVF2WTFSdlVTd21OSUdZVk50N2pNb2MwNG1WenAiLHFvcD0iYXV0aCIsY2hhcnNldD11dGYtOCxhbGdvcml0aG09bWQ1LXNlc3M=</challenge>
SENT (0): <response xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>Y2hhcnNldD11dGYtOCx1c2VybmFtZT0idGVzdFVzZXIxIixyZWFsbT0iMTI3LjAuMC4xIixub25jZT0iWW5zVnhTZXJjZFJlUXZZMVJ2VVN3bU5JR1lWTnQ3ak1vYzA0bVZ6cCIsbmM9MDAwMDAwMDEsY25vbmNlPSJpL0FSanM3MWFXeXJlN2xhY3hSQ2NtVDROTVNlRXd1ay9hUUtwT3k4IixkaWdlc3QtdXJpPSJ4bXBwLzEyNy4wLjAuMSIsbWF4YnVmPTY1NTM2LHJlc3BvbnNlPWQ3NjZjMmZlZWY3YjI0ZWZmODMyYTdlY2ZiNWU1YzgyLHFvcD1hdXRo</response>
RECV (0): <success xmlns="urn:ietf:params:xml:ns:xmpp-sasl">cnNwYXV0aD1kN2IxZjQzNWI4ODEwZDVkZTc0NmNjMzcwYmUzMGRjOQ==</success>
SENT (0): <stream:stream xmlns='jabber:client' to='127.0.0.1' xmlns:stream='http://etherx.jabber.org/streams' version='1.0' id='96fafe03' xml:lang='en'>
RECV (0): <?xml version='1.0' encoding='UTF-8'?><stream:stream xmlns:stream="http://etherx.jabber.org/streams" xmlns="jabber:client" from="127.0.0.1" id="96fafe03" xml:lang="en" version="1.0"><stream:features><compression xmlns="http://jabber.org/features/compress"><method>zlib</method></compression><bind xmlns="urn:ietf:params:xml:ns:xmpp-bind"/><session xmlns="urn:ietf:params:xml:ns:xmpp-session"/></stream:features>
SENT (0): <iq id='bBkWS-1' type='set'><bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'><resource>Smack</resource></bind></iq>
RECV (0): <iq type="result" id="bBkWS-1" to="127.0.0.1/96fafe03"><bind xmlns="urn:ietf:params:xml:ns:xmpp-bind"><jid>testuser1@127.0.0.1/Smack</jid></bind></iq>
SENT (0): <iq id='bBkWS-3' type='set'><session xmlns='urn:ietf:params:xml:ns:xmpp-session'/></iq>
RECV (0): <iq type="result" id="bBkWS-3" to="testuser1@127.0.0.1/Smack"/>
User logged (0): testuser1@127.0.0.1:5222/Smack
XMPPConnection authenticated (0)
SENT (0):<iq id='bBkWS-5' type='get'><query xmlns='jabber:iq:roster'></query></iq>
SENT (0): <presence id='bBkWS-6'></presence>
RECV (0): <iq type="result" id="bBkWS-5" to="testuser1@127.0.0.1/Smack"><query xmlns="jabber:iq:roster"/></iq>
RECV (0): <presence id="bBkWS-6" from="testuser1@127.0.0.1/Smack" to="testuser1@127.0.0.1/Smack"/>
SmackClient.(68) - testUser1 login successful.
SENT (0): <message to='testUser2@127.0.0.1' id='bBkWS-9' type='chat'><body>hello,too.</body><thread>090684cf-ba64-4c9b-a03d-edad92dc4ea6</thread></message>
SmackClient.(159) - 向[testUser2]发送消息[hello,too.]
安全
TSL(传输层安全协议)
XMPP 为防止篡改和偷听.TLS加密方法, 模拟了类似的其他"STARTTLS" 的扩展,如 IMAP [IMAP], POP3 [POP3]"STARTTLS"。扩展命名空间是'urn:ietf:params:xml:ns:xmpp-tls'.
STARTTLS 是对纯文本通信协议的扩展。它提供一种方式将纯文本连接升级为加密连接(TLS或SSL),而不是另外使用一个端口作加密通信。
交互过程:客户端先询问服务端是否支持TSL/SSL,如果服务端回复支持,那么客户端就以TSL/SSL的方式发送数据,否则以原来的方式发送数据
TSL、SSL、STARTTLS区别和关系:TSL/SSL提供一种方式网络间的一个加密通信通道,STARTTLS可选择使用TSL/SSL
想了解更多详情可点击进入 Xmpp官网
服务器端选择
客户端选择
即可选择现成的开源实现,也可基于实现了XML的开源框架来定制开发.下面是几个例子.
- spark
- jwchat
- conversejs
- Strophe.js
- iJab
后续会对每个部分进行详细说明.