dbus 消息和消息总线实例讲解-一
应用程序A和消息总线连接,这个连接获取了一个众所周知的公共名(记作连接A)。应用程序A中有对象A1提供了接口I1,接口I1有方法M1。 应用程序B和消息总线连接,要求调用连接A上对象A1的接口I1的方法M1。
在上一讲的加法例子中,上面这段话可以实例化为:应用程序example-service和会话总线连接。这个连接获取了一个众所周知的公共名“org.fmddlmyy.Test”。 应用程序example-servic中有对象“/TestObj”提供了接口“org.fmddlmyy.Test.Basic”,接口“org.fmddlmyy.Test.Basic”有方法“Add”。 应用程序d-feet和会话总线连接,要求调用连接“org.fmddlmyy.Test”上对象“/TestObj”的接口“org.fmddlmyy.Test.Basic”的方法“Add”。
应用程序B调用应用程序A的方法,其实就是应用程序B向应用程序A发送了一个类型为“method_call”的消息。 应用程序A通过一个类型为“method_retutn”的消息将返回值发给应用程序B。我们简单介绍一下D-Bus总线上的消息。
1、D-Bus的消息
上一讲说过最基本的D-Bus协议是一对一的通信协议。与直接使用socket不同,D-Bus是面向消息的协议。 D-Bus的所有功能都是通过在连接上流动的消息完成的。
1.1、消息类型
D-Bus有四种类型的消息:
- method_call 方法调用
- method_return 方法返回
- error 错误
- signal 信号
前面介绍的远程方法调用就用到了method_call和method_return消息。顾名思义,在发生错误时会产生error消息。 如果把method_call看作打电话,那么signal消息就是来电了。后面还会详细讨论。
1.2、dbus-send和dbus-monitor
dbus提供了两个小工具:dbus-send和dbus-monitor。我们可以用dbus-send发送消息。用dbus-monitor监视总线上流动的消息。 让我们通过dbus-send发送消息来调用前面的Add方法,这时dbus-send充当了应用程序B。用dbus-monitor观察调用过程中的消息。
启动example-service:
$ ./example-service
在另一个控制台启动dbus-monitor:
$ dbus-monitor
dbus-monitor默认监视会话总线。执行:
$ dbus-send --session --type=method_call --print-reply --dest=org.fmddlmyy.Test /TestObj org.fmddlmyy.Test.Basic.Add int32:100 int32:999
输出为:
method return sender=:1.21 -> dest=:1.22 reply_serial=2
int32 1099
dbus-monitor的相关输出包括:
signal sender=org.freedesktop.DBus -> dest=(null destination) path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=NameOwnerChanged
string ":1.22"
string ""
string ":1.22"
method call sender=:1.22 -> dest=org.freedesktop.DBus path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=Hello
method call sender=:1.22 -> dest=org.fmddlmyy.Test path=/TestObj; interface=org.fmddlmyy.Test.Basic; member=Add
int32 100
int32 999
method return sender=:1.21 -> dest=:1.22 reply_serial=2
int32 1099
signal sender=org.freedesktop.DBus -> dest=(null destination) path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=NameOwnerChanged
string ":1.22"
string ":1.22"
string ""
:1.22就是dbus-send在本次调用中与会话总线所建立连接的唯一名。:1.21是连接“org.fmddlmyy.Test”的唯一名。 在以上输出中我们可以看到:1.22向“org.fmddlmyy.Test”发送method_call消息,调用Add方法。 :1.21通过method_return消息将调用结果发回:1.22。其它输出信息会在以后说明。
dbus-send的详细用法可以参阅手册。调用远程方法的一般形式是:
$ dbus-send [--system | --session] --type=method_call --print-reply --dest=连接名 对象路径 接口名.方法名 参数类型:参数值 参数类型:参数值
dbus-send支持的参数类型包括:string, int32, uint32, double, byte, boolean。
2、消息总线的方法和信号
2.1、概述
消息总线是一个特殊的应用,它可以在与它连接的应用之间传递消息。 可以把消息总线看作一台路由器。正是通过消息总线,D-Bus才在一对一的通信协议基础上实现了多对一和一对多的通信。
消息总线虽然有特殊的转发功能,但消息总线也还是一个应用。 其它应用与消息总线的通信也是通过1.1节的基本消息类型完成的。作为一个应用,消息总线也提供了自己的接口,包括方法和信号。
我们可以通过向连接“org.freedesktop.DBus ”上对象“/”发送消息来调用消息总线提供的方法。 事实上,应用程序正是通过这些方法连接到消息总线上的其它应用,完成请求公共名等工作的。
2.2、清单
消息总线对象支持第一讲中提到的标准接口"org.freedesktop.DBus.Introspectable", 我们可以调用org.freedesktop.DBus.Introspectable.Introspect方法查看消息总线对象支持的接口。例如:
$ dbus-send --session --type=method_call --print-reply --dest=org.freedesktop.DBus / org.freedesktop.DBus.Introspectable.Introspect
输出为:
method return sender=org.freedesktop.DBus -> dest=:1.20 reply_serial=2
string "<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
<node>
<interface name="org.freedesktop.DBus.Introspectable">
<method name="Introspect">
<arg name="data" direction="out" type="s"/>
</method>
</interface>
<interface name="org.freedesktop.DBus">
<method name="Hello">
<arg direction="out" type="s"/>
</method>
<method name="RequestName">
<arg direction="in" type="s"/>
<arg direction="in" type="u"/>
<arg direction="out" type="u"/>
</method>
<method name="ReleaseName">
<arg direction="in" type="s"/>
<arg direction="out" type="u"/>
</method>
<method name="StartServiceByName">
<arg direction="in" type="s"/>
<arg direction="in" type="u"/>
<arg direction="out" type="u"/>
</method>
<method name="NameHasOwner">
<arg direction="in" type="s"/>
<arg direction="out" type="b"/>
</method>
<method name="ListNames">
<arg direction="out" type="as"/>
</method>
<method name="ListActivatableNames">
<arg direction="out" type="as"/>
</method>
<method name="AddMatch">
<arg direction="in" type="s"/>
</method>
<method name="RemoveMatch">
<arg direction="in" type="s"/>
</method>
<method name="GetNameOwner">
<arg direction="in" type="s"/>
<arg direction="out" type="s"/>
</method>
<method name="ListQueuedOwners">
<arg direction="in" type="s"/>
<arg direction="out" type="as"/>
</method>
<method name="GetConnectionUnixUser">
<arg direction="in" type="s"/>
<arg direction="out" type="u"/>
</method>
<method name="GetConnectionUnixProcessID">
<arg direction="in" type="s"/>
<arg direction="out" type="u"/>
</method>
<method name="GetConnectionSELinuxSecurityContext">
<arg direction="in" type="s"/>
<arg direction="out" type="ay"/>
</method>
<method name="ReloadConfig">
</method>
<method name="GetId">
<arg direction="out" type="s"/>
</method>
<signal name="NameOwnerChanged">
<arg type="s"/>
<arg type="s"/>
<arg type="s"/>
</signal>
<signal name="NameLost">
<arg type="s"/>
</signal>
<signal name="NameAcquired">
<arg type="s"/>
</signal>
</interface>
</node>
"
从输出可以看到会话总线对象支持标准接口“org.freedesktop.DBus.Introspectable”和接口“org.freedesktop.DBus”。 接口“org.freedesktop.DBus”有16个方法和3个信号。下表列出了“org.freedesktop.DBus”的12个方法的简要说明:
org.freedesktop.DBus.RequestName (in STRING name, in UINT32 flags, out UINT32 reply) | 请求公众名。其中flag定义如下: DBUS_NAME_FLAG_ALLOW_REPLACEMENT 1 DBUS_NAME_FLAG_REPLACE_EXISTING 2 DBUS_NAME_FLAG_DO_NOT_QUEUE 4 返回值reply定义如下: |
org.freedesktop.DBus.ReleaseName (in STRING name, out UINT32 reply) | 释放公众名。返回值reply定义如下: DBUS_RELEASE_NAME_REPLY_RELEASED 1 DBUS_RELEASE_NAME_REPLY_NON_EXISTENT 2 DBUS_RELEASE_NAME_REPLY_NOT_OWNER 3 |
org.freedesktop.DBus.Hello (out STRING unique_name) | 一个应用在通过消息总线向其它应用发消息前必须先调用Hello获取自己这个连接的唯一名。返回值就是连接的唯一名。dbus没有定义专门的切断连接命令,关闭socket就是切断连接。 在1.2节的dbus-monitor输出中可以看到dbus-send调用消息总线的Hello方法。 |
org.freedesktop.DBus.ListNames (out ARRAY of STRING bus_names) | 返回消息总线上已连接的所有连接名,包括所有公共名和唯一名。例如连接“org.fmddlmyy.Test”同时有公共名“org.fmddlmyy.Test”和唯一名“:1.21”, 这两个名称都会被返回。 |
org.freedesktop.DBus.ListActivatableNames (out ARRAY of STRING bus_names) | 返回所有可以启动的服务名。dbus支持按需启动服务,即根据应用程序的请求启动服务。 |
org.freedesktop.DBus.NameHasOwner (in STRING name, out BOOLEAN has_owner) | 检查是否有连接拥有指定名称。 |
org.freedesktop.DBus.StartServiceByName (in STRING name, in UINT32 flags, out UINT32 ret_val) | 按名称启动服务。参数flags暂未使用。返回值ret_val定义如下: 1 服务被成功启动 2 已经有连接拥有要启动的服务名 |
org.freedesktop.DBus.GetNameOwner (in STRING name, out STRING unique_connection_name) | 返回拥有指定公众名的连接的唯一名。 |
org.freedesktop.DBus.GetConnectionUnixUser (in STRING connection_name, out UINT32 unix_user_id) | 返回指定连接对应的服务器进程的Unix用户id。 |
org.freedesktop.DBus.AddMatch (in STRING rule) | 为当前连接增加匹配规则。 |
org.freedesktop.DBus.RemoveMatch (in STRING rule) | 为当前连接去掉指定匹配规则。 |
org.freedesktop.DBus.GetId (out STRING id) | 返回消息总线的ID。这个ID在消息总线的生命期内是唯一的。 |
接口“org.freedesktop.DBus”的3个信号是:
org.freedesktop.DBus.NameOwnerChanged (STRING name, STRING old_owner, STRING new_owner) | 指定名称的拥有者发生了变化。 |
org.freedesktop.DBus.NameLost (STRING name) | 通知应用失去了指定名称的拥有权。 |
org.freedesktop.DBus.NameAcquired (STRING name) | 通知应用获得了指定名称的拥有权。 |
2.3、练习
让我们来试试消息总线提供的方法。
2.3.1、从ListName到d-feet的基本逻辑
用dbus-send调用:
$ dbus-send --session --type=method_call --print-reply --dest=org.freedesktop.DBus / org.freedesktop.DBus.ListNames
输出为:
method return sender=org.freedesktop.DBus -> dest=:1.23 reply_serial=2
array [
string "org.freedesktop.DBus"
string "org.freedesktop.Notifications"
string "org.freedesktop.Tracker"
string "org.freedesktop.PowerManagement"
string ":1.7"
string ":1.8"
string "org.gnome.ScreenSaver"
string ":1.9"
string ":1.10"
string ":1.22"
string ":1.11"
string "org.gnome.GnomeVFS.Daemon"
string ":1.23"
string ":1.12"
string ":1.13"
string ":1.0"
string ":1.14"
string ":1.1"
string ":1.15"
string ":1.2"
string ":1.16"
string ":1.3"
string "org.gnome.GkbdConfigRegistry"
string ":1.4"
string "org.fmddlmyy.Test"
string ":1.5"
string "org.gnome.SettingsDaemon"
string ":1.6"
]
这是会话总线当前已连接的连接名。在d-feet窗口的左侧窗口显示的就是ListNames返回的连接名。 聪明的读者也许已经想到使用消息总线的“org.freedesktop.DBus.ListNames”方法和各连接的“org.freedesktop.DBus.Introspectable.Introspect”, 我们就可以像d-feet一样查看总线上所有连接的所有对象的所有接口的所有方法和信号。
你的想法很好。但有一个问题,我们必须对连接中的对象调用“org.freedesktop.DBus.Introspectable.Introspect”方法。 ListNames只列出了连接名,我们怎么获取连接中的对象路径呢?
答案很简单,如果我们不知道对象路径就从根目录开始吧。连接中的对象是按照树型结构组织的。我们遍历连接的对象树就可以找到所有的对象。 调用对象的“org.freedesktop.DBus.Introspectable.Introspect”方法就可以查看对象的所有接口的所有方法和信号。 例如:假设我们不知道连接"org.fmddlmyy.Test"里有什么对象,我们可以对根对象"/"执行:
$ dbus-send --session --type=method_call --print-reply --dest=org.fmddlmyy.Test / org.freedesktop.DBus.Introspectable.Introspect
输出为:
method return sender=:1.22 -> dest=:1.25 reply_serial=2
string "<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
<node>
<node name="TestObj"/>
</node>
"
"org.fmddlmyy.Test"的对象树的根节点只有一个子节点"TestObj",再查看"/TestObj":
$ dbus-send --session --type=method_call --print-reply --dest=org.fmddlmyy.Test /TestObj org.freedesktop.DBus.Introspectable.Introspect
输出为:
method return sender=:1.22 -> dest=:1.26 reply_serial=2
string "<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
<node>
<interface name="org.freedesktop.DBus.Introspectable">
<method name="Introspect">
<arg name="data" direction="out" type="s"/>
</method>
</interface>
<interface name="org.freedesktop.DBus.Properties">
<method name="Get">
<arg name="interface" direction="in" type="s"/>
<arg name="propname" direction="in" type="s"/>
<arg name="value" direction="out" type="v"/>
</method>
<method name="Set">
<arg name="interface" direction="in" type="s"/>
<arg name="propname" direction="in" type="s"/>
<arg name="value" direction="in" type="v"/>
</method>
<method name="GetAll">
<arg name="interface" direction="in" type="s"/>
<arg name="props" direction="out" type="a{sv}"/>
</method>
</interface>
<interface name="org.fmddlmyy.Test.Basic">
<method name="Add">
<arg name="arg0" type="i" direction="in"/>
<arg name="arg1" type="i" direction="in"/>
<arg name="ret" type="i" direction="out"/>
</method>
</interface>
</node>
"
作为一个练习,让我们来查看系统总线的上的bluez接口。执行:
$ dbus-send --system --type=method_call --print-reply --dest=org.freedesktop.DBus / org.freedesktop.DBus.ListNames
输出为:
method return sender=org.freedesktop.DBus -> dest=:1.30 reply_serial=2
array [
string "org.freedesktop.DBus"
string ":1.7"
string ":1.8"
string ":1.9"
string "org.freedesktop.SystemToolsBackends"
string ":1.30"
string "org.freedesktop.NetworkManagerInfo"
string ":1.20"
string "org.freedesktop.Avahi"
string ":1.21"
string "org.bluez"
string ":1.22"
string "org.freedesktop.NetworkManager"
string "org.freedesktop.ConsoleKit"
string ":1.23"
string "com.redhat.dhcp"
string ":1.13"
string ":1.0"
string ":1.14"
string ":1.1"
string ":1.15"
string ":1.2"
string "org.freedesktop.Hal"
string "com.redhat.NewPrinterNotification"
string ":1.16"
string ":1.3"
string ":1.17"
string ":1.4"
string ":1.18"
string ":1.5"
string ":1.19"
string ":1.6"
]
我们看到连接"org.bluez"。查看它的根对象:
$ dbus-send --system --type=method_call --print-reply --dest=org.bluez / org.freedesktop.DBus.Introspectable.Introspect
输出为:
method return sender=:1.7 -> dest=:1.31 reply_serial=2
string "<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
<node>
<node name="org"/>
</node>
"
接着查对象"/org":
$ dbus-send --system --type=method_call --print-reply --dest=org.bluez /org org.freedesktop.DBus.Introspectable.Introspect
输出为:
method return sender=:1.7 -> dest=:1.32 reply_serial=2
string "<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
<node>
<node name="bluez"/>
</node>
"
接着查对象"/org/bluez":
$ dbus-send --system --type=method_call --print-reply --dest=org.bluez /org/bluez org.freedesktop.DBus.Introspectable.Introspect
输出为:
method return sender=:1.7 -> dest=:1.33 reply_serial=2
string "<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
<node name="/org/bluez">
<interface name="org.bluez.Manager">
<method name="InterfaceVersion">
<arg type="u" direction="out"/>
</method>
<method name="DefaultAdapter">
<arg type="s" direction="out"/>
</method>
<method name="FindAdapter">
<arg type="s" direction="in"/>
<arg type="s" direction="out"/>
</method>
<method name="ListAdapters">
<arg type="as" direction="out"/>
</method>
<method name="FindService">
<arg type="s" direction="in"/>
<arg type="s" direction="out"/>
</method>
<method name="ListServices">
<arg type="as" direction="out"/>
</method>
<method name="ActivateService">
<arg type="s" direction="in"/>
<arg type="s" direction="out"/>
</method>
<signal name="AdapterAdded">
<arg type="s"/>
</signal>
<signal name="AdapterRemoved">
<arg type="s"/>
</signal>
<signal name="DefaultAdapterChanged">
<arg type="s"/>
</signal>
<signal name="ServiceAdded">
<arg type="s"/>
</signal>
<signal name="ServiceRemoved">
<arg type="s"/>
</signal>
</interface>
<interface name="org.bluez.Database">
<method name="AddServiceRecord">
<arg type="ay" direction="in"/>
<arg type="u" direction="out"/>
</method>
<method name="AddServiceRecordFromXML">
<arg type="s" direction="in"/>
<arg type="u" direction="out"/>
</method>
<method name="UpdateServiceRecord">
<arg type="u" direction="in"/>
<arg type="ay" direction="in"/>
</method>
<method name="UpdateServiceRecordFromXML">
<arg type="u" direction="in"/>
<arg type="s" direction="in"/>
</method>
<method name="RemoveServiceRecord">
<arg type="u" direction="in"/>
</method>
<method name="RegisterService">
<arg type="s" direction="in"/>
<arg type="s" direction="in"/>
<arg type="s" direction="in"/>
</method>
<method name="UnregisterService">
<arg type="s" direction="in"/>
</method>
<method name="RequestAuthorization">
<arg type="s" direction="in"/>
<arg type="s" direction="in"/>
</method>
<method name="CancelAuthorizationRequest">
<arg type="s" direction="in"/>
<arg type="s" direction="in"/>
</method>
</interface>
<interface name="org.bluez.Security">
<method name="RegisterDefaultPasskeyAgent">
<arg type="s" direction="in"/>
</method>
<method name="UnregisterDefaultPasskeyAgent">
<arg type="s" direction="in"/>
</method>
<method name="RegisterPasskeyAgent">
<arg type="s" direction="in"/>
<arg type="s" direction="in"/>
</method>
<method name="UnregisterPasskeyAgent">
<arg type="s" direction="in"/>
<arg type="s" direction="in"/>
</method>
<method name="RegisterDefaultAuthorizationAgent">
<arg type="s" direction="in"/>
</method>
<method name="UnregisterDefaultAuthorizationAgent">
<arg type="s" direction="in"/>
</method>
</interface>
<node name="service_audio"/>
<node name="service_input"/>
<node name="service_network"/>
<node name="service_serial"/>
</node>
"
我们看到了对象"/org/bluez"的所有接口。对象"/org/bluez"还有子节点"service_audio"、"service_input"、"service_network"和"service_serial"。 必要时我们可以接着查下去。d-feet的基本逻辑就是这样。 后面我们会自己实现一个dteeth。dteeth是命令行程序,可以遍历指定连接的对象树,列出所有对象的所有接口的方法和信号。
dbus 消息和消息总线实例讲解-一的更多相关文章
- dbus 消息和消息总线实例讲解-二
转自:http://www.fmddlmyy.cn/text53.html 2.3.2.ListActivatableNames和服务器的自动启动 运行: $ dbus-send --system - ...
- WinForm中 Asp.Net Signalr消息推送测试实例
p{ text-align:center; } blockquote > p > span{ text-align:center; font-size: 18px; color: #ff0 ...
- Linux 进程间通信(posix消息队列 简单)实例
Linux 进程间通信(posix消息队列 简单)实例 详情见: http://www.linuxidc.com/Linux/2011-10/44828.htm 编译: gcc -o consumer ...
- WCF把书读薄(2)——消息交换、服务实例、会话与并发
上一篇:WCF把书读薄(1)——终结点与服务寄宿 八.消息交换模式 WCF服务的实现是基于消息交换的,消息交换模式一共有三种:请求回复模式.单向模式与双工模式. 请求回复模式很好理解,比如int Ad ...
- 对WM_NCHITTEST消息的了解+代码实例进行演示(消息产生消息,共24个枚举值)
这个消息比较实用也很关键,它代表非显示区域命中测试.这个消息优先于所有其他的显示区域和非显示区域鼠标消息.其中lParam参数含有鼠标位置的x和y屏幕坐标,wParam 这里没有用. Windows应 ...
- SQL Server 之 事务与隔离级别实例讲解
SQL Server 之 事务与隔离级别实例讲解 SQL Server 实现了6个隔离级别来防止并发情况下,类似企图并发的访问或修改同一数据时问题的发生.本文将带你体验全部6个隔离级别.正如你接下来将 ...
- jQuery.uploadify文件上传组件实例讲解
1.jquery.uploadify简介 在ASP.NET中上传的控件有很多,比如.NET自带的FileUpload,以及SWFUpload,Uploadify等等,尤其后面两个控件的用户体验比较好, ...
- ContentProvider数据库共享之——实例讲解
版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/harvic880925/article/details/44591631 前言:现在这段时间没这 ...
- 基于pythonselect.select模块通信的实例讲解
基于python select.select模块通信的实例讲解 要理解select.select模块其实主要就是要理解它的参数, 以及其三个返回值. select()方法接收并监控3个通信列表, 第一 ...
随机推荐
- py-day2-3 python 字典
# 字典的value可以是任何值 test = {'k1':'v1', 'k2':True, 'k3':[1,2,3,4,{'t1':'v2','t2':'v3'},5,6], 'k4':(88,99 ...
- mongodb 超出内存限制
mongodb 查询数据默认占用最大内存为100M,如果查询涉及到大量数据进行$group.$sort时会抛出类似以下的异常: Received error :: { $err: \"Exc ...
- Lepus监控之Oracle配置
1.安装cx_Oracle a.官网下载客户端组件包 oracle-instantclient12.2-basic-12.2.0.1.0-1.x86_64.rpmoracle-instantclien ...
- std::set
std::set 不重复key 默认less排序 代码 #include <iostream> #include <set> class Person { public: ...
- 使用jquery+css实现瀑布流布局
虽然可以直接使用css实现瀑布流布局,但显示的方式有点问题,所以这儿就直接使用jquery+css来实现瀑布流布局,最终效果如下: 思路是通过将每个小块的position设置为relativ ...
- String类中的equals()方法:
String类中的equals()方法: public boolean equals(Object anObject) { //如果是同一个对象 if (this == anObject) { ret ...
- sysctl-p报错:error: "net.bridge.bridge-nf-call-ip6ta
1.刚配置完sysctl,加载时报错:[root@itpux1 yum.repos.d]# sysctl -pnet.ipv4.ip_forward = 0net.ipv4.conf.default. ...
- 《算法导论》——重复元素的随机化快排Optimization For RandomizedQuickSort
昨天讨论的随机化快排对有重复元素的数组会陷入无限循环.今天带来对其的优化,使其支持重复元素. 只需修改partition函数即可: int partition(int *numArray,int he ...
- leetcode494
public class Solution { public int FindTargetSumWays(int[] nums, int S) { Queue<int> Q = new Q ...
- CSS表单3 光标样式 (每个位置鼠标放上去的样式不同)
<!DOCTYPE html> <html> <head> <title>单选按钮对齐</title> ...