AIDL:Android Interface Definition Language,即 Android 接口定义语言。

AIDL 是什么

Android 系统中的进程之间不能共享内存,因此,需要提供一些机制在不同进程之间进行数据通信。

为了使其他的应用程序也可以访问本应用程序提供的服务,Android 系统采用了远程过程调用(Remote Procedure Call,RPC)方式来实现。与很多其他的基于 RPC 的解决方案一样,Android 使用一种接口定义语言(Interface Definition Language,IDL)来公开服务的接口。我们知道 Android 四大组件中的 3 种(Activity、BroadcastReceiver和ContentProvider)都可以进行跨进程访问,另外一种 Android 组件 Service 同样可以。因此,可以将这种可以跨进程访问的服务称为 AIDL(Android Interface Definition Language)服务。

在介绍 AIDL 的使用以及其它特性前,我们先来了解下 AIDL 的核心——Binder

Android 的 Binder 机制浅析

看过一些关于 Binder 的文章,总得来说 Binder 机制的底层实现很复杂,相当复杂,要完全搞清楚,得花大量的时间。从某种角度来说,个人觉得,对于 Binder,我们只需要了解其上层原理以及使用方法即可。

直观来看,从代码的角度来说,Binder 是 Android 系统源码中的一个类,它实现了 IBinder 接口;从 IPC 角度来说,Binder 是 Android 中的一种跨进程通信方式;从 Android Framework 角度来讲,Binder 是 ServiceManager 连接各种 Manager(ActivityManager、WindowManager 等等)和相应 ManagerService 的桥梁;从 Android 应用层来说,Binder 是客户端和服务端进行通信的媒介,当 bindService 的时候,服务端会返回一个包含了服务端业务调用的 Binder 对象,通过这个 Binder 对象,客户端就可以和服务端进行通信,这里的服务包括普通服务和基于 AIDL 的服务。

接下来,我们通过一个 AIDL 示例,来分析 Binder 的工作机制。在工程目录中新建一个名为 aidl 的 package,然后新建 Book.Java、Book.aidl(创建此文件时,as 会提示已存在,需要先用其它命令,创建成功后再重命名为 Book.aidl )和 IBookManager.aidl,代码如下:

  1. // Book.java
  2. package com.cy.ipcsample.aidl;
  3.  
  4. import android.os.Parcel;
  5. import android.os.Parcelable;
  6.  
  7. /**
  8. * 数据类
  9. * @author cspecialy
  10. * @version v1.0.0
  11. * @date 2018/5/14 21:38
  12. */
  13. public class Book implements Parcelable {
  14.  
  15. public int bookId;
  16. public String bookName;
  17.  
  18. public Book(int bookId, String bookName) {
  19. this.bookId = bookId;
  20. this.bookName = bookName;
  21. }
  22.  
  23. protected Book(Parcel in) {
  24. bookId = in.readInt();
  25. bookName = in.readString();
  26. }
  27.  
  28. public static final Creator<Book> CREATOR = new Creator<Book>() {
  29. @Override
  30. public Book createFromParcel(Parcel in) {
  31. return new Book(in);
  32. }
  33.  
  34. @Override
  35. public Book[] newArray(int size) {
  36. return new Book[size];
  37. }
  38. };
  39.  
  40. @Override
  41. public int describeContents() {
  42. return 0;
  43. }
  44.  
  45. @Override
  46. public void writeToParcel(Parcel parcel, int i) {
  47. parcel.writeInt(bookId);
  48. parcel.writeString(bookName);
  49. }
  50.  
  51. @Override
  52. public String toString() {
  53. return "Book{" +
  54. "bookId=" + bookId +
  55. ", bookName='" + bookName + '\'' +
  56. '}';
  57. }
  58. }
  1. // Book.aidl
  2. package com.cy.ipcsample.aidl;
  3.  
  4. parcelable Book;/
  1. // IBookManager.aidl
  2. package com.cy.ipcsample.aidl;
  3.  
  4. import com.cy.ipcsample.aidl.Book;
  5. import com.cy.ipcsample.aidl.IOnNewBookArrivedListener;
  6.  
  7. interface IBookManager {
  8. List<Book> getBookList();
  9. void addBook(in Book book);
  10. void registerListener(IOnNewBookArrivedListener listener);
  11. void unRegisterListener(IOnNewBookArrivedListener listener);
  12. }
  1. 创建好三个文件之后,编译一下,as 会在app/build/generated/source/aidl/debug 目录下的 com.cy.ipcsample 包中生成一个名为 IBookManager.Java 的类,如下图所示:

