IM基础概念和原理
即时通信是什么?
把一个人要发送给另外一个人的消息对象(文字,音视频,文件)通过消息通道(C/S实时通信)进行传输的服务.
IP TCP UDP Socket HTTP XMPP的概念
TCP/IP:即传输控制协议/网间协议,定义了主机如何连入因特网及数据如何再它们之间传输的标准
TCP UDP :TCP和UDP是传输层协议。
- TCP:面向连接,安全可靠,效率稍低。通过三次握手确保连接的建立。
UDP:面向无连接。不可靠。速度快。将数据封包传输,数据包最大64k。
Socket:又称“套接字”, 在应用层和传输层之间的一个抽象层,它把TCP/IP层复杂的操作抽象为几个简单的接口供应用层调用已实现进程在网络中通信。
HTTP:超文本传输协议, 是一种规定了浏览器和万维网服务器之间互相通信的规则,通过因特网传送万维网文档的数据传送应用层协议。
* 特点:客户端发送的每次请求都需要服务器回送响应,在请求结束后,会主动释放连接。HTTPS: 由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议。
FTP:应用于文件传输场景的应用协议
XMMP:应用于即时通讯场景的应用层协议。
网络通信的三大要素?
两个进程如果需要进行通讯最基本的一个前提能能够唯一的标示一个进程,在本地进程通讯中我们可以使用PID来唯一标示一个进程,但PID只在本地唯一,网络中的两个进程PID冲突几率很大,这时候我们需要另辟它径了,我们知道IP层的ip地址可以唯一标示主机,而TCP层协议和端口号可以唯一标示主机的一个进程,这样我们可以利用ip地址+协议+端口号唯一标示网络中的一个进程。
- ip
- port(端口)
- 协议
IP , TCP/UDP , HTTP之间的关系?
ip是网络层协议,是一种网络地址
TCP/UDP建立在IP基础上的传输层协议,具有消息通信内容
Http是基于TCP/UDP的基础上建立的应用层协议,对数据的包装
数据传输形式有哪些?
- 在线直传: 不经过服务器,进行直传
- 在线代理: 消息经过服务器 中转 到达目标账号
- 离线代理: 消息经过服务器 中转 到达目标账号 对方不在线 消息暂存服务器的数据库,在其上线再传发
- 离线扩展: 将暂存消息以邮件,短信等方式转发给目标账号
消息内容-IM接口文档
实现IM的方案有几种?
- 自主研发
- 借助开源工具(服务器openfire)
IM接口文档概念?
以文档的形式,规定通讯消息的字段和格式
IM接口文档格式有两种
- xml格式
- json格式
xml和json两种格式的不同点?
定义格式方面。
xml:value
json:{“name”:value}
可扩展性方面。
XML相对json有良好的扩展性。
文件大小方面。
XML 文件庞大,文件格式复杂,传输占带宽
json 数据格式比较简单,易于读写,格式都是压缩的,占用带宽小
解码难度方面。
XML的解析得考虑子节点父节点,让人头昏眼花,而JSON的解析难度几乎为0。这一点XML输的真是没话说。
传输速度方面。
JSON的速度要远远快于XML。
消息对象
消息对象:消息内容 + 附加字段的封装
消息对象 ---------------------------------------------------------
String type = QQMessageType.MSG_TYPE_CHAT_P2P;// 类型的数据 chat login
String from = 0;// 发送者 account
String fromNick = "";// 昵称
int fromAvatar = 1;// 头像
String to = 0; // 接收者 account
String content = ""; // 消息的内容 约不?
String sendTime = MyTime.geTime(); // 发送时间
数据转换工具
xml — Xtream
使用:
Xstream x=new XStream();
toXml 对象转xml
fromXml xml转对象
json — Gson
GSON gson=new Gson();
toJson 对象转换为json
fromJson json转换为对象
消息内容-Xstream
Android Junit单元测试配置
1:添加Junit Library
2:清单文件配置
<applocation>同级节点:
<instrumentation
android:name="android.test.InstrumentationTestRunner"
android:targetPackage="com.itcast.imtest" >
</instrumentation>
<activity/>同级节点:
<uses-library
android:name="android.test.runner"/>
3:编写Junit Test Case代码
Xstream使用
@Test
public void testToXMLObject() {
//创建XStream核心对象
XStream x = new XStream();
//设置别名
x.alias("QQMessage", QQMessage.class);
//创建消息对象
QQMessage msg = new QQMessage();
msg.type = QQMessageType.MSG_TYPE_CHAT_P2P;
msg.content = "今晚有空没";
msg.fromNick = "老王@qq.com";
msg.from = 007;
msg.to = 10086;
//对象转化为xml
String xml = x.toXML(msg);
//xml转换为对象
QQMessage m2 = (QQMessage) x.fromXML(xml);
}
//----------------------------------------
<QQMessage>
<content>今晚有空没</content>
<type>chatp2p</type>
<sendTime>08-141 02:08:07</sendTime>
<fromNick>老王@qq.com</fromNick>
<from>7</from>
<to>10086</to>
<fromAvatar>1</fromAvatar>
</QQMessage>
消息内容-基类ProtocalObj
基类ProtocalObj封装
public class ProtocalObj implements Serializable{
//该方法将当前对象转化为xml格式的字符串
public String toXml() {
//创建XStream核心对象
XStream x = new XStream();
//向生产的xml添加别名(也就是根节点)
x.alias(this.getClass().getSimpleName(), this.getClass());
//将对象转化为xml
return x.toXML(this);
}
//该方法将参数xml字符串转化为当前对象模型
public Object fromXml(String xml) {
//创建XStream核心对象
XStream x = new XStream();
//指定类名
x.alias(this.getClass().getSimpleName(), this.getClass());
//将xml字符串转化为对象
return x.fromXML(xml);
}
}
this.getClass().getName() 和 this.getClass().getCanonicalName()分别获取什么?
this.getClass().getName():包名+类名
this.getClass().getCanonicalName(): 包名+类名
消息通道-发送与接收消息
消息通道创建以及消息的接收和发送实现
1.建立连接
Socket client = new Socket(HOST, PORT);//通过host:port建立socket连接
2.接收数据
DataInputStream reader = new DataInputStream(client.getInputStream());//获取输入流并用DataInputStream包装
String xml = reader.readUTF();//通过输入流读取server发来的数据
3.发送数据
DataOutputStream writer = new DataOutputStream(client.getOutputStream());//获取输出流并用DataOutputStream包装
writer.writeUTF(xml);//通过输出流往server发送数据
创建子线程的几种方法?
第一种方式:
new Thread(){
run(){
}
}.start
第二种方式:
new Thread(new Runnable(){}).start();
第三种方式
new FutureTask(new Callable){
@Override
protected void done() {
//获取执行结果值
}
};
第四种方式:
AsyncTask()
消息通道-QQConnection与监听器
QQConnection需要负责哪些业务逻辑?
创建连接:程序启动时
public void connect() throws UnknownHostException, IOException { if (client == null) { //创建消息通道 client = new Socket(HOST, PORT); //通过消息通道获取读取消息对象 reader = new DataInputStream(client.getInputStream()); //通过消息通道获取发送消息对象 writer = new DataOutputStream(client.getOutputStream()); flag = true; start(); }
}
发送消息:消息发送请求时
//封装了一个发送消息是xml字符串 public void sendMessage(String xml) throws IOException { //发送消息 writer.writeUTF(xml); writer.flush(); } //封装了一个发送消息是对象 public void sendMessage(QQMessage msg) throws IOException { writer.writeUTF(msg.toXml()); writer.flush();
}
接收消息:注册监听器接收消息
/** * 申明接口 * */ public static interface OnQQMessageReceiveListener { /** * 抽象方法 用于接收消息 * @param msg */ public void onReceive(QQMessage msg); } private List<OnQQMessageReceiveListener> listeners = new ArrayList<OnQQMessageReceiveListener>(); /** * 注册接收消息的监听 * @param listener */ public void addOnQQMessageReceiveListener(OnQQMessageReceiveListener listener) { listeners.add(listener); } /** * 移除消息监听 * @param listener */ public void removeOnQQMessageReceiveListener(OnQQMessageReceiveListener listener) { listeners.remove(listener); } private boolean flag = true; /** * 在子线程中,读取消息 */ @Override public void run() { super.run(); while (flag) { try { String xml = reader.readUTF(); QQMessage msg = new QQMessage(); msg = (QQMessage) msg.fromXml(xml); if (msg != null) { //监听接收消息 for (OnQQMessageReceiveListener listener : listeners) { listener.onReceive(msg); } } } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
断开连接:程序退出时
/** * 断开连接 */ public void disconnect() { if (client != null) { flag = false; stop(); try { reader.close(); } catch (Exception e) { e.printStackTrace(); } try { writer.close(); } catch (Exception e) { e.printStackTrace(); } try { client.close(); } catch (Exception e) { e.printStackTrace(); } } }