Ibatis做为一个半自动化的Orm框架有他的缺点和优点。在这里我就不宽泛的说这些了。就说说为什么SqlMapClient是线程安全的,他是怎么实现的。
提出问题:
Java代码
private static SqlMapClient sqlMapper;
/**
* It's not a good idea to put code that can fail in a class initializer,
* but for sake of argument, here's how you configure an SQL Map.
*/
static {
try {
Reader reader = Resources.getResourceAsReader("com/mydomain/data/SqlMapConfig.xml");
sqlMapper = SqlMapClientBuilder.buildSqlMapClient(reader);
reader.close();
} catch (IOException e) {
// Fail fast.
throw new RuntimeException("Something bad happened while building the SqlMapClient instance." + e, e);
}
}
这是一段ibatis simple工程的代码,大家都能看明白这是一个单例,只有一个SqlMapClient对象存在,在多线程的情况下,SqlMapClient是怎么解决事务隔离呢,怎么共享资源的呢?
[newpage]
一、 SqlMapClient是怎么被创建的
打开SqlMapClientBuilder发现buildSqlMapClien一句话
Java代码
public static SqlMapClient buildSqlMapClient(Reader reader) {
// return new XmlSqlMapClientBuilder().buildSqlMap(reader);
return new SqlMapConfigParser().parse(reader);
}
我们顺着这条线一路看下去
SqlMapConfigParser类的做了两件事把reader交个一个NodeletParser去解析reader(也就是我们的配置文件),在一个就是XmlParserState的一个属性产生一个SqlMapClient对象
Java代码
public class SqlMapConfigParser {
protected final NodeletParser parser = new NodeletParser();
private XmlParserState state = new XmlParserState();
public SqlMapClient parse(Reader reader) {
try {
usingStreams = false;
parser.parse(reader);
return state.getConfig().getClient();
} catch (Exception e) {
throw new RuntimeException("Error occurred. Cause: " + e, e);
}
}
打开NodeletParser的parse方法,我们发现他就是解析xml配置文件的
Java代码
public void parse(Reader reader) throws NodeletException {
try {
Document doc = createDocument(reader);
parse(doc.getLastChild());
} catch (Exception e) {
throw new NodeletException("Error parsing XML. Cause: " + e, e);
}
}
最后这些文件被分门别类的放在了XmlParserState的这些属性里
Java代码
private SqlMapConfiguration config = new SqlMapConfiguration();
private Properties globalProps = new Properties();
private Properties txProps = new Properties();
private Properties dsProps = new Properties();
private Properties cacheProps = new Properties();
private boolean useStatementNamespaces = false;
private Map sqlIncludes = new HashMap();
private ParameterMapConfig paramConfig;
private ResultMapConfig resultConfig;
private CacheModelConfig cacheConfig;
private String namespace;
private DataSource dataSource;
现在我们回过头看return state.getConfig().getClient();
是这句话获得了SqlMapClient对象,这个对象是怎么创建的呢,在SqlMapConfiguration的构造方法里面就已经创建好了。
Java代码
public SqlMapConfiguration() {
errorContext = new ErrorContext();
delegate = new SqlMapExecutorDelegate();
typeHandlerFactory = delegate.getTypeHandlerFactory();
client = new SqlMapClientImpl(delegate);
registerDefaultTypeAliases();
}
原来我们的到的并不是SqlMapClient(接口不能实现)对象,而是他的一个实现SqlMapClientImpl
二、 深入SqlMapClientImpl内部
SqlMapClientImpl类中只有三个字段
Java代码
private static final Log log = LogFactory.getLog(SqlMapClientImpl.class);
public SqlMapExecutorDelegate delegate;
protected ThreadLocal localSqlMapSession = new ThreadLocal();
log是一个日志记录的对象,与线程安全肯定是无关的
SqlMapExecutorDelegate这个类里面有什么东西呢
Java代码
private static final Probe PROBE = ProbeFactory.getProbe();
private boolean lazyLoadingEnabled;
private boolean cacheModelsEnabled;
private boolean enhancementEnabled;
private boolean useColumnLabel = true;
private boolean forceMultipleResultSetSupport;
private TransactionManager txManager;
private HashMap mappedStatements;
private HashMap cacheModels;
private HashMap resultMaps;
private HashMap parameterMaps;
protected SqlExecutor sqlExecutor;
private TypeHandlerFactory typeHandlerFactory;
private DataExchangeFactory dataExchangeFactory;
private ResultObjectFactory resultObjectFactory;
private boolean statementCacheEnabled;
这些属性都是一些关于跟sqlMap配置的一些信息,这些信息和线程安全也没有很大的关系。
最后就剩下localSqlMapSession字段了,其实有经验的同学一眼就能看出来这点的,ThreadLocal就是为处理线程安全而来的,他的实质为每个线程保存一个副本。他的实现就是存在一个全局的Map存放localSqlMapSession,key是线程的id号value值是一个localSqlMapSession的副本。
SqlMapClientImpl里面的方法:
Java代码
public Object insert(String id, Object param) throws SQLException {
return getLocalSqlMapSession().insert(id, param);
}
public Object insert(String id) throws SQLException {
return getLocalSqlMapSession().insert(id);
}
public int update(String id, Object param) throws SQLException {
return getLocalSqlMapSession().update(id, param);
}
public int update(String id) throws SQLException {
return getLocalSqlMapSession().update(id);
}
public int delete(String id, Object param) throws SQLException {
return getLocalSqlMapSession().delete(id, param);
}
public int delete(String id) throws SQLException {
return getLocalSqlMapSession().delete(id);
}
public Object queryForObject(String id, Object paramObject) throws SQLException {
return getLocalSqlMapSession().queryForObject(id, paramObject);
}
多么熟悉的方法啊,这就是我们经常用的curd的方法。从代码上证明了我们的推测,线程安全就是和localSqlMapSession有关
虽然找到了相关的属性,但是他们是怎么实现的呢。
三、 线程安全的实现。
就dao部分的线程安全来说一个是主要是事务的完成性。如果事务能够保证完整性,那么就可以说是线程安全的。
localSqlMapSession存的是什么什么东西呢,我们打开代码看看。
Java代码
protected SqlMapSessionImpl getLocalSqlMapSession() {
SqlMapSessionImpl sqlMapSession = (SqlMapSessionImpl) localSqlMapSession.get();
if (sqlMapSession == null || sqlMapSession.isClosed()) {
sqlMapSession = new SqlMapSessionImpl(this);
localSqlMapSession.set(sqlMapSession);
}
return sqlMapSession;
}
再研究一下SqlMapSessionImpl,这个类只有三个字段
protected SqlMapExecutorDelegate delegate;
protected SessionScope sessionScope;
protected boolean closed;
很明显SessionScope这是我们要找的东西
Java代码
private static long nextId;
private long id;
// Used by Any
private SqlMapClient sqlMapClient;
private SqlMapExecutor sqlMapExecutor;
private SqlMapTransactionManager sqlMapTxMgr;
private int requestStackDepth;
// Used by TransactionManager
private Transaction transaction;
private TransactionState transactionState;
// Used by SqlMapExecutorDelegate.setUserProvidedTransaction()
private TransactionState savedTransactionState;
// Used by StandardSqlMapClient and GeneralStatement
private boolean inBatch;
// Used by SqlExecutor
private Object batch;
private boolean commitRequired;
private Map preparedStatements;
根据我们的分析事务的完整性足以保证dao层的线程安全。Transaction保存在ThreadLocal里面证明了SqlMapClient是线程安全的,我们在整个工程中只要一个SqlMapClient对象就够了。
再来看下SessionScope这个类的字段
private SqlMapClient sqlMapClient;保存的是一个SqlMapClient
private SqlMapExecutor sqlMapExecutor; 执行sql用的
private SqlMapTransactionManager sqlMapTxMgr; 管理事务的
private int requestStackDepth;
// Used by TransactionManager
private Transaction transaction; 事务
private TransactionState transactionState; 事务的状态
// Used by SqlMapExecutorDelegate.setUserProvidedTransaction()
private TransactionState savedTransactionState; 事务的保存状态
// Used by StandardSqlMapClient and GeneralStatement
private boolean inBatch;是否批处理
// Used by SqlExecutor
private Object batch;
private boolean commitRequired;是否用提交
private Map preparedStatements;这个应该是保存批处理的PreparedStatement
我们突然发现没有连接类Connection,如果用jdbc的话Connection是多么重要的一个对象啊,在这里没有保存Connection呢。打开JdbcTransaction(一个Transaction的实现)
Java代码
private static final Log connectionLog = LogFactory.getLog(Connection.class);
private DataSource dataSource;
private Connection connection;
private IsolationLevel isolationLevel = new IsolationLevel();
public JdbcTransaction(DataSource ds, int isolationLevel) throws TransactionException {
// Check Parameters
dataSource = ds;
if (dataSource == null) {
throw new TransactionException("JdbcTransaction initialization failed. DataSource was null.");
}
this.isolationLevel.setIsolationLevel(isolationLevel);
}
private void init() throws SQLException, TransactionException {
// Open JDBC Transaction
connection = dataSource.getConnection();
if (connection == null) {
throw new TransactionException("JdbcTransaction could not start transaction. Cause: The DataSource returned a null connection.");
}
// Isolation Level
isolationLevel.applyIsolationLevel(connection);
// AutoCommit
if (connection.getAutoCommit()) {
connection.setAutoCommit(false);
}
// Debug
if (connectionLog.isDebugEnabled()) {
connection = ConnectionLogProxy.newInstance(connection);
}
}
public void commit() throws SQLException, TransactionException {
if (connection != null) {
connection.commit();
}
}
public void rollback() throws SQLException, TransactionException {
if (connection != null) {
connection.rollback();
}
}
public void close() throws SQLException, TransactionException {
if (connection != null) {
try {
isolationLevel.restoreIsolationLevel(connection);
} finally {
connection.close();
connection = null;
}
}
}
public Connection getConnection() throws SQLException, TransactionException {
if (connection == null) {
init();
}
return connection;
}
原来Connection在这里保存着呢,事务的提交,回滚也是在这里实现的。
到这里大致明白了,ibatis为每一个操作SqlMapClient的线程建立一个SessionScope对象,这里面保存了Transaction,Connection,要执行的PreparedStatement。
SqlMapClient对象里面保存的是全局有关的缓存策略,ParameterMap,ResultMap,jdbc到Java对象的类型转换,别名等信息。
在每个执行的Statement中还有一个StatementScope,这里保存的是每个执行语句的状态。这里就不看了。
第一次分析代码,思路有点混乱哈,自己挺有收获的,给大家分享一下,欢迎拍砖哈。
原文来自:雨枫技术教程网 http://www.fengfly.com
原文网址:http://www.fengfly.com/plus/view-168261-1.html
--------------------------------------------------------------------------------
Search: LoginSettingsAbout Trac
RoadmapView TicketsSearchManagementTagsDiscussionBlog
Wiki Navigation
Forum Index java
--------------------------------------------------------------------------------
java (#5) - jetty配置实例 (#376) - Message List
[newpage]
1. <?xml version="1.0"?>
2. <!DOCTYPE Configure PUBLIC "-//Mort Bay Consulting//DTD Configure//EN" "http://jetty.mortbay.org/configure.dtd">
3. <!-- =============================================================== -->
4. <!-- Configure the Jetty Server 配置Jetty服务器 -->
5. <!-- Documentation of this file format can be found at: -->
6. <!-- http://docs.codehaus.org/display/JETTY/jetty.xml -->
7. <!-- =============================================================== -->
8. <Configure id="Server" class="org.mortbay.jetty.Server">
9. <!-- =========================================================== -->
10. <!-- Server Thread Pool 线程池 -->
11. <!-- =========================================================== -->
12. <Set name="ThreadPool">
13. <!-- Default bounded blocking threadpool 默认的阻断线程边界 -->
14. <New class="org.mortbay.thread.BoundedThreadPool">
15. <Set name="minThreads">10</Set>
16. <Set name="maxThreads">250</Set>
17. <Set name="lowThreads">25</Set>
18. </New>
19. <!-- Optional Java 5 bounded threadpool with job queue 可选的使用Java5的带工作队列的线程池
20. <New class="org.mortbay.thread.concurrent.ThreadPool">
21. <Set name="corePoolSize">250</Set>
22. <Set name="maximumPoolSize">250</Set>
23. </New>
24. -->
25. </Set>
26. <!-- =========================================================== -->
27. <!-- Set connectors 设置连接器 -->
28. <!-- =========================================================== -->
29. <!-- One of each type! 其中每一类! -->
30. <!-- =========================================================== -->
31. <!-- Use this connector for many frequently idle connections and for threadless continuations. -->
32. <!-- 大量频繁的空闲连接、无线程的继续使用这个连接器 -->
33. <Call name="addConnector">
34. <Arg>
35. <New class="org.mortbay.jetty.nio.SelectChannelConnector">
36. <Set name="port"><SystemProperty name="jetty.port" default="8080"/></Set>
37. <Set name="maxIdleTime">30000</Set>
38. <Set name="Acceptors">2</Set>
39. <Set name="statsOn">false</Set>
40. <Set name="confidentialPort">8443</Set>
41. <Set name="lowResourcesConnections">5000</Set>
42. <Set name="lowResourcesMaxIdleTime">5000</Set>
43. </New>
44. </Arg>
45. </Call>
46. <!-- Use this connector if NIO is not available.如果NIO不可用使用这个连接器
47. <Call name="addConnector">
48. <Arg>
49. <New class="org.mortbay.jetty.bio.SocketConnector">
50. <Set name="port">8081</Set>
51. <Set name="maxIdleTime">50000</Set>
52. <Set name="lowResourceMaxIdleTime">1500</Set>
53. </New>
54. </Arg>
55. </Call>
56. -->
57. <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
58. <!-- To add a HTTPS SSL listener 添加一个HTTPS SSL监听器 -->
59. <!-- see jetty-ssl.xml to add an ssl connector. use -->
60. <!-- java -jar start.jar etc/jetty.xml etc/jetty-ssl.xml -->
61. <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
62.
63. <!-- =========================================================== -->
64. <!-- Set up global session ID manager 设置全局会话ID管理器 -->
65. <!-- =========================================================== -->
66. <!--
67. <Set name="sessionIdManager">
68. <New class="org.mortbay.jetty.servlet.HashSessionIdManager">
69. <Set name="workerName">node1</Set>
70. </New>
71. </Set>
72. -->
73. <!-- =========================================================== -->
74. <!-- Set handler Collection Structure 设置处理器结构集合 -->
75. <!-- =========================================================== -->
76. <Set name="handler">
77. <New id="Handlers" class="org.mortbay.jetty.handler.HandlerCollection">
78. <Set name="handlers">
79. <Array type="org.mortbay.jetty.Handler">
80. <Item>
81. <New id="Contexts" class="org.mortbay.jetty.handler.ContextHandlerCollection"/>
82. </Item>
83. <Item>
84. <New id="DefaultHandler" class="org.mortbay.jetty.handler.DefaultHandler"/>
85. </Item>
86. <Item>
87. <New id="RequestLog" class="org.mortbay.jetty.handler.RequestLogHandler"/>
88. </Item>
89. </Array>
90. </Set>
91. </New>
92. </Set>
93. <!-- =========================================================== -->
94. <!-- Configure the context deployer 设置上下文展开器 -->
95. <!-- A context deployer will deploy contexts described in configuration files discovered in a directory. -->
96. <!-- The configuration directory can be scanned for hot deployments at the configured scanInterval. -->
97. <!-- This deployer is configured to deploy contexts configured -->
98. <!-- in the $JETTY_HOME/contexts directory -->
99. <!-- -->
100. <!-- =========================================================== -->
101. <Call name="addLifeCycle">
102. <Arg>
103. <New class="org.mortbay.jetty.deployer.ContextDeployer">
104. <Set name="contexts"><Ref id="Contexts"/></Set>
105. <Set name="configurationDir"><SystemProperty name="jetty.home" default="."/>/contexts</Set>
106. <Set name="scanInterval">1</Set>
107. </New>
108. </Arg>
109. </Call>
110.
111. <!-- =========================================================== -->
112. <!-- Configure the webapp deployer. 配置webapp展开器 -->
113. <!-- A webapp deployer will deploy standard webapps discovered -->
114. <!-- in a directory at startup, without the need for additional -->
115. <!-- configuration files. It does not support hot deploy or -->
116. <!-- non standard contexts (see ContextDeployer above). -->
117. <!-- This deployer is configured to deploy webapps from the 这个展开器用来展开-->
118. <!-- $JETTY_HOME/webapps directory $JETTY_HOME/webapps目录的应用-->
119. <!-- Normally only one type of deployer need be used. 通常仅仅需要使用一种类型展开器 -->
120. <!-- =========================================================== -->
121. <Call name="addLifeCycle">
122. <Arg>
123. <New class="org.mortbay.jetty.deployer.WebAppDeployer">
124. <Set name="contexts"><Ref id="Contexts"/></Set>
125. <Set name="webAppDir"><SystemProperty name="jetty.home" default="."/>/webapps</Set>
126. <Set name="parentLoaderPriority">false</Set>
127. <Set name="extract">true</Set>
128. <Set name="allowDuplicates">false</Set>
129. <Set name="defaultsDescriptor"><SystemProperty name="jetty.home" default="."/>/etc/webdefault.xml</Set>
130. </New>
131. </Arg>
132. </Call>
133. <!-- =========================================================== -->
134. <!-- Configure Authentication Realms 配置认证领域 -->
135. <!-- Realms may be configured for the entire server here, or 全服务器的领域可以在这里配置-->
136. <!-- they can be configured for a specific web app in a context 也可以在在特定应用程序的上下文中-->
137. <!-- configuration (see $(jetty.home)/contexts/test.xml for an -->
138. <!-- example). -->
139. <!-- =========================================================== -->
140. <Set name="UserRealms">
141. <Array type="org.mortbay.jetty.security.UserRealm">
142. <!--
143. <Item>
144. <New class="org.mortbay.jetty.security.HashUserRealm">
145. <Set name="name">Test Realm</Set>
146. <Set name="config"><SystemProperty name="jetty.home" default="."/>/etc/realm.properties</Set>
147. </New>
148. </Item>
149. -->
150. </Array>
151. </Set>
152. <!-- =========================================================== -->
153. <!-- Configure Request Log 配置请求日志 -->
154. <!-- Request logs may be configured for the entire server here,这个服务器的请求日志可以在这里配置 -->
155. <!-- or they can be configured for a specific web app in a 或者在特定程序的上下文配置文件中 -->
156. <!-- contexts configuration (see $(jetty.home)/contexts/test.xml for an example).-->
157. <!-- =========================================================== -->
158. <Ref id="RequestLog">
159. <Set name="requestLog">
160. <New id="RequestLogImpl" class="org.mortbay.jetty.NCSARequestLog">
161. <Set name="filename"><SystemProperty name="jetty.logs" default="./logs"/>/yyyy_mm_dd.request.log</Set>
162. <Set name="filenameDateFormat">yyyy_MM_dd</Set>
163. <Set name="retainDays">90</Set>
164. <Set name="append">true</Set>
165. <Set name="extended">true</Set>
166. <Set name="logCookies">false</Set>
167. <Set name="LogTimeZone">GMT</Set>
168. </New>
169. </Set>
170. </Ref>
171.
172. <!-- =========================================================== -->
173. <!-- extra options -->
174. <!-- =========================================================== -->
175. <Set name="stopAtShutdown">true</Set>
176. <Set name="sendServerVersion">true</Set>
177. <Set name="sendDateHeader">true</Set>
178. <Set name="gracefulShutdown">1000</Set>
179.
180. </Configure>
webmaster 03/05/09 16:20:42
--------------------------------------------------------------------------------
Powered by Trac 0.10.4
By Edgewall Software.
Visit the Trac open source project at
http://trac.edgewall.org/
大多数公司普遍存在以下问题,您公司是否可以幸免?
* 1. 简单任务布置得不明确、不清晰,导致员工理解偏差
* 2. 在布置复杂工作时,只交代要求,不与下属一起做工作计划,造成下属工作无法开展,或无法达到预期目的
* 3. 对决定不加以跟进,在执行过程缺乏监控,导致任务不能及时完整完成
* 4. 授权或布置任务时选错对象:或者下属不胜任、下属抱怨任务无聊、或者下属工作负担太重
* 5. 对下属缺乏指导,致使后继乏人,而下属则抱怨缺少支持
* 6. 对下属原则性指导多,给具体方法少
* 7. 不能很好区分员工的技能性问题和态度问题
* 8. 不能有效鞭策员工,导致员工说一下动一下,不推不动
* 9. 批评员工不得要领,要么太软,要么太硬
* 10. 解决问题就事论事,同一个问题长期存在
管理为了结果,但是有多少管理没有达到预期的结果?
原因固然很多,但管理者管理不到位是最普遍、相对容易解决和改变以后最见成效的问题。
4C是通过加强管理技能,解决以上问题的四把钥匙:
* 1. 简单任务布置得不明确、不清晰,导致员工理解偏差
* 2. 在布置复杂工作时,只交代要求,不与下属一起做工作计划,造成下属工作无法开展,或无法达到预期目的
* 3. 对决定不加以跟进,在执行过程缺乏监控,导致任务不能及时完整完成
* 4. 授权或布置任务时选错对象:或者下属不胜任、下属抱怨任务无聊、或者下属工作负担太重
* 5. 对下属缺乏指导,致使后继乏人,而下属则抱怨缺少支持
* 6. 对下属原则性指导多,给具体方法少
* 7. 不能很好区分员工的技能性问题和态度问题
* 8. 不能有效鞭策员工,导致员工说一下动一下,不推不动
* 9. 批评员工不得要领,要么太软,要么太硬
* 10. 解决问题就事论事,同一个问题长期存在
管理为了结果,但是有多少管理没有达到预期的结果?
原因固然很多,但管理者管理不到位是最普遍、相对容易解决和改变以后最见成效的问题。
4C是通过加强管理技能,解决以上问题的四把钥匙:
“有什么业务是腾讯不做的吗?”美团网CEO王兴的语气中难掩郁闷。7月9日,腾讯QQ团购网上线,这让王兴如闻惊雷,也如坐针毡。从2003年回国到现在,王兴先后创办了校内、海内、饭否和美团4个网站,而美团网被他视为“最靠谱”的一次创业。3月初上线的美团网是国内第一家团购网站,创立仅仅4个月,美团网已经能够盈亏平衡。就在这时候,一直悄无声息的腾讯杀了进来,这让王兴完全猝不及防,也让处于草创时期的数百家团购网站倒吸了一口凉气。谁也不知道,这一次,这个“企鹅仔”将是搅局者、掠食者,还是终结者。
别上腾讯盯上其实,王兴应该早就想到会有这么一天。因为在中国互联网发展历史上,腾讯几乎没有缺席过任何一场互联网盛宴。它总是在一开始就亦步亦趋地跟随、然后细致地模仿,然后决绝地超越。比如当初的游戏。
“从QQ游戏平台上线那天起,联众的失败就已经注定了。”多年以后,在北京知春路的一家咖啡馆,联众创始人鲍岳桥谈起当年腾讯对联众的围剿和逼迫,仍然耿耿于怀。在两个小时的采访中,他连续抽了两包烟。
联众是中国最早做游戏平台的公司,一度占有在线棋牌游戏市场85%以上的市场份额,在新浪、搜狐等门户网站亏损缠身的时候,联众是最早实现赢利的中国互联网企业,一时风光无两。
2003年8月,腾讯QQ游戏第一个公开测试版本正式发布。鲍岳桥发现,从平台到游戏设计,QQ游戏完全是联众游戏的翻版。愤怒之余,“感到危险很大”的鲍岳桥首先想到的是“主动低头”寻求合作,于是他赶赴深圳,约见马化腾和时任腾讯公司首席运营官的曾李青,但是遭到了腾讯方面的拒绝。
“现在想来,那时候是太天真了。”鲍岳桥说,“与大型网游不同,棋牌类游戏规则固定,没有技术门槛,玩家又与QQ用户高度重合,腾讯很容易模仿。”
2004年9月,QQ游戏平台将联众赶下了中国第一休闲游戏门户的宝座。而在此之后,联众的业绩一路下滑,出售、转型,经历了一系列风波后,联众在中国网络游戏市场份额已不足1%。
腾讯则扶摇直上,在今年一季度,QQ游戏同时在线人数达到了680万。而更重要的是,依托QQ游戏平台,腾讯终于在2009年第二季度超越盛大,坐上了中国网络游戏领域的头把交椅。
对鲍岳桥来说,腾讯就是自己的终结者。2006年底,鲍岳桥离开了江河日下的联众,成为了一名天使投资人。他告诉记者,现在他做投资的原则之一就是:只做腾讯不会做、不能做的项目。所以三年来,他绝对不碰游戏,已经投资的医疗器械和数据存储项目都跟腾讯毫无关联。
而这个终结者又有了新的目标,那就是“站长之王”蔡文胜的4399小游戏平台。
“说不担心QQ竞争那是骗人的。”蔡文胜在微博上表达了自己的忧虑,直接原因就是今年7月初,腾讯旗下小游戏平台3366.com上线公测。
据记者调查,去年蔡文胜买下的4399小游戏平台,通过广告联盟和联合运营网页游戏,月营收已达3000~5000万元,正在筹备国内A股上市。而腾讯刚刚上线的3366,在游戏种类和网站设计上与4399几无二致。
而且这只“企鹅仔”似乎更加来势汹汹。从7月1日开始,不断有网友看到QQ弹窗对这一游戏平台的推广信息,而截止记者发稿时,3366.com同时在线人数已突破10万。
只要是一个领域前景看好,腾讯就肯定会伺机充当掠食者。除了王兴和蔡文胜,腾讯最近还“默默地”动了另外一个人的奶酪,他就是奇虎360董事长周鸿祎。
5月31日,杀毒领域两大巨头360与金山的一场口水战激战正酣,腾讯的QQ医生3.3升级版却悄然上线。很快人们就发现,这款原本只是用来查杀QQ盗号木马的防护软件,已经了包含云查杀木马、系统漏洞修补、实时防护、清理插件等多项安全防护功能,甚至还搭载了免费半年的诺顿杀毒。
此前,周鸿祎曾在多个公开场合对腾讯创始人马化腾在产品上的功力赞不绝口,同时还声称,腾讯绝不会成为360的竞争对手,因为“腾讯是一个娱乐公司,在安全方面,应该由一个很专业的公司更专注地去解决问题”。
很显然,马化腾毫不客气地给了周鸿祎当头一棒。
在腾讯还没有出手的互联网领域,小企鹅那些潜在的竞争对手们仍是战战兢兢,如履薄冰。比如暴风影音CEO冯鑫。自从2008年9月腾讯发布了本地播放软件 QQ影音首个Beta版本,冯鑫恐怕就没睡过一天好觉。因为这款无广告、无插件播放软件,让暴风影音的盈利模式变得岌岌可危。
而在各大视频网站因为版权打得不可开交,频频对簿公堂之时,同样有一种声音在业内流传:无论你们现在打得多欢实,等市场培育得差不多了,就该轮到腾讯来收场了。事实确实如此,QQLive的平台早就搭好了,拼版权,中国的互联网公司谁敢说自己比腾讯更有钱?
这就是腾讯,中国第一、全球第三大互联网公司,一家全球罕见的互联网全业务公司,即时通讯、门户、游戏、电子商务、搜索等等无所不做。它总是默默地布局、悄无声息地出现在你的背后;它总是在最恰当的时候出来搅局,让同业者心神不定。而一旦时机成熟,它就会毫不留情地划走自己的那块蛋糕,有时它甚至会成为终结者,霸占整个市场。
“某网站贪得无厌,没有它不染指的领域,没有它不想做的产品,这样下去物极必反,与全网为敌,必将死无葬身之地。”6月29日,新浪网总编陈彤以“老沉”为名发布了一则微博,言辞之激烈,让人震惊。这条微博迅速被转发了500多次,无数的人力挺“老沉”。
谈起此事,一位互联网创业者几乎是脱口而出,“狗日的腾讯!”
始终“贪得无厌” “既没有马云那么好的口才,也没有李彦宏那么帅。”马化腾曾经多次自嘲,说自己“很不幸”,“大家都是圈地,他们(马云、李彦宏)圈的都是楼,可以直接住。我们圈到的却是荒地,只能从铲沙、挖土开始,建自己的楼。”
实际上,马化腾算不上纯粹的“草根创业”。据传,在腾讯创立初期,其父马陈术曾开着奔驰前来给儿子做账。在11年的发展历史上,腾讯只是在早期遭遇过资金困局,从获得第一笔融资开始就一直是稳扎稳打,先利用无线增值服务实现盈利,转而依靠互联网增值服务壮大,布局网络游戏和门户业务。2010年最新一季财报显示,腾讯的网络广告业务收入为2990万美元,已经远远超过网易的1340万美元,稳居门户第三。
马化腾在业界以低调、务实著称,这在一定程度上决定了腾讯的企业风格:其疾如风,其徐如林,侵掠如火,不动如山。
2006年7月,QQ同时在线突破2000万人,腾讯公司内部决定办一个庆功会,会上腾讯联席CTO熊明华问了马化腾一个问题:QQ同时在线人数何时能够到1亿?马化腾一笑:“这辈子我可能看不到了。”事实上,2010年3月5日,他就看到了。熊明华一定很后悔,没有和马化腾打赌“裸奔”。
实际上,马化腾有很多值得“裸奔式”庆祝的理由。目前,腾讯是中国最赚钱的互联网公司,公司现金储备达到15亿美元;拥有中国本土用户量最大的即时通讯软件,账户数近10亿;是中国第一流量的门户;在网络游戏市场排名第一,占据超过20%以上的市场份额;电子邮箱流量也已经超过网易,雄踞榜首。
资本市场对这只彪悍的企鹅也是极力追捧。在香港,腾讯的股价一度高达每股171.80港元,上市6年间腾讯股价上涨了超过了35倍。要知道,被世界公认为近年来最具创新能力的苹果,其股价增幅才只有腾讯的一半。
腾讯为什么还不满足?一只企鹅为何如此贪婪?
是的, “腾讯不是一般的有钱”,但股东的钱不是用来供着的,腾讯必须不断寻找新的利润增长点。蔡文胜就曾表示,腾讯现在什么都想做,从中可以看出它面对快速增长的巨大压力,这个压力终有一天会压垮腾讯。
在美团网创始人王兴看来,腾讯之所以染指团购,是因为这模式已经被证明“能赚钱”。“做团购没有技术门槛,盈利模式又清晰,腾讯没有理由不做。”王兴指出,团购与他之前创办的校内和饭否最大的不同在于,“网站从上线第一天开始就有收入”。——如此唾手可得的生意,腾讯怎么可能放过?
搜索也将是腾讯的下一个目标。今年3月,马化腾与李彦宏在深圳有过一场对话。李彦宏问马化腾,“腾讯凭什么做搜索?”马化腾给出了两点理由:一是用户需要,腾讯这个一站式互联网服务平台中的很多环节都需要搜索功能;二是搜索能赚钱,腾讯拥有全球最大互联网社交网络系统,社区的盈利模式中,除了个人收费以外,未来还要结合页面内容分析,匹配相关性的广告。因此,已有业内人士指出,而在这一类似于Google的AdWords模式的探索过程中,腾讯未来必将对百度正在培育的广告联盟形成威胁。
也许,在马化腾看来,无论是搜索,还是团购,甚至是将来的视频,这些业务都是腾讯水到渠成的业务延伸。因为马化腾为腾讯未来的构想是,一站式互联网服务提供商。——围绕腾讯QQ打造“在线生活社区”,也就是“用户要什么,腾讯就有什么”。百度董事局主席兼CEO李彦宏对腾讯所谓的“在线生活”、“一站式服务”的评价是:基本上就是不给别人任何空间。
在CSDN总裁蒋涛看来,腾讯之所以什么都做,是因为它是一家以人(用户)为中心的企业,同类型的企业还有软件巨头微软,两家公司的产品战略更是惊人的相似。
长期以来,以操作系统为核心的微软也是个典型的“全民公敌”。为了“抓住”用户,微软每个阶段都会根据市场变化,布局新的应用,以巩固其用户终端的垄断地位。在个人消费领域上,微软先后推出了浏览器IE、邮件系统Hotmail、即时通讯MSN、邮件客户端outlook、免费杀毒软件MSE,以及今年5 月刚刚发布的在线版Office软件。
而从另一方面讲,腾讯的进攻也是一种防御。互联网产业往往形势突变,Google市值超越雅虎,Facebook流量超越Google都发生在旦夕之间。腾讯最怕的就是突然冒出一个企业,被一种意想不到的商业模式或竞争策略打败。所以腾讯对于任何一个互联网的新应用都不敢掉以轻心。
“360安全卫士、暴风影音的装机量都已经上亿了,如果周鸿祎或者冯鑫有一天跟新浪合作,也推新闻弹出框,马化腾不就郁闷了?”蒋涛认为,腾讯的产品策略之一就是:所有的互联网应用,只要用户量到了一定级别,腾讯一定要有,别人的产品可以暂时比腾讯做得好,但腾讯绝不会让它不可替代。
当被问及腾讯的核心竞争力时,腾讯CTO熊明华给记者的答案不是超过10亿的QQ的注册用户,也不是某一项产品、技术方面优势,而是“耐心”:懂得在合适的时间推出合适的产品。”
因此,究竟腾讯还会做什么,没有人知道。
一直在模仿 腾讯从来不做第一个吃螃蟹的人,却总能在成熟的市场中找到空间,横插一杠子。然而它选择的路径也使其饱受争议,那就是模仿,有时甚至是肆无忌惮地“山寨”。
早在2006年,新浪网创始人王志东就公开指责马化腾是业内有名的“抄袭大王”,而且是明目张胆地抄袭。几年以来,类似的声音一直不绝于耳。直到最近,DCCI互联网数据中心主任胡延平还在质疑腾讯的创新能力,说它不仅不是卓越创新者,反倒是中小互联网企业的“创新天敌”。
从模仿ICQ推出自己的第一款产品OICQ(腾讯QQ的前身)开始,腾讯似乎就埋下了自己的“模仿基因”——先是从韩国引入了QQ秀和其他一系列增值服务,又模仿新浪建起了门户网站;在网游领域,学联众开发平台,跟着盛大引进国外网友,随着网易自主研发,之后布局的C2C电子商务网站拍拍,以及第三方支付财付通,无一不是“山寨货”,这也是腾讯遭人恨的根本原因。
“微博、杀毒、电子商务到今天的团购,这些领域的商业模式在那儿摆着,人人都在抄,你凭什么要求腾讯高抬贵手,不去挣这个钱了?”互联网资深人士谢文在接受记者采访时表示,业界这种对腾讯的埋怨,就像“小孩儿撒娇”,是五十步笑一百步。
对于模仿的指责,马化腾的回应是:模仿是最稳妥的创新。
“创新可以分为三个层次:技术创新、产品创新和应用创新,产品和应用层面的创新比较容易被人忽略。”一位资深互联网产品经理告诉记者,几乎腾讯的每款产品都能找出市场上其他同类产品所没有的优点,如腾讯QQ的群和显示最近联系人功能,QQ邮箱的超大附件功能,QQ游戏平台一上线就号称能承载上千万的同时在线,QQ还解决了困扰很多IM产品的联通、电信的互联互通问题等等。
事实上,腾讯获得突破的领域往往得益于应用层面的创新,腾讯总是能够通过QQ用户行为习惯的把握,将新产品与腾讯QQ这一核心进行结合,使其用户的优势得到发挥。同为技术出身,奇虎360董事长周鸿祎坦言如果同是做即时通讯,自己在产品细节和技术上能够比马化腾做得好,但很难比QQ成功。因为马化腾是把互联网产品当成服务来做,其成功在于“打动人心”。
CSDN总裁蒋涛在接受本报记者采访时也表示,虽然从商业竞争的角度,腾讯通过复制别人的商业模式进行无限扩张,是无可厚非的,但在客观上必然会扼杀一些创新的好苗头。这也和胡延平的观点一致,从某种程度上说,腾讯是互联网创新者的杀手。
腾讯的麻烦四面制造麻烦的腾讯并非每次都能凯旋而归,甚至给自己惹上了不少麻烦。2009年6月,搜狐就因为输入法将腾讯告上法庭,称腾讯侵犯了其旗下搜狗拼音输入法的软件自主知识产权,并且利用QQ拼音输入法破坏搜狗拼音输入法服务,对搜狗实施不正当竞争,因此请求法院判令腾讯停止不正当竞争行为,并索赔2000万元。
能够让同为互联网巨头的搜狐撕下脸面,腾讯“与全网为敌”所招致的民愤可见一斑。
不过,身为山寨之王的腾讯也在遭遇“被山寨”。2005年成立的51.com,几乎腾讯每推出一个新的功能与应用,它都会加以“学习”、“消化”,并迅速在自己的平台上开发出来。如目前在51.com平台上的“51商城”、“51群组”、“51秀”、“51问问”,它甚至曾经开发出彩虹QQ,免费提供IP 地址探测、显示隐身好友等腾讯QQ的“增值”功能。
怎样才能抗衡腾讯呢
“能不能给大家一点建议,怎样才能抗衡腾讯呢?”在2009年游戏产业年会的高峰对话环节,当主持人抛给腾讯游戏总裁任宇昕这样一个问题时,除了任宇昕自己一脸骄傲,举坐皆苦笑。这位中国最大互联网公司的游戏业务负责人也不谦虚: 只有跟腾讯合作,共同把市场一同做大。
在外界看来,腾讯庞大的身躯,依然潜伏着诸多暗流。实际上,因为腾讯在互联网界“无耻模仿抄袭”的恶名,使得腾讯全线树敌,成为众矢之的。当越来越多的互联网企业开始时时提防着腾讯的时候,腾讯将不再像以前那样收放自如。比如,为应对腾讯的搜索,百度就将搜索的提成比例从10%提升到15%。
而且,腾讯还算不上真正强大。互联网资深人士谢文则表示,腾讯的模仿充其量只能让保持强大的现状,却不能使其引领潮流,真正走向伟大。“事实上,如果腾讯一味模仿下去,随着平台上的服务越来越多,单个服务的效率会大幅降低。”谢文表示,“而且,如果腾讯只是针对现有的QQ用户群体开发应用,未来QQ用户的人口特性将被固定在年轻群体的娱乐需求上,随着网民年龄结构的变化,腾讯就会被最终边缘化,而开心网、人人网及新浪微博的崛起已经为腾讯的迟钝敲下了警钟。”
在很多人眼中,腾讯是最近接近Google的一家本土互联网公司。因为虽然Google目前的主要盈利点还是围绕其搜索产品的AdSense和 AdWords,但它也是邮箱、地图、音乐无所不做,腾讯也是如此,虽然号称全民公敌,但它的主要收入仍然来自IM和网络游戏所带来的互联网增值服务。
但谢文却指出这只是表面现象:“腾讯和Google完全不在一个档次上”。他指出,Gmail、Google地图、Google Earth等产品虽然不赚钱,但是它们之所以被开发都是围绕着一个核心理念:就信息整合与信息呈现。相比之下,腾讯的产品则显得杂乱无章,IM、网游、电子商务与门户业务之间并不必然的关联,其他公司单独做也能成功。据此,谢文认为,腾讯只是利用先发优势抓住了一大批用户,产品研发都是针对用户市场展开,追求短期效益,而对自己的未来缺乏清晰地规划。
“建立在用户群上的腾讯是不牢靠的。”蒋涛认为,一旦未来人们更喜欢用Facebook和Twitter这样的工具彼此联络,不再以IM为中心,腾讯的“大本营”就被攻克了,这意味其虚拟货币系统必将被超越,而网游、门户这些现有盈利点也不能保证一直有市场竞争力。
“如果人们未来都不再依赖PC,改用Ipad和手机的话,微软无疑就完蛋了”蒋涛说。微软的今天可能就是腾讯的明天,IT产业往往形势突变,用户习惯的变化又是在旦夕之间,看看facebook和google所带来的一场场变革,腾讯当以微软和雅虎为戒。
新闻来源:计算机世界
别上腾讯盯上其实,王兴应该早就想到会有这么一天。因为在中国互联网发展历史上,腾讯几乎没有缺席过任何一场互联网盛宴。它总是在一开始就亦步亦趋地跟随、然后细致地模仿,然后决绝地超越。比如当初的游戏。
“从QQ游戏平台上线那天起,联众的失败就已经注定了。”多年以后,在北京知春路的一家咖啡馆,联众创始人鲍岳桥谈起当年腾讯对联众的围剿和逼迫,仍然耿耿于怀。在两个小时的采访中,他连续抽了两包烟。
联众是中国最早做游戏平台的公司,一度占有在线棋牌游戏市场85%以上的市场份额,在新浪、搜狐等门户网站亏损缠身的时候,联众是最早实现赢利的中国互联网企业,一时风光无两。
2003年8月,腾讯QQ游戏第一个公开测试版本正式发布。鲍岳桥发现,从平台到游戏设计,QQ游戏完全是联众游戏的翻版。愤怒之余,“感到危险很大”的鲍岳桥首先想到的是“主动低头”寻求合作,于是他赶赴深圳,约见马化腾和时任腾讯公司首席运营官的曾李青,但是遭到了腾讯方面的拒绝。
“现在想来,那时候是太天真了。”鲍岳桥说,“与大型网游不同,棋牌类游戏规则固定,没有技术门槛,玩家又与QQ用户高度重合,腾讯很容易模仿。”
2004年9月,QQ游戏平台将联众赶下了中国第一休闲游戏门户的宝座。而在此之后,联众的业绩一路下滑,出售、转型,经历了一系列风波后,联众在中国网络游戏市场份额已不足1%。
腾讯则扶摇直上,在今年一季度,QQ游戏同时在线人数达到了680万。而更重要的是,依托QQ游戏平台,腾讯终于在2009年第二季度超越盛大,坐上了中国网络游戏领域的头把交椅。
对鲍岳桥来说,腾讯就是自己的终结者。2006年底,鲍岳桥离开了江河日下的联众,成为了一名天使投资人。他告诉记者,现在他做投资的原则之一就是:只做腾讯不会做、不能做的项目。所以三年来,他绝对不碰游戏,已经投资的医疗器械和数据存储项目都跟腾讯毫无关联。
而这个终结者又有了新的目标,那就是“站长之王”蔡文胜的4399小游戏平台。
“说不担心QQ竞争那是骗人的。”蔡文胜在微博上表达了自己的忧虑,直接原因就是今年7月初,腾讯旗下小游戏平台3366.com上线公测。
据记者调查,去年蔡文胜买下的4399小游戏平台,通过广告联盟和联合运营网页游戏,月营收已达3000~5000万元,正在筹备国内A股上市。而腾讯刚刚上线的3366,在游戏种类和网站设计上与4399几无二致。
而且这只“企鹅仔”似乎更加来势汹汹。从7月1日开始,不断有网友看到QQ弹窗对这一游戏平台的推广信息,而截止记者发稿时,3366.com同时在线人数已突破10万。
只要是一个领域前景看好,腾讯就肯定会伺机充当掠食者。除了王兴和蔡文胜,腾讯最近还“默默地”动了另外一个人的奶酪,他就是奇虎360董事长周鸿祎。
5月31日,杀毒领域两大巨头360与金山的一场口水战激战正酣,腾讯的QQ医生3.3升级版却悄然上线。很快人们就发现,这款原本只是用来查杀QQ盗号木马的防护软件,已经了包含云查杀木马、系统漏洞修补、实时防护、清理插件等多项安全防护功能,甚至还搭载了免费半年的诺顿杀毒。
此前,周鸿祎曾在多个公开场合对腾讯创始人马化腾在产品上的功力赞不绝口,同时还声称,腾讯绝不会成为360的竞争对手,因为“腾讯是一个娱乐公司,在安全方面,应该由一个很专业的公司更专注地去解决问题”。
很显然,马化腾毫不客气地给了周鸿祎当头一棒。
在腾讯还没有出手的互联网领域,小企鹅那些潜在的竞争对手们仍是战战兢兢,如履薄冰。比如暴风影音CEO冯鑫。自从2008年9月腾讯发布了本地播放软件 QQ影音首个Beta版本,冯鑫恐怕就没睡过一天好觉。因为这款无广告、无插件播放软件,让暴风影音的盈利模式变得岌岌可危。
而在各大视频网站因为版权打得不可开交,频频对簿公堂之时,同样有一种声音在业内流传:无论你们现在打得多欢实,等市场培育得差不多了,就该轮到腾讯来收场了。事实确实如此,QQLive的平台早就搭好了,拼版权,中国的互联网公司谁敢说自己比腾讯更有钱?
这就是腾讯,中国第一、全球第三大互联网公司,一家全球罕见的互联网全业务公司,即时通讯、门户、游戏、电子商务、搜索等等无所不做。它总是默默地布局、悄无声息地出现在你的背后;它总是在最恰当的时候出来搅局,让同业者心神不定。而一旦时机成熟,它就会毫不留情地划走自己的那块蛋糕,有时它甚至会成为终结者,霸占整个市场。
“某网站贪得无厌,没有它不染指的领域,没有它不想做的产品,这样下去物极必反,与全网为敌,必将死无葬身之地。”6月29日,新浪网总编陈彤以“老沉”为名发布了一则微博,言辞之激烈,让人震惊。这条微博迅速被转发了500多次,无数的人力挺“老沉”。
谈起此事,一位互联网创业者几乎是脱口而出,“狗日的腾讯!”
始终“贪得无厌” “既没有马云那么好的口才,也没有李彦宏那么帅。”马化腾曾经多次自嘲,说自己“很不幸”,“大家都是圈地,他们(马云、李彦宏)圈的都是楼,可以直接住。我们圈到的却是荒地,只能从铲沙、挖土开始,建自己的楼。”
实际上,马化腾算不上纯粹的“草根创业”。据传,在腾讯创立初期,其父马陈术曾开着奔驰前来给儿子做账。在11年的发展历史上,腾讯只是在早期遭遇过资金困局,从获得第一笔融资开始就一直是稳扎稳打,先利用无线增值服务实现盈利,转而依靠互联网增值服务壮大,布局网络游戏和门户业务。2010年最新一季财报显示,腾讯的网络广告业务收入为2990万美元,已经远远超过网易的1340万美元,稳居门户第三。
马化腾在业界以低调、务实著称,这在一定程度上决定了腾讯的企业风格:其疾如风,其徐如林,侵掠如火,不动如山。
2006年7月,QQ同时在线突破2000万人,腾讯公司内部决定办一个庆功会,会上腾讯联席CTO熊明华问了马化腾一个问题:QQ同时在线人数何时能够到1亿?马化腾一笑:“这辈子我可能看不到了。”事实上,2010年3月5日,他就看到了。熊明华一定很后悔,没有和马化腾打赌“裸奔”。
实际上,马化腾有很多值得“裸奔式”庆祝的理由。目前,腾讯是中国最赚钱的互联网公司,公司现金储备达到15亿美元;拥有中国本土用户量最大的即时通讯软件,账户数近10亿;是中国第一流量的门户;在网络游戏市场排名第一,占据超过20%以上的市场份额;电子邮箱流量也已经超过网易,雄踞榜首。
资本市场对这只彪悍的企鹅也是极力追捧。在香港,腾讯的股价一度高达每股171.80港元,上市6年间腾讯股价上涨了超过了35倍。要知道,被世界公认为近年来最具创新能力的苹果,其股价增幅才只有腾讯的一半。
腾讯为什么还不满足?一只企鹅为何如此贪婪?
是的, “腾讯不是一般的有钱”,但股东的钱不是用来供着的,腾讯必须不断寻找新的利润增长点。蔡文胜就曾表示,腾讯现在什么都想做,从中可以看出它面对快速增长的巨大压力,这个压力终有一天会压垮腾讯。
在美团网创始人王兴看来,腾讯之所以染指团购,是因为这模式已经被证明“能赚钱”。“做团购没有技术门槛,盈利模式又清晰,腾讯没有理由不做。”王兴指出,团购与他之前创办的校内和饭否最大的不同在于,“网站从上线第一天开始就有收入”。——如此唾手可得的生意,腾讯怎么可能放过?
搜索也将是腾讯的下一个目标。今年3月,马化腾与李彦宏在深圳有过一场对话。李彦宏问马化腾,“腾讯凭什么做搜索?”马化腾给出了两点理由:一是用户需要,腾讯这个一站式互联网服务平台中的很多环节都需要搜索功能;二是搜索能赚钱,腾讯拥有全球最大互联网社交网络系统,社区的盈利模式中,除了个人收费以外,未来还要结合页面内容分析,匹配相关性的广告。因此,已有业内人士指出,而在这一类似于Google的AdWords模式的探索过程中,腾讯未来必将对百度正在培育的广告联盟形成威胁。
也许,在马化腾看来,无论是搜索,还是团购,甚至是将来的视频,这些业务都是腾讯水到渠成的业务延伸。因为马化腾为腾讯未来的构想是,一站式互联网服务提供商。——围绕腾讯QQ打造“在线生活社区”,也就是“用户要什么,腾讯就有什么”。百度董事局主席兼CEO李彦宏对腾讯所谓的“在线生活”、“一站式服务”的评价是:基本上就是不给别人任何空间。
在CSDN总裁蒋涛看来,腾讯之所以什么都做,是因为它是一家以人(用户)为中心的企业,同类型的企业还有软件巨头微软,两家公司的产品战略更是惊人的相似。
长期以来,以操作系统为核心的微软也是个典型的“全民公敌”。为了“抓住”用户,微软每个阶段都会根据市场变化,布局新的应用,以巩固其用户终端的垄断地位。在个人消费领域上,微软先后推出了浏览器IE、邮件系统Hotmail、即时通讯MSN、邮件客户端outlook、免费杀毒软件MSE,以及今年5 月刚刚发布的在线版Office软件。
而从另一方面讲,腾讯的进攻也是一种防御。互联网产业往往形势突变,Google市值超越雅虎,Facebook流量超越Google都发生在旦夕之间。腾讯最怕的就是突然冒出一个企业,被一种意想不到的商业模式或竞争策略打败。所以腾讯对于任何一个互联网的新应用都不敢掉以轻心。
“360安全卫士、暴风影音的装机量都已经上亿了,如果周鸿祎或者冯鑫有一天跟新浪合作,也推新闻弹出框,马化腾不就郁闷了?”蒋涛认为,腾讯的产品策略之一就是:所有的互联网应用,只要用户量到了一定级别,腾讯一定要有,别人的产品可以暂时比腾讯做得好,但腾讯绝不会让它不可替代。
当被问及腾讯的核心竞争力时,腾讯CTO熊明华给记者的答案不是超过10亿的QQ的注册用户,也不是某一项产品、技术方面优势,而是“耐心”:懂得在合适的时间推出合适的产品。”
因此,究竟腾讯还会做什么,没有人知道。
一直在模仿 腾讯从来不做第一个吃螃蟹的人,却总能在成熟的市场中找到空间,横插一杠子。然而它选择的路径也使其饱受争议,那就是模仿,有时甚至是肆无忌惮地“山寨”。
早在2006年,新浪网创始人王志东就公开指责马化腾是业内有名的“抄袭大王”,而且是明目张胆地抄袭。几年以来,类似的声音一直不绝于耳。直到最近,DCCI互联网数据中心主任胡延平还在质疑腾讯的创新能力,说它不仅不是卓越创新者,反倒是中小互联网企业的“创新天敌”。
从模仿ICQ推出自己的第一款产品OICQ(腾讯QQ的前身)开始,腾讯似乎就埋下了自己的“模仿基因”——先是从韩国引入了QQ秀和其他一系列增值服务,又模仿新浪建起了门户网站;在网游领域,学联众开发平台,跟着盛大引进国外网友,随着网易自主研发,之后布局的C2C电子商务网站拍拍,以及第三方支付财付通,无一不是“山寨货”,这也是腾讯遭人恨的根本原因。
“微博、杀毒、电子商务到今天的团购,这些领域的商业模式在那儿摆着,人人都在抄,你凭什么要求腾讯高抬贵手,不去挣这个钱了?”互联网资深人士谢文在接受记者采访时表示,业界这种对腾讯的埋怨,就像“小孩儿撒娇”,是五十步笑一百步。
对于模仿的指责,马化腾的回应是:模仿是最稳妥的创新。
“创新可以分为三个层次:技术创新、产品创新和应用创新,产品和应用层面的创新比较容易被人忽略。”一位资深互联网产品经理告诉记者,几乎腾讯的每款产品都能找出市场上其他同类产品所没有的优点,如腾讯QQ的群和显示最近联系人功能,QQ邮箱的超大附件功能,QQ游戏平台一上线就号称能承载上千万的同时在线,QQ还解决了困扰很多IM产品的联通、电信的互联互通问题等等。
事实上,腾讯获得突破的领域往往得益于应用层面的创新,腾讯总是能够通过QQ用户行为习惯的把握,将新产品与腾讯QQ这一核心进行结合,使其用户的优势得到发挥。同为技术出身,奇虎360董事长周鸿祎坦言如果同是做即时通讯,自己在产品细节和技术上能够比马化腾做得好,但很难比QQ成功。因为马化腾是把互联网产品当成服务来做,其成功在于“打动人心”。
CSDN总裁蒋涛在接受本报记者采访时也表示,虽然从商业竞争的角度,腾讯通过复制别人的商业模式进行无限扩张,是无可厚非的,但在客观上必然会扼杀一些创新的好苗头。这也和胡延平的观点一致,从某种程度上说,腾讯是互联网创新者的杀手。
腾讯的麻烦四面制造麻烦的腾讯并非每次都能凯旋而归,甚至给自己惹上了不少麻烦。2009年6月,搜狐就因为输入法将腾讯告上法庭,称腾讯侵犯了其旗下搜狗拼音输入法的软件自主知识产权,并且利用QQ拼音输入法破坏搜狗拼音输入法服务,对搜狗实施不正当竞争,因此请求法院判令腾讯停止不正当竞争行为,并索赔2000万元。
能够让同为互联网巨头的搜狐撕下脸面,腾讯“与全网为敌”所招致的民愤可见一斑。
不过,身为山寨之王的腾讯也在遭遇“被山寨”。2005年成立的51.com,几乎腾讯每推出一个新的功能与应用,它都会加以“学习”、“消化”,并迅速在自己的平台上开发出来。如目前在51.com平台上的“51商城”、“51群组”、“51秀”、“51问问”,它甚至曾经开发出彩虹QQ,免费提供IP 地址探测、显示隐身好友等腾讯QQ的“增值”功能。
怎样才能抗衡腾讯呢
“能不能给大家一点建议,怎样才能抗衡腾讯呢?”在2009年游戏产业年会的高峰对话环节,当主持人抛给腾讯游戏总裁任宇昕这样一个问题时,除了任宇昕自己一脸骄傲,举坐皆苦笑。这位中国最大互联网公司的游戏业务负责人也不谦虚: 只有跟腾讯合作,共同把市场一同做大。
在外界看来,腾讯庞大的身躯,依然潜伏着诸多暗流。实际上,因为腾讯在互联网界“无耻模仿抄袭”的恶名,使得腾讯全线树敌,成为众矢之的。当越来越多的互联网企业开始时时提防着腾讯的时候,腾讯将不再像以前那样收放自如。比如,为应对腾讯的搜索,百度就将搜索的提成比例从10%提升到15%。
而且,腾讯还算不上真正强大。互联网资深人士谢文则表示,腾讯的模仿充其量只能让保持强大的现状,却不能使其引领潮流,真正走向伟大。“事实上,如果腾讯一味模仿下去,随着平台上的服务越来越多,单个服务的效率会大幅降低。”谢文表示,“而且,如果腾讯只是针对现有的QQ用户群体开发应用,未来QQ用户的人口特性将被固定在年轻群体的娱乐需求上,随着网民年龄结构的变化,腾讯就会被最终边缘化,而开心网、人人网及新浪微博的崛起已经为腾讯的迟钝敲下了警钟。”
在很多人眼中,腾讯是最近接近Google的一家本土互联网公司。因为虽然Google目前的主要盈利点还是围绕其搜索产品的AdSense和 AdWords,但它也是邮箱、地图、音乐无所不做,腾讯也是如此,虽然号称全民公敌,但它的主要收入仍然来自IM和网络游戏所带来的互联网增值服务。
但谢文却指出这只是表面现象:“腾讯和Google完全不在一个档次上”。他指出,Gmail、Google地图、Google Earth等产品虽然不赚钱,但是它们之所以被开发都是围绕着一个核心理念:就信息整合与信息呈现。相比之下,腾讯的产品则显得杂乱无章,IM、网游、电子商务与门户业务之间并不必然的关联,其他公司单独做也能成功。据此,谢文认为,腾讯只是利用先发优势抓住了一大批用户,产品研发都是针对用户市场展开,追求短期效益,而对自己的未来缺乏清晰地规划。
“建立在用户群上的腾讯是不牢靠的。”蒋涛认为,一旦未来人们更喜欢用Facebook和Twitter这样的工具彼此联络,不再以IM为中心,腾讯的“大本营”就被攻克了,这意味其虚拟货币系统必将被超越,而网游、门户这些现有盈利点也不能保证一直有市场竞争力。
“如果人们未来都不再依赖PC,改用Ipad和手机的话,微软无疑就完蛋了”蒋涛说。微软的今天可能就是腾讯的明天,IT产业往往形势突变,用户习惯的变化又是在旦夕之间,看看facebook和google所带来的一场场变革,腾讯当以微软和雅虎为戒。
新闻来源:计算机世界
Proxyサーバ情報の自動設定
わたくしの所属する会社ではインターネットにアクセスする場合、通常はWebのProxyサーバを経由するようにしている。そのため各クライアントのIEに対しては、Proxyサーバを使うように設定が行われている。以下のようなJavaScriptファイル(IEでは「自動構成スクリプト」と呼ぶ)を使って、自動的にProxyサーバへ要求を振り向けるように設定している。
WWWサーバに以下の設定が必要です。
MIME Type → application/x-ns-proxy-autoconfig
Apacheの場合はsrm.confファイルに
AddType application/x-ns-proxy-autoconfig .pac
NCSAの場合はmime.typesファイルに
application/x-ns-proxy-autoconfig pac
IIS(microsoft)の場合は
MIME Type → application/x-ns-proxy-autoconfig
proxy.pacの例
プロクシサーバがproxy.betalog.comでポート番号が1080のとき
function FindProxyForURL(url, host) {
if (isPlainHostName(host) ||
dnsDomainIs(host,".betalog.com") ||
isInNet(host, "192.168.0.0", "255.255.0.0") ||
isInNet(host, "127.0.0.0", "255.255.0.0")){
return "DIRECT"}
else {
return "PROXY proxy.betalog.com:1080; PROXY proxyn.betalog.com:1080; direct" ;
}
}
プロクシサーバが止まったときのためにセンターのサーバを予備に指定してください。
わたくしの所属する会社ではインターネットにアクセスする場合、通常はWebのProxyサーバを経由するようにしている。そのため各クライアントのIEに対しては、Proxyサーバを使うように設定が行われている。以下のようなJavaScriptファイル(IEでは「自動構成スクリプト」と呼ぶ)を使って、自動的にProxyサーバへ要求を振り向けるように設定している。
WWWサーバに以下の設定が必要です。
MIME Type → application/x-ns-proxy-autoconfig
Apacheの場合はsrm.confファイルに
AddType application/x-ns-proxy-autoconfig .pac
NCSAの場合はmime.typesファイルに
application/x-ns-proxy-autoconfig pac
IIS(microsoft)の場合は
MIME Type → application/x-ns-proxy-autoconfig
proxy.pacの例
プロクシサーバがproxy.betalog.comでポート番号が1080のとき
function FindProxyForURL(url, host) {
if (isPlainHostName(host) ||
dnsDomainIs(host,".betalog.com") ||
isInNet(host, "192.168.0.0", "255.255.0.0") ||
isInNet(host, "127.0.0.0", "255.255.0.0")){
return "DIRECT"}
else {
return "PROXY proxy.betalog.com:1080; PROXY proxyn.betalog.com:1080; direct" ;
}
}
プロクシサーバが止まったときのためにセンターのサーバを予備に指定してください。
以前写过一个刷校内网的人气的工具,Java的(以后再也不行Java程序了),里面用到了验证码识别,那段代码不是我自己写的:-) 校内的验证是完全单色没有任何干挠的验证码,识别起来比较容易,不过从那段代码中可以看到基本的验证码识别方式。这几天在写一个程序的时候需要识别验证码,因为程序是Python写的自然打算用Python进行验证码的识别。
以前没用Python处理过图像,不太了解PIL(Python Image Library)的用法,这几天看了看PIL,发现它太强大了,简直和ImageMagic,PS可以相比了。(这里有PIL不错的文档)
由于上面的验证码是24位的jpeg图像,并且包含了噪点,所以我们要做的就是去噪和去色,我拿PS找了张验证码试了试,使用PS滤镜中的去噪效果还行,但是没有在PIL找到去噪的函数,后来发现中值过滤后可以去掉大部分的噪点,而且PIL里有现成的函数,接下来我试着直接把图像转换为单色,结果发现还是会有不过的噪点留了下来,因为中值过滤时把不少噪点淡化了,但转换为音色时这些噪点又被强化显示了,于是在中值过滤后对图像亮度进行加强处理,然后再转换为单色,这样验证码图片就变得比较容易识别了:
上面这些处理使用Python才几行:
PYTHON:
im = Image.open(image_name)
im = im.filter(ImageFilter.MedianFilter())
enhancer = ImageEnhance.Contrast(im)
im = enhancer.enhance(2)
im = im.convert('1')
im.show()
接下来就是提取这些数字的字模,使用shell脚本下载100幅图片,抽出三张图片获取字模:
PYTHON:
#!/usr/bin/env python
#encoding=utf-8
import Image,ImageEnhance,ImageFilter
import sys
image_name = "./images/81.jpeg"
im = Image.open(image_name)
im = im.filter(ImageFilter.MedianFilter())
enhancer = ImageEnhance.Contrast(im)
im = enhancer.enhance(2)
im = im.convert('1')
#im.show()
#all by pixel
s = 12 #start postion of first number
w = 10 #width of each number
h = 15 #end postion from top
t = 2 #start postion of top
im_new = []
#split four numbers in the picture
for i in range(4):
im1 = im.crop((s+w*i+i*2,t,s+w*(i+1)+i*2,h))
im_new.append(im1)
f = file("data.txt","a")
for k in range(4):
l = []
#im_new[k].show()
for i in range(13):
for j in range(10):
if (im_new[k].getpixel((j,i)) == 255):
l.append(0)
else:
l.append(1)
f.write("l=[")
n = 0
for i in l:
if (n%10==0):
f.write("\n")
f.write(str(i)+",")
n+=1
f.write("]\n")
把字模保存为list,用于接下来的匹配;
提取完字模后剩下来的就是对需要处理的图片进行与数据库中的字模进行匹配了,基本的思路就是看相应点的重合率,但是由于噪点的影响在对(6,8)(8,3) (5,9)的匹配时容易出错,俺自己针对已有的100幅图片数据采集进行分析,采用了双向匹配(图片与字模分别作为基点),做了半天的测试终于可以实现 100%的识别率。
PYTHON:
#!/usr/bin/env python
#encoding=utf-8
import Image,ImageEnhance,ImageFilter
import Data
DEBUG = False
def d_print(*msg):
global DEBUG
if DEBUG:
for i in msg:
print i,
print
else:
pass
def Get_Num(l=[]):
min1 = []
min2 = []
for n in Data.N:
count1=count2=count3=count4=0
if (len(l) != len(n)):
print "Wrong pic"
exit()
for i in range(len(l)):
if (l[i] == 1):
count1+=1
if (n[i] == 1):
count2+=1
for i in range(len(l)):
if (n[i] == 1):
count3+=1
if (l[i] == 1):
count4+=1
d_print(count1,count2,count3,count4)
min1.append(count1-count2)
min2.append(count3-count4)
d_print(min1,"\n",min2)
for i in range(10):
if (min1[i] <= 2 or min2[i] <= 2):
if ((abs(min1[i] - min2[i])) <10):
return i
for i in range(10):
if (min1[i] <= 4 or min2[i] <= 4):
if (abs(min1[i] - min2[i]) <= 2):
return i
for i in range(10):
flag = False
if (min1[i] <= 3 or min2[i] <= 3):
for j in range(10):
if (j != i and (min1[j] <5 or min2[j] <5)):
flag = True
else:
pass
if (not flag):
return i
for i in range(10):
if (min1[i] <= 5 or min2[i] <= 5):
if (abs(min1[i] - min2[i]) <= 10):
return i
for i in range(10):
if (min1[i] <= 10 or min2[i] <= 10):
if (abs(min1[i] - min2[i]) <= 3):
return i
#end of function Get_Num
def Pic_Reg(image_name=None):
im = Image.open(image_name)
im = im.filter(ImageFilter.MedianFilter())
enhancer = ImageEnhance.Contrast(im)
im = enhancer.enhance(2)
im = im.convert('1')
im.show()
#all by pixel
s = 12 #start postion of first number
w = 10 #width of each number
h = 15 #end postion from top
t = 2 #start postion of top
im_new = []
#split four numbers in the picture
for i in range(4):
im1 = im.crop((s+w*i+i*2,t,s+w*(i+1)+i*2,h))
im_new.append(im1)
s = ""
for k in range(4):
l = []
#im_new[k].show()
for i in range(13):
for j in range(10):
if (im_new[k].getpixel((j,i)) == 255):
l.append(0)
else:
l.append(1)
s+=str(Get_Num(l))
return s
print Pic_Reg("./images/22.jpeg")
这里再提一下验证码识别的基本方法:截图,二值化、中值滤波去噪、分割、紧缩重排(让高矮统一)、字库特征匹配识别。
这里只是针对一般的验证码,高级验证码的识别这里有篇不错的文章,太复杂的话涉及的东西就多了,那俺就没兴趣了,人工智能(好恐怖),俺只喜欢简单的东西。
以前没用Python处理过图像,不太了解PIL(Python Image Library)的用法,这几天看了看PIL,发现它太强大了,简直和ImageMagic,PS可以相比了。(这里有PIL不错的文档)
由于上面的验证码是24位的jpeg图像,并且包含了噪点,所以我们要做的就是去噪和去色,我拿PS找了张验证码试了试,使用PS滤镜中的去噪效果还行,但是没有在PIL找到去噪的函数,后来发现中值过滤后可以去掉大部分的噪点,而且PIL里有现成的函数,接下来我试着直接把图像转换为单色,结果发现还是会有不过的噪点留了下来,因为中值过滤时把不少噪点淡化了,但转换为音色时这些噪点又被强化显示了,于是在中值过滤后对图像亮度进行加强处理,然后再转换为单色,这样验证码图片就变得比较容易识别了:
上面这些处理使用Python才几行:
PYTHON:
im = Image.open(image_name)
im = im.filter(ImageFilter.MedianFilter())
enhancer = ImageEnhance.Contrast(im)
im = enhancer.enhance(2)
im = im.convert('1')
im.show()
接下来就是提取这些数字的字模,使用shell脚本下载100幅图片,抽出三张图片获取字模:
PYTHON:
#!/usr/bin/env python
#encoding=utf-8
import Image,ImageEnhance,ImageFilter
import sys
image_name = "./images/81.jpeg"
im = Image.open(image_name)
im = im.filter(ImageFilter.MedianFilter())
enhancer = ImageEnhance.Contrast(im)
im = enhancer.enhance(2)
im = im.convert('1')
#im.show()
#all by pixel
s = 12 #start postion of first number
w = 10 #width of each number
h = 15 #end postion from top
t = 2 #start postion of top
im_new = []
#split four numbers in the picture
for i in range(4):
im1 = im.crop((s+w*i+i*2,t,s+w*(i+1)+i*2,h))
im_new.append(im1)
f = file("data.txt","a")
for k in range(4):
l = []
#im_new[k].show()
for i in range(13):
for j in range(10):
if (im_new[k].getpixel((j,i)) == 255):
l.append(0)
else:
l.append(1)
f.write("l=[")
n = 0
for i in l:
if (n%10==0):
f.write("\n")
f.write(str(i)+",")
n+=1
f.write("]\n")
把字模保存为list,用于接下来的匹配;
提取完字模后剩下来的就是对需要处理的图片进行与数据库中的字模进行匹配了,基本的思路就是看相应点的重合率,但是由于噪点的影响在对(6,8)(8,3) (5,9)的匹配时容易出错,俺自己针对已有的100幅图片数据采集进行分析,采用了双向匹配(图片与字模分别作为基点),做了半天的测试终于可以实现 100%的识别率。
PYTHON:
#!/usr/bin/env python
#encoding=utf-8
import Image,ImageEnhance,ImageFilter
import Data
DEBUG = False
def d_print(*msg):
global DEBUG
if DEBUG:
for i in msg:
print i,
else:
pass
def Get_Num(l=[]):
min1 = []
min2 = []
for n in Data.N:
count1=count2=count3=count4=0
if (len(l) != len(n)):
print "Wrong pic"
exit()
for i in range(len(l)):
if (l[i] == 1):
count1+=1
if (n[i] == 1):
count2+=1
for i in range(len(l)):
if (n[i] == 1):
count3+=1
if (l[i] == 1):
count4+=1
d_print(count1,count2,count3,count4)
min1.append(count1-count2)
min2.append(count3-count4)
d_print(min1,"\n",min2)
for i in range(10):
if (min1[i] <= 2 or min2[i] <= 2):
if ((abs(min1[i] - min2[i])) <10):
return i
for i in range(10):
if (min1[i] <= 4 or min2[i] <= 4):
if (abs(min1[i] - min2[i]) <= 2):
return i
for i in range(10):
flag = False
if (min1[i] <= 3 or min2[i] <= 3):
for j in range(10):
if (j != i and (min1[j] <5 or min2[j] <5)):
flag = True
else:
pass
if (not flag):
return i
for i in range(10):
if (min1[i] <= 5 or min2[i] <= 5):
if (abs(min1[i] - min2[i]) <= 10):
return i
for i in range(10):
if (min1[i] <= 10 or min2[i] <= 10):
if (abs(min1[i] - min2[i]) <= 3):
return i
#end of function Get_Num
def Pic_Reg(image_name=None):
im = Image.open(image_name)
im = im.filter(ImageFilter.MedianFilter())
enhancer = ImageEnhance.Contrast(im)
im = enhancer.enhance(2)
im = im.convert('1')
im.show()
#all by pixel
s = 12 #start postion of first number
w = 10 #width of each number
h = 15 #end postion from top
t = 2 #start postion of top
im_new = []
#split four numbers in the picture
for i in range(4):
im1 = im.crop((s+w*i+i*2,t,s+w*(i+1)+i*2,h))
im_new.append(im1)
s = ""
for k in range(4):
l = []
#im_new[k].show()
for i in range(13):
for j in range(10):
if (im_new[k].getpixel((j,i)) == 255):
l.append(0)
else:
l.append(1)
s+=str(Get_Num(l))
return s
print Pic_Reg("./images/22.jpeg")
这里再提一下验证码识别的基本方法:截图,二值化、中值滤波去噪、分割、紧缩重排(让高矮统一)、字库特征匹配识别。
这里只是针对一般的验证码,高级验证码的识别这里有篇不错的文章,太复杂的话涉及的东西就多了,那俺就没兴趣了,人工智能(好恐怖),俺只喜欢简单的东西。
一般的文件上传是通过html表单进行的,通过CURL可以不经过浏览器,直接在服务器端模拟进行表单提交,完成POST数据、文件上传等功能。需要被上传的文件需要在文件名前加上“@”以示区分,并且,文件名需要是完整路径。
以下php函数来模拟html表单的提交数据:
function uploadByCURL($post_data,$post_url){
$curl = curl_init();
curl_setopt($curl, CURLOPT_URL, $post_url);
curl_setopt($curl, CURLOPT_POST, 1 );
curl_setopt($curl, CURLOPT_POSTFIELDS, $post_data);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($curl,CURLOPT_USERAGENT,"Mozilla/4.0");
$result = curl_exec($curl);
$error = curl_error($curl);
return $error ? $error : $result;
}
函数的使用:
$url = "http://www.beatlog.com/app.php";
$data = array(
"username" => $username,
"password" => $password,
"file1" => "@".realpath("photo1.jpg"),
"file2" => "@".realpath("file2.xml")
);
print_r(uploadByCURL($data,$url));
以下php函数来模拟html表单的提交数据:
function uploadByCURL($post_data,$post_url){
$curl = curl_init();
curl_setopt($curl, CURLOPT_URL, $post_url);
curl_setopt($curl, CURLOPT_POST, 1 );
curl_setopt($curl, CURLOPT_POSTFIELDS, $post_data);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($curl,CURLOPT_USERAGENT,"Mozilla/4.0");
$result = curl_exec($curl);
$error = curl_error($curl);
return $error ? $error : $result;
}
函数的使用:
$url = "http://www.beatlog.com/app.php";
$data = array(
"username" => $username,
"password" => $password,
"file1" => "@".realpath("photo1.jpg"),
"file2" => "@".realpath("file2.xml")
);
print_r(uploadByCURL($data,$url));
pgbench 是一个简单的给 PostgreSQL 做性能测试的程序。它反复运行同样的 SQL 命令序列,可能是在多个并发数据库会话上头,然后检查平均的事务速度(每秒的事务数 tps)。缺省的时候,pgbench 测试一个(松散的)接近 TPC-B 的情况,每个事务包括五个 SELECT,UPDATE,和 INSERT命令。不过,我们可以很轻松地使用自己的事务脚本文件来实现其它情况。
典型的输出看上去会是这样:
transaction type: TPC-B (sort of)
scaling factor: 10
number of clients: 10
number of transactions per client: 1000
number of transactions actually processed: 10000/10000
tps = 85.184871 (including connections establishing)
tps = 85.296346 (excluding connections establishing)
头四行只是报告一些最重要的参数设置。跟着的一行报告完成的事务数和期望完成的事务数(后者只是客户端数乘以事务数);这两个会相等,除非在完成之前运行就失败了。最后两行报告 TPS 速率,分别有计算启动数据库会话时间和不计算启动会话时间的。
概述
缺省的 TPC-B 类的事务测试需要事先设定特定的表。要使用 pgbench 的 -i (初始化)选项来创建和填充这些表。(如果你测试的是客户自己的脚本,那就不需要这一步,但是你需要自己设置所需的环境)。初始化看上去像:
pgbench -i [ other-options ] dbname
这里的 dbname 是一个已经创建的准备用来测试的数据库名称。(你可能还需要 -h,-p,和/或 -U 选项来声明如何连接到数据库服务器。)
注意:pgbench -i 创建四个表:accounts,branches,history 和 tellers,删除任何现有同名的表。如果你的数据库里有同名的表,要非常非常的小心!
缺省的“缩放因子(scale factor)”为 1 的时候,表初始化的行数为:
table # of rows
-------------------------
branches 1
tellers 10
accounts 100000
history 0
你可以(而且,在大多数情况下,可能会)增加表中数据行的方法是使用 -s (缩放因子)选项。-F (填充因子 fillfactor)选项在这个时候也可以用。
一旦你完成了必要的步骤,就可以用不包括 -i 选项的命令来执行性能测试,也就是
pgbench [ options ] dbname
几乎在所有场合下,你都需要一些选项来做有效的测试。最重要的选项是 -c(客户端数目),-t(事务的数目),和 -f (声明一个客户的脚本文件)。参阅下表获取完整的列表。
表 F-14 显示了在数据库初始化过程中使用的选项,表 F-15 显示了运行性能套件的时候的选项,表 F-16 显示了可用于两种场合的选项:
表 F-14. pgbench 初始化选项 选项 描述
-i 要求调用初始化模式。
-s scale_factor 生成的数据行放大 scale_factor 指定的倍数。比如 -s 100 讲隐含着账号表里有 10,000,000 行。缺省是 1。
-F fillfactor 用给出的 fillfactor 创建 accounts, tellers 和 branches 表缺省是 100。
表 F-15. pgbench 性能测试选项 选项 描述
-c clients 模拟的客户数,也就是并发数据库会话数目。缺省是 1。
-t transactions 每个客户端跑的事务数目。缺省是 10。
-N 不要更新 tellers 和 branches。这样会避免在这些表上的更新竞争,但是让测试案例更不像 TPC-B 了。
-S 执行纯 SELECT 的事务,而不是类 TPC-B 的测试。
-f filename 从 filename 中读取事务脚本。参阅下文获取细节。-N,-S,和 -f 是相互排斥的。
-n 在运行测试之前不执行 vacuum。如果你跑的是客户化的测试,不包括标准的 accounts,branches,history 和 tellers 表,那么这个选项就是必须的。
-v 在运行测试之前清理(vacuum)所有四个标准的表。如果不带 -n 也不带 -v,那么 pgbench 将清理 tellers 和 branches 表,并将删除 history 中的所有记录。
-D varname=value 定义一个客户端脚本使用的变量(见下文)。允许多个 -D 选项。
-C 为每个事务建立一个新连接,而不是每个客户端线程一个事务。这个有助于测量连接过荷。
-l 把每个事务花的时间写到 logfile。参阅下文获取细节。
-s scale_factor 在 pgbench 的输出汇报特定的缩放因子。对于内置的测试来说,这个是没必要的;正确的缩放因子可以通过计算 branches 表的行数得出。不过,在测试客户性能的时候(-f 选项),缩放因子就会报告为 1,除非使用了这个选项。
-d 打印调试输出。
表 F-16. pgbench 通常选项 选项 描述
-h hostname 数据库服务器的主机
-p port 数据库服务器的端口
-U login 连接的用户名
pgbench 里实际执行的“事务”是什么?
缺省的事务脚本每个事务发出七个命令:
1. BEGIN;
2. UPDATE accounts SET abalance = abalance + :delta WHERE aid = :aid;
3. SELECT abalance FROM accounts WHERE aid = :aid;
4. UPDATE tellers SET tbalance = tbalance + :delta WHERE tid = :tid;
5. UPDATE branches SET bbalance = bbalance + :delta WHERE bid = :bid;
6. INSERT INTO history (tid, bid, aid, delta, mtime) VALUES (:tid, :bid, :aid, :delta, CURRENT_TIMESTAMP);
7. END;
如果你声明了 -N,那么第 4 和第 5 步并不包括在事务里。如果你声明了 -S,那么就只发出 SELECT。
客户脚本
pgbench 支持通过运行客户脚本测试客户化的性能情景,而不是缺省的事务脚本(上面描述的),方法是从一个文件读取脚本(-f 选项)。在这种场合下,一个“事务”的意思是该脚本文件的一次运行。你甚至可以声明多个脚本(多个 -f 选项),在这种场合下,每开始一个客户端会话就启动一个新的事务。
脚本文件的各是是每行一个 SQL 命令;不支持多行的 SQL 命令。空行和以 -- 开头的行被忽略。脚本文件的行也可以是“元命令”,它们是由 pgbench 自己解析的,如下所述。
脚本文件里有一个简单的变量替换极致。变量可以用命令行选项 -D 来设置,如上面解释的,或者用下面解释的元命令设置。除了用命令行参数 -D 设置的变量之外,变量 scale 预置成当前的缩放因子。在设置了变量之后,变量值就可以通过书写 :variablename 的方式插入到 SQL 命令中。在执行多个客户会话的时候,每个会话拥有自己的变量集。
脚本元命令用一个反斜杠开始(\)。原命令的参数是用空白分隔的。支持下列元命令:
\set varname operand1 [ operator operand2 ]
设置变量 varname 为一个计算出的整数值。每个操作数要么是一个整数常量,要么是一个具有整数值的 :variablename 引用。操作符可以是 +,-,*,或者 /。
例子:
\set ntellers 10 * :scale
\setrandom varname min max
设置变量 varname 为一个范围是从 min 到 max 之间的随机整数值。每个限制可以是一个整数,也可以是一个具有整数值的 :variablename 引用。
例子:
\setrandom aid 1 :naccounts
\sleep number [ us | ms | s ]
令脚本执行睡眠指定的微秒(us),毫秒(ms)或秒(s)数。如果省略单位,则缺省是秒。数字可以是一个整数常量,也可以是一个具有整数值的 :variablename 引用。
例子:
\sleep 10 ms
比如,内置的 TCP-B 类的事务的完整定义类似于:
\set nbranches :scale
\set ntellers 10 * :scale
\set naccounts 100000 * :scale
\setrandom aid 1 :naccounts
\setrandom bid 1 :nbranches
\setrandom tid 1 :ntellers
\setrandom delta -5000 5000
BEGIN;
UPDATE accounts SET abalance = abalance + :delta WHERE aid = :aid;
SELECT abalance FROM accounts WHERE aid = :aid;
UPDATE tellers SET tbalance = tbalance + :delta WHERE tid = :tid;
UPDATE branches SET bbalance = bbalance + :delta WHERE bid = :bid;
INSERT INTO history (tid, bid, aid, delta, mtime) VALUES (:tid, :bid, :aid, :delta, CURRENT_TIMESTAMP);
END;
这个脚本允许事务的每次循环都引用不同的,随机选取的行。(这个例子还说明了为啥每个子会话需要拥有自己的变量 -- 否则它们就不会各自修改不同的行。)
每事务的日志
如果使用了 -l 选项,pgbench 把每个事务花的时间写到 logfile 里。日志文件的名称会是:pgbench_log.nnn,这里的 nnn 是 pgbench 进程的 PID。日志的格式是:
client_id transaction_no time file_no time_epoch time_us
这里的 time 是以微秒计的事务执行时间,file_no 表示使用了哪个脚本文件(在用 -f 声明了多个脚本文件的时候有用),time_epoch/time_us 是微秒计的 UNIX 纪元格式的事务完成时的时间戳(可用于创建一个带小数秒的 ISO 8601 的时间戳)。
下面是样例输出:
0 199 2241 0 1175850568 995598
0 200 2465 0 1175850568 998079
0 201 2513 0 1175850569 608
0 202 2038 0 1175850569 2663
好习惯
我们很容易用 pgbench 产生完全没意义的数字。下面是一些帮助你获取有用结果的原则。
首先,绝对不要相信只跑了几秒钟的测试。把 -t 设置增大到让运行足以跑上几分钟的水平,这样就可以把噪声给平均掉了。有时候你需要跑上几个小时才能获取可复现的数字。最好是让测试跑上几分钟,看看你的数值是否可复现。
对于缺省的 TPC-B 类的测试场景,初始的缩放因子(-s)应该至少和你试图运行的客户端数目(-c)一样大;否则你就实际上是在测量更新竞争。在 branches 表里只有 -s 行,而每个事务都想更新它们之一,所以大于 -s 的 -c 数值将无疑是有大量事务阻塞在等待其它事务上头。
缺省的测试场景也跟表自初始化以来它已经存在的时间有关:在表中积累的死去的行和无用的空间也会改变结果。要理解这些结果,你必须跟踪更新的总数以及何时发生清理。如果打开了 autovacuum,那么可能会看到测量出来的性能有不可预期的变化。
pgbench 的一个限制是在测试大量客户端会话的时候,它自己可能会成为瓶颈。这个问题可以通过在不同的数据库服务器上运行 pgbench 来缓解。或者是在不同的客户机器上并发地运行好几个 pgbench 来测试同一个数据库服务器。
典型的输出看上去会是这样:
transaction type: TPC-B (sort of)
scaling factor: 10
number of clients: 10
number of transactions per client: 1000
number of transactions actually processed: 10000/10000
tps = 85.184871 (including connections establishing)
tps = 85.296346 (excluding connections establishing)
头四行只是报告一些最重要的参数设置。跟着的一行报告完成的事务数和期望完成的事务数(后者只是客户端数乘以事务数);这两个会相等,除非在完成之前运行就失败了。最后两行报告 TPS 速率,分别有计算启动数据库会话时间和不计算启动会话时间的。
概述
缺省的 TPC-B 类的事务测试需要事先设定特定的表。要使用 pgbench 的 -i (初始化)选项来创建和填充这些表。(如果你测试的是客户自己的脚本,那就不需要这一步,但是你需要自己设置所需的环境)。初始化看上去像:
pgbench -i [ other-options ] dbname
这里的 dbname 是一个已经创建的准备用来测试的数据库名称。(你可能还需要 -h,-p,和/或 -U 选项来声明如何连接到数据库服务器。)
注意:pgbench -i 创建四个表:accounts,branches,history 和 tellers,删除任何现有同名的表。如果你的数据库里有同名的表,要非常非常的小心!
缺省的“缩放因子(scale factor)”为 1 的时候,表初始化的行数为:
table # of rows
-------------------------
branches 1
tellers 10
accounts 100000
history 0
你可以(而且,在大多数情况下,可能会)增加表中数据行的方法是使用 -s (缩放因子)选项。-F (填充因子 fillfactor)选项在这个时候也可以用。
一旦你完成了必要的步骤,就可以用不包括 -i 选项的命令来执行性能测试,也就是
pgbench [ options ] dbname
几乎在所有场合下,你都需要一些选项来做有效的测试。最重要的选项是 -c(客户端数目),-t(事务的数目),和 -f (声明一个客户的脚本文件)。参阅下表获取完整的列表。
表 F-14 显示了在数据库初始化过程中使用的选项,表 F-15 显示了运行性能套件的时候的选项,表 F-16 显示了可用于两种场合的选项:
表 F-14. pgbench 初始化选项 选项 描述
-i 要求调用初始化模式。
-s scale_factor 生成的数据行放大 scale_factor 指定的倍数。比如 -s 100 讲隐含着账号表里有 10,000,000 行。缺省是 1。
-F fillfactor 用给出的 fillfactor 创建 accounts, tellers 和 branches 表缺省是 100。
表 F-15. pgbench 性能测试选项 选项 描述
-c clients 模拟的客户数,也就是并发数据库会话数目。缺省是 1。
-t transactions 每个客户端跑的事务数目。缺省是 10。
-N 不要更新 tellers 和 branches。这样会避免在这些表上的更新竞争,但是让测试案例更不像 TPC-B 了。
-S 执行纯 SELECT 的事务,而不是类 TPC-B 的测试。
-f filename 从 filename 中读取事务脚本。参阅下文获取细节。-N,-S,和 -f 是相互排斥的。
-n 在运行测试之前不执行 vacuum。如果你跑的是客户化的测试,不包括标准的 accounts,branches,history 和 tellers 表,那么这个选项就是必须的。
-v 在运行测试之前清理(vacuum)所有四个标准的表。如果不带 -n 也不带 -v,那么 pgbench 将清理 tellers 和 branches 表,并将删除 history 中的所有记录。
-D varname=value 定义一个客户端脚本使用的变量(见下文)。允许多个 -D 选项。
-C 为每个事务建立一个新连接,而不是每个客户端线程一个事务。这个有助于测量连接过荷。
-l 把每个事务花的时间写到 logfile。参阅下文获取细节。
-s scale_factor 在 pgbench 的输出汇报特定的缩放因子。对于内置的测试来说,这个是没必要的;正确的缩放因子可以通过计算 branches 表的行数得出。不过,在测试客户性能的时候(-f 选项),缩放因子就会报告为 1,除非使用了这个选项。
-d 打印调试输出。
表 F-16. pgbench 通常选项 选项 描述
-h hostname 数据库服务器的主机
-p port 数据库服务器的端口
-U login 连接的用户名
pgbench 里实际执行的“事务”是什么?
缺省的事务脚本每个事务发出七个命令:
1. BEGIN;
2. UPDATE accounts SET abalance = abalance + :delta WHERE aid = :aid;
3. SELECT abalance FROM accounts WHERE aid = :aid;
4. UPDATE tellers SET tbalance = tbalance + :delta WHERE tid = :tid;
5. UPDATE branches SET bbalance = bbalance + :delta WHERE bid = :bid;
6. INSERT INTO history (tid, bid, aid, delta, mtime) VALUES (:tid, :bid, :aid, :delta, CURRENT_TIMESTAMP);
7. END;
如果你声明了 -N,那么第 4 和第 5 步并不包括在事务里。如果你声明了 -S,那么就只发出 SELECT。
客户脚本
pgbench 支持通过运行客户脚本测试客户化的性能情景,而不是缺省的事务脚本(上面描述的),方法是从一个文件读取脚本(-f 选项)。在这种场合下,一个“事务”的意思是该脚本文件的一次运行。你甚至可以声明多个脚本(多个 -f 选项),在这种场合下,每开始一个客户端会话就启动一个新的事务。
脚本文件的各是是每行一个 SQL 命令;不支持多行的 SQL 命令。空行和以 -- 开头的行被忽略。脚本文件的行也可以是“元命令”,它们是由 pgbench 自己解析的,如下所述。
脚本文件里有一个简单的变量替换极致。变量可以用命令行选项 -D 来设置,如上面解释的,或者用下面解释的元命令设置。除了用命令行参数 -D 设置的变量之外,变量 scale 预置成当前的缩放因子。在设置了变量之后,变量值就可以通过书写 :variablename 的方式插入到 SQL 命令中。在执行多个客户会话的时候,每个会话拥有自己的变量集。
脚本元命令用一个反斜杠开始(\)。原命令的参数是用空白分隔的。支持下列元命令:
\set varname operand1 [ operator operand2 ]
设置变量 varname 为一个计算出的整数值。每个操作数要么是一个整数常量,要么是一个具有整数值的 :variablename 引用。操作符可以是 +,-,*,或者 /。
例子:
\set ntellers 10 * :scale
\setrandom varname min max
设置变量 varname 为一个范围是从 min 到 max 之间的随机整数值。每个限制可以是一个整数,也可以是一个具有整数值的 :variablename 引用。
例子:
\setrandom aid 1 :naccounts
\sleep number [ us | ms | s ]
令脚本执行睡眠指定的微秒(us),毫秒(ms)或秒(s)数。如果省略单位,则缺省是秒。数字可以是一个整数常量,也可以是一个具有整数值的 :variablename 引用。
例子:
\sleep 10 ms
比如,内置的 TCP-B 类的事务的完整定义类似于:
\set nbranches :scale
\set ntellers 10 * :scale
\set naccounts 100000 * :scale
\setrandom aid 1 :naccounts
\setrandom bid 1 :nbranches
\setrandom tid 1 :ntellers
\setrandom delta -5000 5000
BEGIN;
UPDATE accounts SET abalance = abalance + :delta WHERE aid = :aid;
SELECT abalance FROM accounts WHERE aid = :aid;
UPDATE tellers SET tbalance = tbalance + :delta WHERE tid = :tid;
UPDATE branches SET bbalance = bbalance + :delta WHERE bid = :bid;
INSERT INTO history (tid, bid, aid, delta, mtime) VALUES (:tid, :bid, :aid, :delta, CURRENT_TIMESTAMP);
END;
这个脚本允许事务的每次循环都引用不同的,随机选取的行。(这个例子还说明了为啥每个子会话需要拥有自己的变量 -- 否则它们就不会各自修改不同的行。)
每事务的日志
如果使用了 -l 选项,pgbench 把每个事务花的时间写到 logfile 里。日志文件的名称会是:pgbench_log.nnn,这里的 nnn 是 pgbench 进程的 PID。日志的格式是:
client_id transaction_no time file_no time_epoch time_us
这里的 time 是以微秒计的事务执行时间,file_no 表示使用了哪个脚本文件(在用 -f 声明了多个脚本文件的时候有用),time_epoch/time_us 是微秒计的 UNIX 纪元格式的事务完成时的时间戳(可用于创建一个带小数秒的 ISO 8601 的时间戳)。
下面是样例输出:
0 199 2241 0 1175850568 995598
0 200 2465 0 1175850568 998079
0 201 2513 0 1175850569 608
0 202 2038 0 1175850569 2663
好习惯
我们很容易用 pgbench 产生完全没意义的数字。下面是一些帮助你获取有用结果的原则。
首先,绝对不要相信只跑了几秒钟的测试。把 -t 设置增大到让运行足以跑上几分钟的水平,这样就可以把噪声给平均掉了。有时候你需要跑上几个小时才能获取可复现的数字。最好是让测试跑上几分钟,看看你的数值是否可复现。
对于缺省的 TPC-B 类的测试场景,初始的缩放因子(-s)应该至少和你试图运行的客户端数目(-c)一样大;否则你就实际上是在测量更新竞争。在 branches 表里只有 -s 行,而每个事务都想更新它们之一,所以大于 -s 的 -c 数值将无疑是有大量事务阻塞在等待其它事务上头。
缺省的测试场景也跟表自初始化以来它已经存在的时间有关:在表中积累的死去的行和无用的空间也会改变结果。要理解这些结果,你必须跟踪更新的总数以及何时发生清理。如果打开了 autovacuum,那么可能会看到测量出来的性能有不可预期的变化。
pgbench 的一个限制是在测试大量客户端会话的时候,它自己可能会成为瓶颈。这个问题可以通过在不同的数据库服务器上运行 pgbench 来缓解。或者是在不同的客户机器上并发地运行好几个 pgbench 来测试同一个数据库服务器。
まだよくわかっていないことが多いので,お気づきの点はブログの 日本語ファイル名の問題 のコメントでお教えください。
[2006-05-08] WindowsのIEで化けていた(^^;)ので直しました。
[2007-03-14] IE5/6はUTF-8をURLエンコードした名前でもOKだそうです。
問題点
日本語ファイル名の扱い方はブラウザによって異なり,たいへん厄介です。
まず,ファイル名はURLのパスとして与えることができます:
クリックしてね
この場合,ブラウザは「日本語ファイル名.txt」というファイルを見ているつもりになりますが,サーバ側では fakefile.php を実行し,/日本語ファイル名.txt はその実行の際に環境変数 PATH_INFO として渡されるだけです。
また,ファイル名はHTTPヘッダによっても与えることができます:
Content-Type: text/plain; charset=UTF-8
Content-Disposition: attachment; filename="日本語ファイル名.txt"
ブラウザはこのどちらのファイル名を見るかという問題と,それらのファイル名をどういうエンコーディングにすればいいかという問題があります。
まず考えられるのはUTF-8で,今後はこれが主流になっていくと思いますが,IEはファイル名についてはまだShift JISでないと文字化けするようです。
さらに,URLエンコード(%に続く2桁の16進表記)するか,RFC 2047流にMIMEエンコード(=?ISO-2022-JP?B?....?= のような形式)するか,それとも生のままにするか,という問題があります。また,RFC 2231によれば filename*=iso-2022-jp'ja'URLエンコードされたファイル名 のように書くのが正しそうです。
また,少なくともURLに含ませる場合は,特殊文字の扱いには要注意です。例えば 日本語#ファイル.txt と書くと 日本語.txt のように扱われてしまうので,日本語%23ファイル.txt のようにURLエンコードしなければならないようです。
例
PHPで生成したファイルを日本語ファイル名でダウンロードさせます。
* テキストファイル
* バイナリファイル
ソース
飛び先の fakefile.php は次のようになっています:
if (isset($_SERVER['PATH_INFO'])) {
$filename = substr($_SERVER['PATH_INFO'], 1);
} else {
$filename = "不明.txt";
}
$ext = substr($filename, -4);
if ($ext == '.txt')
$type = 'text/plain';
else
$type = 'application/octet-stream';
$ua = $_SERVER['HTTP_USER_AGENT'];
if (strstr($ua, 'MSIE') && !strstr($ua, 'Opera')) {
// IE(オペラの仮装でない)はSJISにしないと化ける
$filename = mb_convert_encoding($filename, "SJIS", "UTF-8");
// $filename = urlencode($filename);
$content = 'あなたのブラウザはIEですね。';
} elseif (strstr($ua, "Safari")) {
// どうやっても化けるのでContent-Dispositionでなく
// URLの最後の部分(PATH_INFO)でファイル名を指定
$filename = "";
$content = 'あなたのブラウザはSafariですね。';
} else {
// $filename = '=?UTF-8?B?' . base64_encode($filename) . '?=';
$content = 'あなたのブラウザはIEでもSafariでもありませんね。';
}
header('Content-Type: ' . $type);
header('Content-Disposition: attachment; filename="' . $filename . '"');
echo $content, "\n";
?>
[2006-05-08] WindowsのIEで化けていた(^^;)ので直しました。
[2007-03-14] IE5/6はUTF-8をURLエンコードした名前でもOKだそうです。
問題点
日本語ファイル名の扱い方はブラウザによって異なり,たいへん厄介です。
まず,ファイル名はURLのパスとして与えることができます:
クリックしてね
この場合,ブラウザは「日本語ファイル名.txt」というファイルを見ているつもりになりますが,サーバ側では fakefile.php を実行し,/日本語ファイル名.txt はその実行の際に環境変数 PATH_INFO として渡されるだけです。
また,ファイル名はHTTPヘッダによっても与えることができます:
Content-Type: text/plain; charset=UTF-8
Content-Disposition: attachment; filename="日本語ファイル名.txt"
ブラウザはこのどちらのファイル名を見るかという問題と,それらのファイル名をどういうエンコーディングにすればいいかという問題があります。
まず考えられるのはUTF-8で,今後はこれが主流になっていくと思いますが,IEはファイル名についてはまだShift JISでないと文字化けするようです。
さらに,URLエンコード(%に続く2桁の16進表記)するか,RFC 2047流にMIMEエンコード(=?ISO-2022-JP?B?....?= のような形式)するか,それとも生のままにするか,という問題があります。また,RFC 2231によれば filename*=iso-2022-jp'ja'URLエンコードされたファイル名 のように書くのが正しそうです。
また,少なくともURLに含ませる場合は,特殊文字の扱いには要注意です。例えば 日本語#ファイル.txt と書くと 日本語.txt のように扱われてしまうので,日本語%23ファイル.txt のようにURLエンコードしなければならないようです。
例
PHPで生成したファイルを日本語ファイル名でダウンロードさせます。
* テキストファイル
* バイナリファイル
ソース
飛び先の fakefile.php は次のようになっています:
if (isset($_SERVER['PATH_INFO'])) {
$filename = substr($_SERVER['PATH_INFO'], 1);
} else {
$filename = "不明.txt";
}
$ext = substr($filename, -4);
if ($ext == '.txt')
$type = 'text/plain';
else
$type = 'application/octet-stream';
$ua = $_SERVER['HTTP_USER_AGENT'];
if (strstr($ua, 'MSIE') && !strstr($ua, 'Opera')) {
// IE(オペラの仮装でない)はSJISにしないと化ける
$filename = mb_convert_encoding($filename, "SJIS", "UTF-8");
// $filename = urlencode($filename);
$content = 'あなたのブラウザはIEですね。';
} elseif (strstr($ua, "Safari")) {
// どうやっても化けるのでContent-Dispositionでなく
// URLの最後の部分(PATH_INFO)でファイル名を指定
$filename = "";
$content = 'あなたのブラウザはSafariですね。';
} else {
// $filename = '=?UTF-8?B?' . base64_encode($filename) . '?=';
$content = 'あなたのブラウザはIEでもSafariでもありませんね。';
}
header('Content-Type: ' . $type);
header('Content-Disposition: attachment; filename="' . $filename . '"');
echo $content, "\n";
?>
现成的,直接copy再正确修改就可以了
上传:
RushApp.FTP.Transfer(0, ”, ‘本地目录’, ”, ‘上传ftp名字’, ‘/远程目标目录’, ”, RS_UP or RS_LOGOUT, ”, ”, ”, ”, ”, 0, 0, 0, RS_SORTDATE or RS_SORTDES, RS_SORTDATE or RS_SORTDES, 0, 5);
FXP
RushApp.FTP.Transfer(0, ‘指定01ftp名字’, ‘/目录’, ”, ‘目的02ftp名字’, ‘/目录’, ”, RS_DIRDES or RS_DIRSRC or RS_APPEND, ”, ”, ”, ”, ”, 0, 0, 0, RS_SORTDATE or RS_SORTDES or RS_APPEND, 0, 0, 10);
下载
RushApp.FTP.Transfer(0,’ftp名字’,'/目录’,”,”,’本地目录’,”,RS_DIRDES or RS_DIRSRC or RS_APPEND or RS_DOWN or RS_DIRDES or RS_DIRSRC or RS_LOGOUT,”,”,”,”,”,0,0,0,0,0,0,10);
—————————————————————————————
RushApp.FTP.Transfer(0,'ebook','new','','','D:\ftpDownload\ebook\','',RS_DOWN or RS_DIRSRC or RS_DIRDES or RS_NOSKIP or RS_APPEND or RS_LOGOUT, '*-ebook','','','','',0,0,0,RS_SORTDATE or RS_SORTDES,0,3,0)
—————————————————————————————
第二个是删除所有失败队列:
========================
RushApp.FTP.RemoveQueue(”,”,”,RS_FAIL);
退出所有空闲窗口的FTP:
=========================
RushApp.FTP.Logout(”,0);
FtpRush 脚本
RushApp.FTP.Transfe\ RushApp.FTP\ RushApp.FTP.Delete
RushApp.FTP.CWD\ RushApp.FTP.Login\ RushApp.FTP.RAW
RushApp.FTP.Logout\ RushApp.FTP.RemoveQueue
RushApp.FTP.Transfe
Declare
procedureTransfer(
Handle: Integer;
SrcList, SrcFolder, SrcItem, DesList, DesFolder, DesItem: string;
Flag: Integer
FolderAllow, FolderSkip, FileAllow, FileSkip: string
CompleteFlag: string
RepeatCount: Integer
RetryCount: Integer
Timeout: Integer
FolderSort, FileSort: Integer
Depth, SubCount: Integer
);
//参数类型说明:integer——整型 string——字符串
Description
Transfer Queue , support upload download and FXP
//队列传输函数,支持上传,下载和FXP
Parameters
Handle
specifies which window to place queues, first window is 1, 2th window is 2…, if you do not use this param, set it to zero.
//定制哪个容器放置队列,第一个窗口就是1,第二个就是2,依此类推,如果你不使用这个参数,那就设为0。
SrcList
SrcList is the source FTP name or a List of FTP names, if it’s a list of FTP names, be splitted with RushApp.FTP.Splitter, This param be used for Download and FXP
//源FTP的名字,如果大于一个,则各个FTP名字前需要用分隔符(参见最后的引用)分开,这个参数对下载和FXP有效。如果上传,留空即可。
SrcFolder
SrcFolder is the source folder name or a bookmark, for Upload, it’s a Local folder path or a Local bookmark
//源目录或者源目录的书签,如果是上传,则这个是本地目录或者本地目录的书签
SrcItem
SrcItem is the source folder or file name, for Upload, it’s a Local folder name or file name
//单个文件就输入文件名,留空则表示整个目录。
DesList
same as SrcList
//用法同SrcList参数
DesFolder
same as SrcFolder
//用法同ScrFolder参数
DesItem
same as SrcItem
//用法同SrcItem参数
Flag
If you don’t use Flag, use zero instead of it.
//如果这个参数不用就设为0。注意:如果Flag中有一个以上参数,则用’or’连接,非and。
RS_FILE
tell FTPRush this queue is a file, without this flag this queue is a folder
//告诉FTPRush这个队列是一个文件,否则队列个文件夹。
RS_UP
tell FTPRush this queue is upload mode, without this flag this queue is FXP mode
//告诉FTPush这是个上传队列,否则默认队列为FXP模式。
RS_DOWN
tell FTPRush this queue is download mode, without this flag this queue is FXP mode
//告诉FTPush这是个下载队列,否则默认队列为FXP模式。
RS_DIRSRC
specifies SrcFolder is a folder, without this flag, SrcFolder is a bookmark
//说明SrcFolder是个文件夹,否则默认为书签。
RS_DIRDES
specifies DesFolder is a folder, without this flag, DesFolder is a bookmark
//说明DesFolder是个文件夹,否则为书签。
RS_NOSKIP
tell FTPRush do not use skiplist to filter DesItem and DesItem
//告诉FTPRush不要使用过滤列表过滤DesItem。(这里是不是应该为SrcItem和DesItem?)
RS_APPEND
tell FTPRush combines all queue items at ONE window, otherwise, every queue item running at a NEW window.
//告诉FTPush把所有队列放到一个容器里,否则每个队列占用一个容器。
RS_EXPR
if includes this flag, FolderAllow, FolderSkip, FileAllow, FileSkip match Perl Expressions, otherwise match wildcard
//如果加上这个标记,则FolderAllow, FolderSkip, FileAllow, FileSkip匹配的时候都支持Perl语句,否则匹配通配符
RS_LOGOUT
disconnected from server after queue transferred
//队列结束后断开连接
RS_GHOST
use !username as login name
//用!username做为登陆名 (liucl@DRL:ioftpd&glftpd里面在登陆用户名前面加”!”可以把挂在上面的尸体踢下来)
RS_INVERT
add or remove ‘ – ‘ from login password
//加上或去掉密码前的’-'(z-z@DRL:这个功能是给登录 glftpd 的用户的, 如果在登录的时候, 密码之前加一个 “-” 符号, 那么glftpd不会发送冗余的信息给你.)
RS_NOCIRCLE
Folder2 (n+1) won’t start before folder1 (n) is all over
//前一个文件夹完全完成后开始第二个文件夹
RS_CHECKLOGIN
only works for without RS_APPEND and dont use ContainerID.
then when no free logins to transfer, FTPRush auto select a container which have the same transfer (src/dest) and append it
//没有加RS_APPEND的时候此参数有效,并且不要使用容器编号。当某一站点的登陆数达到上限的时候,FTPRush将自动选择一个“源-目标”相同的容器,将需要传输的任务加到这个容器队列的后面。
RS_POPUP
to bring FTPRush on top
//前端显示FTPRush程序窗口
RS_NOMKD
when transferring folders with sub dirs, FTPRush will not create the sub dirs in dest FTP if sub dirs exists in dest FTP(just do CWD)
//如果队列传输包含子目录,且在目标FTP上存在相同子目录,则FTPRush只执行CWD命令而不创建文件夹
FolderAllow
Allow sub folders match FolderAllow param, can be a empty string
//设置目录的允许列表,可以留空。
FolderSkip
Skip sub folders match FolderSkip param, can be a empty string
//设置目录的过滤列表,可以留空。
FileAllow
Allow sub files match FileAllow param, can be a empty string
//设置文件允许列表,可以留空。
FileSkip
Skip sub files match FileSkip param, can be a empty string
//设置文件的过滤列表,可以留空。
CompleteFlag
when set RepeatCount > 0, CompleteFlag can let the queue thread stop when thread found a folder/file name at Source/Destination directory list match the CompleteFlag, CompleteFlag only supports Perl Expressions
//在RepeatCount>0的时候此参数有效。当源(目标)FTP上找到一个文件夹(文件)名字与CompleteFlag匹配,则队列停止。CompleteFlag只支持Perl语句。
RepeatCount
indicates how many times to refresh for a folder
//设定刷新一个文件夹几次。
RetryCount
indicates retry count when transfer a file
//设定单个文件的重试次数。
Timeout
set timeout(seconds) for a FXP queue, when timeout and transfer not started, this queue will be removed*not implented*
//设置FXP队列的超时时限(单位:秒),超过时限还没开始传输的队列将被移除。
//查不到“implented”这个词啥意思,怀疑是“not implemented”,该功能未现实
FolderSort
define sort method for sub folders, set it to zero to use default sort method (sort by name ascending)
//定义目录的排序规则,设为’0′时使用默认规则(按名称升序排列)
RS_SORTDES
specifies sort sorting as descending
//目录按降序排列(可以结合下面两个参数)
RS_SORTDATE
specifies sort by date
//按日期排列
RS_SORTSIZE
specifies sort by size
//目录按大小排列
FileSort
define sort method for sub folders, set it to zero to use default sort method (sort by name ascending)
//定义文件的排序规则,设为’0′时使用默认规则(按名称升序排列)
RS_SORTDES
specifies sort sorting as descending
//目录按降序排列(可以结合下面两个参数
RS_SORTDATE
specifies sort by date
//按日期排列
RS_SORTSIZE
specifies sort by size
//目录按大小排列
Depth
depth of sub folders
//设置目录深度,’0′则操作整个目录
SubCount
when transfer a folder includes sub folder/files, the first SubCount file/folders will be transferred. set it to zero to disable this feature
//当传输有子目录的目录(文件)时,只传输最上面的SubCount个目录(文件)。设为’0′可以屏蔽这个功能。
RushApp.FTP
Variables
RushApp.FTP.Splitter
Declare
property Splitter: string;
Description
Indicates splitter for Names of FTP Server, default splitter is ‘;’
//定义分隔符FTP脚本的分隔符,默认的是’;'。一般用用就默认好了,这个定义对下面7个函数均有效。
Example
RushApp.FTP.Splitter := ‘|’;
//这个例子就是把分隔符定义为’|'(不包括引号)。
RushApp.FTP.Delete
Declare
procedureDelete(NameList: string; Path: string; Name: string; Flag: Integer);
Description
Delete folder or file from specifies connected/idle FTP Servers
//删除指定FTP上的文件或文件夹
Parameters
NameList
Specifies which FTP need to delete folders or files, NameList can not be empty string
//指定需要删除文件(文件夹)的FTP,多个FTP则用分隔符隔开,不能为空。
Path
Specifies a bookmark name or a directory
//指定一个目标FTP上的目录或书签。
Name
a file name or a folder name, allows be a empty string
//指定目录下要删除的文件(文件夹)的名字。
Flag
if you don’t use Flag, use zero instead of it
//就使用这个参数就设为0吧。
RS_BOOKMARK
Param Path is a bookmark
//说明Path是个书签,否则为目录。
RS_RECURSIVE
when delete a folder, do not use recursive delete
//关于这个参数解释一下,照字面翻译是递归的意思,看得真是晕里雾里的,我用例子3和4分别测试了一下删除的效果(G6 v3.7.0 build24&FTPRush v1.0.0.0581)。例4是不管三七二十一直接删掉“test”文件夹。例3则分情况,如果“test”下无子目录,则删除“test”整个文件夹(不管是否有文件存在),如果“test”下有子目录,则只清空该文件夹下的文件,保留子目录和该文件 夹本身。
RS_FOLDER
tell FTPRush that’s a folder, otherwise it will be executed as deleting file
//告诉FTPRush这是一个文件夹,否则将做为文件来删除。
RS_LOGOUT
disconnect from FTP after deleted
//完成删除操作后断开连接。
RS_LOGIN
connect to FTP when have no active FTP connections
//如果当前没有连接状态的FTP,则连接FTP。
RS_POPUP
to bring FTPRush on top
//前端显示FTPRush。
Examples
//delete file ‘/public/test.txt’ from FTP1
RushApp.FTP.Delete(‘FTP1′,’/public’,'test.txt’,0);
//删除FTP1上“/public”目录下的test.txt文件。
//delete file ‘test.txt’ from bookmark ‘public’ of FTP1 and FTP2
RushApp.FTP.Delete(‘FTP1;FTP2′,’public’,'test.txt’,RS_BOOKMARK);
//删除FTP1和FTP2上“public”书签下的“test.txt”文件。
//delete file from folder ‘test’ from bookmark ‘public’ of FTP1
RushApp.FTP.Delete(‘FTP1′,’public’,'test’,RS_FOLDER or RS_BOOKMARK);
//删除FTP1上“public”书签下的“test”文件夹。(文件夹无子目录时有效)
//delete file from folder ‘test’ from bookmark ‘public’ of FTP1
RushApp.FTP.Delete(‘FTP1′,’public’,'test’,RS_FOLDER or RS_BOOKMARK or RS_RECURSIVE);
//删除FTP1上“public”书签下的“test”文件夹。(不论是否为空连带子目录一起删除)
RushApp.FTP.CWD
Declare
procedureCWD(NameList: string; Param: string; Flag: Integer);
Description
Let all or specifies connected/idle FTP Servers change remote directory.
//改变指定FTP的远程路径
Parameters
NameList
Specifies which FTP need to change remote directory, you can use a splitter for multiple FTP servers, if it’s empty then all connected idle FTP servers will change remote directory
//定制哪个FTP改变远程路径,可以是多个FTP,不过需要用分隔符分开。如果为空的话则所有空闲FTP均改变远程路径。
Param
Specifies a bookmark name or a directory
//设置FTP以书签或者路径为依据进行目录跳转。
Flag
if you don’t use Flag, use zero instead of it
//如果不用这个参数就设为0吧。
RS_BOOKMARK
Param is a bookmark
//告诉程序Param这个参数是书签
RS_POPUP
to bring FTPRush on top
//前端显示FTPRush
Examples
//tell all non-transfering connections change to bookmark ‘public’
RushApp.FTP.CWD(”,’public’,RS_BOOKMARK);
//非传输状态的FTP都跳转到书签“public”。
//tell all non-transfering connections change to directory ‘/public/’
RushApp.FTP.CWD(”,’/public/’,0);
//非传输状态的FTP都跳转到目录“public”。
//tell ‘FTP1′ server change to bookmark ‘pub’
RushApp.FTP.CWD(‘FTP1′,’pub’,RS_BOOKMARK);
//将FTP1的远程路径跳转到书签“pub”。
//tell ‘FTP1′ and ‘FTP2′ server change to bookmark ‘pub’
RushApp.FTP.CWD(‘FTP1;FTP2′,’pub’,RS_BOOKMARK);
//将FTP1和FTP2的远程路径都跳转到书签“pub”。
//tell ‘FTP1′ server change to directory ‘/public/’
RushApp.FTP.CWD(‘FTP1′,’/public/’,0);
//将FTP1的远程路径跳转到目录“/public/”。
//tell ‘FTP1′ and ‘FTP2′ server change to directory ‘/public/’
RushApp.FTP.CWD(‘FTP1;FTP2′,’/public/’,0);
//将FTP1和FTP2的远程路径跳转到目录“/public/”。
RushApp.FTP.Login
Declare
procedureLogin(NameList: string; Param: string; Flag: Integer);
Description
Connect to one or more FTP Servers and jump to a FTP path or bookmark.
//连接一个或多个FTP服务器同时跳转到指定路径或书签。
Parameters
NameList
Specifies which FTP to connect, you can use a splitter to connect multiple FTP servers
//指定要连接的FTP,多个FTP则用分隔符分开。
Param
Specifies a bookmark name or a directory name, or a empty string
//FTP连上后要跳转到的书签或路径,可以为空。
Flag
if you don’t use Flag, use zero instead of it
//如果不用这个参数就设为0吧。
RS_BOOKMARK
Param is a bookmark
//告诉程序Param这个参数是书签。
RS_CHECKBM
if bookmark is invalid, don’t connect it
//如果书签不存在,不连接FTP。
RS_GHOST
use !username as login name
//用!username登陆 (即用户名前加!)
RS_INVERT
add or remove ‘ – ‘ from login password
//在密码前添加或移除’-'。
RS_PAIR
combine FTP name as pair
//将两个FTP配对。
RS_POPUP
to bring FTPRush on top
//前端显示FTPRush
Examples
//Connect to FTP1
RushApp.FTP.Login(‘FTP1′,”,0);
//连接到FTP1。
//Connect to FTP1 and FTP2
RushApp.FTP.Login(‘FTP1;FTP2′,”,0);
//同时连接到FTP1和FTP2。
//Connect to FTP1 and change to directory /pub/
RushApp.FTP.Login(‘FTP1′,’/pub/’,0);
//连接到FTP1并同时跳转到“/pub/”目录。
//connect to FTP1 and FTP2 and change to bookmark ‘public’
RushApp.FTP.Login(‘FTP1;FTP2′,’public’,RS_BOOKMARK);
//同时连接到FTP1和FTP2并跳转到书签“public”。
//connect to FTP1 and FTP2, if FTP2 have no bookmark named ‘public’, FTPRush will connect to FTP1 only
RushApp.FTP.Login(‘FTP1;FTP2′,’public’,RS_BOOKMARK or RS_CHECKBM);
//同时连接到FTP1和FTP2,并跳转到书签“public”,哪个FTP上不存在这个书签则断开。
//connect to FTP1 and FTP2 and FTP3 with pair format: ‘FTP1;FTP2′ and ‘FTP2:FTP3′
RushApp.FTP.Login(‘FTP1;FTP2;FTP3′,’public’,RS_BOOKMARK or RS_PAIR);
//连接到FTP1,FTP2,FTP3,同时FTP1和FTP2配对,FTP2和FTP3配对。
RushApp.FTP.RAW
Declare
procedureRAW(NameList: string; Param: string; Flag: Integer);
Description
Send raw command to all or specical connected/idle FTP Servers, if Param ANameList is empty string then send to all connected/idle FTP servers. this function will not send raw command to transfering connections.
//发送RAW命令到所有指定的FTP。如果Param和NameList这两个参数是空的则发送到所有空闲FTP。这个函数对在传输中的FTP无效。
Parameters
NameList
Specifies which FTP to send raw command, you can use a splitter to connect multiple FTP servers, if it’s empty then send to all connected idle FTP servers
//指定要发送RAW命令的FTP,多个FTP则用分隔符隔开,留空的话就发送命令到所有已连接的空闲FTP。
Param
Specifies a raw command to send
//定制要发送的RAW命令。
Flag
if you don’t use Flag, use zero instead of it
//如果不用这个参数就设为0吧。
RS_LOGIN
if no specifies FTP connected, then connect it first
//如果没有FTP处在连接状态,那么先登陆。
RS_LOGOUT
disconnect after executed raw command
//发送RAW命令后断开连接
RS_POPUP
to bring FTPRush on top
//前端显示FTPRush
RS_ONE
to only allow to execute command on one connected site even if there have 2+ same sites connected
//如果同一个站点有有两个或两个以上容器里连接了,那么只对一个执行RAW命令
Examples
//send ‘NOOP’ to all connected non-transfering connections
RushApp.FTP.RAW(”,’NOOP’,0);
//发送“NOOP”命令给所有没在传输的FTP。
//send ‘NOOP’ to all connected non-transfering connections then logout
RushApp.FTP.RAW(”,’NOOP’,RS_LOGOUT);
//发送“NOOP”命令给所有没在传输的FTP然后退出。
//connect to ‘FTP1′ and send ‘NOOP’
RushApp.FTP.RAW(‘FTP1′,’NOOP’,RS_LOGIN);
//给FTP1发送“NOOP”命令。
//connect to ‘FTP1′ and ‘FTP2′, send ‘NOOP’, then logout
RushApp.FTP.RAW(‘FTP1;FTP2′,’NOOP’,RS_LOGIN or RS_LOGOUT);
//给FTP1和FTP2发送“NOOP”命令,然后退出。
RushApp.FTP.Logout
Declare
procedure Logout(NameList: string; Flag: Integer);
Description
Disconnect from one or more FTP Servers, If Param ANameList is empty string, all idle FTP connections will be disconnected.
//断开一个或多个FTP的连接,如果NameList这个参数是空的,则断开所有空闲FTP的连接。
Parameters
NameList
Specifies which FTP to disconnect, you can use a splitter to connect multiple FTP servers, a empty string means disconnect from all active/idle FTP connections(non-transfering)
//定制哪个需要断开连接的FTP,多个FTP则用分隔符分开。留空则断开所有非传输状态的FTP。
Flag
if you don’t use Flag, use zero instead of it
//如果不用这个参数就设为0吧。
RS_ASYNC
when use this flag, FTPRush will do a disconnect action after command executed.
//执行命令后断开连接
Examples
//logout from all idle FTP connections
RushApp.FTP.Logout(”,0);
//断开所有空闲FTP。
//logout from connections which named ‘FTP1′
RushApp.FTP.Logout(‘FTP1′,0);
//断开与FTP1的连接。
//logout from connections which named ‘FTP1′ or ‘FTP2′
RushApp.FTP.Logout(‘FTP1;FTP2′,0);
//断开与FTP1和FTP2的连接。
//send command ‘NOOP’ to ‘FTP1′, after ‘NOOP’ be executed, then disconnect from ‘FTP1′
RushApp.FTP.Raw(‘FTP1′,’NOOP’,0);
RushApp.FTP.Logout(‘FTP1′,RS_ASYNC);
//先发送“NOOP”命令到FTP1,然后断开连接。
RushApp.FTP.RemoveQueue
Declare
procedureRemoveQueue(Source: string; Dest: string; Param: string;
Flag: Integer);
Description
Remove Queue items from Queue window.
//清除队列窗口的队列。
Parameters
Source
Specifies source FTP name or a empty string, use ‘local’ to specifies upload queue
//指定源FTP(此处可以为空),如果是上传队列则使用“local”。
Dest
Specifies dest FTP name or a empty string, use ‘local’ to specifies download queue
//指定目标FTP(此处可以为空),如果是下载队列则使用“local”。
Param
Specifies a condition for Queue dest path or dest filename/foldername, or a empty string
//指定移除队列的匹配条件,可以是路径,文件名和文件夹名,留空则匹配所有。
Flag
You must use at least one flag as below
//Flag这项至少要使用一个参数。
RS_TRANSFER
remove transfering queues
//清除传输队列。
RS_NORMAL
remove non-transfering queues
//清除非传输中的队列。
RS_FAIL
remove failed queues
//清除失败的队列。
RS_WAITING
remove waiting queues (with blue clock icon)
//清除等待中的队列。
RS_WILD
Param condition is wildcard
//告诉程序Param这个参数采用通配符了。
RS_EXPR
Param condition is regular expression
//告诉程序Param这个参数采用正则表达式了。
RS_FULLPATH
Use queue dest full path to match Param condition, otherwise only use queue dest name
//声明Param中匹配的是队列的目标路径,否则采用名字来匹配。
RS_CURRENT
remove queues from current window only
//只移除当前容器的队列。
RS_POPUP
to bring FTPRush on top
//前端显示FTPRush。
Examples
//Clear all queues from all window
RushApp.FTP.RemoveQueue(”,”,”,RS_TRANSFER or RS_NORMAL or RS_WAITING);
//清除所有容器的所有队列。
//Clear all queues from current window
RushApp.FTP.RemoveQueue(”,”,”,RS_TRANSFER or RS_NORMAL or RS_WAITING or RS_CURRENT);
//清除当前容器的所有队列。
//Remove all download queue
RushApp.FTP.RemoveQueue(”,’local’,”,RS_TRANSFER or RS_NORMAL or RS_WAITING or RS_CURRENT);
//清除所有的下载队列。
//Remove all upload queue
RushApp.FTP.RemoveQueue(‘local’,”,”,RS_TRANSFER or RS_NORMAL or RS_WAITING or RS_CURRENT);
//清除所有的上传队列。
//remove all failed queue items from all window
RushApp.FTP.RemoveQueue(”,”,”,RS_FAIL);
//清除所有容器的失败队列。
//remove all failed queue items from current window
RushApp.FTP.RemoveQueue(”,”,”,RS_FAIL or RS_CURRENT);
//清除当前容器的所有失败队列。
//remove all queues matchs ‘/public*’
RushApp.FTP.RemoveQueue(”,”,’/public*’,RS_TRANSFER or RS_NORMAL or RS_WAITING or RS_CURRENT or RS_WILD or RS_FULLPATH);
//清除所有路径中含“/public”的队列。
//remove all queues which file name matchs ‘*2004*’
RushApp.FTP.RemoveQueue(”,”,’*2004*’,RS_TRANSFER or RS_NORMAL or RS_WAITING or RS_CURRENT or RS_WILD);
//清除所有文件名中含“2004”的队列。
//remove all queues which dest name is ‘FTP1′
RushApp.FTP.RemoveQueue(”,’FTP1′,”,RS_TRANSFER or RS_NORMAL or RS_WAITING);
//清除所有以FTP1为目标FTP的队列。
//remove all queues which source name is ‘FTP1′
RushApp.FTP.RemoveQueue(‘FTP1′,”,”,RS_TRANSFER or RS_NORMAL or RS_WAITING);
//清除所有以FTP1为源目标的队列。
上传:
RushApp.FTP.Transfer(0, ”, ‘本地目录’, ”, ‘上传ftp名字’, ‘/远程目标目录’, ”, RS_UP or RS_LOGOUT, ”, ”, ”, ”, ”, 0, 0, 0, RS_SORTDATE or RS_SORTDES, RS_SORTDATE or RS_SORTDES, 0, 5);
FXP
RushApp.FTP.Transfer(0, ‘指定01ftp名字’, ‘/目录’, ”, ‘目的02ftp名字’, ‘/目录’, ”, RS_DIRDES or RS_DIRSRC or RS_APPEND, ”, ”, ”, ”, ”, 0, 0, 0, RS_SORTDATE or RS_SORTDES or RS_APPEND, 0, 0, 10);
下载
RushApp.FTP.Transfer(0,’ftp名字’,'/目录’,”,”,’本地目录’,”,RS_DIRDES or RS_DIRSRC or RS_APPEND or RS_DOWN or RS_DIRDES or RS_DIRSRC or RS_LOGOUT,”,”,”,”,”,0,0,0,0,0,0,10);
—————————————————————————————
RushApp.FTP.Transfer(0,'ebook','new','','','D:\ftpDownload\ebook\','',RS_DOWN or RS_DIRSRC or RS_DIRDES or RS_NOSKIP or RS_APPEND or RS_LOGOUT, '*-ebook','','','','',0,0,0,RS_SORTDATE or RS_SORTDES,0,3,0)
—————————————————————————————
第二个是删除所有失败队列:
========================
RushApp.FTP.RemoveQueue(”,”,”,RS_FAIL);
退出所有空闲窗口的FTP:
=========================
RushApp.FTP.Logout(”,0);
FtpRush 脚本
RushApp.FTP.Transfe\ RushApp.FTP\ RushApp.FTP.Delete
RushApp.FTP.CWD\ RushApp.FTP.Login\ RushApp.FTP.RAW
RushApp.FTP.Logout\ RushApp.FTP.RemoveQueue
RushApp.FTP.Transfe
Declare
procedureTransfer(
Handle: Integer;
SrcList, SrcFolder, SrcItem, DesList, DesFolder, DesItem: string;
Flag: Integer
FolderAllow, FolderSkip, FileAllow, FileSkip: string
CompleteFlag: string
RepeatCount: Integer
RetryCount: Integer
Timeout: Integer
FolderSort, FileSort: Integer
Depth, SubCount: Integer
);
//参数类型说明:integer——整型 string——字符串
Description
Transfer Queue , support upload download and FXP
//队列传输函数,支持上传,下载和FXP
Parameters
Handle
specifies which window to place queues, first window is 1, 2th window is 2…, if you do not use this param, set it to zero.
//定制哪个容器放置队列,第一个窗口就是1,第二个就是2,依此类推,如果你不使用这个参数,那就设为0。
SrcList
SrcList is the source FTP name or a List of FTP names, if it’s a list of FTP names, be splitted with RushApp.FTP.Splitter, This param be used for Download and FXP
//源FTP的名字,如果大于一个,则各个FTP名字前需要用分隔符(参见最后的引用)分开,这个参数对下载和FXP有效。如果上传,留空即可。
SrcFolder
SrcFolder is the source folder name or a bookmark, for Upload, it’s a Local folder path or a Local bookmark
//源目录或者源目录的书签,如果是上传,则这个是本地目录或者本地目录的书签
SrcItem
SrcItem is the source folder or file name, for Upload, it’s a Local folder name or file name
//单个文件就输入文件名,留空则表示整个目录。
DesList
same as SrcList
//用法同SrcList参数
DesFolder
same as SrcFolder
//用法同ScrFolder参数
DesItem
same as SrcItem
//用法同SrcItem参数
Flag
If you don’t use Flag, use zero instead of it.
//如果这个参数不用就设为0。注意:如果Flag中有一个以上参数,则用’or’连接,非and。
RS_FILE
tell FTPRush this queue is a file, without this flag this queue is a folder
//告诉FTPRush这个队列是一个文件,否则队列个文件夹。
RS_UP
tell FTPRush this queue is upload mode, without this flag this queue is FXP mode
//告诉FTPush这是个上传队列,否则默认队列为FXP模式。
RS_DOWN
tell FTPRush this queue is download mode, without this flag this queue is FXP mode
//告诉FTPush这是个下载队列,否则默认队列为FXP模式。
RS_DIRSRC
specifies SrcFolder is a folder, without this flag, SrcFolder is a bookmark
//说明SrcFolder是个文件夹,否则默认为书签。
RS_DIRDES
specifies DesFolder is a folder, without this flag, DesFolder is a bookmark
//说明DesFolder是个文件夹,否则为书签。
RS_NOSKIP
tell FTPRush do not use skiplist to filter DesItem and DesItem
//告诉FTPRush不要使用过滤列表过滤DesItem。(这里是不是应该为SrcItem和DesItem?)
RS_APPEND
tell FTPRush combines all queue items at ONE window, otherwise, every queue item running at a NEW window.
//告诉FTPush把所有队列放到一个容器里,否则每个队列占用一个容器。
RS_EXPR
if includes this flag, FolderAllow, FolderSkip, FileAllow, FileSkip match Perl Expressions, otherwise match wildcard
//如果加上这个标记,则FolderAllow, FolderSkip, FileAllow, FileSkip匹配的时候都支持Perl语句,否则匹配通配符
RS_LOGOUT
disconnected from server after queue transferred
//队列结束后断开连接
RS_GHOST
use !username as login name
//用!username做为登陆名 (liucl@DRL:ioftpd&glftpd里面在登陆用户名前面加”!”可以把挂在上面的尸体踢下来)
RS_INVERT
add or remove ‘ – ‘ from login password
//加上或去掉密码前的’-'(z-z@DRL:这个功能是给登录 glftpd 的用户的, 如果在登录的时候, 密码之前加一个 “-” 符号, 那么glftpd不会发送冗余的信息给你.)
RS_NOCIRCLE
Folder2 (n+1) won’t start before folder1 (n) is all over
//前一个文件夹完全完成后开始第二个文件夹
RS_CHECKLOGIN
only works for without RS_APPEND and dont use ContainerID.
then when no free logins to transfer, FTPRush auto select a container which have the same transfer (src/dest) and append it
//没有加RS_APPEND的时候此参数有效,并且不要使用容器编号。当某一站点的登陆数达到上限的时候,FTPRush将自动选择一个“源-目标”相同的容器,将需要传输的任务加到这个容器队列的后面。
RS_POPUP
to bring FTPRush on top
//前端显示FTPRush程序窗口
RS_NOMKD
when transferring folders with sub dirs, FTPRush will not create the sub dirs in dest FTP if sub dirs exists in dest FTP(just do CWD)
//如果队列传输包含子目录,且在目标FTP上存在相同子目录,则FTPRush只执行CWD命令而不创建文件夹
FolderAllow
Allow sub folders match FolderAllow param, can be a empty string
//设置目录的允许列表,可以留空。
FolderSkip
Skip sub folders match FolderSkip param, can be a empty string
//设置目录的过滤列表,可以留空。
FileAllow
Allow sub files match FileAllow param, can be a empty string
//设置文件允许列表,可以留空。
FileSkip
Skip sub files match FileSkip param, can be a empty string
//设置文件的过滤列表,可以留空。
CompleteFlag
when set RepeatCount > 0, CompleteFlag can let the queue thread stop when thread found a folder/file name at Source/Destination directory list match the CompleteFlag, CompleteFlag only supports Perl Expressions
//在RepeatCount>0的时候此参数有效。当源(目标)FTP上找到一个文件夹(文件)名字与CompleteFlag匹配,则队列停止。CompleteFlag只支持Perl语句。
RepeatCount
indicates how many times to refresh for a folder
//设定刷新一个文件夹几次。
RetryCount
indicates retry count when transfer a file
//设定单个文件的重试次数。
Timeout
set timeout(seconds) for a FXP queue, when timeout and transfer not started, this queue will be removed*not implented*
//设置FXP队列的超时时限(单位:秒),超过时限还没开始传输的队列将被移除。
//查不到“implented”这个词啥意思,怀疑是“not implemented”,该功能未现实
FolderSort
define sort method for sub folders, set it to zero to use default sort method (sort by name ascending)
//定义目录的排序规则,设为’0′时使用默认规则(按名称升序排列)
RS_SORTDES
specifies sort sorting as descending
//目录按降序排列(可以结合下面两个参数)
RS_SORTDATE
specifies sort by date
//按日期排列
RS_SORTSIZE
specifies sort by size
//目录按大小排列
FileSort
define sort method for sub folders, set it to zero to use default sort method (sort by name ascending)
//定义文件的排序规则,设为’0′时使用默认规则(按名称升序排列)
RS_SORTDES
specifies sort sorting as descending
//目录按降序排列(可以结合下面两个参数
RS_SORTDATE
specifies sort by date
//按日期排列
RS_SORTSIZE
specifies sort by size
//目录按大小排列
Depth
depth of sub folders
//设置目录深度,’0′则操作整个目录
SubCount
when transfer a folder includes sub folder/files, the first SubCount file/folders will be transferred. set it to zero to disable this feature
//当传输有子目录的目录(文件)时,只传输最上面的SubCount个目录(文件)。设为’0′可以屏蔽这个功能。
RushApp.FTP
Variables
RushApp.FTP.Splitter
Declare
property Splitter: string;
Description
Indicates splitter for Names of FTP Server, default splitter is ‘;’
//定义分隔符FTP脚本的分隔符,默认的是’;'。一般用用就默认好了,这个定义对下面7个函数均有效。
Example
RushApp.FTP.Splitter := ‘|’;
//这个例子就是把分隔符定义为’|'(不包括引号)。
RushApp.FTP.Delete
Declare
procedureDelete(NameList: string; Path: string; Name: string; Flag: Integer);
Description
Delete folder or file from specifies connected/idle FTP Servers
//删除指定FTP上的文件或文件夹
Parameters
NameList
Specifies which FTP need to delete folders or files, NameList can not be empty string
//指定需要删除文件(文件夹)的FTP,多个FTP则用分隔符隔开,不能为空。
Path
Specifies a bookmark name or a directory
//指定一个目标FTP上的目录或书签。
Name
a file name or a folder name, allows be a empty string
//指定目录下要删除的文件(文件夹)的名字。
Flag
if you don’t use Flag, use zero instead of it
//就使用这个参数就设为0吧。
RS_BOOKMARK
Param Path is a bookmark
//说明Path是个书签,否则为目录。
RS_RECURSIVE
when delete a folder, do not use recursive delete
//关于这个参数解释一下,照字面翻译是递归的意思,看得真是晕里雾里的,我用例子3和4分别测试了一下删除的效果(G6 v3.7.0 build24&FTPRush v1.0.0.0581)。例4是不管三七二十一直接删掉“test”文件夹。例3则分情况,如果“test”下无子目录,则删除“test”整个文件夹(不管是否有文件存在),如果“test”下有子目录,则只清空该文件夹下的文件,保留子目录和该文件 夹本身。
RS_FOLDER
tell FTPRush that’s a folder, otherwise it will be executed as deleting file
//告诉FTPRush这是一个文件夹,否则将做为文件来删除。
RS_LOGOUT
disconnect from FTP after deleted
//完成删除操作后断开连接。
RS_LOGIN
connect to FTP when have no active FTP connections
//如果当前没有连接状态的FTP,则连接FTP。
RS_POPUP
to bring FTPRush on top
//前端显示FTPRush。
Examples
//delete file ‘/public/test.txt’ from FTP1
RushApp.FTP.Delete(‘FTP1′,’/public’,'test.txt’,0);
//删除FTP1上“/public”目录下的test.txt文件。
//delete file ‘test.txt’ from bookmark ‘public’ of FTP1 and FTP2
RushApp.FTP.Delete(‘FTP1;FTP2′,’public’,'test.txt’,RS_BOOKMARK);
//删除FTP1和FTP2上“public”书签下的“test.txt”文件。
//delete file from folder ‘test’ from bookmark ‘public’ of FTP1
RushApp.FTP.Delete(‘FTP1′,’public’,'test’,RS_FOLDER or RS_BOOKMARK);
//删除FTP1上“public”书签下的“test”文件夹。(文件夹无子目录时有效)
//delete file from folder ‘test’ from bookmark ‘public’ of FTP1
RushApp.FTP.Delete(‘FTP1′,’public’,'test’,RS_FOLDER or RS_BOOKMARK or RS_RECURSIVE);
//删除FTP1上“public”书签下的“test”文件夹。(不论是否为空连带子目录一起删除)
RushApp.FTP.CWD
Declare
procedureCWD(NameList: string; Param: string; Flag: Integer);
Description
Let all or specifies connected/idle FTP Servers change remote directory.
//改变指定FTP的远程路径
Parameters
NameList
Specifies which FTP need to change remote directory, you can use a splitter for multiple FTP servers, if it’s empty then all connected idle FTP servers will change remote directory
//定制哪个FTP改变远程路径,可以是多个FTP,不过需要用分隔符分开。如果为空的话则所有空闲FTP均改变远程路径。
Param
Specifies a bookmark name or a directory
//设置FTP以书签或者路径为依据进行目录跳转。
Flag
if you don’t use Flag, use zero instead of it
//如果不用这个参数就设为0吧。
RS_BOOKMARK
Param is a bookmark
//告诉程序Param这个参数是书签
RS_POPUP
to bring FTPRush on top
//前端显示FTPRush
Examples
//tell all non-transfering connections change to bookmark ‘public’
RushApp.FTP.CWD(”,’public’,RS_BOOKMARK);
//非传输状态的FTP都跳转到书签“public”。
//tell all non-transfering connections change to directory ‘/public/’
RushApp.FTP.CWD(”,’/public/’,0);
//非传输状态的FTP都跳转到目录“public”。
//tell ‘FTP1′ server change to bookmark ‘pub’
RushApp.FTP.CWD(‘FTP1′,’pub’,RS_BOOKMARK);
//将FTP1的远程路径跳转到书签“pub”。
//tell ‘FTP1′ and ‘FTP2′ server change to bookmark ‘pub’
RushApp.FTP.CWD(‘FTP1;FTP2′,’pub’,RS_BOOKMARK);
//将FTP1和FTP2的远程路径都跳转到书签“pub”。
//tell ‘FTP1′ server change to directory ‘/public/’
RushApp.FTP.CWD(‘FTP1′,’/public/’,0);
//将FTP1的远程路径跳转到目录“/public/”。
//tell ‘FTP1′ and ‘FTP2′ server change to directory ‘/public/’
RushApp.FTP.CWD(‘FTP1;FTP2′,’/public/’,0);
//将FTP1和FTP2的远程路径跳转到目录“/public/”。
RushApp.FTP.Login
Declare
procedureLogin(NameList: string; Param: string; Flag: Integer);
Description
Connect to one or more FTP Servers and jump to a FTP path or bookmark.
//连接一个或多个FTP服务器同时跳转到指定路径或书签。
Parameters
NameList
Specifies which FTP to connect, you can use a splitter to connect multiple FTP servers
//指定要连接的FTP,多个FTP则用分隔符分开。
Param
Specifies a bookmark name or a directory name, or a empty string
//FTP连上后要跳转到的书签或路径,可以为空。
Flag
if you don’t use Flag, use zero instead of it
//如果不用这个参数就设为0吧。
RS_BOOKMARK
Param is a bookmark
//告诉程序Param这个参数是书签。
RS_CHECKBM
if bookmark is invalid, don’t connect it
//如果书签不存在,不连接FTP。
RS_GHOST
use !username as login name
//用!username登陆 (即用户名前加!)
RS_INVERT
add or remove ‘ – ‘ from login password
//在密码前添加或移除’-'。
RS_PAIR
combine FTP name as pair
//将两个FTP配对。
RS_POPUP
to bring FTPRush on top
//前端显示FTPRush
Examples
//Connect to FTP1
RushApp.FTP.Login(‘FTP1′,”,0);
//连接到FTP1。
//Connect to FTP1 and FTP2
RushApp.FTP.Login(‘FTP1;FTP2′,”,0);
//同时连接到FTP1和FTP2。
//Connect to FTP1 and change to directory /pub/
RushApp.FTP.Login(‘FTP1′,’/pub/’,0);
//连接到FTP1并同时跳转到“/pub/”目录。
//connect to FTP1 and FTP2 and change to bookmark ‘public’
RushApp.FTP.Login(‘FTP1;FTP2′,’public’,RS_BOOKMARK);
//同时连接到FTP1和FTP2并跳转到书签“public”。
//connect to FTP1 and FTP2, if FTP2 have no bookmark named ‘public’, FTPRush will connect to FTP1 only
RushApp.FTP.Login(‘FTP1;FTP2′,’public’,RS_BOOKMARK or RS_CHECKBM);
//同时连接到FTP1和FTP2,并跳转到书签“public”,哪个FTP上不存在这个书签则断开。
//connect to FTP1 and FTP2 and FTP3 with pair format: ‘FTP1;FTP2′ and ‘FTP2:FTP3′
RushApp.FTP.Login(‘FTP1;FTP2;FTP3′,’public’,RS_BOOKMARK or RS_PAIR);
//连接到FTP1,FTP2,FTP3,同时FTP1和FTP2配对,FTP2和FTP3配对。
RushApp.FTP.RAW
Declare
procedureRAW(NameList: string; Param: string; Flag: Integer);
Description
Send raw command to all or specical connected/idle FTP Servers, if Param ANameList is empty string then send to all connected/idle FTP servers. this function will not send raw command to transfering connections.
//发送RAW命令到所有指定的FTP。如果Param和NameList这两个参数是空的则发送到所有空闲FTP。这个函数对在传输中的FTP无效。
Parameters
NameList
Specifies which FTP to send raw command, you can use a splitter to connect multiple FTP servers, if it’s empty then send to all connected idle FTP servers
//指定要发送RAW命令的FTP,多个FTP则用分隔符隔开,留空的话就发送命令到所有已连接的空闲FTP。
Param
Specifies a raw command to send
//定制要发送的RAW命令。
Flag
if you don’t use Flag, use zero instead of it
//如果不用这个参数就设为0吧。
RS_LOGIN
if no specifies FTP connected, then connect it first
//如果没有FTP处在连接状态,那么先登陆。
RS_LOGOUT
disconnect after executed raw command
//发送RAW命令后断开连接
RS_POPUP
to bring FTPRush on top
//前端显示FTPRush
RS_ONE
to only allow to execute command on one connected site even if there have 2+ same sites connected
//如果同一个站点有有两个或两个以上容器里连接了,那么只对一个执行RAW命令
Examples
//send ‘NOOP’ to all connected non-transfering connections
RushApp.FTP.RAW(”,’NOOP’,0);
//发送“NOOP”命令给所有没在传输的FTP。
//send ‘NOOP’ to all connected non-transfering connections then logout
RushApp.FTP.RAW(”,’NOOP’,RS_LOGOUT);
//发送“NOOP”命令给所有没在传输的FTP然后退出。
//connect to ‘FTP1′ and send ‘NOOP’
RushApp.FTP.RAW(‘FTP1′,’NOOP’,RS_LOGIN);
//给FTP1发送“NOOP”命令。
//connect to ‘FTP1′ and ‘FTP2′, send ‘NOOP’, then logout
RushApp.FTP.RAW(‘FTP1;FTP2′,’NOOP’,RS_LOGIN or RS_LOGOUT);
//给FTP1和FTP2发送“NOOP”命令,然后退出。
RushApp.FTP.Logout
Declare
procedure Logout(NameList: string; Flag: Integer);
Description
Disconnect from one or more FTP Servers, If Param ANameList is empty string, all idle FTP connections will be disconnected.
//断开一个或多个FTP的连接,如果NameList这个参数是空的,则断开所有空闲FTP的连接。
Parameters
NameList
Specifies which FTP to disconnect, you can use a splitter to connect multiple FTP servers, a empty string means disconnect from all active/idle FTP connections(non-transfering)
//定制哪个需要断开连接的FTP,多个FTP则用分隔符分开。留空则断开所有非传输状态的FTP。
Flag
if you don’t use Flag, use zero instead of it
//如果不用这个参数就设为0吧。
RS_ASYNC
when use this flag, FTPRush will do a disconnect action after command executed.
//执行命令后断开连接
Examples
//logout from all idle FTP connections
RushApp.FTP.Logout(”,0);
//断开所有空闲FTP。
//logout from connections which named ‘FTP1′
RushApp.FTP.Logout(‘FTP1′,0);
//断开与FTP1的连接。
//logout from connections which named ‘FTP1′ or ‘FTP2′
RushApp.FTP.Logout(‘FTP1;FTP2′,0);
//断开与FTP1和FTP2的连接。
//send command ‘NOOP’ to ‘FTP1′, after ‘NOOP’ be executed, then disconnect from ‘FTP1′
RushApp.FTP.Raw(‘FTP1′,’NOOP’,0);
RushApp.FTP.Logout(‘FTP1′,RS_ASYNC);
//先发送“NOOP”命令到FTP1,然后断开连接。
RushApp.FTP.RemoveQueue
Declare
procedureRemoveQueue(Source: string; Dest: string; Param: string;
Flag: Integer);
Description
Remove Queue items from Queue window.
//清除队列窗口的队列。
Parameters
Source
Specifies source FTP name or a empty string, use ‘local’ to specifies upload queue
//指定源FTP(此处可以为空),如果是上传队列则使用“local”。
Dest
Specifies dest FTP name or a empty string, use ‘local’ to specifies download queue
//指定目标FTP(此处可以为空),如果是下载队列则使用“local”。
Param
Specifies a condition for Queue dest path or dest filename/foldername, or a empty string
//指定移除队列的匹配条件,可以是路径,文件名和文件夹名,留空则匹配所有。
Flag
You must use at least one flag as below
//Flag这项至少要使用一个参数。
RS_TRANSFER
remove transfering queues
//清除传输队列。
RS_NORMAL
remove non-transfering queues
//清除非传输中的队列。
RS_FAIL
remove failed queues
//清除失败的队列。
RS_WAITING
remove waiting queues (with blue clock icon)
//清除等待中的队列。
RS_WILD
Param condition is wildcard
//告诉程序Param这个参数采用通配符了。
RS_EXPR
Param condition is regular expression
//告诉程序Param这个参数采用正则表达式了。
RS_FULLPATH
Use queue dest full path to match Param condition, otherwise only use queue dest name
//声明Param中匹配的是队列的目标路径,否则采用名字来匹配。
RS_CURRENT
remove queues from current window only
//只移除当前容器的队列。
RS_POPUP
to bring FTPRush on top
//前端显示FTPRush。
Examples
//Clear all queues from all window
RushApp.FTP.RemoveQueue(”,”,”,RS_TRANSFER or RS_NORMAL or RS_WAITING);
//清除所有容器的所有队列。
//Clear all queues from current window
RushApp.FTP.RemoveQueue(”,”,”,RS_TRANSFER or RS_NORMAL or RS_WAITING or RS_CURRENT);
//清除当前容器的所有队列。
//Remove all download queue
RushApp.FTP.RemoveQueue(”,’local’,”,RS_TRANSFER or RS_NORMAL or RS_WAITING or RS_CURRENT);
//清除所有的下载队列。
//Remove all upload queue
RushApp.FTP.RemoveQueue(‘local’,”,”,RS_TRANSFER or RS_NORMAL or RS_WAITING or RS_CURRENT);
//清除所有的上传队列。
//remove all failed queue items from all window
RushApp.FTP.RemoveQueue(”,”,”,RS_FAIL);
//清除所有容器的失败队列。
//remove all failed queue items from current window
RushApp.FTP.RemoveQueue(”,”,”,RS_FAIL or RS_CURRENT);
//清除当前容器的所有失败队列。
//remove all queues matchs ‘/public*’
RushApp.FTP.RemoveQueue(”,”,’/public*’,RS_TRANSFER or RS_NORMAL or RS_WAITING or RS_CURRENT or RS_WILD or RS_FULLPATH);
//清除所有路径中含“/public”的队列。
//remove all queues which file name matchs ‘*2004*’
RushApp.FTP.RemoveQueue(”,”,’*2004*’,RS_TRANSFER or RS_NORMAL or RS_WAITING or RS_CURRENT or RS_WILD);
//清除所有文件名中含“2004”的队列。
//remove all queues which dest name is ‘FTP1′
RushApp.FTP.RemoveQueue(”,’FTP1′,”,RS_TRANSFER or RS_NORMAL or RS_WAITING);
//清除所有以FTP1为目标FTP的队列。
//remove all queues which source name is ‘FTP1′
RushApp.FTP.RemoveQueue(‘FTP1′,”,”,RS_TRANSFER or RS_NORMAL or RS_WAITING);
//清除所有以FTP1为源目标的队列。