这是系统生成的 Binder 类,接下来我们要利用这个类来分析 Binder 的工作原理。其代码如下:(生成的代码格式很乱,可以格式化代码之后看)

  1. /*
  2. * This file is auto-generated. DO NOT MODIFY.
  3. * Original file: G:\\Android\\Github\\Bugly-Android-Demo\\sample\\ipcsample\\src\\main\\aidl\\com\\cy\\ipcsample
  4. * \\aidl\\IBookManager.aidl
  5. */
  6. package com.cy.ipcsample.aidl;
  7.  
  8. public interface IBookManager extends android.os.IInterface {
  9. public java.util.List<com.cy.ipcsample.aidl.Book> getBookList() throws android.os.RemoteException;
  10.  
  11. public void addBook(com.cy.ipcsample.aidl.Book book) throws android.os.RemoteException;
  12.  
  13. /** Local-side IPC implementation stub class. */
  14. public static abstract class Stub extends android.os.Binder implements com.cy.ipcsample.aidl.IBookManager {
  15. static final int TRANSACTION_getBookList = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
  16. static final int TRANSACTION_addBook = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
  17. private static final java.lang.String DESCRIPTOR = "com.cy.ipcsample.aidl.IBookManager";
  18.  
  19. /** Construct the stub at attach it to the interface. */
  20. public Stub() {
  21. this.attachInterface(this, DESCRIPTOR);
  22. }
  23.  
  24. /**
  25. * Cast an IBinder object into an com.cy.ipcsample.aidl.IBookManager interface,
  26. * generating a proxy if needed.
  27. */
  28. public static com.cy.ipcsample.aidl.IBookManager asInterface(android.os.IBinder obj) {
  29. if ((obj == null)) {
  30. return null;
  31. }
  32. android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
  33. if (((iin != null) && (iin instanceof com.cy.ipcsample.aidl.IBookManager))) {
  34. return ((com.cy.ipcsample.aidl.IBookManager) iin);
  35. }
  36. return new com.cy.ipcsample.aidl.IBookManager.Stub.Proxy(obj);
  37. }
  38.  
  39. @Override
  40. public android.os.IBinder asBinder() {
  41. return this;
  42. }
  43.  
  44. @Override
  45. public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws
  46. android.os.RemoteException {
  47. switch (code) {
  48. case INTERFACE_TRANSACTION: {
  49. reply.writeString(DESCRIPTOR);
  50. return true;
  51. }
  52. case TRANSACTION_getBookList: {
  53. data.enforceInterface(DESCRIPTOR);
  54. java.util.List<com.cy.ipcsample.aidl.Book> _result = this.getBookList();
  55. reply.writeNoException();
  56. reply.writeTypedList(_result);
  57. return true;
  58. }
  59. case TRANSACTION_addBook: {
  60. data.enforceInterface(DESCRIPTOR);
  61. com.cy.ipcsample.aidl.Book _arg0;
  62. if ((0 != data.readInt())) {
  63. _arg0 = com.cy.ipcsample.aidl.Book.CREATOR.createFromParcel(data);
  64. } else {
  65. _arg0 = null;
  66. }
  67. this.addBook(_arg0);
  68. reply.writeNoException();
  69. return true;
  70. }
  71. }
  72. return super.onTransact(code, data, reply, flags);
  73. }
  74.  
  75. private static class Proxy implements com.cy.ipcsample.aidl.IBookManager {
  76. private android.os.IBinder mRemote;
  77.  
  78. Proxy(android.os.IBinder remote) {
  79. mRemote = remote;
  80. }
  81.  
  82. public java.lang.String getInterfaceDescriptor() {
  83. return DESCRIPTOR;
  84. } @Override
  85. public android.os.IBinder asBinder() {
  86. return mRemote;
  87. }
  88.  
  89. @Override
  90. public java.util.List<com.cy.ipcsample.aidl.Book> getBookList() throws android.os.RemoteException {
  91. android.os.Parcel _data = android.os.Parcel.obtain();
  92. android.os.Parcel _reply = android.os.Parcel.obtain();
  93. java.util.List<com.cy.ipcsample.aidl.Book> _result;
  94. try {
  95. _data.writeInterfaceToken(DESCRIPTOR);
  96. mRemote.transact(Stub.TRANSACTION_getBookList, _data, _reply, 0);
  97. _reply.readException();
  98. _result = _reply.createTypedArrayList(com.cy.ipcsample.aidl.Book.CREATOR);
  99. } finally {
  100. _reply.recycle();
  101. _data.recycle();
  102. }
  103. return _result;
  104. }
  105.  
  106. @Override
  107. public void addBook(com.cy.ipcsample.aidl.Book book) throws android.os.RemoteException {
  108. android.os.Parcel _data = android.os.Parcel.obtain();
  109. android.os.Parcel _reply = android.os.Parcel.obtain();
  110. try {
  111. _data.writeInterfaceToken(DESCRIPTOR);
  112. if ((book != null)) {
  113. _data.writeInt(1);
  114. book.writeToParcel(_data, 0);
  115. } else {
  116. _data.writeInt(0);
  117. }
  118. mRemote.transact(Stub.TRANSACTION_addBook, _data, _reply, 0);
  119. _reply.readException();
  120. } finally {
  121. _reply.recycle();
  122. _data.recycle();
  123. }
  124. }
  125.  
  126. }
  127. }
  128. }

