网络层架构介绍

网络层架构介绍

中国移动开发的OPhone OS系统的主要目标就是未来3G业务,因此与Internet的接轨与应用,是顺其自然的,下面我们就来看一下OPhone OS的网络层架构。
OPhone为开发者提供了三个网络层的类包,如下:
Android.net:主要是提供基于Socket协议的服务
Android.net.http:提供基于http协议的访问
Android.net.wifi:主要是用来管理手机上WI-FI功能
一、 当人们一想到网络的时候,首先想到的是对于Http Protocal的支持功能,因此我们就先来看一下Http连接部分。在OPhone OS系统中已经集成了Apache的HttpClient模块,这个模块。有这个模块,要写出与http协议相关的程序就不是难事。在OPhone中,所集成的HttpClient并非是常见的JaKara Commons HttpClient3.x,而是http4.0,有兴趣的朋友可以到AndroidLibrary了解。
1、android.net.http.SslCertificate SSL证书信息(证书细节)类.

下面就来看一下它的API文档:
构造函数:public SslCertificate(String issuedTo, String issuedBy, String validNotBefore, String validNotAfter)
创建一个新的SSL证书对象。

参数:
issuedTo 证书发行的实体
issuedBy 发行证书的实体
validNotBefore 在证书验证之前的数据
validNotAfter 在证书验证之后的数据

public SslCertificate(X509Certificate certificate)
从X509证书创建一个新的SSL证书对象
参数:
certificate X509 certificate

公共方法:
public SslCertificate.DName getIssuedBy()
返回:
显示被指定的出版者的名字,如果没有的话则显示为null
public SslCertificate.DName getIssuedTo()
返回:
显示指定到的出版者的名字,如果没有的话则显示为null
public String getValidNotAfter()
返回:
在证书验证之前的数据,如如果没有的话则显示为null。
public String getValidNotBefore()
返回:
Not-before date from the certificate validity period or "" if none has been set
public static SslCertificate restoreState(Bundle bundle)
恢复存储在包里的证书。
参数:
bundle SL证书存储在包中,如果没有则返回null

返回:
保存证书的包,如果没有返回为null
public static Bundle saveState(SslCertificate certificate)
把证书保存到包中
参数:
certificate The SSL certificate to store

返回:
保存证书的包,如果没有返回为null
public String toString()
从接收器返回一个简单的,容易读懂的,描述的字符串类型r.
返回:
为调试用的证书的描述
2、android.net.http.SslCertificate.DName
构造函数:
public SslCertificate.DName(String dName)
创建一个发行者的名称。
参数:
dName The distinguished name

公共方法:
public String getCName()
返回:
CN组织的名称
public String getDName()
返回:
出版者的名字(一般包括CN,0,OU等名称)
public String getOName()
返回:
O组织的名称。
public String getUName()
返回:
OU组织的名称


二、Android.net包:
接口:UrlQuerySanitizer.ValueSanitizer 用来简化一个单一询问值的功能组件
public String sanitize(String value) 清理一个没有编码的值.,返回清理后的无编码值
类:
1、 ConnectivityManager 回应有关网络连接状态查询的类。
应答查询网络连接状态的类.同时当网络连接改变时要通知应用程序.调用 Context.getSystemService(Context.CONNECTIVITY_SERVICE)得到此类的实体对象.
主要的功能是:
(1)监视网络连接 (Wi-Fi, GPRS, UMTS 等.)
(2)当网络连接变化时广播intents
(3)连接的网络断掉时尝试"切换"
(4)提供API给应用程序使其查询可用网络的coarse-grained 或 fine-grained 状态
这个类提供了一些常量值,具体意义如下:
常量:
public static final String CONNECTIVITY_ACTION
一个引起的网络连接的变化.一个连接被建立或断开.被影响的网络信息可用通过附加信息送出;还可以被用来表明何种连接事件发生了.如果这个连接是一个网络断开一起的切换而建立的,那么FAILOVER_CONNECTION 值被设为true.

对于连接丢失,如果连接管理器正在尝试连接(或已经连接上)另一个网络,则新网络的网络信息也要作为附加信息传送.这让任何广播的接收者直到他们不需要告诉用户没有可用的数据传输.相反,接收者应该期待另一个将要到来的广播,指示切换尝试成功了(那么仍然有所有的数据连接),或者切换尝试失败了,那就说所有的连接丢失了.

