大橙子网站建设,新征程启航
为企业提供网站建设、域名注册、服务器等服务
本篇文章针对向ServiceManager注册服务 和 获取服务两个流程来做总结。在这两个过程中,ServiceManager都扮演的是服务端,与客户端之间的通信也是通过Binder IPC。
德兴网站建设公司创新互联公司,德兴网站设计制作,有大型网站制作公司丰富经验。已为德兴近1000家提供企业网站建设服务。企业网站搭建\外贸网站制作要多少钱,请找那个售后服务好的德兴做网站的公司定做!
在此之前先了解下Binder的进程与线程的关系:
用户空间 :ProcessState描述一个进程,IPCThreadState对应一个进程中的一个线程。
内核空间 :binder_proc描述一个进程,统一由binder_procs全局链表保存,binder_thread对应进程的一个线程。
ProcessState与binder_proc是一一对应的。
Binder线程池 :每个Server进程在启动时会创建一个binder线程池,并向其中注册一个Binder线程;之后Server进程也可以向binder线程池注册新的线程,或者Binder驱动在探测到没有空闲binder线程时会主动向Server进程注册新的的binder线程。对于一个Server进程有一个最大Binder线程数限制15,(#define DEFAULT_MAX_BINDER_THREADS 15)。对于所有Client端进程的binder请求都是交由Server端进程的binder线程来处理的。我的理解是:binder线程是进程进行binder ipc时的一条数据处理路径。
MediaPlayerService向ServiceManager注册过程如下:
相关类:
整个过程总结如下:
1 获取BpServiceManager 与 BpBinder
由defaultServiceManager()返回的是BpServiceManager,同时会创建ProcessState对象和BpBinder对象。然后通过BpBinder执行transact,把真正工作交给IPCThreadState来处理。
2 BpBinder transact
Binder代理类调用transact()方法,真正工作还是交给IPCThreadState来进行transact工作。
3 通过IPCThreadState 包装并转换数据并进行transact事务处理
每个线程都有一个IPCThreadState,每个IPCThreadState中都有一对Parcel变量:mIn、mOut。相当于两根数据管道:
最后执行talkWithDriver。
writeTransactionData:将BC Protocol + binder_transaction_data结构体 写入mOut, 然后执行waitForResponse:
由talkWithDriver将数据进一步封装到binder_write_read结构体,通过ioctl(BINDER_WRITE_READ)与驱动通信。同时等待驱动返回的接收BR命令,从mIn取出返回的数据。
mIn包装的数据结构(注册服务handle = 0 ,code 为ADD_SERVICE_TRANSACTION):
4 Binder Driver
把binder_write_read结构体write_buffer里数据取出来,分别得到BC命令和封装好数据的事务binder_transaction_data, 然后根据handler,在当前binder_proc中,找到相应的binder_ref,由binder_ref再找到目标binder_node实体,由目标binder_node再找到目标进程binder_proc。然后就是插入数据:当binder驱动可以找到合适的线程,就会把binder_transaction节点插入到servciemanager的线程的todo队列中,如果找不到合适的线程,就把节点之间插入servciemanager的binder_proc的todo队列。
5 ServiceManager
经过Binder Driver的处理,数据已经到了ServiceManager进程,在BR_TRANSACTION的引导下,在binder_loop()中执行binder_parser()取出数据,执行do_add_service()操作,最终向 svcinfo 列表中添加已经注册的服务(没有数据的返回)。最后发送 BR_REPLY 命令唤醒等待的线程,通知注册成功。结束MediaPlayerService进程 waitForResponse()的状态,整个注册过程结束。
获取服务的过程与注册类似,首先 ServiceManager 向 Binder 驱动发送 BC_TRANSACTION 命令携带 CHECK_SERVICE_TRANSACTION 命令,同时获取服务的线程进入等待状态 waitForResponse()。Binder 驱动收到请求命令向 ServiceManager 的发送 BC_TRANSACTION 查询已注册的服务,会区分请求服务所属进程情况。
查询到直接响应 BR_REPLY 唤醒等待的线程。若查询不到将与 binder_procs 链表中的服务进行一次通讯再响应。
以startService为例来简单总结下执行流程:
3.1 从方法执行流程来看:
Client :
1 AMP.startService 标记方法以及通过Parcel包装数据;
2 BinderProxy.transact 实际调用native的 android_os_BinderProxy_transact 传递数据;
3 获取BpServiceManager 与 BpBinder 同时会创建ProcessState。然后通过BpBinder执行transact,把真正工作交给IPCThreadState来处理;
4 IPC.transact 主要执行writeTransactionData,将上层传来的数据重新包装成binder_transaction_data,并将BC Protocol + binder_transaction_data结构体 写入mOut;
5 IPC waitForResponse talkWithDriver + 等待返回数据;
6 talkWithDriver 将数据进一步封装成binder_write_read,通过ioctl(BINDER_WRITE_READ)与驱动通信;
Kernel :
7 binder ioctl 接收BINDER_WRITE_READ ioctl命令;
8 binder_ioctl_write_read 把用户空间数据ubuf拷贝到内核空间bwr;
9 binder_thread_write 当bwr写缓存有数据,则执行binder_thread_write;当写失败则将bwr数据写回用户空间并退出;
10 binder_transaction 找到目标进程binder_proc并插入数据到目标进程的线程todo队列,最终执行到它
时,将发起端数据拷贝到接收端进程的buffer结构体;
11 binder_thread_read 根据binder_transaction结构体和binder_buffer结构体数据生成新的binder_transaction_data结构体,写入bwr的read_buffer,当bwr读缓存有数据,则执行binder_thread_read;当读失败则再将bwr数据写回用户空间并退出;最后,把内核数据bwr拷贝到用户空间ubuf。
12 binder_thread_write + binder_ioctl BR命令和数据传递
Server:
13 IPC.executeCommand 解析kernel传过来的binder_transaction_data数据,找到目标BBinder并调用其transact()方法;
14 IPC.joinThreadPool 采用循环不断地执行getAndExecuteCommand()方法, 处理事务。当bwr的读写buffer都没有数据时,则阻塞在binder_thread_read的wait_event过程. 另外,正常情况下binder线程一旦创建则不会退出.
15 BBinder.transact 到Binder.exeTransact 调用 AMN.onTransact
16 AMN.onTransact 把数据传递到AMS.starService去执行
17 AMS.starService Server处理了Client的请求了
然后原路replay回去,talkWithDriver 到Kernel ,然后找到Client进程,把数据拷贝到read_buffer里,最终唤醒IPC,把反馈传递回AMP.startService。完成启动服务。
3.2 从通信协议流程来看:
非oneWay:
oneway:
oneway与非oneway区别: 都是需要等待Binder Driver的回应消息BR_TRANSACTION_COMPLETE. 主要区别在于oneway的通信收到BR_TRANSACTION_COMPLETE则返回,而不会再等待BR_REPLY消息的到来. 另外,oneway的binder IPC则接收端无法获取对方的pid.
3.3 从数据流来看
从用户空间开始:
进入驱动后:
回到用户空间:
参考:
由身份或持有的令牌确认享有的权限,登录过程实质上的目的也是为了确认权限。
Cookie是客户端给服务器用的,setCookie是服务器给客户端用的。cookie由服务器处理,客户端负责存储
客户端发送cookie:账户和密码
服务端收到后确认登录setCookie:sessionID=1,记下sessionID
客户端收到sessionID后记录,以后请求服务端带上对比记录下sessionID,说明已经登录
会话管理:登录状态,购物车
个性化:用户偏好,主题
Tracking:分析用户行为
XXS:跨脚本攻击,及使用JavaScript拿到浏览器的cookie之后,发送到自己的网站,以这种方式来盗用用户Cookie。Server在发送Cookie时,敏感的Cookie加上HttpOnly,这样Cookie只能用于http请求,不能被JavaScript调用
XSRF:跨站请求伪造。Referer 从哪个网站跳转过来
两种方式:Basic和Bearer
首先第三方网站向授权网站申请第三方授权合作,拿到授权方颁发的client_id和client_secret(一般都是appid+appkey的方式)。
在这就过程中申请的client_secret是服务器持有的,安全起见不能给客户端,用服务端去和授权方获取用户信息,再传给客户端,包括④,⑤的请求过程也是需要加密的。这才是标准的授权过程。
有了access_token之后,就可以向授权方发送请求来获取用户信息
步骤分析就是上面的内容,这里把第4,6,8请求的参数分析一下
第④步参数:
response_type:指授权类型,必选,这里填固定值‘code’
client_id:指客户端id,必选,这里填在平台报备时获取的appid
redirect_uri:指重定向URI,可选
scope:指申请的权限范围,可选
state:指客户端当前状态,可选,若填了,则认证服务器会原样返回该值
第⑥步参数:
grant_type:指使用哪种授权模式,必选,这里填固定值‘authorization_code’
code:指从第⑤步获取的code,必选
redirect_uri:指重定向URI,必选,这个值需要和第④步中的redirect_uri保持一致
client_id:指客户端id,必选,这里填在平台报备时获取的appid
client_secret:指客户端密钥,必选,这里填在平台报备时获取的appkey
第⑧步参数:
access_token:指访问令牌,必选,这里填第⑦步获取的access_token
token_type:指令牌类型,必选,大小写不敏感,bearer类型 / mac类型
expires_in:指过期时间,单位秒,当其他地方已设置过期时间,此处可省略该参数
refresh_token:指更新令牌,可选,用即将过期token换取新token
scope:指权限范围,可选,第④步中若已申请过某权限,此处可省略该参数
我们在上面的第八步中会有refresh_token的参数,这个在实际操作中也比较常见
有时候我们在自己的项目中,将登陆和授权设计成类似OAuth2的过程,不过去掉Authorization code。登陆成功返回access_token,然后客户端再请求时,带上access_token。
我们常常会说到TCP/IP,那到底是什么呢。这就需要讲到网络分层模型。TCP在传输层,IP在网络层。那为什么需要分层?因为网络不稳定,导致需要重传的问题。为了提高传输效率我们就需要分块,在传输层中就会进行分块。TCP还有两个重要的内容就是三次握手,四次分手。
HTTPS 协议是由 HTTP 加上TLS/SSL协议构建的可进行加密传输、身份认证的网络协议,主要通过数字证书、加密算法、非对称密钥等技术完成互联网数据传输加密,实现互联网传输安全保护
1.客户端通过发送Client Hello报文开始SSL通信。报文中包含客户端支持的SSL的指定版本、加密组件列表(所使用的加密算法及密钥长度),客户端随机数,hash算法。
2.服务器可进行SSL通信时,会以Server Hello报文作为应答。和客户端一样,在报文中包含SSL版本以及加密组件,服务端随机数。服务器的加密组件内容是从接收到客户端加密组件内筛选出来的。
3.之后服务器发送Certificate报文。报文中包含公开密钥证书。一般实际有三层证书嵌套,其实像下面图二直接用根证书机构签名也是可以的,但是一般根证书机构比较忙,需要类似中介的证书机构来帮助。
4.最后服务器发送Server Hello Done报文通知客户端,最初阶段的SSL握手协商部分结束。
5.SSL第一次握手结束后,客户端以Client Key Exchange报文作为回应。报文中包含通信加密中使用的一种被称为Pre-master secret的随机密码串。该报文已用步骤3中的公开密钥进行加密。
6.接着客户端继续发送Change Cipher Spec报文。该报文会提示服务器,在此报文之后的通信会采用Pre-master secret密钥加密。
7.客户端发送Finished报文。该报文包含连接至今全部报文的整体校验值。这次握手协商是否能够成功,要以服务器是否能够正确解密报文作为判定标准。
8.服务器同样发送Change Cipher Spec报文。
9.服务器同样发送Finished报文。
10.服务器和客户端的Finished报文交换完毕之后,SSL连接就算建立完成。当然,通信会受到SSL的保护。从此处开始进行应用层协议的通信,即发送HTTP响应。
11.应用层协议通信,即发送HTTP响应。
12.最后由客户端断开连接。断开连接时,发送close_notify报文。这步之后再发送TCP FIN报文来关闭与TCP的通信。
利用客户端随机数,服务端随机数,per-master secret随机数生成master secret,再生成客户端加密密钥,服务端加密密钥,客户端MAC secert,服务端MAC secert。MAC secert用于做报文摘要,这样能够查知报文是否遭到篡改,从而保护报文的完整性。
Android网络请求知识(一)HTTP基础概念
Android网络请求知识(二)对称和非对称加密、数字签名,Hash,Base64编码
Android网络请求知识(三)授权,TCP/IP,HTTPS建立过程
android socket通信协议的封装和解析,其实是和java一样的,都是通过http中的相关知识来封装和解析,主要是通过多次握手,如下代码:
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Main {
private static final int PORT = 9999;
private ListSocket mList = new ArrayListSocket();
private ServerSocket server = null;
private ExecutorService mExecutorService = null; //thread pool
public static void main(String[] args) {
new Main();
}
public Main() {
try {
server = new ServerSocket(PORT);
mExecutorService = Executors.newCachedThreadPool(); //create a thread pool
System.out.println("服务器已启动...");
Socket client = null;
while(true) {
client = server.accept();
//把客户端放入客户端集合中
mList.add(client);
mExecutorService.execute(new Service(client)); //start a new thread to handle the connection
}
}catch (Exception e) {
e.printStackTrace();
}
}
class Service implements Runnable {
private Socket socket;
private BufferedReader in = null;
private String msg = "";
public Service(Socket socket) {
this.socket = socket;
try {
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
//客户端只要一连到服务器,便向客户端发送下面的信息。
msg = "服务器地址:" +this.socket.getInetAddress() + "come toal:"
+mList.size()+"(服务器发送)";
this.sendmsg();
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void run() {
try {
while(true) {
if((msg = in.readLine())!= null) {
//当客户端发送的信息为:exit时,关闭连接
if(msg.equals("exit")) {
System.out.println("ssssssss");
mList.remove(socket);
in.close();
msg = "user:" + socket.getInetAddress()
+ "exit total:" + mList.size();
socket.close();
this.sendmsg();
break;
//接收客户端发过来的信息msg,然后发送给客户端。
} else {
msg = socket.getInetAddress() + ":" + msg+"(服务器发送)";
this.sendmsg();
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 循环遍历客户端集合,给每个客户端都发送信息。
*/
public void sendmsg() {
System.out.println(msg);
int num =mList.size();
for (int index = 0; index num; index ++) {
Socket mSocket = mList.get(index);
PrintWriter pout = null;
try {
pout = new PrintWriter(new BufferedWriter(
new OutputStreamWriter(mSocket.getOutputStream())),true);
pout.println(msg);
}catch (IOException e) {
e.printStackTrace();
}
}
}
}
}