可见,系统为我们生成了一个 IBookManager 接口,它继承了 IInterface 这个接口,所以这里要注意下,所有可以在 Binder 中传输的接口,都需要继承 IInterface 接口。

接下来分析下,该类的工作机制。仔细看,可以发现,该类主要分成三个部分:

  • 定义自身方法( getBookList 方法和 addBook 方法);
  • 内部静态类-Stub,该类继承 Binder,同时实现 IBookManager 接口
  • Stub的内部代理类-Proxy,也实现 IBookManager 接口

第一部分我们不用管它,主要看 Stub 类和 Proxy 类。在 Stub 中,首先声明了两个用于标识 IBookManager 方法的整型变量,这两个变量用于标识在 transact 过程中客户端所请求的是哪个方法。接着,是 asInterface 方法,该方法用于将服务端的 Binder 对象转换成客户端所需的 AIDL 接口类型的对象,该方法通过调用 Binder 的 queryLocalInterface 方法,判断客户端和服务端是否处于同一进程,如果客户端、服务端处于同一进程,那么此方法直接返回服务端的 Stub,否则,返回 Stub 的代理对象 Proxy。queryLocalInterface 实现如下:

  1. /**
  2. * Use information supplied to attachInterface() to return the
  3. * associated IInterface if it matches the requested
  4. * descriptor.
  5. */
  6. public @Nullable IInterface queryLocalInterface(@NonNull String descriptor) {
  7. if (mDescriptor.equals(descriptor)) {
  8. return mOwner;
  9. }
  10. return null;
  11. }

mOwner 是在 Stub 构造函数中传进去的 this 参数。

接下来是 Stub 的代理类 Stub.Proxy,由上面的分析可知,Stub.Proxy 是运行在客户端的(由 asInterface 方法返回给客户端的对象),Stub.Proxy对象创建后,持有服务端的 Binder 对象,用于客户端请求时调用服务端方法进行远程调用。客户端在向服务端发起请求时,调用 Stub.Proxy 的相应方法,Stub.Proxy 方法的流程如下:

  • 首先创建该方法所需要的输入型 Parcel 对象 _data、输出型对象 _reply 和返回值对象(如果有);
  • 然后把该方法的参数信息写入 _data 中(如果有);
  • 接着调用 transact 方法进行 RPC(远程过程调用)请求,同时当前线程挂起;
  • 然后在 transact 方法通过服务端的 Binder 对象调用服务端的 onTransact 方法,即 Stub 中的 onTransact 方法;
  • onTransact 方法返回后,当前线程继续执行,并从 _reply 中取出 RPC 过程返回的结果;
  • 最后返回 _reply 中的数据(如果客户端请求方法需要返回值)。

以上,就是系统生成的 IBookManager 的工作过程,需要注意下,服务端的 onTransact 方法是运行在 Binder 线程池中的。由于 IBookManager.Stub 类继承 Binder,所以上述分析即 Binder 的工作机制,简单总结下:

  • 在客户端和服务端连接时(一般通过 bindService 方法),服务端通过 asInterface 方法给客户端返回一个 IInterface 接口类型的对象(如果客户端和服务端在同一个进程,则返回服务端的 Binder 对象,否则返回 服务端 Binder 对象的代理);
  • 客户端通过该对象向服务端进行请求,如果客户端和服务端不在同一个进程,则通过该对象所代理的 Binder 对象进行 RPC 请求,否则,直接通过 Binder 对象调用服务端相应方法。