对于断开事件,布尔值extra EXTRA_NO_CONNECTIVITY 被设为 true 如果完全没有连接的网络.

Constant Value: "android.net.conn.CONNECTIVITY_CHANGE"
public static final int DEFAULT_NETWORK_PREFERENCE
Constant Value: 1 (0x00000001)
public static final String EXTRA_EXTRA_INFO
字符串查找关键字提供可选的额外的网络状态信息.此信息可通过底层网络层传送,并且它可表示为一个特定的网络类型.通过getStringExtra(String)获取.

public static final String EXTRA_IS_FAILOVER
布尔值的查找关键字指示是否一个连接事件表明一个网络由于断开连接而切换到另一个网络.通过 getBooleanExtra(String, boolean)获取.
Constant Value: "isFailover"
public static final String EXTRA_NETWORK_INFO
NetworkInfo 对象的查找关键字.通过 getParcelableExtra(String)获取.
Constant Value: "networkInfo"
public static final String EXTRA_NO_CONNECTIVITY
布尔值的查找关键字指示是否有一个连接丢失.比如没有可用网络.通过 getBooleanExtra(String, boolean)获取.
Constant Value: "noConnectivity"
public static final String EXTRA_OTHER_NETWORK_INFO
NetworkInfo 对象的查找关键字. 被用来当另一个网络可能连接时.通过 getParcelableExtra(String)获取.
Constant Value: "otherNetwork"
public static final String EXTRA_REASON
查找关键字指示为什么尝试连接网络失败.此字符串没有特定的结构.它只是被用来通知用户.可用用getStringExtra(String)获取.
Constant Value: "reason"
public static final int TYPE_MOBILE
Constant Value: 0 (0x00000000)
public static final int TYPE_WIFI
Constant Value: 1 (0x00000001)
主要方法:
public NetworkInfo getActiveNetworkInfo()
public NetworkInfo[] getAllNetworkInfo()
public NetworkInfo getNetworkInfo(int networkType)
public int getNetworkPreference()
public static boolean isNetworkTypeValid(int networkType)
public boolean requestRouteToHost(int networkType, int hostAddress)
确保网络路由的存在能通过指定的网络接口发送数据到指定的主机.尝试增加一个已经存在的路由会被忽略,但是被视为成功.
参数
networkType 路由信息到指定主机的网络类型
hostAddress 主机的IP地址

返回值
true 表示成功, false 表示失败
public void setNetworkPreference(int preference)
public int startUsingNetworkFeature(int networkType, String feature)
告诉网络系统调用者要开始使用给定的feature.feature 的解释完全取决于每个网络的实现.
参数
networkType 指定请求附属的网络类型
feature 将要使用的feature的名字

返回值
一个整数,代表请求的输出.这个值的解释依赖于每个网络的实现和feature的结合,除了 -1 总是表示错误.
public int stopUsingNetworkFeature(int networkType, String feature)
告诉网络系统调用者使用给定的feature结束. feature的定义完全取决于每个网络的实现.
参数
networkType 指定请求附属的网络类型
feature 不在需要的feature的名字

返回值
一个整数,代表请求的输出.这个值的解释依赖于每个网络的实现和feature的结合,除了 -1 总是表示错误.

2、Credentials 一个表现在UNIX 域套接字内通过附属数据传递来的UNIX凭证的类。
3、DhcpInfo 一个获取DHCP请求的简单对象。
4、LocalServerSocket 在Android平台创建UNIX域套接字的非标准类。这是在linux非文件系统命名空间内创建的。
在Android平台上创建带内的UNIX-domain套接字的非标准类,此类被创建在Linux的非文件系统空间. 在模拟器平台,此类可被创建在文件系统的临时文件目录下.
构造方法:
Public Constructors
public LocalServerSocket(String name)
创建一个新的服务器套接字监听指定的端口名.在Android平台,名字创建在Linux abstract namespace (取代文件系统).
参数
name 套接字地址
public LocalServerSocket(FileDescriptor fd)
从一个已经创建并绑定的描述符上创建一个LocalServerSocket.listen()将会被立即调用.用在描述符是通过环境变量传递的情况.
参数
fd 描述符
公共方法:
public LocalSocket accept()
接受一个新的套接字连接.阻塞直到一个新的连接.
返回值
一个新连接的套接字.
Throws
IOException

