导图社区 第三方SDK接口整理总结分享
第三方SDK接口整理总结分享、指客服端和服务器之间建立一个通信连接,在连接没有中断之前,客服端和服务器之间可以随时进行通信()
编辑于2022-11-01 17:15:08 广东第三方SDK接口整理总结分享
社 会 化 分 享
ShareSdk
1. OAuth认证
1. 什么是OAuth协议
1. OAuth(开放授权)是一个开放标准。
2. 允许第三方网站或者应用在用户授权的前提下访问在用户在服务商那里存储的各种信息。
3. 而这种授权无需将用户提供用户名和密码提供给该第三方网站或者应用。
4. OAuth允许用户提供一个令牌给第三方网站,一个令牌对应一个特定的第三方网站,同时该令牌只能在特定的时间内访问特定的资源
2. OAuth的原理
1. OAuth的认证和授权的过程中涉及的三方包括
1. 服务商:用户使用服务的提供方,一般用来存消息、储照片、视频、联系人、文件等(比如Sina微博,QQ,微信等)
2. 用 户:服务商的用户
3. 第三方:通常是网站,应用,该网站或者应用想要访问用户存储在服务商那里的信息
3. OAuth认证和授权的过程如下:
3.1. 1、用户访问第三方网站或者应用,想对用户存放在服务商的某些资源进行操作。
3.2. 2、第三方网站向服务商请求一个临时令牌。
3.3. 3、服务商验证第三方网站的身份后,授予一个临时令牌。
3.4. 4、第三方网站获得临时令牌后,将用户导向至服务商的授权页面请求用户授权,然后这个过程中将临时令牌和第三方网站的返回地址发送给服务商。
3.5. 5、用户在服务商的授权页面上输入自己的用户名和密码,授权第三方网站访问所相应的资源。
3.6. 6、授权成功后,服务商将用户导向第三方网站的返回地址。
3.7. 7、第三方网站根据临时令牌从服务商那里获取访问令牌。
3.8. 8、服务商根据令牌和用户的授权情况授予第三方网站访问令牌。
3.9. 9、第三方网站使用获取到的访问令牌访问存放在服务商的对应的用户资源
3.10.
2. 快速集成
1. 第一步 : ShareSdk官网注册开发者账号
2. 第二步:添加开发者应用
3. 第三步 : 下载ShareSdk的jar包,将library导入工程 添加相应的分享jar包
4. 第四步: AndroidManifest添加权限
4.1.
5. 第五步 : 添加Activity
1.
2. 如果您集成了微信或者易信,还需要添加下面两个Activity
3.
6. 第六步 : 添加Appkey
1. 将官方demo下assets-->>ShareSDK.xml文件复制到自己的项目中
2. 添加ShareSdk-AppKey
3. 第三方分享平台App-key
3. 自定义UI
1. 如何改变分享UI界面的平台图标顺序
1. 可任意调整分享菜单中的社交平台的排序功能介绍:将编辑框的标题栏颜色与您的APP统一风格只需要手动更新数组的顺序即可,SortId属性控制平台图标的排列顺序,从“1”开始,越大越后面,九宫格按照左到右,上到下顺序排列
2. 修改asset/share.xml文件
3.
2. 在新浪微博和腾迅微博的授权界面添加“关注官方微博”功能
3. 点击分享菜单直接分享,不显示编辑框
1.
4. 自定义UI分享
1.
5. 自定义分享菜单项
1.
6. 为不同平台定义差异化分享内容
1.
7. 自定义外部回调
1.
8. 自定义九宫格
1. 自定义九宫格背景颜色
2. 自定义九宫格文字颜色
3. 自定义九宫格取消按钮
9. 授权页面标题栏去掉ShareSDK Logo部分
1.
4. 高级功能
1. 授权与取消授权
1. Android 授权以及授权页面自定义
1.1. ShareSDK的授权分为“手动授权”和“自动授权”。前者是指显式调用ShareSDK的授权代码来执行授权操作,后者是执行ShareSDK的操作前不考虑具体平台是否已经完成授权操作, 直接调用其对应方法执行操作(如分享操作),ShareSDK内部会根据平台数据库中的数据判断其是否已经完成授权,若是未授权或授权信息已经失效,则自行启动授权流程,执行授权。自动授权于开发者是透明的,但是于用户来说依然存在。 不管您选择的是“手动授权”还是“自动授权”,授权都是您接触ShareSDK的第一个操作。 对于大部分的应用来说,手动授权是没有必要的,但是如果您只是想做一个“账号系统”,或者是说您的应用不需要注册,只需要是微博的用户,就能登录,那么这个方法还是十分有用途的
2. 手动授权
1. 下面是新浪微博授权操作的例子
2. Platform weibo = ShareSDK.getPlatform(context, SinaWeibo.NAME); weibo.setPlatformActionListener(paListener); weibo.authorize(); //移除授权 //weibo.removeAccount();
3. 说明:调用authorize方法,会弹出一个基于ShareSDKUIShell的授权页面,填写账号和密码以后,会执行授权操作。这个方法的操作回调paListener并不实际带回什么数据,只是通过回调告知外部成功或者失败。但是每一个平台都具备一个PlatformDb的成员,这里面存储了此平台的授权信息。可以参考章节平台数据库的操作的说明或查看获取授权用户资料章节,通过方法getToken、getUserId等方法,获取授权用户在此平台上的授权信息。并由此建立“账户系统
3. 自动授权
1. 自动授权就是直接无视授权操作而调用其他的操作(如关注或者分享)。在自动授权下,授权操作对开发者而言是透明的,它由ShareSDK内部控制,开发者没有调用授权的代码,也没有办法接收到授权的结果。比方说如果开发者执行关注,则发起时调用的方法是关注,操作回调中得到的结果(不管成败)也是关注
4. SSO (Single Sign-On)
1. SSO授权方式,简单来说就是使用目标平台客户端来完成授权
2. Platform weibo = ShareSDK.getPlatform(SinaWeibo.NAME); weibo.SSOSetting(false); //设置false表示使用SSO授权方式 weibo.setPlatformActionListener(this); // 设置分享事件回调 weibo.authorize();
3. 说明:使用了SSO授权后,有客户端的都会优先启用客户端授权,没客户端的则任然使用网页版进行授权。 这里需要注意的是新浪微博客户端授权是需要用户在开发者平台(网址:http://open.weibo.com)申请的应用用过了新浪的审核。而且要通过keystore进行签名打包测试。注意打包所用的keystore上的md5签名、项目的包名要与新浪开发者平台上填写的签名与包名一致
5. 删除授权信息
5.1. 由于ShareSDK所有的授权数据都存在PlatformDb中,所以“用户是否”授权,和“取消授权(清除授权信息)”的操作依据其实PlatformDb。在ShareSDK中,判断此平台是否授权的方法是isValid,而取消授权的方法是removeAccount,下面的代码演示客户端判断是否已经授权,如果授权就删除授权资料,否则就执行授权
5.2. If (qzone.isValid ()) { qzone.removeAccount(); } qzone.setPlatformActionListener(paListener); qzone.authorize(); //isValid和removeAccount不开启线程,会直接返回。
2. 获取授权用户资料
1. 如果您想获取用户资料,不同平台获取其他用户资料传递的参数是不同的,请阅读分享平台获取用户资料接
1. Platform weibo = ShareSDK.getPlatform(context, SinaWeibo.NAME); weibo.setPlatformActionListener(paListener); weibo.showUser(null);//执行登录,登录后在回调里面获取用户资料 //weibo.showUser(“3189087725”);//获取账号为“3189087725”的资料
1. 说明:示例代码获取了新浪微博的用户信息的实例,调用showUser方法获取用户的资料,如果account为null,则表示获取授权账户自己的资料。 其结果将通过操作回调paListener返回给外部代码,在oncomplete中的hashmap返回数据,然后开发者再自己解析数据
2. 获取用户资料成功会执行回调方法onComplete方法,注意res里获取的数据是从平台接口获取的用户数据,我们是不做任何处理,直接返回给开发者的
2. 简单获取res里面的用户信息
2.1. public void onComplete(Platform platform, int action, HashMap<String, Object> res) { //解析部分用户资料字段 String id,name,description,profile_image_url; id=res.get("id").toString();//ID name=res.get("name").toString();//用户名 description=res.get("description").toString();//描述 profile_image_url=res.get("profile_image_url").toString();//头像链接 String str="ID: "+id+";\n"+ "用户名: "+name+";\n"+ "描述:"+description+";\n"+ "用户头像地址:"+profile_image_url; System.out.println("用户资料: "+str); }
3. 获取平台数据库的用户信息
3.1. public void onComplete(Platform plat, int action, HashMap<String, Object> res) { //用户资源都保存到res //通过打印res数据看看有哪些数据是你想要的 if (action == Platform.ACTION_USER_INFOR) { PlatformDb platDB = platform.getDb();//获取数平台数据DB //通过DB获取各种数据 platDB.getToken(); platDB.getUserGender(); platDB.getUserIcon(); platDB.getUserId(); platDB.getUserName(); } } Platform weibo = ShareSDK.getPlatform(context, SinaWeibo.NAME); weibo.setPlatformActionListener(paListener);
5. 第三方登陆
1. 所谓的第三方登录,就是利用用户在第三方平台上已有的账号来快速完成自己应用的登录或者注册的功能。而这里的第三方平台,一般是已经有大量用户的平台,如国内的新浪微博、QQ空间,微信,外国的Facebook、twitter等等。第三方登录不是一个具体的接口,而是一种思想或者一套步骤。 要实现第三方登录,首先你需要选择一个第三方平台。新浪微博和QQ空间都是好的选择,这些平台拥有大量的用户,而且还开放了API,供我们调用接入
1. 开放了API
2. 具备获取用户资料或至少可以进行授权验证
2. 实现方法
1. 你的应用是否具备独立账户系统? 这个问题是第三方登录时接口选择的重要标准。如果你选择“是”,则意味着你的应用只是需要第三方平台的用户,而不是他们的账户验证功能——也就是“要数据,不要功能”。而如果你选择“否”,则表示你实际上是’“要功能,不要数据(用户)”’。对于ShareSDK来说,前者你的入口方法是showUser(null),而后者是authorize()
2. 要数据,不要功能
2.1. 1、用户触发第三方登录事件
2.2. 2、showUser(null)请求授权用户的资料(这个过程中可能涉及授权操作)
2.3. 3、如果onComplete()方法被回调,将其参数Hashmap代入你应用的Login流程
2.4. 4、否则提示错误,调用removeAccount()方法,删除可能的授权缓存数据
2.5. 5、Login时客户端发送用户资料中的用户ID给服务端
2.6. 6、服务端判定用户是已注册用户,则引导用户进入系统,否则返回特定错误码
2.7. 7、客户端收到“未注册用户”错误码以后,代入用户资料到你应用的Register流程
2.8. 8、Register时在用户资料中挑选你应用的注册所需字段,并提交服务端注册
2.9. 9、服务端完成用户注册,成功则反馈客户端引导用户进入系统
2.10. 10、否则提示错误,调用removeAccount()方法,删除可能的授权缓存数据
3. 要功能,不要数据
3.1. 1、用户触发第三方登录事件
3.2. 2、调用platform.getDb().getUserId()请求用户在此平台上的ID
3.3. 3、如果用户ID存在,则认为用户是合法用户,允许进入系统;否则调用authorize()
3.4. 4、authorize()方法将引导用户在授权页面输入帐号密码,然后目标平台将验证此用户
3.5. 5、如果onComplete()方法被回调,表示授权成功,引导用户进入系统
3.6. 6、否则提示错误,调用removeAccount()方法,删除可能的授权缓存数据
4. 具体代码
4.1. private void authorize(Platform plat) { if (plat == null) { popupOthers(); return; } //判断指定平台是否已经完成授权 if(plat.isValid()) { String userId = plat.getDb().getUserId(); if (userId != null) { UIHandler.sendEmptyMessage(MSG_USERID_FOUND, this); login(plat.getName(), userId, null); return; } } plat.setPlatformActionListener(this); // true不使用SSO授权,false使用SSO授权 plat.SSOSetting(true); //获取用户资料 plat.showUser(null); }
4.2. 说明: 上面的代码是当用户触发第三方登录按钮的时候的处理。plat.isValid()判断指定平台是否已经完成授权,如果已经完成授权,ShareSDK的用户数据库应该已经存在userId,因此代码尝试获取userId,如果得到的为null,当作为授权,否则用此ID来执行登录。如果此平台没有完成授权,则调用plat.showUser(null)方法来获取用户资料。 获取用户资料以后,并不能立刻用来注册,因为可能只是因为授权时间太久导致AccessToken过期,因此完成授权以后需要先将userId发送给你应用的服务端进行检查,如果服务端发现确实没有注册过,才引导客户端进入注册页面
支付
支付宝
开发准备
1、首先,我们需要前往支付宝开放平台,申请我们的支付功能:https://open.alipay.com/platform/home.htm
2、创建一个应用 提交给支付宝进行审核。
3、移动支付-接入指南
https://doc.open.alipay.com/doc2/detail.htm?spm=a219a.7629140.0.0.vCcVse&treeId=58&articleId=103541&docType=1
代码接入流程
1. 下载相关的sdk
1. 下载地址
2. https://doc.open.alipay.com/doc2/detail.htm?spm=a219a.7629140.0.0.P0wrvI&treeId=54&articleId=104509&docType=1
3.
2. 导入Alipay Jar包
1. 将下载的Jar包alipaySDK-xxx.jar复制到libs文件下,如果libs文件夹不存在则新建一个,然后右键Jar包,选择Add As library即可。
3. 配置Manifest文件
1. 添加权限
1.1.
1.2.
1.3.
1.4.
1.5.
2. 添加支付宝的H5支付页面(当手机没有安装支付宝时调用H5支付页面)
2.1.
4. 订单信息生成
1. 获取订单信息
1.1. String orderInfo = getOrderInfo("测试的商品", "该测试商品的详细描述", "0.01");
2. 签名
2.1. String sign = sign(orderInfo);
3. 仅需对sign 做URL编码
3.1. sign = URLEncoder.encode(sign, "UTF-8");
4. 生成完整的
4.1. 完整的符合支付宝参数规范的订单信息
5.
5. 发起支付
1. // 构造PayTask 对象 PayTask alipay = new PayTask(MainActivity.this); // 调用支付接口,获取支付结果 String result = alipay.pay(payInfo, true);
6. 处理支付结果
1. 同步
1.1. private Handler mHandler = new Handler() { @SuppressWarnings("unused") public void handleMessage(Message msg) { switch (msg.what) { case SDK_PAY_FLAG: { PayResult payResult = new PayResult((String) msg.obj); String resultInfo = payResult.getResult();// 同步返回需要验证的信息 String resultStatus = payResult.getResultStatus(); // 判断resultStatus 为“9000”则代表支付成功,具体状态码代表含义可参考接口文档 if (TextUtils.equals(resultStatus, "9000")) { Toast.makeText(MainActivity.this, "支付成功", Toast.LENGTH_SHORT).show(); } else { // 判断resultStatus 为非"9000"则代表可能支付失败 // "8000"代表支付结果因为支付渠道原因或者系统原因还在等待支付结果确认,最终交易是否成功以服务端异步通知为准(小概率状态) if (TextUtils.equals(resultStatus, "8000")) { Toast.makeText(MainActivity.this, "支付结果确认中", Toast.LENGTH_SHORT).show(); } else { // 其他值就可以判断为支付失败,包括用户主动取消支付,或者系统返回的错误 Toast.makeText(MainActivity.this, "支付失败", Toast.LENGTH_SHORT).show(); } } break; } default: break; } } }
2. 异步获取支付结果
2.1. 有服务器发送支付是否成功
3. 注意:同步返回的结果必须放置到服务端进行验证 建议商户依赖异步通知
7. 其他参数
1. 由商户在支付宝注册生成 public static final String PARTNER = "";
2. // 商户收款账号 public static final String SELLER = "";
3. // 商户私钥,pkcs8格式 public static final String RSA_PRIVATE = "";
4. //支付宝处理完请求后,当前页面跳转到商户指定页面的路径,由公司后台服务器提供,可空 public static final String RETURN_URL = ""
微信
业务流程
支付业务流程图
步骤1: 用户进入商户APP,选择商品下单、确认购买,进入支付环节。商户服务后台生成支付订单,签名后将数据传输到APP端
步骤2:用户点击后发起支付操作,进入到微信界面,调起微信支付,出现确认支付界面
步骤3:用户确认收款方和金额,点击立即支付后出现输入密码界面可选择零钱或银行卡
步骤4:输入正确密码后,支付完成,用户端微信出现支付详情页面
步骤5:回跳到商户APP中,商户APP根据支付结果个性化展示订单处理结果
配置
https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=11_1
注册开发者账号(注册时需要填应用的包名和签名,注意这里的签名是App正式版的签名),待审核通过后,会得到一个AppID和AppSecret, 如果需要登录或者支付功能需要企业用户,不对个人开放,申请成功后会拿到一个商户id,后面生成sign时会用到
配置微信后台
商户在微信开放平台申请开发应用后,微信开放平台会生成APP的唯一标识APPID。由于需要保证支付安全,需要在开放平台绑定商户应用包名和应用签名,设置好后才能正常发起支付。设置界面在【开放平台】中的栏目【管理中心 / 修改应用 / 修改开发信息】里面,如图8.8红框内所示。
图片
下载官方jar包
使用
配置回调activity
调用支付
生成订单
请求服务器,有服务器生成订单
从后台服务器获得微信支付参数
调起支付需要的参数
https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=9_12
结构图
partnerId、prapayId、packageValue、nonceStr、timeStamp、sign等都是由服务器端生成
调用微信支付
支付结果回调
微信会回调 WXPayEntryActivity 的public void onResp(BaseResp resp)方法,所以后续操作,放在这个回调函数中操作就可以了。
resp.errCode== 0 :表示支付成功 resp.errCode== -1 :表示支付失败 resp.errCode== -2 :表示取消支付
这个类中的布局是可以自定义的,如果你不需要展示什么布局,而是要跳转页面,删除setContentView(R.layout.xxx)即可
官方直接弹出dialog提示,如果不需要可以删除,可以根据自己的业务需求确定
问题总结
在集成微信支付时,遇到第一次可以调起微信的支付页面,之后再调用支付,总是返回到支付结果页,返回的errorCode总是为 -1。
在申请微信支付接口时,需要填写app 包名称和签名。
demo 的包名称换成申请时填写的包名称。
用提交的签名的keystore文件打包。
注意事项
1. 首先如果要使用微信支付的话,必须先到微信开放平台注册应用,具体地址为https://open.weixin.qq.com/,注册时需要填应用的包名和签名,注意这里的签名是App正式版的签名,可以找一个已上线的包或打一个正式包,使用微信提供的工具(签名工具下载地址为https://open.weixin.qq.com/zh_CN/htmledition/res/dev/download/sdk/Gen_Signature_Android.apk)来获取,获取后填上即可。待审核通过后,会得到一个AppID和AppSecret,AppID分享和支付都要用到,AppSecret没什么实际用途,此时微信分享能力是直接拥有的,支付能力还要额外申请,其中涉及到财务信息等,最好让公司财务部门去申请,申请成功后会拿到一个商户id,后面生成sign时会用到。只有所有审核都通过后,才可调用微信支付功能,这点是前提。
2. 微信分享和微信支付SDK是同一个架包,名为libammsdk.jar
3. 测试微信支付时,务必对自己的App做正式签名,因为一开始就在微信平台注册过签名信息,微信SDK会做校验,只有这样才能调起微信分享和微信支付,直接debug版的包则绝对调不起来,这点务必注意,很多人是跌在这里了!当初做微信分享曾遇到过,所以会很留心,也因为如此,如果微信分享能调起来,微信支付不行,那就不要怀疑签名问题了
4. 还是签名,网上有人说要注意大小写,这点其实是不必的。在微信开放平台看到审核通过的App的签名是大写的,而用微信签名获取工具获得的则显示小写,这个没关系,不要贸然改动平台注册信息,不然又可能导致漫长的审核等待,上面也说了,微信分享如可以,那就不是签名问题
5. 对于IWXAPI实例的创建,官方代码为: IWXAPI api = WXAPIFactory.createWXAPI(context, null);这样写就可以,如果调用另一个工厂方法:IWXAPI api = WXAPIFactory.createWXAPI(context, APP_ID, false);也是OK的
6. req.packageValue=”Sign=WXPay”,一般都是这样写死这个参数值。也有人说写成req.packageValue=”prepay_id=” + prepayid,经测试Android两种写法都是可以调起微信支付的,至少最新版本SDK是可以的,以后则不清楚,官方也建议写Sign=WXPay
7. 生成sign时特别需要注意,首先将key-value键值对拼成字符串,注意key都要小写,如appid,noncestr,package,partnerid,prepayid,timestamp,key,并且名字得按上述名称,我们遇到的错误就是因为partnerid写成了partnerId,prepayid写成了PrepayId,当然我们是在服务端写的,如果在客户端生成sign的话,也需要注意大小写及名称,详细信息请参考官方文档。还有这里的key并非AppID或AppSectet,而是在商户平台设置的,官方描述为“key设置路径:微信商户平台(pay.weixin.qq.com)-->账户设置-->API安全-->密钥设置”。对于noncestr,申请prepayid和生成sign时两次需要用到,由于iOS同事看到相关文章说noncestr前后需要一致,因此这个随机字符串我们是设置成一样的了,这样做Android平台也是OK的,不过个人感觉这里可以不一致,由于这个逻辑在服务器端,我并没有验证,方便的同学可以验证下
8. 对于errCode返回-1,有人说清除微信缓存或切换账户就好了,这种解决方案治标不治本啊,根本不能算解决方案。虽然我没遇到能用这方法解决的问题,但目测是签名的问题
9. 网上有人说需要给调用支付的Activity配置如下intent-filter逻辑上来看,根本不会跳这个界面啊,所以当然是非必需的
二维码
及时通信-推送
Xmpp
1. 基础概念
1. 起源
1.1. xmpp协议起源于著名的Linux即时通讯服务服务器jabber,有时候我们会把xmpp协议也叫jabber协议,其实这是不规范的,xmpp是个协议,而jabber是个服务器,因为jabber开源,设计精良,安全,稳定,跨语言,跨平台,封装开发简便,越来越多人开始使用它,并且逐步完善,不久它便形成了一个强大的标准化体系,Google GTalk、Pidgin、PSI、Spark、Pandion、MSN、Yahoo、ICQ..诸如此类一些软件在这个强大的标准体系下实现了互联.那么XMPP到底是什么意思,用通俗的话讲它和基于xml格式的一些协议原理差不多,只不过是个针对服务器的软件协议罢了
2. 1、即时通讯技术
2.1. 1、即时通讯技术(IM -- Instant Messaging)支持用户在线实时交谈。如果要发送一条信息,用户需要打开一个小窗口,以便让用户及其朋友在 其中输入信息并让交谈双方都看到交谈的内容
2.2. 2、有许多的IM系统,如AOL、Yahoo IM、 Skpe以及QQ,它们最大的区别在于各自通讯协议的实现,所以即时通讯技术的核心在于它的传输协议
2.3. 3、XMPP前身是Jabber(1998年),是一个开源组织定义的网络即时通信协议
3. 2、XMPP诞生的由来
3.1. 设计一款全世界都使用的即时通讯协议,无论使用什么即时通讯软件,都可以互联互通
4. 3、XMPP特点
4.1. 1、XMPP是基于XML的协议
4.2. 2、点对点
4.3. 3、即时通讯
5. 4、XMPP的基本结构
5.1. 1、XMPP是一个典型的C/S架构户端通过TCP/IP连接到单服务器,然后在之上传输XML流
5.2. 2、XMPP中定义了三个角色,客户端,服务器,网关
5.3. 3、服务器同时承担了客户端信息记录,连接管理和信息的路由功能
5.4. 4、基本的网络形式是单客
6. 5、XMPP工作原理
6.1. 1.节点连接到服务器
6.2. 2.服务器利用本地目录系统中的证书对其认证
6.3. 3.节点指定目标地址,让服务器告知目标状态
6.4. 4.服务器查找、连接并进行相互认证5.节点之间进行交互
7. 6、XMPP的优缺点
7.1. 1、优点:开放、安全、分散、可扩展
7.2. 2、缺点:数据负载过重XML、没有二进制传输
8. 7、OSI七层模型
8.1. 结构图
8.1.1.
8.2. 物理层:主要定义物理设备的标准,如网线的接口类型,各种传输介质传输速率等。主要作用是传输比特流(由1、0转化为电流的强弱进行传输,到达目的地后在转为1、0)这一层的数据叫做比特(bit)主要设备是集线器。
8.3. 数据链路层:主要是将物理层接收来的数据进行MAC地址的封装与解封装,常把这一层的数据叫做帧。主要设备是网卡和交换机。
8.4. 网络层:选择合适的网间路由交换节点,确保数据的及时传送。将数据链路层传来的数据进行IP地址的封装与解封装。常把这一层的数据叫做数据包,主要设备有路由器。
8.5. 传输层:定义了一些传输数据的协议和端口,如TCP、UDP协议。
8.6. 会话层:通过传输层建立数据传输通道,在系统之间发起会话和接受会话请求。
8.7. 表示层:主要对接收的数据进行解释和解压缩。把计算机能够识别的东西转化为人能够识别的东西(如图片,声音等)。
8.8. 应用层:主要是一些终端的应用,比如FTP(各种文件下载)、浏览器,qq等
9. 8、IP地址、端口号、传输协议
9.1. IP地址
9.1.1. 1、网络中的设备唯一标识符
9.1.2. 2、不易记忆,通常可以用主机名3、本地回环地址:127.0.0.1,主机名:localhost
9.2. 端口号
9.2.1. 1、应用的标识符
9.2.2. 2、有效端口:0-65535,其中0-1024有系统使用或者系统保留。
9.3. 传输协议
9.3.1. 1、通讯的规则
9.3.2. 2、常见协议:TCP、UDP
10. 9、TCP和UDP的区别
10.1. 1、通讯的规则2、常见协议:TCP、UDP
10.2. TCP:传输控制协议,长连接
10.2.1. 建立连接,形成通信管道。
10.2.2. 必须经过三次握手完成链接,是可靠的传输协议。
10.2.3. 传输过程中数据大小不受限制。
10.2.4. 必须建立连接,效率就会低
10.3. UDP:用户数据包协议
10.3.1. 1、将数据的源和目的封装在数据包,不需要建立连接。
10.3.2. 2、正因为不需要链接,所以是不可靠的协议
10.3.3. 3、传输数据在64K之内
10.3.4. 4、不需要建立连接所以,速度快。
11. 10、Socket机制
11.1. 1、Socket是网络服务的一种机制
11.2. 2、通信两端都是Socket
11.3. 3、网络通信其实就是Socket间的通信
11.4. 4、数据在两个Socket之间通过IO传输
2. 优缺点
2.1. 优点
1. 开放性
2. 标准性
3. 可扩展
4. 跨平台
2.2. 缺点
1. 数据冗余
2. 不支持二进制数据
3. androidpn(推送)
1. 推送
1. 服务器端向移动端发送消息的功能
2.
2. 重要概念
1. 长连接
1. 指客服端和服务器之间建立一个通信连接,在连接没有中断之前,客服端和服务器之间可以随时进行通信()
1.1.
2. 短连接
1. 指通讯的双方有数据交互时就建立一个连接,数据发送完成之后,就断开此连接(比如我们常用的post,get请求跟服务器交互)
3. Socket
1. ServerSocket
1. 构造方法
1. ServerSocket() throws IOException
2. ServerSocket(int port) throws IOException
3. ServerSocket(int port, int backlog) throws IOException
4. ServerSocket(int port, int backlog, InetAddress bindAddr) throws IOException
1. 参数 port 指定服务器要绑定的端口( 服务器要监听的端口)
2. 参数 backlog 指定客户连接请求队列的长度
3. 参数 bindAddr 指定服务器要绑定的IP 地址.
5. 注意
5.1. ServerSocket 构造方法的 backlog 参数用来显式设置连接请求队列的长度, 它将覆盖操作系统限定的队列的最大长度. 值得注意的是, 在以下几种情况中, 仍然会采用操作系统限定的队列的最大长度:
5.2. 1 backlog 参数的值大于操作系统限定的队列的最大长度;
5.3. 2 backlog 参数的值小于或等于0;
5.4. 3 在ServerSocket 构造方法中没有设置 backlog 参数.
2. Socket
4. mina框架
1. Mina框架相关知识
1. 介绍:Apache Mina Server 是一个网络通信应用框架,也就是说,它主要是对基于TCP/IP、UDP/IP协议栈的通信框架(当然,也可以提供JAVA 对象的序列化服务、虚拟机管道通信服务等),Mina 可以帮助我们快速开发高性能、高扩展性的网络通信应用,Mina 提供了事件驱动、异步(Mina 的异步IO 默认使用的是JAVA NIO 作为底层支持)操作的编程模型
2. 简介:当客户首次访问采用MINA编写的程序时,IoAcceptor作为线程运行,负责接受来自客户的请求。当有客户请求连接时,创建一个 Session,该Session与IoProcessor、SocketChannel以及IOService联系起来。IoProcessor也作为 另外一个线程运行,定时检查客户是否有数据到来,并对客户请求进行处理,依次调用在IOService注册的各个IoFilter,最后调用 IoHandler进行最终的逻辑处理,再将处理后的结果Filter后返回给客户端
3. 依赖包
3.1. Jdk1.7
3.2. mina-core-2.0.9.jar
3.3. slf4j-api-1.7.12.jar
3.4. slf4j-log4j12-1.7.12.jar
2. 重要类介绍
1. NioSocketAcceptor用于创建服务端监听;
2. NioSocketConnector用于创建客户端连接;
3. IoSession用来保存会话属性和发送消息;
4. IoHandlerAdapter用于定义业务逻辑
5. IoFilter 过滤器用于悬接通讯层接口与业务层接口
3. 使用
1. 服务器
1. 第一步:编写IoService
1. IoAcceptor acceptor=new NioSocketAcceptor();
2. acceptor.getSessionConfig().setReadBufferSize(2*1024);
3. acceptor.getSessionConfig.setIdleTime(IdleStatus.BOTH_IDLE,10);
4. acceptor.bind(new InetSocketAddress(9123));
2. 第二步:编写过滤器
2.1. oFilter作何用途呢?答案是我们想作何用途都可以。但是有一个用途却是必须的,那就是作为IoService和IoHandler之间的桥梁。IoHandler接口中最重要的一个方法是messageReceived,这个方法的第二个参数是一个Object型的消息,众所周知,Object是所有Java对象的基础,那到底谁来决定这个消息到底是什么类型呢?答案也就在这个IoFilter中。在我们的应用中,我们添加了一个IoFilter是new ProtocolCodecFilter(new TextLineCodecFactory()),这个过滤器的作用是将来自客户端输入的信息转换成一行行的文本后传递给IoHandler,因此我们可以在messageReceived中直接将msg对象强制转换成String对象。 而如果我们不提供任何过滤器的话,那么在messageReceived方法中的第二个参数类型就是一个byte的缓冲区,对应的类是org.apache.mina.common.ByteBuffer。虽然你也可以将解析客户端信息放在IoHandler中来做,但这并不是推荐的做法,使原来清晰的模型又模糊起来,变得IoHandler不只是业务处理,还得充当协议解析的任务
2.2. acceptor.getFilterChain().addLast("codec", new ProtocolCodecFilter( new TextLineCodecFactory(Charset.forName("UTF-8"), LineDelimiter.WINDOWS.getValue(), LineDelimiter.WINDOWS.getValue())));
3. 第三步:编写IoHandler
1. void exceptionCaught(IoSession session, Throwable cause) 当接口中其他方法抛出异常未被捕获时触发此方法
2. void messageReceived(IoSession session, Object message) 当接收到客户端的请求信息后触发此方法.
3. void messageSent(IoSession session, Object message) 当信息已经传送给客户端后触发此方法.
4. void sessionClosed(IoSession session) 当连接被关闭时触发,例如客户端程序意外退出等等.
5. void sessionCreated(IoSession session) 当一个新客户端连接后触发此方法.
6. void sessionIdle(IoSession session, IdleStatus status) 当连接空闲时触发此方法.
7. void sessionOpened(IoSession session) 当连接后打开时触发此方法,一般此方法与 sessionCreated 会被同时触发
4. 第四步:把这个IoHandler 注册到IoService
4.1. acceptor.setHandler(new MyIoHandler());
2. 客服端
5. 服务器端实现原理
1. connection收到packet,使用tsc.push.server.xmpp.codec解码。
2. router根据packet的namespace等信息,将packet路由到相应的handler
3. handler进行处理
3.1. PersistentConnectionListener,PhoneStateChangeListener,ReconnectionThread.java三个类则负责监听手机的状态并进行断线重连
6. 客服端实现原理
1. NotificationIQ,NotificationIQProvider,NotificationPacketListener三个类负责对收到的Notification格式的消息进行解析和处理
2. XmppManager是主控制器,NotificationService通过这个类,在后台维护androidpn连接
7. 资源下载
1. http://sourceforge.net/projects/androidpn/ (官方)
2. https://github.com/dannytiehui/androidpn(镜像)
4. Openfire
1. Openfire是开源的实时协作服务器(RTC),它是基于公开协议XMPP(也成为Jabber)消息的。Openfire的核心功能可以概括为:连接管理、消息解析、消息路由、消息发送
2. Openfire(服务器端)
1. openfire是一个即时通讯服务器,也称之为即时通讯平台。它是基于XMPP协议的
2. 下载地址:http://www.igniterealtime.org/downloads/index.jsp
3. 注意:使用openfire需要配置机器的域名。如果局域网内没有安装域服务器,则需要手工为机器配置域名,打开C:\WINDOWS\system32\drivers\etc\hosts文件,增加一新行: 127.0.0.1 www.qianfeng.com (根据自己的需要可配置称别的名字,但最好符合带.的域名格式)
3. Spark(客服端)
1. spark从本质上来说就是一个运行在PC上的java程序,你可以看成是官方为我们实现好的运行在PC上的客户端,我们只需要下载使用即可
2. 下载地址: http://www.igniterealtime.org/downloads/index.jsp
4. Smack(jar )
1. 一套封装好了的用于实现XMPP协议传输的API,它是一个非常简单并且功能强大的类库,给用户发送消息只需要三行代码
2. 下载地址: https://github.com/igniterealtime/Smack
3. compile 'org.igniterealtime.smack:smack-android:4.1.4'
4. compile 'org.igniterealtime.smack:smack-tcp:4.1.4'
推送
5. compile 'org.igniterealtime.smack:smack-im:4.1.4'
6. compile 'org.igniterealtime.smack:smack-extensions:4.1.4'
即时通讯
5. 使用
1. 导包
1.1. Eclipse导入相关jar
1. jxmpp-core-0.4.2
2. jxmpp-util-cache-0.4.2
3. minidns-0.1.7
4. smack-core-4.1.4
5. smack-im-4.1.4
6. smack-resolver-minidns-4.1.4
7. smack-sasl-provided-4.1.4
8. smack-tcp-4.1.4
1.2. Gradle导入相关jar
1. 在工程下的build.bgradle/buildscript和allprojects下添加
1.1. maven { url 'https://oss.sonatype.org/content/repositories/snapshots' }
2. 在moudle下面的build.gradle/dependencies
1. compile 'org.igniterealtime.smack:smack-android:4.1.4'
2. compile 'org.igniterealtime.smack:smack-tcp:4.1.4'
2. 创建连接
2.1. XMPPTCPConnectionConfiguration.Builder builder = XMPPTCPConnectionConfiguration.builder(); SASL全称Simple Authentication and Security Layer,是一种用来扩充C/S模式验证能力的机制。在Postfix可以利用SASL来判断用户是否有权使用转发服务,或是辨认谁在使用你的服务器。 SASL提供了一个通用的方法为基于连接的协议增加验证支持,而XMPP使用了一个普通的XML名字空间来满足SASL的需要 // SASLAuthentication.registerSASLMechanism(new SASLDigestMD5Mechanism()); /** 设置服务器IP地址 */ builder.setHost(SERVER_HOST); /** 设置服务器端口号*/ builder.setPort(SERVER_PORT); builder.setServiceName(SERVICE_NAME); /** 开启dbug模式*/ // builder.setDebuggerEnabled(true); /** 启用数据压缩 */ builder.setCompressionEnabled(true); /** 连接超时时间 */ builder.setConnectTimeout(15 * 1000); /** 是否接受离线消息*/ builder.setSendPresence(true); /** 关闭SASL */ builder.setSecurityMode(ConnectionConfiguration.SecurityMode.disabled); /** 如果是消息推送 可随机生成用户密码*/ // builder.setUsernameAndPassword("username", "password"); /** 设置登陆设备标识*/ // builder.setResource("Android"); /** 设置代理信息*/ // ProxyInfo proxyInfo = new ProxyInfo(ProxyInfo.ProxyType.HTTP, "pHost", 8080,"pUser", "pPass"); // builder.setProxyInfo(proxyInfo) // //设置TLS安全协议 // builder.setSecurityMode(ConnectionConfiguration.SecurityMode.disabled); // if (Build.VERSION.SDK_INT >= 14) { // builder.setKeystoreType("AndroidCAStore"); // } else { // builder.setKeystoreType("BKS"); // String path = System.getProperty("javax.net.ssl.trustStore"); // if (path == null) { // path = System.getProperty("java.home") + File.separator // + "etc" + File.separator + "security" // + File.separator + "cacerts.bks"; // } // builder.setKeystorePath(path); // }
3. 步注册
3.1. /** * 注册 * * @param account 注册帐号 * @param password 注册密码 * @return 1、注册成功 2、这个账号已经存在 */ public int registerUser(String account, String password) { int state = 0; AccountManager manager = AccountManager.getInstance(connection); try { manager.createAccount(account, password); state = 1; } catch (XMPPException e) { e.printStackTrace(); state = 2; } return state; }
4. 登陆
4.1. /** * 0 链接服务器失败 1 登陆成功 2 登陆异常 * * @param name * @param psw * @return */ public int login(String name, String psw) { int state = 0; if (getConnection() != null) { try { getConnection().login(name, psw); state = 1; } catch (XMPPException e) { e.printStackTrace(); state = 2; } } return state; }
5. 注销(登出)
5.1. /** * 注销 登出 * * @return */ public void loginOut() { if (!isConnect()) { try { connection.instantShutdown(); } catch (Exception e) { e.printStackTrace(); } } }
6. 删除账号
6.1. public void deleteAccount() { try { getAccountManager().getInstance(connection).deleteAccount(); } catch (SmackException.NoResponseException e) { e.printStackTrace(); } catch (XMPPException.XMPPErrorException e) { e.printStackTrace(); } catch (SmackException.NotConnectedException e) { e.printStackTrace(); } }
7. 获取用户属性
7.1. public Set<String> getAccountAttributes() { try { return getAccountManager().getAccountAttributes(); } catch (SmackException.NoResponseException e) { e.printStackTrace(); } catch (XMPPException.XMPPErrorException e) { e.printStackTrace(); } catch (SmackException.NotConnectedException e) { e.printStackTrace(); } return null; }
8. 所有好友信息
8.1. public Set<RosterEntry> loadGroupAllFriends() { return isConnect() ? Roster.getInstanceFor(connection).getEntries() : null; }
9. 好友的用户资料
9.1. public RosterEntry getFriend(String user) { return isConnect() ? Roster.getInstanceFor(connection).getEntry(user) : null; }
10. 聊天发送消息
10.1. public void sendMessage(String jid, String msg) { if (isConnect()) { ChatManager chatManager = ChatManager.getInstanceFor(connection); Chat chat = chatManager.createChat(jid); try { chat.sendMessage(msg); } catch (SmackException.NotConnectedException e) { e.printStackTrace(); } } }
11. 创建群聊
11.1. public MultiUserChat createChatRoom(String roomName, String nickName, String password) { MultiUserChat muc = null; if (!isConnect()) { try { // 创建一个MultiUserChat muc = MultiUserChatManager.getInstanceFor(connection).getMultiUserChat(roomName + "@conference." + connection.getServiceName()); // 创建聊天室 boolean isCreated = muc.createOrJoin(nickName); if (isCreated) { // 获得聊天室的配置表单 Form form = muc.getConfigurationForm(); // 根据原始表单创建一个要提交的新表单。 Form submitForm = form.createAnswerForm(); // 向要提交的表单添加默认答复 List<FormField> fields = form.getFields(); for (int i = 0; fields != null && i < fields.size(); i++) { if (FormField.Type.hidden != fields.get(i).getType() && fields.get(i).getVariable() != null) { // 设置默认值作为答复 submitForm.setDefaultAnswer(fields.get(i).getVariable()); } } // 设置聊天室的新拥有者 List owners = new ArrayList(); owners.add(connection.getUser());// 用户JID submitForm.setAnswer("muc#roomconfig_roomowners", owners); // 设置聊天室是持久聊天室,即将要被保存下来 submitForm.setAnswer("muc#roomconfig_persistentroom", true); // 房间仅对成员开放 submitForm.setAnswer("muc#roomconfig_membersonly", false); // 允许占有者邀请其他人 submitForm.setAnswer("muc#roomconfig_allowinvites", true); if (password != null && password.length() != 0) { // 进入是否需要密码 submitForm.setAnswer("muc#roomconfig_passwordprotectedroom", true); // 设置进入密码 submitForm.setAnswer("muc#roomconfig_roomsecret", password); } // 能够发现占有者真实 JID 的角色 // submitForm.setAnswer("muc#roomconfig_whois", "anyone"); // 登录房间对话 submitForm.setAnswer("muc#roomconfig_enablelogging", true); // 仅允许注册的昵称登录 submitForm.setAnswer("x-muc#roomconfig_reservednick", true); // 允许使用者修改昵称 submitForm.setAnswer("x-muc#roomconfig_canchangenick", false); // 允许用户注册房间 submitForm.setAnswer("x-muc#roomconfig_registration", false); // 发送已完成的表单(有默认值)到服务器来配置聊天室 muc.sendConfigurationForm(submitForm); } } catch (XMPPException | SmackException e) { e.printStackTrace(); return null; } } return muc; }
12. 加入群聊
12.1. public MultiUserChat joinChatRoom(String roomName, String nickName, String password) { if (!isConnect()) { throw new NullPointerException("服务器连接失败,请先连接服务器"); } try { // 使用XMPPConnection创建一个MultiUserChat窗口 MultiUserChat muc = MultiUserChatManager.getInstanceFor(connection). getMultiUserChat(roomName + "@conference." + connection.getServiceName()); // 聊天室服务将会决定要接受的历史记录数量 DiscussionHistory history = new DiscussionHistory(); history.setMaxChars(0); // 用户加入聊天室 muc.join(nickName, password); return muc; } catch (XMPPException | SmackException e) { e.printStackTrace(); return null; } }
13. 发送群聊消息
13.1. public void sendMultiMsg(MultiUserChat multiUserChat, String msg) { try { multiUserChat.sendMessage(msg); } catch (SmackException.NotConnectedException e) { e.printStackTrace(); } }
14. 群聊消息监听
14.1. public void addMultiMessageListener(MultiUserChat multiUserChat) { MessageListener messageListener = new MessageListener() { @Override public void processMessage(Message message) { //获得聊天的消息 String body = message.getBody(); //获得发送者名称 String from = message.getFrom(); } }; //设置聊天室消息监听 multiUserChat.addMessageListener(messageListener); }
15. 创建发送文件
15.1. public OutgoingFileTransfer getSendFileTransfer(String jid) { if (isConnect()) { return FileTransferManager.getInstanceFor(connection).createOutgoingFileTransfer(jid); } return null; }
16. 发送文件
16.1. public void sendFile(File file, String description, String jid) { //获取文件传输对象 OutgoingFileTransfer transfer = getSendFileTransfer(jid); try { //发送文件 transfer.sendFile(file, description); //transfer.sendFile(file.getName(), file.length(), description); //文件传输过程中的状态监听分析 if (transfer.getProgress() < 1) {//开始传输 //传输进度,值为0~1 } while (!transfer.isDone()) {//判断传输是否完成,传输取消、传输完成、传输发生错误都会返回true try { Thread.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); } } if (FileTransfer.Status.complete.equals(transfer.getStatus())) { //传输完成 } else if (FileTransfer.Status.cancelled.equals(transfer.getStatus())) { //传输取消 } else if (FileTransfer.Status.error.equals(transfer.getStatus())) { //传输错误 } else if (FileTransfer.Status.refused.equals(transfer.getStatus())) { //传输拒绝 } } catch (Exception e) { e.printStackTrace(); } }
17. 添加文件接收的监听
17.1. public void addFileTransferListener(final String fileDir) { if (isConnect()) { FileTransferManager.getInstanceFor(connection).addFileTransferListener(new FileTransferListener() { @Override public void fileTransferRequest(FileTransferRequest request) { // Accept it IncomingFileTransfer transfer = request.accept(); try { String description = request.getDescription(); //在目录fileDir目录下新建一个名字为request.getFileName()的文件 File file = new File(fileDir, request.getFileName()); //开始接收文件(将传输过来的文件内容输出到file中) transfer.recieveFile(file); //此处执行文件传输监听 } catch (SmackException | IOException e) { e.printStackTrace(); } } }); } }
18.
18.1.
6. 常用API
1. XMPPTCPConnection
1. (xmpp连接核心类)
2. public XMPPTCPConnection(XMPPTCPConnectionConfiguration config)构造方法
3. public synchronized AbstractXMPPConnection connect()连接服务器
4. public void addConnectionListener(ConnectionListener connectionListener)添加连接监听
2. ConnectionConfiguration(连接配置类)
2.1. XMPPTCPConnectionConfiguration.Builder builder = XMPPTCPConnectionConfiguration.builder();
2.2. builder.setHost(SERVER_HOST) 设置服务器IP地址
2.3. builder.setPort(SERVER_PORT); 设置服务器端口号
2.4. builder.setServiceName(SERVICE_NAME); 设置服务器名称
2.5. builder.setDebuggerEnabled(true);开启dbug模式
2.6. builder.setCompressionEnabled(true); 启用数据压缩
2.7. builder.setConnectTimeout(15 * 1000); 连接超时时间
2.8. builder.setSendPresence(true);是否接受离线消息
2.9. builder.setUsernameAndPassword("username", "password");如果是消息推送 可随机生成用户密码 服务器可配置无密码
2.10. builder.setResource("Android");设置登陆设备标识
2.11. ProxyInfo proxyInfo = new ProxyInfo(ProxyInfo.ProxyType.HTTP, "pHost", 8080,"pUser", "pPass"); builder.setProxyInfo(proxyInfo) /** 设置代理信息*/
2.12. builder.setKeystoreType("AndroidCAStore")设置CA证书
2.12.1. 采用android自带证书 if (Build.VERSION.SDK_INT >= 14) { builder.setKeystoreType("AndroidCAStore"); } else { //java证书 builder.setKeystoreType("BKS"); String path = System.getProperty("javax.net.ssl.trustStore"); if (path == null) { path = System.getProperty("java.home") + File.separator + "etc" + File.separator + "security" + File.separator + "cacerts.bks"; } builder.setKeystorePath(path); }
2.13. builder.allowEmptyOrNullUsernames(); //允许使用空用户名登陆,需要服务器支持
2.14. builder.setCallbackHandler(new CallbackHandler()设置CallbackHandler获取信息,如密码或主要在SASL验证信息 主要用在使用SASL验证没有指定密码在登录时
3. AccountManager(注册用户管理类)
1. AccountManager manager = AccountManager.getInstance(getXMPPTCPConnection());
2. manager.createAccount(userName, pwd); manager.createAccount(userName, pwd, map);
3. 可选参数
3.1. HashMap<String, String> map = new HashMap<>(); map.put("name", ""); map.put("first", ""); map.put("last", ""); map.put("email", ""); map.put("city", ""); map.put("state", ""); map.put("zip", ""); map.put("phone", ""); map.put("date", ""); map.put("misc", ""); map.put("text", ""); map.put("remove", "");
4. Registration(可选参数分装类)
5. deleteAccount();(删除当前用户)
4. XmppConnectionListener(连接监听类)
5. ChatManagerListener(聊天事监听事件)
6. MultiUserChat(多用户聊天)
7. MessageListener(消息监听事件)
8. Presence(用户状态)
8.1. case 0: presence = new Presence(Presence.Type.available); connection.sendPacket(presence); Log.v("state", "设置在线"); break; case 1: presence = new Presence(Presence.Type.available); presence.setMode(Presence.Mode.chat); connection.sendPacket(presence); Log.v("state", "设置Q我吧"); System.out.println(presence.toXML()); break; case 2: presence = new Presence(Presence.Type.available); presence.setMode(Presence.Mode.dnd); connection.sendPacket(presence); Log.v("state", "设置忙碌"); System.out.println(presence.toXML()); break; case 3: presence = new Presence(Presence.Type.available); presence.setMode(Presence.Mode.away); connection.sendPacket(presence); Log.v("state", "设置离开"); System.out.println(presence.toXML()); break; case 4: Roster roster = connection.getRoster(); Collection<RosterEntry> entries = roster.getEntries(); for (RosterEntry entry : entries) { presence = new Presence(Presence.Type.unavailable); presence.setPacketID(Packet.ID_NOT_AVAILABLE); presence.setFrom(connection.getUser()); presence.setTo(entry.getUser()); connection.sendPacket(presence); System.out.println(presence.toXML()); } // 向同一用户的其他客户端发送隐身状态 presence = new Presence(Presence.Type.unavailable); presence.setPacketID(Packet.ID_NOT_AVAILABLE); presence.setFrom(connection.getUser()); presence.setTo(StringUtils.parseBareAddress(connection.getUser())); connection.sendPacket(presence); Log.v("state", "设置隐身"); break; case 5: presence = new Presence(Presence.Type.unavailable); connection.sendPacket(presence); Log.v("state", "设置离线"); break; default: break; }
9. Roster(表示一个用户)
9.1. 获取所有组
9.1.1. /获取所有组 * * * @param roster * @return 所有组集合 */ public static List<RosterGroup> getGroups(Roster roster) { List<RosterGroup> grouplist = new ArrayList<RosterGroup>(); Collection<RosterGroup> rosterGroup = roster.getGroups(); Iterator<RosterGroup> i = rosterGroup.iterator(); while (i.hasNext()) { grouplist.add(i.next()); } return grouplist; }
9.2. 添加分组
9.2.1. public static boolean addGroup(Roster roster, String groupName) { try { roster.createGroup(groupName); return true; } catch (Exception e) { e.printStackTrace(); return false; } }
10. OutgoingFileTransfer(文件传输)
7. 其他资料
1. https://github.com/igniterealtime/Smack/wiki/Smack-4.2-Readme-and-Upgrade-Guide
2. SASL
2.1. 简单验证安全层 (Simple Authentication Security Layer)
2.2. SASL 为应用程序和共享库的开发者提供了用于验证、数据完整性检查和加密的机制。开发者可通过 SASL 对通用 API 进行编码。此方法避免了对特定机制的依赖性。SASL 特别适用于使用 IMAP、SMTP、ACAP 和 LDAP 协议的应用程序,因为这些协议全都支持 SASL
3. JID
3.1. 每个用户的好友列表中有一个JID
3.1.1. 格式:用户账号的JID为:zhangwei
3.2. 好友之间聊天时有一个JID
3.2.1. 格式:用户账号的JID为:zhangwei@127.0.0.1
3.3. 好友之间传输文件时又有一个JID
3.3.1. 格式:用户账号的JID为:zhangwei@127.0.0.1/Smack
第三方
1. 环信即时通讯
1. http://www.easemob.com/
2. 融云即时通信
1. http://www.rongcloud.cn/
3. 百度推聊
4. 极光推送
5. 阿里百川
MQTT
https://mcxiaoke.gitbooks.io/mqtt-cn/content/?hmsr=toutiao.io&utm_medium=toutiao.io&utm_source=toutiao.io
WebSocket
视频直播
音视频的基础知识
基本概念
视频是什么
静止的画面叫图像(picture)。连续的图像变化每秒超过24帧(frame)画面以上时,根椐视觉暂留原理, 人眼无法辨别每付单独的静态画面,看上去是平滑连续的视觉效果。这样的连续画面叫视频。 当连续图像变化每秒低于24帧画面时,人眼有不连续的感觉叫动画(cartoon)
流媒体
指采用流式传输的方式在Internet / Intranet播放的媒体格式.流媒体的数据流随时传送随 时播放,只是在开始时有些延迟 边下载边播入的流式传输方式不仅使启动延时大幅度地缩短,而且对系统缓存容量的需求也大大降低,极大地减少用户用在等待的时间
码流
数据传输时单位时间传送的数据位数,可以理解其为取样率,单位时间内取样率越大,精度就越高,处理出来的文件就越接近原始文件,但是文件体积与取样率是成正比的 如何用最低的码率达到最少的失真,一般我们用的单位是kbps即千位每秒
帧率
帧/秒(frames per second)的缩写,也称为帧速率,测量用于保存、显示动态视频的信息数量。每一帧都是静止的图象,快速连续地显示帧便形成了运动的假象。 每秒钟帧数 (fps) 愈多,所显示的动作就会愈流畅,可理解为1秒钟时间里刷新的图片的帧数,也可以理解为图形处理器每秒钟能够刷新几次,也就是指每秒钟能够播放(或者录制)多少格画面。
多媒体的格式分类
封装格式(专业上讲叫容器,通俗的叫文件格式),视频编解码,音频编解码
MPEG : 编码采用的容器,具有流的特性。里面又分为 PS,TS 等,PS 主要用于 DVD 存储,TS 主要用于 HDTV.
MPEG Audio Layer 3 :大名鼎鼎的 MP3,已经成为网络音频的主流格式,能在 128kbps 的码率接近 CD 音质
MPEG-4(Mp4) : 编码采用的容器,基于 QuickTime MOV 开发,具有许多先进特性;实际上是对Apple公司开发的MOV格式(也称Quicktime格式)的一种改进.
MKV: 它能把 Windows Media Video,RealVideo,MPEG-4 等视频音频融为一个文件,而且支持多音轨,支持章节字幕等;开源的容器格式
3GP : 3GPP视频采用的格式, 主要用于流媒体传送;3GP其实是MP4格式的一种简化版本,是手机视频格式的绝对主流.
MOV : QuickTime 的容器,恐怕也是现今最强大的容器,甚至支持虚拟现实技术,Java等,它的变种 MP4,3GP都没有这么厉害;广泛应用于Mac OS操作系统,在Windows操作系统上也可兼容,但是远比不上AVI格式流行
WAV : 一种音频容器,大家常说的 WAV 就是没有压缩的 PCM 编码,其实 WAV 里面还可以包括 MP3 等其他 ACM 压缩编码等等
流媒体协议
RTCP
Real-time Transport Control Protocol或RTP Control Protocol或简写RTCP)实时传输控制协议,是实时传输协议(RTP)的一个姐妹协议RTP协议和RTP控制协议RTCP一起使用,而且它是建立在UDP协议上的传输时所用的网络通讯协定并不在其定义的范围内,服务器端可以自行选择使用TCP或UDP来传送串流内容,比较能容忍网络延迟
RTSP:
(Real Time Streaming Protocol)是用来控制声音或影像的多媒体串流协议,RTSP提供了一个可扩展框架,使实时数据,如音频与视频的受控、点播成为可能。数据源包括现场数据与存储在剪辑中的数据。该协议目的在于控制多个数据发送连接,为选择发送通道,如UDP、多播UDP与TCP提供途径,并为选择基于RTP上发送机制提供方法传输时所用的网络通讯协定并不在其定义的范围内,服务器端可以自行选择使用TCP或UDP来传送串流内容,比较能容忍网络延迟
RTSP与RTP区别
RTSP是一种双向实时数据传输协议,它允许客户端向服务器端发送请求,如回放、快进、倒退等操作。当然,RTSP可基于RTP来传送数据,还可以选择TCP、UDP、组播UDP等通道来发送数据,具有很好的扩展性。它时一种类似与http协议的网络应用层协议
RTMP
RTMP是Real Time Messaging Protocol(实时消息传输协议)的首字母缩写。该协议基于TCP,是一个协议族,包括RTMP基本协议及RTMPT/RTMPS/RTMPE等多种变种。RTMP是一种设计用来进行实时数据通信的网络协议,主要用来在Flash/AIR平台和支持RTMP协议的流媒体/交互服务器之间进行音视频和数据通信。支持该协议的软件包括Adobe Media Server/Ultrant Media Server/red5等。
详细介绍
http://blog.csdn.net/tttyd/article/details/12032357
Android音视频的开发
流程
播放流程: 获取流-->解码-->播放
录制播放路程: 录制音频视频-->剪辑-->编码-->上传服务器 别人播放.
直播过程 : 录制音视频-->编码-->流媒体传输-->服务器--->流媒体传输到其他app-->解码-->播放
主要环节
录制音视频 AudioRecord/MediaRecord
视频剪辑 mp4parser 或ffmpeg
音视频编码 aac&h264
上传大文件 网络框架,进度监听,断点续传
流媒体传输 流媒体传输协议rtmp rtsp hls
音视频解码 aac&h264
渲染播放 MediaPlayer
主流开源框架
Vitamio
vitamio这个是功能很强大视频播放库,但是企业收费版的,个人用户免费
webRTC
只适合小范围(8人以内)音视频会议
ffmpeg
ffmpeg是一个非常强大的音视频编解码开源库,目前市场上流行的播放器,大部分都是基于此开发的,包括暴风,腾讯,等等以及上面提到的vitamio,vlc,ijkplayer
vlc
ijkplayer
https://github.com/Bilibili/ijkplayer
参考http://www.jianshu.com/p/7d9b86919682
使用
导入相应的JAR
1. compile 'tv.danmaku.ijk.media:ijkplayer-java:0.5.1'
2. compile 'tv.danmaku.ijk.media:ijkplayer-armv7a:0.5.1'
3. compile 'tv.danmaku.ijk.media:ijkplayer-armv5:0.5.1'
4. compile 'tv.danmaku.ijk.media:ijkplayer-arm64:0.5.1'
5. compile 'tv.danmaku.ijk.media:ijkplayer-x86:0.5.1'
6. compile 'tv.danmaku.ijk.media:ijkplayer-x86_64:0.5.1'
7. compile 'tv.danmaku.ijk.media:ijkplayer-exo:0.5.1'
8. 或者你也可以更改部分源码,然后再像上面说的那样编译,最后把各个平台的so加入进来。
9. 不过不管你如何更改,这几个必须是得导入的:
10. compile 'tv.danmaku.ijk.media:ijkplayer-armv7a:0.5.1'
11. 下面这几个是不同平台的链接库,根据需要可以适当删除:
12. compile 'tv.danmaku.ijk.media:ijkplayer-armv5:0.5.1'
13. compile 'tv.danmaku.ijk.media:ijkplayer-arm64:0.5.1'
14. compile 'tv.danmaku.ijk.media:ijkplayer-x86:0.5.1'
15. compile 'tv.danmaku.ijk.media:ijkplayer-x86_64:0.5.1'
16. 下面这个是一个MediaPlayer,因为我们后面可以在settings下设置用不同player来渲染多媒体显示
17. compile 'tv.danmaku.ijk.media:ijkplayer-exo:0.5.1'
代码
mSettings = new Settings(this); videoView = (IjkVideoView) findViewById(R.id.videoview); // init player IjkMediaPlayer.loadLibrariesOnce(null); IjkMediaPlayer.native_profileBegin("libijkplayer.so"); videoView.setVideoURI(Uri.parse("http://106.36.45.36/live.aishang.ctlcdn.com/00000110240001_1/encoder/1/playlist.m3u8")); videoView.setOnPreparedListener(new IMediaPlayer.OnPreparedListener() { @Override public void onPrepared(IMediaPlayer mp) { videoView.start(); } });
第三放
暴风
腾讯
网易
http://vcloud.163.com/vcloud-sdk-manual/LiveStreaming_Android/sdk/android_livestreaming.html
百度云推流
https://bce.baidu.com/doc/LSS/Android-Capture-SDK.html
rtmp
简介
RTMP协议是一个互联网TCP/IP五层体系结构中应用层的协议。RTMP协议中基本的数据单元称为消息(Message)。当RTMP协议在互联网中传输数据的时候,消息会被拆分成更小的单元,称为消息块
子主题 2
nginx+rtmp服务器搭建
安装Homebrow 已经安装了brow的可以直接跳过这一步。
安装
ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
卸载
ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/uninstall)"
安装nginx
先glone nginx项目到本地:
brew tap homebrew/nginx
执行安装:
brew install nginx-full --with-rtmp-module
推流
ffmpeg(软编码)
视频播放