或者参考下图理解下:

由此可见,Binder 在 AIDL 中承载着重要的职能,是 AIDL 的核心,理解了 Binder 的工作机制,其实在很多方面都很有用。

AIDL 的使用

一套 AIDL 服务搭建的步骤如下:

  • 创建 .aidl 文件,系统生成相应的继承 IInterface 的接口类(暴露给客户端的接口)。
  • 创建一个 Service(服务端),实现 .aidl 文件中的接口。
  • 创建客户端,绑定服务端的 Service。
  • 客户端绑定服务端成功后,将服务端返回的 Binder 对象转成 AIDL 接口所属的 IInterface 类型。调用 AIDL 中的方法,实现和服务端的通信。

接下来,我们使用上面的 IBookManager 来实现 AIDL。

  1. 创建 .aidl 文件
    直接使用上面创建好的 Book.aidl、IBookManager.aidl 文件即可
  2. 创建服务端
    创建一个 Service,命名为 BookManagerService,代码如下:

    1. package com.cy.ipcsample.aidl
    2.  
    3. import android.app.Service
    4. import android.content.Intent
    5. import android.os.IBinder
    6. import android.os.RemoteCallbackList
    7. import android.util.Log
    8. import java.util.concurrent.CopyOnWriteArrayList
    9.  
    10. class BookManagerService : Service() {
    11. private val TAG = "BookManagerService"
    12.  
    13. private val mBookList = CopyOnWriteArrayList<Book>()
    14. private val mListenerList = RemoteCallbackList<IOnNewBookArrivedListener>()
    15.  
    16. /**
    17. * 实现 AIDL 接口的 Binder 对象,客户端绑定服务端时,直接返回此 Binder 对象
    18. */
    19. private val mBinder = object : IBookManager.Stub() {
    20.  
    21. override fun getBookList(): MutableList<Book> {
    22. return mBookList
    23. }
    24.  
    25. override fun addBook(book: Book?) {
    26. mBookList.add(book)
    27. }
    28.  
    29. }
    30.  
    31. override fun onBind(intent: Intent): IBinder {
    32. return mBinder
    33. }
    34.  
    35. override fun onCreate() {
    36. super.onCreate()
    37.  
    38. // 创建两本图书
    39. mBookList.add(Book(1, "Android"))
    40. mBookList.add(Book(2, "iOS"))
    41. }
    42. }

    然后在 AndroidManifest 中注册 Service,注意启动多进程:

    1. <service
    2. android:name=".aidl.BookManagerService"
    3. android:process="com.cy.ipcsample.bookManagerService">
    4. </service>
  3. 客户端绑定服务端的 Service
    客户端的创建,直接使用 Activity 即可,绑定远程服务的代码如下:

    1. private val mConnection = object : ServiceConnection {
    2.  
    3. override fun onServiceDisconnected(name: ComponentName?) {
    4. }
    5.  
    6. /**
    7. * 连接远程服务成功的回调
    8. */
    9. override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
    10. }
    11.  
    12. }
    13.  
    14. // 绑定远程服务
    15. bindService(Intent(this, BookManagerService::class.java),
    16. mConnection, Context.BIND_AUTO_CREATE)
  4. 与服务端通信
    与服务器绑定成功后,首先在 ServiceConnection 的回调中,将服务端返回的 Binder 对象转换成 AIDL 接口所属的对象,就可以调用相应方法和服务端通信了,代码如下:

    1. // 将服务端返回的 Binder 对象转换成 IBookManager 对象
    2. val bookManager = IBookManager.Stub.asInterface(service)
    3.  
    4. // 与服务端通信
    5. try {
    6. // 获取图书列表
    7. val list = bookManager.bookList
    8. Log.i(TAG, "query book list, list type: ${list.javaClass.canonicalName}")
    9. Log.i(TAG, "query book list: $list")
    10.  
    11. // 添加一本图书
    12. val book = Book(3, "Android开发艺术探索")
    13. bookManager.addBook(book)
    14. Log.i(TAG, "add book: $book")
    15.  
    16. // 获取图书列表
    17. val newList = bookManager.bookList
    18. Log.i(TAG, "query book list: $newList")
    19. } catch (e: RemoteException) {
    20. e.printStackTrace()
    21. }

    代码中,我们先查询了服务端的图书列表,接着向服务端添加一本书Android艺术开发探索,然后再次查询看是否添加成功。运行下看 log,如下图所示:

    可见,运行结果和预期结果一致。完整的客户端代码如下:

    1. package com.cy.ipcsample.aidl
    2.  
    3. import android.content.ComponentName
    4. import android.content.Context
    5. import android.content.Intent
    6. import android.content.ServiceConnection
    7. import android.os.Bundle
    8. import android.os.IBinder
    9. import android.os.RemoteException
    10. import android.support.v7.app.AppCompatActivity
    11. import android.util.Log
    12. import com.cy.ipcsample.R
    13.  
    14. class BookManagerActivity : AppCompatActivity() {
    15.  
    16. private val TAG = "BookManagerActivity"
    17.  
    18. private val mConnection = object : ServiceConnection {
    19.  
    20. override fun onServiceDisconnected(name: ComponentName?) {
    21. Log.d(TAG, "binder died.")
    22. }
    23.  
    24. /**
    25. * 连接远程服务成功的回调
    26. */
    27. override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
    28. // 将服务端返回的 Binder 对象转换成 IBookManager 对象
    29. val bookManager = IBookManager.Stub.asInterface(service)
    30.  
    31. // 与服务端通信
    32. try {
    33. // 获取图书列表
    34. val list = bookManager.bookList
    35. Log.i(TAG, "query book list, list type: ${list.javaClass.canonicalName}")
    36. Log.i(TAG, "query book list: $list")
    37.  
    38. // 添加一本图书
    39. val book = Book(3, "Android开发艺术探索")
    40. bookManager.addBook(book)
    41. Log.i(TAG, "add book: $book")
    42.  
    43. // 获取图书列表
    44. val newList = bookManager.bookList
    45. Log.i(TAG, "query book list: $newList")
    46. } catch (e: RemoteException) {
    47. e.printStackTrace()
    48. }
    49. }
    50.  
    51. }
    52.  
    53. override fun onCreate(savedInstanceState: Bundle?) {
    54. super.onCreate(savedInstanceState)
    55. setContentView(R.layout.activity_book_manager)
    56.  
    57. // 绑定远程服务
    58. bindService(Intent(this, BookManagerService::class.java),
    59. mConnection, Context.BIND_AUTO_CREATE)
    60. }
    61.  
    62. override fun onDestroy() {
    63. unbindService(mConnection)
    64. super.onDestroy()
    65. }
    66. }