public void close()
关闭服务器套接字.
Throws
IOException

public FileDescriptor getFileDescriptor()
返回描述符,如果没有打开或已经关闭则返回空.
返回值
描述符或空值
public LocalSocketAddress getLocalSocketAddress()
得到套接字的本地地址.
返回值
本地地址

5、LocalSocket 在UNIX域名空间内创建一个套接字(非服务器)。

在UNIX域命名空间创建一个(非服务器)套接字.这个接口跟java.net.Socket不是完全的相像.
构造方法:
public LocalSocket()
创建一个AF_LOCAL/UNIX 套接字.
公共方法:
public void bind(LocalSocketAddress bindpoint)
绑定套接字到一个端口.仅可由还没有被绑定的实体对象来调用.
参数
bindpoint 端口地址

public void close()
关闭套接字.


public void connect(LocalSocketAddress endpoint, int timeout)


public void connect(LocalSocketAddress endpoint)
连接套接字到一个端口.仅可由还没有连接的实体对象来调用.
参数
endpoint 端口地址

Throws
IOException 如果套接字是无效的或地址不存在.

public FileDescriptor[] getAncillaryFileDescriptors()
获取一组由对端通过附加消息传送过来的文件描述符.此方法获取所有最近送过来的描述符,并且返回空直到收到一个新的描述符.文件描述符仅可以通过常规数据传送,此方法读取一个描述符后仅能返回非空值.
返回值
空或文件描述符数组
Throws
IOException

public FileDescriptor getFileDescriptor()
返回文件描述符,如果没有打开或已经关闭则返回空.
返回值
描述符或空值
public InputStream getInputStream()
获取输入流.
返回值
输入流
Throws
IOException 如果套接字被关闭或不能创建.

public LocalSocketAddress getLocalSocketAddress()
获取任何这个套接字被绑定的名字.
返回值
本地地址,如果匿名则返回空
public OutputStream getOutputStream()
获取输出流.
返回值
输出流
Throws
IOException 如果套接字被关闭或不能创建.

public Credentials getPeerCredentials()
获取套接字对的认证信息.仅对已连接的套接字有效.
返回值
非空; 认证信息
Throws
IOException

public int getReceiveBufferSize()
Throws
IOException

public LocalSocketAddress getRemoteSocketAddress()
public int getSendBufferSize()
Throws
IOException

public int getSoTimeout()
Throws
IOException

public synchronized boolean isBound()
public boolean isClosed()
public synchronized boolean isConnected()
public boolean isInputShutdown()
public boolean isOutputShutdown()
public void setFileDescriptorsForSend(FileDescriptor[] fds)
把一组文件描述符排入队列并送给对端.队列为一列队列.文件描述符将被正常数据的下一次写时送出,并且是在一个单一的附加消息中送出.参见 "man 7 unix" SCM_RIGHTS 在linux机器的桌面.
参数
fds 非空; 要发送的文件描述符.

public void setReceiveBufferSize(int size)
Throws
IOException

public void setSendBufferSize(int n)
Throws
IOException

public void setSoTimeout(int n)
Throws
IOException

public void shutdownInput()
关闭套接字的输入端.
Throws
IOException

public void shutdownOutput()
关闭套接字的输出端.
Throws
IOException

public String toString()
返回一个接收者的简明的,可读的字符串描述.
返回值
接收者的字符串描述.
5、LocalSocketAddress 一个UNIX域(AF_LOCAL)套接字地址.
UNIX-domain (AF_LOCAL) 套接字地址. 与 android.net.LocalSocket and android.net.LocalServerSocket一起使用. 在Android系统中,这些名字存在于Linux abstract (非文件系统) UNIX domain 命名空间.
构造函数:
public LocalSocketAddress(String name, LocalSocketAddress.Namespace namespace)
给定一个名字创建一个实体.
参数
name 非空名字
namespace 名字创建的命名空间

public LocalSocketAddress(String name)
给定一个名字创建一个实体在 ABSTRACT namespace
参数
name 非空名字