到此,一个简单的 AIDL 示例就完成了,当然,AIDL 的使用远没有那么简单,还有很多情景需要考虑的,比如:客户端需要随时服务端在状态变化时同时客户端,类似观察这模式,那么订阅与反订阅怎么实现;Binder 意外死亡,怎么重连等等,更多内容,可以参考《Android艺术开发探索》电子书下载

AIDL 的异步调用

AIDL 的调用过程是同步还是异步的?

这个问题其实看这一节的标题大家都知道了,AIDL 的调用过程是同步的。同时,上面分析 Binder 的机制时,也提到了,客户端进行远程 RPC 请求时,线程会挂起,等待结果,由此也可知,AIDL 的调用过程是同步的,下面来验证下。

首先,在服务端的 BookManagerService 中实现的 Binder 对象的 getBookList 方法添加延时执行,如下图所示:

然后,在客户端的 BookManagerActivity 中添加一个按钮,点击按钮时调用服务端 Binder 的 getBookList 做 RPC 请求。代码如下:

运行后,连续点击按钮,结果如下图所示:

由图所知,连续点击按钮过后一段时间,出现了无响应错误( ANR ),由此可知,客户端向服务端做 RPC 请求时,是同步的,也就是说:AIDL 的调用过程是同步的

AIDL 的调用过程是同步的,当我们需要服务端做耗时操作时,肯定是不能使用同步调用的,否则轻者影响用户体验,重者直接 ANR 或者应用崩溃。那么如何使 AIDL 的调用过程是异步的呢?

其实也很简单,只需要把调用放到非 UI 线程即可,如果要对调用的返回做 UI 更新的话,再通过 Handler 处理即可。如下图所示:

本文参考

  • 《Android开发艺术探索》 第二章