公共方法:
public String getName()
获取地址的字符串名字
返回值
字符串名字
public LocalSocketAddress.Namespace getNamespace()
返回地址使用的命名空间
返回值
非空命名空间
7、MailTo MailTo URL解析器,这个类解析一个mailtoURL,并且可以让被解析参数查询。
8、NetworkInfo 描述给定网络类型接口的状态 (目前只有 Mobile 或 Wifi).
9、Proxy 一个用来访问用户和默认代理设置的方便类。
10、SSLCertificateSocketFactory
11、Uri 永久不变的URI引用
持久的URI引用。一个URI引用包括一个URI和一个段,URI部分以“#”开头。构建和解析在与 RFC 2396标准一致的URI引用.

为了好的性能,这个类没有对有效性方面做太多。对于无效输入的行为是没有定义的。在面对无效输入,这个类的确很抱歉,它将会返回一些无用的东西而不是抛出异常,除非另有声明。


12、Uri.Builder 为构建和操作URI 引用的帮助类常量
public static final Creator<Uri> CREATOR
从包中读取Uris。
public static final Uri EMPTY
空URI,相当于 "".
公共方法
public abstract Uri.Builder buildUpon()
构造一个新的 builder对象, 从这个Uri拷贝属性。
public int compareTo(Uri other)
用当前的URI表述字符串与参数中的URI相比较。
public static String decode(String s)
解码'%'-在给定字符串内用UTF-8形式的换码八位字节。用UNICODE的替换字符 ("//uFFFD")来替换无效八位字节。
参数
s 要解码的已编码字符串

返回值
用换码八位字节已解码的给定字符串,如果s是null则返回null。
public static String encode(String s)
用'%'-用UTF-8形式的换码八位字节,给指定字符串编码。留下字母("A-Z", "a-z"),数字 ("0-9"), 和不限制的字符("_-!.~'()*") 。对其他字符编码。
参数
s 要编码的字符串

返回值
一个可以适合作为一个URI部件使用的编码后的s版本。如果s为null则返回null。
public static String encode(String s, String allow)
用'%'-用UTF-8形式的换码八位字节,给指定字符串编码。留下字母("A-Z", "a-z"), 数字 ("0-9"), 和不限制的字符("_-!.~'()*") 。用除了allow参数中指定的字符外的字符编码其他字符。
参数
s 要编码的字符串
allow 一组允许在已编码形式内的额外字符,如果没有字符要被跳过则返回null。

返回值
一个可以适合用作URI部件的编码后版本的s,如果s是null则返回null。
public boolean equals(Object o)
用当前URI和另一个对象比较是否相等。如果这个URI的已编码字符串表述和给定的URI相等则返回真。要考虑这种情况,路径不是正规化的。如果一个URI明确指定了一个默认端口,而另一个没有声明,他们将不被认为是相等的。


参数
o 用来和当前对象比较的对象。

返回值
如果两个对象相等则返回真。如果不同就返回假。
public static Uri fromFile(File file)
创建一个到文件的URI。URI有这样的形式 "file://". 用'/'对路径字符编码。例如"file:///tmp/android.txt"
返回值
一个对给定文件的URI。
异常
NullPointerException 如果文件是null

public static Uri fromParts(String scheme, String ssp, String fragment)
从给定的部件创建一个不透明的URI对象。对ssp编码,也就意味着这个方法不能用来创建一个有层次结构的URIs。
参数
scheme URI的计划部分
ssp 将要被编码的计划的指定部分,每一个在计划分隔符(':')和段分隔符('#')之间的部分。
fragment 将要被编码的段部分,每一个在'#'之后的部分,如果没有定义则为null。

返回值
由给定计划部分,ssp,和段部分组成的URI。
异常
NullPointerException 如果计划和ssp部分为null

请参阅
如果你不像让ssp和段部分被编码。
public abstract String getAuthority()
从URI对象获得解码后的所有者。对于服务器地址,所有者象这样的结构:[ userinfo '@' ] host [ ':' port ]
例如: "google.com", "bob@google.com:80"

返回值
这个URI的所有者,或如果不存在返回null。
public abstract String getEncodedAuthority()
从URI获得已编码的所有者部分。对于服务器地址,所有者的结构如下:[ userinfo '@' ] host [ ':' port ]
例如: "google.com", "bob@google.com:80"