Android AIDL浅析及异步使用的更多相关文章

  1. Android AIDL自动生成Java文件测试

    /******************************************************************************** * Android AIDL自动生成 ...

  2. Using self-defined Parcelable objects during an Android AIDL RPC / IPC call

    Using self-defined Parcelable objects during an Android AIDL RPC / IPC call In my previous post “Usi ...

  3. Android引入高速缓存的异步加载全分辨率

    Android引进高速缓存的异步加载全分辨率 为什么要缓存 通过图像缩放,我们这样做是对的异步加载优化的大图,但现在的App这不仅是一款高清大图.图.动不动就是图文混排.以图代文,假设这些图片都载入到 ...

  4. AIDL/IPC Android AIDL/IPC 进程通信机制——超具体解说及使用方法案例剖析(播放器)

    首先引申下AIDL.什么是AIDL呢?IPC? ------ Designing a Remote Interface Using AIDL 通常情况下,我们在同一进程内会使用Binder.Broad ...

  5. (转载)你真的理解Android AIDL中的in,out,inout么?

    前言 这其实是一个很小的知识点,大部分人在使用AIDL的过程中也基本没有因为这个出现过错误,正因为它小,所以在大部分的网上关于AIDL的文章中,它都被忽视了——或者并没有,但所占篇幅甚小,且基本上都是 ...

  6. Android中的AsyncTask异步任务的简单实例

    在 Android中的AsyncTask异步任务的简介 一文中.已经对 安卓 异步任务操作做了简单的介绍.这里,直接将上文中的异步任务做了一个实例.实现异步操作更新UI线程,相比开启子线程更新来说逻辑 ...

  7. Android AIDL使用详解_Android IPC 机制详解

    一.概述 AIDL 意思即 Android Interface Definition Language,翻译过来就是Android接口定义语言,是用于定义服务器和客户端通信接口的一种描述语言,可以拿来 ...

  8. Android aidl Binder框架浅析

      转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/38461079 ,本文出自[张鸿洋的博客] 1.概述 Binder能干什么?B ...

  9. android aidl

    参考: http://blog.csdn.net/u014614038/article/details/48399935 本文提供了一个关于AIDL使用的简单易懂的例子,分为客户端和服务端两部分,分别 ...

随机推荐

  1. Maven学习总结(五):maven命令的含义及用法

    Maven有许多命令,不管是在命令行(1),还是在Myecplise10的Maven项目--右键Run As(2),还是IDEA的左下角--Maven Projects--Maven项目名--Life ...

  2. 关于 PHPMailer 邮件发送类的使用心得(含多文件上传)

    This is important for send mail PHPMailer 核心文件 class.phpmailer.php class.phpmaileroauth.php class.ph ...

  3. 【MFC】CHtmlView::GetSource中文乱码的问题

    在MFC的SDI中,使用CHtmlView::GetSource来获取网页源码,保存到本地,发现中文中的一部分乱码,有些中文正常.自己先试着转码等各种尝试,发现一无所获.网上也没有正确的解决方案. 自 ...

  4. LintCode2016年8月8日算法比赛----中序遍历和后序遍历构造二叉树

    中序遍历和后序遍历构造二叉树 题目描述 根据中序遍历和后序遍历构造二叉树 注意事项 你可以假设树中不存在相同数值的节点 样例 给出树的中序遍历: [1,2,3] 和后序遍历: [1,3,2] 返回如下 ...

  5. 上海ctf2017 pwn100 && pwn200

    前言 分析的 idb 文件在这: https://gitee.com/hac425/blog_data/tree/master/shanghaictf pwn100 程序是一个经典的 堆管理程序,基本 ...

  6. 结对编程项目总结(core2组)

    结对编程项目总结(core2组) 作业---四则运算(Core 第二组)   ----by 吴雪晴 PB16061514 齐天杨 PB16060706 一.项目简介 项目的任务为制作一个给(貌似是?) ...

  7. 初识WCF1

    参照: http://blog.csdn.net/songyefei/article/details/7363296 第一篇 Hello WCF WCF(Windows Communication F ...

  8. Linux下top命令监控性能状态

    1.性能分析因素:CPU.内存.网络.磁盘读写 2.系统对应的应用类型主要分为以下两种: IO Bound:一般都是高负荷的内存使用以及存储系统,IO范畴的应用就是一个大数据处理的过程:通常数据库软件 ...

  9. Python学习---django重点之视图函数

    django重点之视图函数 http请求中产生两个核心对象: http请求:HttpRequest对象,由Django自己创建 http响应:HttpResponse对象,由开发自己创建,必须返回一个 ...

  10. ZT Linux系统环境下的Socket编程详细解析

    Linux系统环境下的Socket编程详细解析 来自: http://blog.163.com/jiangh_1982/blog/static/121950520082881457775/ 什么是So ...