返回值
这个URI的所有者,或者如果不存在返回null
public abstract String getEncodedFragment()
获得这个URI对象中已编码的所有者部分。也就是每个跟在 '#'后面的部分 。
返回值
已编码的段,或者如果没有则返回null
public abstract String getEncodedPath()
获得已编码路径。
返回值
已编码的路径, 如果这个URI是无效的或者不是一个象"mailto:nobody@google.com"层次结构的URI 则返回null。
public abstract String getEncodedQuery()
从这个URI对象中获得编码后的请求部件。请求部分都跟在请求分隔符('?')后,在段分隔符('#')之前. 对于"http://www.google.com/search?q=android",这个方法会返回 "q=android"
返回值
编码后的请求,或者如果没有则返回null
public abstract String getEncodedSchemeSpecificPart()
获得这个URI的计划指定部分,每一个在计划分隔符':'和段分隔符'#'之间的部分. 如果是一个相对URI,这个方法返回整个URI。把换码字符留下。例如: "//www.google.com/search?q=android"
返回值
已解码的计划的指定部分。
public abstract String getEncodedUserInfo()
从所有者获得已编码的用户信息。例如如果一个所有者是 "nobody@google.com", 这个方法会返回 "nobody".
返回值
这个URI的用户信息,如果没有则返回null
public abstract String getFragment()
获得这个NRI的已解码的段部分,也就是每一个跟在'#'之后的部分。
返回值
已解码的段部分,如果没有则返回null
public abstract String getHost()
为URI从所有者那里获得已编码的服务器。例如如果当前所有者是"bob@google.com", 这个方法返回 "google.com".
返回值
为这个URI的服务器,如果不存在则返回null
public abstract String getLastPathSegment()
获得在路径里的已解码的最后的段。
返回值
已解码的随后部分,如果路径为空,则返回null
public abstract String getPath()
获得已解码的路径。
返回值
已解码路径, 如果URI无效,或不是象"mailto:nobody@google.com"层次结构的URI则返回null
public abstract List<String> getPathSegments()
获得已解码的路径部分
返回值
已解码的路径部分,每一个都没有用'/'开头或结尾。
public abstract int getPort()
为URI从所有者获得端口号。例如,如果所有者是, "google.com:80",这个方法将返回 80.
返回值
这个URI的端口号,如果无效或不存在则返回-1
public abstract String getQuery()
为URI获得已解码的请求部件。请求都是跟在请求分隔符 ('?')后,在段分隔符('#')之前. 对"http://www.google.com/search?q=android",这个方法会返回"q=android"。
返回值
已解码的请求,或如果不存在则返回null
public String getQueryParameter(String key)
用给定的key在请求字符串里检索第一个值。
参数
key 将要被编码的字符串

返回值
已解码值,如果没有参数被找到就返回
异常
UnsupportedOperationException 如果不是一个层次结构的URI。
NullPointerException 如果key是null

public List<String> getQueryParameters(String key)
用给定的key在请求字符串中检索参数值。
参数
key 要被编码的字符串

返回值
一列已解码的值。
异常
UnsupportedOperationException 如果不是一个层次结构的URI
NullPointerException 如果key是null

public abstract String getScheme()
获得URI的计划部分。例如e: "http"
返回值
计划部分,如果是个相对URI返回null
public abstract String getSchemeSpecificPart()
获得NRI的计划指定部分,每一个在计划分隔符 ':' 和段分隔符 '#'之间的部分. 如果是一个相对URI,这个方法将返回整个URI。 解码换码字节。
例如: "//www.google.com/search?q=android"

返回值
解码后的计划指定部分。
public abstract String getUserInfo()
从所有者获得已解码的用户信息。例如,如果所有者是 "nobody@google.com", 这个方法返回 "nobody".
返回值
这个URI的用户信息,如果不存在返回null
public int hashCode()
将和这个URI用 equals(Object)相等的对象表述已编码字符串做成哈希列表。
返回值
接收器的哈希表整型值
public boolean isAbsolute()
如果是URI是绝对的返回真,例如,如果他包含一个明确的计划
返回值
如果URI是绝对的返回真,如果是相对的返回假。
public abstract boolean isHierarchical()
如果这个URI是一个象 "http://google.com"的就返回真.如果计划指定部分用'/'开始则绝对URI是层次结构的。相对URI总是层次结构的。
public boolean isOpaque()
如果这个URI是象"mailto: nobody@google.com"这样不透明的. 一个不透明的URI的计划制定部分不能用'/'开头。
public abstract boolean isRelative()
如果URI是相对的就返回真,例如如果他不含一个明确的计划部分。
返回值
如果是相对URI返回真,绝对URI返回假。
public static Uri parse(String uriString)
创建一个解析给定已编码URI字符串的URI。
参数
uriString 一个符合 RFC 3296标准的, 已编码的 URI

返回值
对给定URI字符串的URI对象。Uri for this given uri string
异常
NullPointerException 如果 uriString是null

public abstract String toString()
返回URI表述的已编码字符串。例如: "http://google.com/"
返回值
一个对接收器表述的可打印字符串。
public static Uri withAppendedPath(Uri baseUri, String pathSegment)
通过编码和想一个基础URI追加一个路径段的方式创建一个新的 URI。
参数
baseUri 要追加路径段的Uri
pathSegment 要编码和追加的部分

返回值
一个基于用给定已编码部分追加进路径的基础URI的新URI。
异常
NullPointerException 如果基础URI是null

public static void writeToParcel(Parcel out, Uri uri)
向包写进URI
参数
out 要写入的包
uri 要写的URI,可以是NULL
13、UrlQuerySanitizer 简化一个URL的查询部分。
14、UrlQuerySanitizer.IllegalCharacterValueSanitizer 基于他们包含特性来化简值。
15、UrlQuerySanitizer.ParameterValuePair 一个持有 parameter-value对的简单的字节组。
三、android.net.wifi包
提供类来在设备上对Wi-Fi(基于IEEE 802.11b标准的无线局域网)功能性进行操作
ScanResult 对一个已经检测出的存储点的信息的介绍.
WifiConfiguration 一个代表已经配置好的Wi-Fi网络的类,包含了安全配置信息.
WifiConfiguration.AuthAlgorithm 经过验证的IEEE 802.11 认证算法.
WifiConfiguration.GroupCipher 经过认证的组密码.
WifiConfiguration.KeyMgmt 经过认证的密钥管理计划.
WifiConfiguration.PairwiseCipher 经过认证的用于WPA的双码.
WifiConfiguration.Protocol 经过认证的安全协议.
WifiConfiguration.Status 网络配置可能的趋势.
WifiInfo 对任何一个活跃的或是正在筹建的Wifi网络连接的状况进行描叙.
WifiManager 这个类为控制所有Wi-Fi的部分提供了主要的应用程序接口.
WifiManager.WifiLock 允许一个应用来保持Wi-Fi无线传输畅通.
天啊,写了这么多,累死了。上面已经比较详细的介绍了OPhone的网络层。以后有时间的话,可以给大家写几个实例,这样就能更好的理解网络包的应用了。
我来给大家一个java接口的实例,后面我会给大家分享一下其他的接口:
import java.io.InputStream;

import java.io.OutputStream;

import java.net.URL;

import java.net.URLConnection;

import java.net.HttpURLConnection;



try {

// 创建一个 URL 对象

URL url = new URL(your_url);



// 创建一个 URL 连接,如果有代理的话可以指定一个代理。

URLConnection connection = url.openConnection(Proxy_yours);

// 对于 HTTP 连接可以直接转换成 HttpURLConnection,

// 这样就可以使用一些 HTTP 连接特定的方法,如 setRequestMethod() 等

//HttpURLConnection connection =

(HttpURLConnection)url.openConnection(Proxy_yours);



// 在开始和服务器连接之前,可能需要设置一些网络参数

connection.setConnectTimeout(10000);

connection.addRequestProperty("User-Agent",

"J2me/MIDP2.0");



// 连接到服务器

connection.connect();



// 往服务器写数据,数据会暂时被放到内存缓存区中

// 如果仅是一个简单的 HTTP GET,这一部分则可以省略

OutputStream outStream = connection.getOutputStream();

ObjectOutputStream objOutput = new ObjectOutputStream(outStream);

objOutput.writeObject(new String("this is a string..."));

objOutput.flush();



// 向服务器发送数据并获取应答

InputStream in = connection.getInputStream();



// 处理数据

...



} catch (Exception e) {

// 网络读写操作往往会产生一些异常,所以在具体编写网络应用时

// 最好捕捉每一个具体以采取相应措施

}
转自
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值