最近我在写数据压缩的程序,经常用到数组。每到一定时候就需要对数组全部元素进行清零。由于C#不提供memset()方法。所以进行了以下的测试。

主要程序部份:

static void Main(string[] args)
{
    int i,k;
    double p = 0;
    DateTime s, e;
    byte[] test = new byte[65536];
    byte[] test2 = new byte[65536];
    for(int i = 0; i < 10; i++)
    {
        s = DateTime.Now;
        for(j=0; j<50000; j++)
        {
            //***************//
        }
        e = DateTime.Now;
        TimeSpan c = e - s;
        Console.WriteLine(c.TotalMilliseconds.ToString());
        p += c.TotalMilliseconds;
    }

    p /= 10;
    Console.WriteLine("***" + p.ToString() + "***");
    Console.Read();
}

将其中红色部份替换成以下几种方法:

1.test = new byte[65536];

    这种方法显而易懂,每次都新建数组,里面的数值当然都是0啦。

2.memset(test,0,65536);

    仿照C++自写一个memset函数:
public static void memset(byte[] buf, byte val, int size)
{
    int i;
    for(i=0; i < size; i++)
        buf[i] = val;
}

3.for(k = 0; k < 65536; test[k++] = 0);
    把函数memset直接写入红色部份。这样的想法很简单,节省调用函数的开销。
    
4.Array.Clear(test,0,65536);

    直接使用.Net自带方法。

5.test2.CopyTo(test, 0);

    用空数组向目标数组中复制内容。

然后理行测试,结果如下:

方法    最长    最短    平均

1    1937.5    1812.5    1842.1875    

2    4593.75   4625      4584.375

3    6046.875  5984.375  6001.5625

4     562.5     640.5     581.25

5     812.5     750       770.3125

计算机:P43.0E(超线程开启)/DDR400 512M(双通道) 软件环境:Win XP - Sp2/VS2003 单位:毫秒(ms)

结果很显然,Array.Clear()方法是最终的优胜者。但它有一个缺点,就是只可以将数组清空(零)。如果要把数组设置为某一特定格式CopyTo()以空间换时间的方式是最好的。
只有一点看不明白,把第2种函数调用改成第3种内嵌式算法后,效率反而下降了。这只能解释为编译器的优化了。看来用C#写程序不用太关心函数调用的开销。

posted @ 2008-03-19 09:34 郭大伟 阅读(1653) | 评论 (0)编辑 收藏

1.test测试命令
test命令用于检查某个条件是否成立,它可以进行数值、字符和文件三个方面的测试,
其测试符和相应的功能分别如下:
(1)数值测试:
-eq:等于则为真
-ne:不等于则为真
-gt:大于则为真
-ge:大于等于则为真
-lt:小于则为真
-le:小于等于则为真
(2)字符串测试:
=:等于则为真
!=:不相等则为真
-z 字符串:字符串长度伪则为真
-n 字符串:字符串长度不伪则为真
(3)文件测试:
-e 文件名:如果文件存在则为真
-r 文件名:如果文件存在且可读则为真
-w 文件名:如果文件存在且可写则为真
-x 文件名:如果文件存在且可执行则为真
-s 文件名:如果文件存在且至少有一个字符则为真
-d 文件名:如果文件存在且为目录则为真
-f 文件名:如果文件存在且为普通文件则为真
-c 文件名:如果文件存在且为字符型特殊文件则为真
-b 文件名:如果文件存在且为块特殊文件则为真
另外,Linux还提供了与(“!”)、或(“-o)、非(“-a”)三个逻辑操作符用于将测试条件连接起来,
其优先级为:“!”最高,“-a”次之,“-o”最低。
同时,bash也能完成简单的算术运算,格式如下:
$[expression]
例如:var1=2
var2=$[var1*10+1]
则:var2的值为21。

2.if条件语句
if [ -x /sbin/quotaon ]; then
echo "Turning on Quota for root filesystem"
/sbin/quotaon /
elif [ -x /sbin/quotaon ]; then
/usr/bin/bash
else
echo "ok"
fi

3.for 循环
#!/bin/sh
WORD="a b c d e f g h i j l m n o p q r s t u v w x y z"
for i in $WORD ; do
echo $i
done

#!/bin/sh
FILES=`ls /txt/*.txt`
for txt in $FILES ; do
doc=`echo $txt | sed "s/.txt/.doc/"`
mv $txt $doc
done

4.while和until 循环
#!/bin/sh
while [ -f /var/run/ppp0.pid ] ; do
killall pppd
done

#!/bin/sh
until [ -f /var/run/ppp0.pid ] ; do
sleep 1
done

Shell还提供了true和false两条命令用于建立无限循环结构的需要,
它们的返回状态分别是总为0或总为非0

5.case 条件选择
#!/bin/sh
case $1 in
start | begin)
echo "start something"
;;
stop | end)
echo "stop something"
;;
*)
echo "Ignorant"
;;
esac
case表达式中也可以使用shell的通配符(“*”、“?”、“[ ]”)。

6.无条件控制语句break和continue
break 用于立即终止当前循环的执行,而contiune用于不执行循环中后面的语句
而立即开始下一个循环的执行。这两个语句只有放在do和done之间才有效。

7.函数定义
shell中还可以定义函数。函数实际上也是由若干条shell命令组成的,
因此它与shell程序形式上是相似的,不同的是它不是一个单独的进程,
而是shell程序的一部分。函数定义的基本格式为:
functionname
{
若干命令
}
调用函数的格式为:
functionname param1 param2 ……
shell函数可以完成某些例行的工作,而且还可以有自己的退出状态,
因此函数也可以作为if、while等控制结构的条件。
在函数定义时不用带参数说明,但在调用函数时可以带有参数,此时
shell将把这些参数分别赋予相应的位置参数$1、$2、...及$*。

8.命令分组
shell中有两种命令分组的方法:“()”和“{}”,前者当shell执行()
中的命令时将再创建一个新的子进程,然后这个子进程去执行圆括弧中的命令
当用户在执行某个命令时不想让命令运行时对状态集合(如位置参数、环境变量、
当前工作目录等)的改变影响到下面语句的执行时,就应该把这些命令放在圆括
弧中,这样就能保证所有的改变只对子进程产生影响,而父进程不受任何干扰;
{}用于将顺序执行的命令的输出结果用于另一个命令的输入(管道方式)。当我们
要真正使用圆括弧和花括弧时(如计算表达式的优先级),则需要在其前面加上转
义符(\)以便让shell知道它们不是用于命令执行的控制所用。

9.信号
trap命令用于在shell程序中捕捉到信号,之后可以有三种反应方式:
(1)执行一段程序来处理这一信号
(2)接受信号的默认操作
(3)忽视这一信号
trap对上面三种方式提供了三种基本形式:
第一种形式的trap命令shell接收到signal list清单中数值相同的信号时,
将执行双引号中的命令串。
trap 'commands' signal-list
trap "commands" signal-list
为了恢复信号的默认操作,使用第二种形式的trap命令
trap signal-list
第三种形式的trap命令允许忽视信号:
trap " " signal-list
注意:
(1)对信号11(段违例)不能捕捉,因为shell本身需要捕捉该信号去进行内存的转储。
(2)在trap中可以定义对信号0的处理(实际上没有这个信号),shell程序在其终止
(如执行exit语句)时发出该信号。
(3)在捕捉到signal-list中指定的信号并执行完相应的命令之后,如果这些命令没有将
shell程序终止的话,shell程序将继续执行收到信号时所执行的命令后面的命令,这样
将很容易导致shell程序无法终止。
另外,在trap语句中,单引号和双引号是不同的,当shell程序第一次碰到trap语句时,
将把commands中的命令扫描一遍。此时若commands是用单引号括起来的话,那么shell
不会对commands中的变量和命令进行替换,否则commands中的变量和命令将用当时具体
的值来替换。

10. 运行shell程序的方法
执行shell程序的方法有三种:
(1)sh shell程序文件名
格式为:
bash shell 程序文件名
这实际上是调用一个新的bash命令解释程序,而把shell程序文件名作为参数传递给它。
新启动的shell将去读指定的文件,执行文件中列出的命令,当所有的命令都执行完结束。
该方法的优点是可以利用shell调试功能。
(2)sh<shell程序文件名
格式为:
bash<shell 程序文件名
这种方式就是利用输入重定向,使shell命令解释程序的输入取自指定的程序文件。
(3)用chmod命令使shell程序成为可执行的

11. bash程序的调试
bash -选择项 shell程序文件名
几个常用的选择项是:
-e:如果一个命令失败就立即退出
-n:读入命令但是不执行它们
-u:置换时把未设置的变量看作出错
-v:当读入shell输入行时把它们显示出来
-x:执行命令时把命令和它们的参数显示出来
上面的所有选项也可以在shell程序内部用“set -选择项”的形式引用,而“set +选择项”则
将禁止该选择项起作用。如果只想对程序的某一部分使用某些选择项时,则可以将该部分用
上面两个语句包围起来。
1.未置变量退出和立即退出
未置变量退出特性允许用户对所有变量进行检查,如果引用了一个未赋值的变量就终止shell
程序的执行。shell通常允许未置变量的使用,在这种情况下,变量的值为空。如果设置了未
置变量退出选择项,则一旦使用了未置变量就显示错误信息,并终止程序的运行。未置变量退
出选择项为“-u”。
shell运行时,若遇到不存在或不可执行的命令、重定向失败或命令非正常结束等情况时,如
果未经重新定向,该出错信息会打印在终端屏幕上,而shell程序仍将继续执行。要想在错误发
生时迫使shell程序立即结束,可以使用“-e”选项将shell程序的执行立即终止。
2.shell程序的跟踪
调试shell程序的主要方法是利用shell命令解释程序的“-v”或“-x”选项来跟踪程序的执行。“-v”
选择项使shell在执行程序的过程中,把它读入的每一个命令行都显示出来,而“-x”选择项使shell
在执行程序的过程中把它执行的每一个命令在行首用一个“+”加上命令名显示出来。并把每一个变量
和该变量所取的值也显示出来,因此,它们的主要区别在于:在执行命令行之前无“-v”则打印出命
令行的原始内容,而有“-v”则打印出经过替换后的命令行的内容。
除了使用shell的“-v”和“-x”选择项以外,还可以在shell程序内部采取一些辅助调试的措施。
例如,可以在shell程序的一些关键地方使用echo命令把必要的信息显示出来,它的作用相当于C语
言中的printf语句,这样就可以知道程序运行到什么地方及程序目前的状态。

12. bash的内部命令
bash命令解释程序包含了一些内部命令。内部命令在目录列表时是看不见的,它们由shell本身提供。
常用的内部命令有:echo、eval、exec、export、readonly、read、shift、wait和点(.)。
下面简单介绍其命令格式和功能。
1.echo
命令格式:echo arg
功能:在屏幕上打印出由arg指定的字符串。
2.eval
命令格式:eval args
功能:当shell程序执行到eval语句时,shell读入参数args,并将它们组合成一个新的命令,然后
执行。
3.exec
命令格式:exec 命令 命令参数
功能:当shell执行到exec语句时,不会去创建新的子进程,而是转去执行指定的命令
当指定的命令执行完时,该进程,也就是最初的shell就终止了,所以shell程序中exec
后面的语句将不再被执行。
4.export
命令格式:export 变量名 或:export 变量名=变量值
功能:shell可以用export把它的变量向下带入子shell从而让子进程继承父进程中的环境变量。
但子shell不能用export把它的变量向上带入父shell
注意:不带任何变量名的export语句将显示出当前所有的export变量。
5.readonly
命令格式:readonly 变量名
功能:将一个用户定义的shell变量标识为不可变的。不带任何参数的readonly命令将显示出
所有只读的shell变量。
6.read
命令格式:
read变量名表
功能:从标准输入设备读入一行,分解成若干字,赋值给shell程序内部定义的变量。
7.shift语句
功能:shift语句按如下方式重新命名所有的位置参数变量:$2成为$1,$3成为$2……在程序中
每使用一次shift语句,都使所有的位置参数依次向左移动一个位置,并使位置参数“$#”减一,
直到减到0。
8.wait
功能:是shell等待在后台启动的所有子进程结束。Wait的返回值总是真。
9.exit
功能:退出shell程序。在exit之后可有选择地指定一个数字作为返回状态。
10.“.”(点)
命令格式:. Shell程序文件名
功能:使shell读入指定的shell程序文件并依次执行文件中的所有语句。

13. 特殊参数:
1. $*: 代表所有参数,其间隔为IFS内定参数的第一个字元
2. $@: 与*星号类同。不同之处在於不参照IFS
3. $#: 代表参数数量
4. $?: 执行上一个指令的返回值
5. $-: 最近执行的foreground pipeline的选项参数
6. $$: 本身的Process ID
7. $!: 执行上一个背景指令的PID
8. $_: 显示出最后一个执行的命令

posted @ 2007-08-29 14:41 郭大伟 阅读(254) | 评论 (0)编辑 收藏

文件传输协议 (FTP)

备忘录状态

本备忘录描述了文件传输协议(FTP)的官方规范。对本备忘录的发布没有限制。

本规范新包括了如下可选命令:

CDUP (返回父目录), SMNT (结构装备), STOU(唯一保存),
RMD (删除目录), MKD (新建目录), PWD(打印目录), and SYST (系统)。

本规范与前一个版本兼容


目录

1. 介绍
2. 概述
  2.1. 历史
  2.2. 术语
  2.3. FTP模型
3. 数据传输功能
  3.1. 数据表示和存储
    3.1.1. 数据类型
     3.1.1.1. ASCII类型
     3.1.1.2. EBCDIC类型
     3.1.1.3. 图像类型
     3.1.1.4. 本地类型
     3.1.1.5. 格式控制
       3.1.1.5.1. 非打印(NON PRINT)
       3.1.1.5.2. TELNET格式控制
       3.1.1.5.2. CARRIAGE CONTROL(ASA)
    3.1.2. 数据结构
      3.1.2.1. 文件结构
      3.1.2.2. 记录结构
      3.1.2.3. 页结构
  3.2. 建立数据连接
  3.3. 数据连接管理
  3.4. 传输模式
    3.4.1. 流模式
    3.4.2. 块模式
    3.4.3. 压缩模式
  3.5. 错误恢复和重新开始
4. 文件传送功能
  4.1. FTP命令
    4.1.1. 访问控制命令
    4.1.2. 传输参数命令
    4.1.3. FTP服务命令
  4.2. FTP响应
    4.2.1. 按功能分组的响应代码
    4.2.2. 按数字顺序排列的响应代码

1. 介绍

FTP的目标是:1)促进程序/数据文件的共享;2)鼓励(通过程序)使用远程计算机;3)使用户不必面对不同主机上不同文件系统的差异;4)对数据进行高效可靠的传输。FTP尽管可以直接在终端上应用,但它主要被设计通过程序来使用。

本规范通过设计简单易实现的协议来试图满足大型机、小型机、个人工作站、TAC等用户的需要。

本文需要文件传输协议(TCP)[2]以及Telnet协议[3]的知识。关于它们的文档可以在ARPA-互联网协议手册[1]中找到。

2. 概述

本章讨论内容包括历史、术语、FTP模型。本章所描述的都是关于FTP的重要内容。一些术语专用于FTP模型;一些读者可能有必要在回顾这些术语时参考关于FTP模型的章节。

2.1. 历史

FTP的发展经历了很多年。附录III是按年代编辑的关于FTP的RFC文档。其中包括最早在1971年提出用在M.I.T.主机上的文件传输机制(RFC 114),以及在RFC 141中的注释和讨论。

RFC 172提供了一个在主机(包括终端IMP)间基于用户层协议的文件传输方法。RFC 265做为其修订,通过附加评论重定义了FTP,RFC 281建议进一步改进。“Set Data Type”在传输中应用在1982年1月的RFC 294中提出。

RFC 354废弃了RFC 264和265。文件传输协议被定义为ARPANET上主机间的文件传输协议,FTP的主要作用则被定义为用来在主机间高效可靠地传输文件以及对远程存储的方便使用。RFC 385进一步讨论了协议的错误,重点和一些附加内容,RFC 414提供了使用中的FTP状态报告。1973年的RFC 430(被其它RFC多次引用),提供了对FTP的进一步讨论。最后,一个“官方的”FTP文件在RFC 454中发布。

至1973年1月,FTP协议又有了大量的变化,但总体的结构仍保持一致。为了应对这些变化,RFC 542中发布了新的“官方”规范。但很多基于老规范的应用并没有被更新。

1974年,RFC 607和614继续讨论FTP。RFC 624提出进一步改变设计和一些小的修补。1975年,RFC 686用题为“Leaving Well Enough Alone”讨论早期以及近期FTP版本的差别。RFC 691对RFC 686中关于打印文件部分做了小的修订。

在之前所有努力的基础上,通过将底层的传输协议由NCP改为TCP,应用TCP的FTP协议在RFC 765中诞生了。

本文描述的FTP规范目的在于纠正之前文档中的某些小错误,进一步解释一些协议特性,以及引入一些可选的命令。

特别地,本版本规范新包含了以下可选命令:

CDUP - 返回父目录

SMNT - 结构装备

STOU - 唯一保存

RMD - 删除目录

MKD - 新建目录

SYST - 系统

本规范兼容之前版本。应用在前一版本的程序应该可以自动应用于本规范。

2.2. 术语

ASCII

ASCII字符集定义于ARPA-互联网协议手册。FTP中ASCII字符指8位编码集的低半部(也就是最高位为0)。

访问控制(access controls)

访问控制定义了用户使用系统、系统文件的访问权力。访问控制必要性在于防止未被授权的或意外的对文件的访问。在服务器端使用访问控制正是FTP的优势所在。

字节长度(byte size)

有两种字节长度与FTP有关:文件的逻辑字节长度和用来传输数据的字节长度。传输字节长度总是8位。传输字节长度不必要和系统存储数据的字节长度或用来描述数据结构的逻辑字节长度相同。

控制连接(control connection)

用户PI与服务器PI用来交换命令和响应的信息路径。这个连接遵守Telnet协议。

数据连接(data connection)

用规定的模式和类型进行数据传输的全双向连接。传输的数据可能是文件的一部分、整个文件或一些文件。传输路径可能是服务器DTP与用户DTP之间或两个服务器DTP之间。

数据端口(data port)

数据接收者在数据端口监听,等待数据传输者从此端口建立数据连接。

DTP(data transfer process)

数据传输过程,用以建立并管理数据连接。DTP可以是被动或主动。

行结束符(End-of-Line)

行结束符序列定义了印刷行间的分隔。行结束符序列为回车符加换行符。

文件结束符(End-of-File)

文件结束符标志定义了传输中一个文件的结束。

记录结束符(End-of-Record)

记录结束符标志定义了传输中一个记录的结束。

错误恢复(error recovery)

允许用户从一些诸如主机系统或传输过程失败中恢复。FTP中,错误恢复可以是在给定点上重新开始文件传输。

FTP命令(FTP commands)

用户FTP到服务器FTP的控制信息流由一些命令集合组成。

文件(file)

计算机数据的有序集合(包括程序),有任意的大小,由路径名唯一指定。

模式(mode)

通过数据连接传输数据的方式。模式定义了包括EOR和EOF的数据传输格式。FTP的传输模式在传输模式一章中介绍。

NVT(Network Virtual Terminal)

网络虚拟终端在Telnet协议中定义。

NVFS(Network Virtual File System)

网络虚拟文件系统用标准命令和路径规定定义了标准网络文件系统概念。

页(page)

一个文件可能被分为彼此独立部分的结构集合,称为页。FTP支持将不连续的文件作为独立的页来传输。

路径名(pathname)

路径名定义为用户为了指定文件系统中某个特定文件而输入的字符串。路径一般包括驱动器名,目录名,以及文件名。FTP尚未规定标准的路径名规则。用户必须传输方文件系统的命名规则。

PI(protocol interpreter)

协议解析器。用户和服务器用其来解析协议,它们的具体实现分别称为用户PI和服务器PI。

记录(record)

顺序文件可能被由很多连续的部分组成称为记录。FTP支持记录结构,但文件不是必须具备记录结构。

响应(reply)

响应是由服务器发给用户的对FTP命令的回应(肯定或否定)。一般的响应格式是完成码(包括错误码)跟上一个文本字符串。完成码用于程序,文本用于用户。

服务器DTP(server-DTP)

数据传输过程,在通常的“主动”状态下是用“监听”的数据端口建立数据连接。它建立传输和存储参数,并在服务器端PI的命令下传输数据。服务器端DTP也可以用于“被动”模式,而不是主动在数据端口建立连接。

服务器FTP过程(server-FTP process)

与用户FTP过程或另一个服务器FTP过程配合实现文件传输功能。由协议解析器(PI)和数据传输过程(DTP)组成。

服务器PI(server-PI)

服务器PI在L端口“监听”用户协议解析器的连接请求并建立控制连接。它从用户PI接收标准的FTP命令,发送响应,并管理服务器DTP

类型(type)

用于数据传输和存储的数据表示类型。类型暗示了在数据存储和数据传输之间的时间变化。FTP中定义的表示类型在建立数据连接一章中描述。

用户(user)

希望得到文件传输服务的人或程序。用户可能直接与服务器FTP过程互相作用,但推荐使用用户FTP过程,协议的设计有利于自动化操作。

用户DTP(user-DTP)

数据传输过程在数据端口“监听”服务器FTP过程的连接。如果两个服务器通过它来传输数据,用户DTP就为非活跃的。

用户FTP过程(user-FTP process)

一系列功能集合,包括协议解析器、数据传输过程和用户界面,它们共同与服务器FTP过程配合完成文件传输功能。用户界面允许使用本地语言对用户发送命令响应。

用户PI(user-PI)

用户协议解析器用U端口建立到服务器FTP过程的控制连接,并在文件传输时管理用户DTP。

2.3. FTP模型

了解了上面的定义,下面的模型(图一所示)用来描述一个FTP服务。

                                            -------------
|/---------\|
||   用户  ||    --------
||   界面  |<--->| 用户 |
|\----^----/|    --------
----------                |     |     |
|/------\|  FTP 命令      |/----V----\|
||服务器|<---------------->|   用户  ||
||  PI  ||   FTP 响应     ||    PI   ||
|\--^---/|                |\----^----/|
|   |    |                |     |     |
--------    |/--V---\|      数据      |/----V----\|    --------
| 文件 |<--->|服务器|<---------------->|  用户   |<--->| 文件 |
| 系统 |    || DTP  ||      连接      ||   DTP   ||    | 系统 |
--------    |\------/|                |\---------/|    --------
----------                -------------
服务器-FTP                   用户-FTP
注意:1. 数据连接可能是任一方向。
2. 数据连接不必须一直存在。
图 1 FTP使用模型

图1中描述的模型中,控制连接由用户PI发起。控制连接遵守Telnet协议。首先用户由用户PI产生标准FTP命令通过控制连接传输到服务器过程。(用户可能建立一个到服务器FTP的直接连接,例如从TAC终端不经过用户FTP过程直接产生标准的FTP命令。)标准响应由服务器端PI通过数据连接发送到用户PI作为命令的回应。

FTP命令指定数据连接参数(端口,传输模式,表示类型,以及结构)和文件系统操作种类(store,retrieve,append,delete等)。用户DTP则应在指定的数据端口“监听”,服务器用相应的参数发起数据连接并传送数据。而数据端口主机不一定必须与发送FTP命令的主机一至,但用户或用户FTP过程要保证指定的端口处在“监听”下。
另需指出的是数据连接可能同时用于发送和接收数据。

另一种情形是用户可能要在两台远程主机间传送文件。用户分别与两台服务器建立控制连接,并安排两服务器间的数据连接。这种情况下,控制信息传送到用户PI,但数据在两服务器间传送。以下是服务器-服务器交互模型:

                      控制     ------------   控制
---------->| User-FTP |<-----------
|          | User-PI  |           |
|          |   "C"    |           |
V          ------------           V
--------------                        --------------
| 服务器-FTP |        数据连接        | 服务器-FTP |
|    "A"     |<---------------------->|    "B"     |
-------------- 端口 (A)      端口 (B) --------------
图 2

协议规定在数据传输过程中控制连接必须一直打开。当FTP服务使用完以后,用户应该要求服务器关闭控制连接。当没有发送关闭命令但控制连接事实已经关闭的情况下,服务器可能终止数据传送。

FTP与Telnet的关系:

FTP在数据连接中使用Telnet协议。这可能以两种方法实现:第一,用户PI或服务器PI在它们的实现过程中应用Telnet协议。第二,用户PI或服务器PI可能用系统中已经存在的Telnet模块。

第二种方法使用更简单,达到了代码共享,模块化编程的目的。比第一种方法更独立和高效。实际上FTP对Telnet协议的依赖非常小,因此第一种方法也不一定会引入大量的代码。

3. 数据传输功能

文件只通过数据连接传输。控制连接用来发送操作命令以及相应的命令响应(参见FTP响应一章)。一些命令与主机间数据传输有关。这些数据传输命令包括:指定数据位怎样被传输的模式(MODE)命令,以及用来定义数据表示方式的结构(STRU)类型(TYPE)命令。传送和表示基本上是独立的,但“流式”传输模式依赖于文件结构参数,而当使用“压缩”传送模式时,填充字节的表示依赖于表示类型。

3.1. 数据表示和存储

数据由发送端主机存储设备传输到接收端主机的存储设备上。由于两个系统的数据存储形式不同,经常需要将数据转换形式。例如,NVT-ASCII在不同的系统中有不同的存储表示。DEC TOP-20一般用5个7位的ASCII字符存储NVT-ASCII,左对齐成36位的字。IBM Mainframe用8位EBCDIC编码存储NVT-ASCII。Multics将NVT-ASCII存储成4个9位字符组成的字。当在不同的系统中传输字符时理应将其转换成标准的NVT-ASCII表示。发送和接收端则应相应地在标准表示法和内部表示法间转换。

当传输二进制数据时表示法的另一个问题就是不同主机有不同的字长度。并不总是明确发送端怎样发送数据以及接收端怎样接收数据。例如,当从一个32位字长的系统传输32位字节到一个36位字长的系统时,应该(为了高效和实用)在后一个系统中将32位字节在36位字中右对齐。无论哪种情况,用户都应该可以选择数据表示形式和传输功能。应该注意FTP提供了非常有限的数据表示形式。传输这些表示形式之外的数据时用户应该自行转换。

3.1.1. 数据类型

当用户指定一个表示类型时,FTP为我们管理数据表示。这种类型可能隐含的(ASCII/EBCDIC)或显式的(本地字节)为解释器作为“逻辑字节长度”定义字节长度。注意这并不是用于在数据连接传输时的字节长度,被称为“传输字节长度”,两种长度不能互相冲突。例如,NVT-ASCII具有8位的逻辑字节长度。如果类型是本地字节,那么TYPE命令必须有第二个参数指定逻辑字节长度。传输字节长度始终是8位。

3.1.1.1. ASCII类型

这是缺省类型,必须被所有FTP实现支持。主要用来传输文本文件,除非主机双方认为EBCDIC类型更方便。

发送方将内部字符表示转换为标准的8位NVT-ASCII表示(参见Telnet协议)。接收方将标准格式数据转换为它自己的内部格式。

NVT规定,<CRLF>序列用来表示文本一行的结尾。(参见本章末文件结构中有关数据表示和存储的讨论)

用标准NVT-ASCII表示意味着数据必须解释为8位字节。

用于ASCII和EBCDIC格式参数将在下面讨论。

3.1.1.2. EBCDIC类型

这种类型用来在使用EBCDIC编码的主机间高效地传输。

传输时,数制被表示为8位的EBCDIC字符。EBCDIC与ASCII类型的区别仅仅是字符编码的不同。

行尾(End-of-Line)很少用在EBCDIC类型中表示结构,必要时应该用<NL>。

3.1.1.3. 图像类型

数据以8位连续字节流传输。接收端必须将数据存储为连续位。存储系统结构可能要将文件(或对于记录结构文件来说,每个记录)填充到合适的边界(字节、字或块)。填充的字节必须为0,并追加到文件末尾(或每个记录末尾)。必须有方法来指出填充字节,当取得文件时,以将填充字节剔除。填充转换方法应当公开,使用户可以方便的处理文件。

图像类型目的是为了高效地存储和检索文件,以及传输二进制文件。建议所有的FTP实现都应该支持这个类型。

3.1.1.4. 本地类型

数据以参数Byte size指定的逻辑字节长度传输。字节长度值必须是十进制整数,并且没有缺省值。逻辑字节长度不一定要和传输字节长度一样。如果字节长度不同,那么逻辑字节将忽略传输字节边界连续打包,并在最后做必要的填充。

当数据到达接收端主机时,将以独立的方式被转换为特定主机的逻辑字节长度。这个转换过程必须是可逆的(就是说,用同样的参数会产生同样的文件)并且应该被FTP实现者公开。

例如,用户发送36位浮点数到一个32位字长的主机时可以以本地字节长度36来发送。接收端主机将存储逻辑字节,以方便操作。在这个例子中,将36位的逻辑字节放入64位双字中将满足需要。

另一个例子中,一对用36位字长的主机间用“TYPE L 36”传送数据。数据将用8位传输字节包装,因此9个传输字节代表两个主机字。

3.1.1.5. 格式控制

ASCII和EBCDIC类型也支持第二个可选的参数。这代表了一种纵向的文件格式控制。以下数据表示类型在FTP中定义:

一个被传输到主机的字符文件可能具有以下三个目的之一:为了打印;为了存储用来以后重现;为了处理。如果文件传送是为了打印,接收主机必须知道垂直控制是如何被表示的。第二种目的中,可能需要在主机中存储为一个文件,日后将其重现为一样的格式。最后一种目的中,必须保证将文件从一主机传输到另一主机并在第二台主机上处理文件并不带来麻烦。单独的ASCII或EBCDIC格式不能满足所有这些条件。因此,这些类型具有第二个参数指定以下三种格式之一:

3.1.1.5.1. 非打印(NON PRINT)

如果第二个格式参数被省略,这将是缺省格式。非打印格式必须被所有FTP实现支持。

文件不要包含垂直格式信息。如果文件被送到打印过程,那么打印过程将假定使用标准的间距和页边距值。

一般来说,这个格式被用作处理或存储。

3.1.1.5.2. TELNET格式控制

文件包含ASCII/EBCDIC垂直格式控制(也就是,<CR>,<LF>,<NL>,<VT>,<FF>),打印过程将适当的解释这些控制符。<CRLF>表示行末。

3.1.1.5.3. CARRIAGE CONTROL(ASA)

文件包含ASA(FORTRAN)垂直格式控制字符。(参见RFC 740附录C;《Communications of the ACM》7卷,10章,606页,1964十月)ASA标准规定,每行或记录的头一个字符不用来打印。这个字符用来决定本行或记录打印机的垂直走纸量。

ASA标准指定如下控制字符:

字符 垂直间距

空 走纸一行
0 走纸两行
1 走纸到下页顶
+ 不移动,也就是覆盖打印

打印机过程必须有方法区分结构体的结束。如果文件具有记录结构(后面将介绍)就没有问题;记录会在传输与存储中显式的标记。如果文件没有记录结构,将以<CRLF>行尾标记作为打印时行的分隔,但这些格式符会被ASA控制符覆盖。

3.1.2. 数据结构

由于表示类型的不同,FTP允许文件具有指定的结构。FTP中定义了以下三种文件结构:

文件结构,内部没有结构,文件被视为连续的数据字节流

记录结构,文件由连续的记录组成

页结构,文件由独立的具有索引的页组成

如果没有使用结构命令(STRU),文件结构是缺省值。但在所有的FTP实现中,文件和记录结构必须用在“文本”文件(就是说,带有TYPE ASCII或EBCDIC的文件)里。文件结构将影响文件的传输模式(参见传输模式一章)以及文件的表示和存储。

文件的“自然”结构取决于存储文件的的主机。源代码文件在IBM Mainframe上以固定长度的记录存储,而在DEC TOPS-20以用类似于<CRLF>的行分隔符行分开的字符流存储。如果文件在这两种站点间传输,就必须要让其中的一个站点知道另一个站点的文件结构。

在基于文件结构的主机和基于记录结构的主机间传输文件时可能会出现问题。如果文件是基于记录结构传输到基于文件结构的主机,应该在内部将记录结构转换为文件结构。显然这种转换应该能够可逆,以便可以再转回记录结构。

在将文件从基于文件结构的主机传输到基于记录结构的主机时,存在如何将文件切分成记录的问题。如果必须切分一个文本文件,那么FTP实现应该使用行末符,ASCII中是<CRLF>,EBCDIC中是<NL>,作为分隔。

3.1.2.1. 文件结构

如果没有使用结构命令(STRU),文件结构就默认使用。

在文件结构中没有内部结构,文件被当作连续的字节流。

3.1.2.2. 记录结构

在所有的FTP实现中,必须支持“文本”文件(就是,使用TYPE ASCII或EBCDIC)的记录结构。

在记录结构文件中,文件由连续的记录组成。

3.1.2.3. 页结构

为了传输不连续的文件,FTP定义了页结构。一般说的“随机存取文件”或“多穴文件”属于这个类型。对于这些文件,一般有另外的对应整个文件信息(例如,文件描述符),或者对应文件部分信息(例如,页存取控制),或两者都有。在FTP中,文件的部分称为页。

为了提供不同页大小以及相关信息,每页传输时将额外包括一个页头。页头有如下定义的域:

头长度

包括这个字节在内的头逻辑长度。最小头长度是4。

页索引

文件区域的逻辑页号。并不是传输的序列号,而是标识本页的索引号。

数据长度

页中数据的逻辑字节数。最小数据长度为0。

页类型

标示了页的类型。定义了如下的类型:

0 = 最末页

用来标示页结构传输结束。页头长度必须为4,数据长度必须为0。

1 = 单独页

对于没有页相关控制信息的单独页来说这是普通的类型。页头长度必须为4。

2 = 描述页

这个类型用来传输整个文件的描述信息。

3 = 存取控制页

此类型包括一个额外的指定页存取信息的头域。头长度必须为5。

可选域

其他的头域可能用来提供每页的控制信息,例如,每页的存取控制。

所有域都是一个逻辑字节。逻辑字节长度由TYPE命令指定。参见附录I中的更详细信息以及一个页结构的例子。

关于参数需要注意的一点:必须用相同参数续传相同的文件。相应的,FTP实现在用相同参数续传文件时要保证传输的文件与原始文件相同。

3.2. 建立数据连接

传输数据的过程包括在指定端口建立数据连接选择传输参数。用户和服务器DTP都有缺省的端口号。用户过程缺省的数据端口与控制连接端口相同(也就是,端口U)。服务器过程的默认端口与控制连接的端口相邻(也就是L-1)。

传输字节长度是8位字节长。这个字节长度只与实际传输数据有关;而与主机文件系统的数据表示无关。

被动数据传输过程(可能是用户DTP或另一服务器DTP)应该在发送FTP请求命令之前“监听”在数据端口。FTP请求命令决定了数据传输方向。服务器在接到传输请求后将建立到指定端口的连接。当连接建立后,数据将在两端DTP间传输,同时服务器PI向用户PI发送确认回复。

每个FTP实现必须支持使用缺省的数据端口,只有用户PI可以使用变化的非缺省端口。

用户可能会用PORT命令指定一个其他的数据端口。用户可能想将文件下载到TAC行式打印机或者从第三方主机下载。后种情况下,用户PI同时建立到两服务器PI的控制连接。一个服务器(用FTP命令)等待连接,另一个服务器建立连接。用户PI给一个服务器PI发送PORT命令指示另一服务器的数据端口。最后,向两端发送合适的传输命令。用户控制端与服务器间传送的详细命令以及回复顺序定义在FTP响应一章。

一般来说,维护数据连接是服务器的责任,包括连接的建立与关闭。例外的情况是当用户DTP在传输模式下发送数据时需要关闭连接表示文件结束。服务器必须在以下条件下关闭数据连接:

1. 服务器在传输模式下完成数据传输,需要关闭连接,表示文件结束。

2. 服务器收到用户发来的ABORT命令。

3. 用户用命令改变了端口设定。

4. 控制连接合法地或由于其他原因关闭。

5. 发生了不可挽回的错误。

其他情况下是否关闭连接是服务器可选择的,这种情况下服务器必须用250或226号响应通知用户过程。

3.3. 数据连接管理

缺省数据连接端口:所有FTP实现必须支持使用缺省数据连接端口,只有用户PI可能使用非缺省端口。

协商非缺省端口:用户PI可能使用PORT命令指定非缺省用户端口。用户PI可能要求服务器端用PASV命令指定非缺省端口。连接用一对地址指定,上面两种动作之一都会得到一个不同的连接,仍然允许同时使用两个命令在两端指定新的端口。

数据连接复用:当使用流模式传输数据时,在文件传输结束后必须关闭连接。如果有多个文件传输时可能带来的问题是TCP为了保证传输可靠要保持连接记录一段时间。因此不能马上重新连接。

有两种解决方案。第一种是协商一个非缺省端口。第二种是使用另一种传输模式。

对于传输模式,流式传输模式有天生的不可靠性,不能确定连接是否过早的关闭。其他的传输模式(块,压缩)不用关闭连接来指示文件结束。他们使用FTP编码来确定文件结束。因此使用这些模式可以在多文件传输时保持使用同一个数据连接。

3.4. 传输模式

传输数据时下一个要考虑的问题是选择合适的传输模式。有三种传输模式:一个对数据格式化,并允许重新开始过程;一个压缩数据提供高效传输;一个不加修改的传输数据。最后一种模式与结构属性配合决定处理过程。在压缩模式中,表示类型决定填充字节。

所有的数据传输必须显式的或隐式的用关闭数据连接来指示文件结束(EOF)。对记录结构的文件,所有的记录结束标志(EOR)都是显式的,包括最后一个记录。对于使用文件结构的传输,使用“末页”的页类型。

注意:在本章其他部分,除非明确指出,字节都表示“传输字节”。

为了使传输标准化,发送主机将根据传输模式和文件结构把行尾或记录尾转换成传输时的格式,接收主机将进行相反的转换。IBM Mainframe的记录记数域可能不会被另一台主机识别,因此在流模式中记录尾信息可能以两字节的控制码来传输,在块模式或压缩模式中作为标志位。没有记录结构的ASCII或EBCDIC文件中的行尾应该分别表示为<CRLF>或<NL>。这些转换工作在某些系统中可能意味着额外的工作,同样的系统在传输非记录结构文本文件时可能希望用流模式直接传输二进制流。

FTP定义了如下传输模式:

3.4.1. 流模式

数据以字节流传输。对表示类型没有限制;可以使用记录结构。

在记录结构文件中,EOR和EOF将分别用两个字节的控制码表示。第一个字节都是同样的escape字符。第二个字节中,EOR将低位置一,其他位置零;EOF则是将第二低位置一;也就是这个字节对于EOR来说是1,对于EOF来说是2。EOR和EOF可能在传输结束时通过使最低两置一来同时指定(就是值3)。如果想发送escape字符,要在第二个字节再重复一次。

如果结构是文件结构,则使用关闭主机连接来指示EOF,传输的所有数据字节就是原始字节。

3.4.2. 块模式

文件以连续的带有数据头的数据块来传输。数据头包括一个计数域和描述码。计数域指示了数据块整个长度,由此可以算出下一数据块的开始位置(没有填充位)。描述码定义了:文件最后一块(EOF),记录最后块(EOR),重开始标记(参见错误恢复和重开始章)或者怀疑数据(也就是被怀疑在传输中可能不可靠的数据)。最后的描述符不是FTP错误控制的一部分。它用来在站点间交换指定类型的数据(比如地震或天气数据)而且简略本地错误(比如磁带读错误)。记录结构可以在这种模式下使用,而且可以用任何表示类型。

头包括3个字节。在这24位的头信息中,低16位表示字节记数,高8位表示描述符。

块头

            +----------------+----------------+----------------+
|    描述符      |      字节记数                   |
|         8 bits |                      16 bits    |
+----------------+----------------+----------------+

描述符字节由各个标志位组成。指定了4个描述码,每一个描述码为描述符的十进制值。

描述码 意义

128 数据块结束是EOR
64 数据块结束是EOF
32 怀疑数据块有错
16 数据块是重开始标志

通过对不同的标志位置一,每个数据块可以使用不同的描述符组合。

重开始标志是在数据流中的8位整数,表示在控制连接中使用的可打印字符(比如,缺省的NVT-ASCII)。在重开始标志中不能使用<SP>(空格)

例如,要传输6个字符标记,应该按如下发送:

            +--------+--------+--------+
|描述符  |  字节记数       |
|    = 16|             = 6 |
+--------+--------+--------+
+--------+--------+--------+
| 标记   | 标记   | 标记   |
| 8 位   | 8 位   | 8 位   |
+--------+--------+--------+
+--------+--------+--------+
| 标记   | 标记   | 标记   |
| 8 位   | 8 位   | 8 位   |
+--------+--------+--------+
3.4.3. 压缩模式

此模式下,有三种信息要发送:常规数据,以字节串发送;压缩数据,包括复本或填充;控制信息,以两字节的转义字符传送。如果发送n>0(最多127)个字节的常规数据,这n个字节前要有一头字节,这字节的最高位为0,其余7位代表数n.

字节串:

 

             1       7                8                     8
+-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+     +-+-+-+-+-+-+-+-+
|0|       n     | |    d(1)       | ... |      d(n)     |
+-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+     +-+-+-+-+-+-+-+-+
^             ^
|---n个字节---|
n字节的字节串d(1),...,d(n)
数n必须为正。

为了压缩n字节的复本,下面两个字节要发送:

复制字节:
              2       6               8
+-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+
|1 0|     n     | |       d       |
+-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+
一串长度n的填充字节可以压缩成一个字节,填充字节与表示类型有关。如果类型是ASCII或EBCDIC,填充字节是<SP>(空格,ASCII码是32,EBCDIC码是64)。如果类型是图像或本地字节,填充字节为0。

填充字节:
              2       6
+-+-+-+-+-+-+-+-+
|1 1|     n     |
+-+-+-+-+-+-+-+-+

转义序列由两个字节组成,第一个字节为转义字节(全0)第二个字节包括块模式中定义的描述码。这里的描述码与块模式中的描述码意义相同,并对后面的字节串有效。

压缩模式适用于在传输大数据时以较小的CPU代价换来一定的网络带宽。最适合用在减少RJE主机产生的打印文件大小。

3.5. 错误恢复和重开始

这里并没有提供是否在传输中存在丢失字节或者数据包混乱的方法。这个级别的错误由TCP控制。但必须提供一个重开始的方法来应对系统错误(包括主机、FTP过程、或网络的失败)。

重开始过程只定义在块模式和压缩模式下。它要求数据发送者在数据流中插入一个特殊的标记。这个标记信息只对发送方有意义,但必须由缺省或协商的控制连接语言(ASCII或EBCDIC)中的可打印字符组成。标记要表示一个位记数,一个记录记数,或者可以表示数据检查点的信息。数据的接收方,如果要实现重开始过程,将用此标记指定数据位置,并将此信息返回给用户。

系统失败的情况下,用户可以用标记的位置信息重新开始传送过程。下面的例子演示了重开始过程的使用。

数据的发送方在数据流中的适当的位置插入了一个标记块。接收主机在它的文件系统中标记对应的数据点,并直接或用110号控制连接响应(取决于发送方是谁)向用户传达最后的标记信息。在系统失败时,用户或控制过程使用重开始命令并以标记信息为其参数来重开始传输。重开始命令通过控制连接传输,后面跟上系统失败时正在执行的命令(比如RETR,STOR或LIST)。

4. 文件传送功能

从用户PI到服务器PI的传输通道是通过一个从用户到标准服务器端口的TCP连接建立的。用户PI负责发送FTP命令并解析接收到的响应;服务器PI解析命令,发送响应以及控制DTP建立数据连接并传送数据。如果数据传输(被动传输过程)的另一端是用户DTP,则用户DTP由用户FTP主机的内部协议控制;如果另一端是另一个服务器DTP,则这个服务器DTP由用户PI通过发送命令来控制。FTP的响应将在下一部分讨论。这部分描述的一些命令对理解可能出现的响应会有帮助。

4.1. FTP命令

4.1.1. 访问控制命令

下面的命令表示访问控制标识符(括号中表示命令代码)

用户名 (USER)

这个命令的参数域是一个用来标识用户的Telnet字符串。用户识别对于服务器控制文件系统存取权限是必需的。这个命令通常是控制连接建立后从用户端发送的第一条命令(一些服务器可能需要保证这一点)。一些服务器可能还需要附加的识别信息如密码或帐号命令。为了改变控制权限和/或帐户信息,服务器可能在任何时候都允许接受一个新的USER命令,来更换存取权限或帐户信息。产生的效果是刷新早先登录的用户名、密码和帐户信息,并重新开始一个登录过程。所有的传输参数不发生变化,并且所有正在传输中的文件传输过程均在原来的访问控制权限下完成。

密码 (PASS)

这个命令的参数域是一个用来指定用户密码的Telnet字符串。这个命令必须紧跟在用户名命令之后,在某些站点上,它用来完成用户访问权限识别。因为密码信息非常敏感,一般应该使用掩码代替或者禁止回显。显然服务器没有安全的办法做到这一点,所以隐藏敏感的密码信息就成了用户FTP进程的责任。

帐户 (ACCT)

这个命令的参数域是一个用来识别用户帐户的Telnet字符串。这个命令不需要和USER命令相关,某些站点可能需要一个帐户用来登录,另一些站点仅用于特殊访问权限,比如存储文件。后一种情况下这个命令可能在任何时候收到。

有一些响应代码用来自动地区分这些情况:当登录过程必须要求帐户信息的时候,PASS命令成功的响应代码是332。相应,如果登录过程不要求帐户信息时,PASS命令成功的响应代码是230;如果帐户信息需要在随后的对话命令中给出,服务器应该根据是保留(等侍收到ACCT命令)还是放弃命令来相应的返回332或532。

改变工作目录 (CWD)

这个命令允许用户在不改变登录用户和帐户信息的情况下改变工作目录或数据集。传输参数保持不变。这个命令的参数是一个路径名,用来指定相应的目录或者其他系统上的文件组名。

返回上层目录 (CDUP)

这个命令是CWD命令的特例,因为在不同的操作系统下表达父目录可能有不同的语法,所以可以用这个命令简化目录树的传输实现。它的响应代码应该和CWD的响应代码相同。更多信息参看附录II。

结构装备 (SMNT)

这个命令允许用户在不改变用户和帐户信息的情况下装备一个不同的文件系统数据结构。传置传输参数不会改变。它的参数是一个用来标识目录或者其他系统中依赖文件组的路径名。

重新初始化 (REIN)

此命令除允许当前正在传输过程完成外,终止一个用户,刷新所有的I/O和帐户信息。所有参数重设为默认值,并保持控制连接。此时等同于控制连接刚刚建立的状态。这条命令之后可能需要USER命令。

注销 (QUIT)

此命令终止一个用户,并且当没有文件正在传输的话,服务器将关闭控制连接。如果当前有文件正在传输,连接会保持并等待回应,之后服务器将关闭连接。如果用户进程想以不同的用户名传输文件,而不想关闭然后再重建立连接的情况下,应该使用REIN命令而不是QUIT。

控制连接的意外关闭将会导致服务器产生等同于放弃(ABOR)和注销(QUIT)动作。

4.1.2. 传输参数命令

所有的数据传输参数都有默认值,只有在默认值需要改变的时候才需要用命令去指定传送数据传输参数。默认值是最后一次指定的值,或者如果未被指定,则是标准默认值。这意味着服务器必须“记住”可用的默认值。这些命令可以在FTP服务请求前以任何顺序执行。下面这些命令用来指定数据传输参数:

数据端口(PORT)

这个参数是用来指定数据连接时的主机数据端口。对于用户和服务器都有默认的数据端口值,并且一般情况下这条命令以及它的响应都不需要。如果使用了这条命令,那它的参数是一个32位的因特网主机地址和一个16位TCP端口号。地址信息被分解为每8位一个段,每个段都作为十进制数(用字符串表示)传送。段之间用逗号分隔,一个PORT命令像下面这样:

PORT h1,h2,h3,h4,p1,p2

h1是因特网主机地址的高8位。

被动 (PASV)

此命令请求服务器DTP在一个数据端口(不是它的默认端口)上“监听”并等待连接而不是在收到传输命令后主动发起连接。这个命令的响应包括服务器监听的地址和端口号。

表示类型(TYPE)

这个命令的参数指定在数据表示和存储部分介绍的表示类型。某些类型需要第二个参数。第一个参数用单个Telnet字符表示,对于ASCII和EBCDIC的第二个格式化参数也是如此;本地字节的第二个参数是一个表示字节长度的十进制整数。参数之间用<SP>(空格,ASCII码的32)分开。

下面的编码用来表示类型:

\    /
A - ASCII |    | N - 非打印
|-><-| T - Telnet格式
E - EBCDIC|    | C - Carriage Control (ASA)
/    \
I - 图像
L <字节长度> - 本地字节长度

默认的表示类型是ASCII非打印。如果格式化参数首先被更改,然后单独更改第一个参数,格式化参数会变回默认的非打印。

文件结构(STRU)

这个命令的参数是单个Telnet字符,用来指定在数据表示和存储部分描述的文件结构。

下面编码用来表示文件结构:

F - 文件 (没有记录的结构)
R - 记录结构
P - 页结构

默认的结构是文件。

传输模式(MODE)

这个命令的参数是单个Telnet字符,用来指定在传输模式部分描述的数据传送传输模式。

下面的编码用来表示传送模式:

S - 流
B - 块
C - 压缩

默认的传送模式是流。

4.1.3. FTP服务命令

FTP服务命令定义了用户请求传送文件或者文件系统的功能。FTP服务命令的参数一般是一个路径。路径的语法必须符合服务器站点的惯例(尽量用默认标准)和控制连接的语言习惯。建议的默认参数是使用最后一个设备,目录或文件名,或者本地用户的默认标准。除"rename from"命令后面必须紧跟"rename to"命令以及restart命令必须紧跟随中断服务命令(例如STOR或RETR)之外,其他命令可以使用任意的顺序。服务器应当总是使用数据连接来发送服务命令响应,只有少数特定的信息响应除外。下面为FTP服务请求命令:

获得 (RETR)

这个命令引起服务器DTP传送一个由路径指定的文件拷贝到数据连接另一端的服务器或用户DTP。服务器文件的状态和内容应该不受影响。

保存 (STOR)

这个命令引起服务器DTP接受经过数据连接传送的数据并将这些数据存储为服务器端的一个文件。如果在路径参数里指定的文件在服务器端已经存在,那么这个文件会被传送过来的数据覆盖。如果指定的文件不存在则会在服务器端新建一个文件。

唯一保存 (STOU)

这个命令类似于STOR命令,但是它会在在当前目录下创建一个名字唯一的文件。在250号标准响应里必须包含创建出的文件名。

追加 (包括创建) (APPE)

这个命令引起服务DTP接受从数据连接传送过来的数据并存储在服务器端的一个文件里。如果指定的文件在服务器端已经存在,则这个数据会附加到文件的后面;否则服务器端会创建这个文件。

分配 (ALLO)

一些服务器可能要求用这个命令来保留足够的空间来容纳新文件。其参数是一个十进制整数,用来指定保留给文件存储用的字节数(用逻辑字节长度)。对于用记录或者而结构传送的文件而言,还需要有最大结构或页的大小(使用逻辑字节),这个值在这个命令的第二个参数域用十进制整数指定。第二个参数是可选的,但当它存在的时候应该用三个Telnet字符<SP>R<SP>和第一个参数分开。这个命令之后应该是STOR或者APPE命令。在那些不需要预先知道文件最大值的服务器上,这个命令应该被作为NOOP(无操作)对待,在那些只对记录或页最大值感兴趣的服务器上应该忽略第一个参数。

重新开始 (REST)

这个命令的参数域指定了需要重新开始传输的文件的位置标记。这个命令不会引起文件的传输,只是忽略文件中指定标记点前的数据。

重命名开始 (RNFR)

这个命令指定了需要重新命名的文件的原始路径名。后面必须马上接着“重命名为”命令,来指定新的文件路径

重命名为 (RNTO)

这个命令为在“重命名开始”命令中指定的文件指定新的路径。这两个命令一起为文件重新命名。

放弃(ABOR)

该命令告诉服务器放弃先前的FTP服务命令和相关的传输的数据。放弃命令也许需要引起服务器的“特别注意”(参见FTP命令部分),使服务器强制识别。当前一个命令(包括数据传输)完成时,将不会产生动作。服务器不会关闭控制连接,但是数据连接必须关闭。

服务器接收这个命令时可能处在两种状态:(1)FTP服务命令已经完成,或者(2)FTP服务命令还在执行中。

第一种情况,服务器关闭数据连接(如果数据连接是打开的)回应226代码,表示放弃命令已经成功处理。

第二种情况,服务器放弃正在进行的FTP服务,关闭数据连接,返回426响应代码,表示请求服务请求异常终止。然后服务器发送226响应代码,表示放弃命令成功处理。

删除 (DELE)

这个命令在服务器端删除指定的文件。如果需要额外的保护(比如讯问“你丫的真的想删除么?”),应该由用户FTP进程提供。

删除目录(RMD)

这个命令移除指定路径下的目录(如果是绝对路径),或者是当前工作目录的子目录(如果是相对路径)。参看附录II

新建目录(MKD)

该命令在指定的路径下新建一个目录(如果是绝对路径),或者在当前工作目录下建子目录(如果路径是相对的)。参看附录II

打印工作目录(PWD)

该命令返回一个当前的工作目录名。参看附录II

列表(LIST)

该命令从服务器端发送一个列表到被动的DTP。如果路径名指定了目录或者别的文件组,服务器应该传送指定目录下的文件列表。如果路径名指定了文件,服务器应当传送这个文件的信息。没有参数,意味着用户的当前工作目录或者缺省目录。数据通过数据连接以ASCII或EBCDIC类型传输。(用户必须确定类型是ASCII或者EBCDIC)。因为不同系统间的文件信息差别很大,这个信息可能不易被程序自动使用,但可能对于用户来说是有用处的。

名字列表 (NLST)

该命令从服务器端传送目录列表到用户端。路径名应该指定一个目录名或者其他系统文件组描述符;无参数意味着当前目录。服务器只返回文件的名字组成的字节流,不包括其他的信息。数据将通过数据连接以ASCII或者EBCDIC类型传输,每个路径名字符串由<CRLF>或<NL>分割。(用户仍必须保证类型使用正确)。这个命令的响应信息将可能被用于程序对文件的自动处理。例如,多线程下载的实现。

站点参数(SITE)

服务器使用这个命令,提供本系统可能对文件传输有帮助的特殊服务。在协议中它的用处不是很普遍。服务的种类和语法规约可以在HELP SITE命令的响应中确定。

系统(SYST)

该命令来得到服务器端操作系统的类型。响应的第一个词应该是Assigned Numbers文档[4]中表示操作系统名字的一个词。

状态 (STAT)

该命令应该通过控制连接以响应码的形式返回状态信息。此命令可能在文件传输过程中发出(与Telnet IP和同步信号一起,参见FTP命令道听部分),此时服务器将返回正在传输的状态。或者这个命令也可能在两个文件传输过程之间发出,这种情况下,命令可能将有一个参数域。如果参数指定了一个路径名,则命令将与列表命令类似,只是数据由控制连接传输。如果给出了部分路径,服务器可能响应指定的路径下的文件名列表或者相关属性。如果没有提供参数,将返回服务器FTP进程一般的状态信息,其中应该包括所有传传输参数的当前值和连接的状态。

帮助(HELP)

该命令使服务器通过控制连接传送关于具体实现状态的帮助信息给用户。该命令可以有参数(例如,命令的名字)返回更加具体的信息。回应代码是211或者214。建议在输入USER命令前允许HELP。服务器可以用这个响应指定站点特定的参数,例如,在HELP SITE响应中指定。

空操作(NOOP)

该命令不应影响任何参数或者之前发送的命令。该命令不指定任何动作,只是要求服务器返回OK响应。

文件传输协议在控制连接上的所有通信都遵守Telnet协议。因为Telnet传输使用的语言可能是一个可协商的选项,下两部分提到的所有参考信息将使用“Telnet语言”和相应的“Telnet行尾符”。当然可以将这些转换成NVT-ASCII和<CRLF>。没有其它的Telnet协议规范被引用。

FTP命令是以"Telnet行末符"结尾的"Telnet字符串"。命令如果带有参数,那么命令代码本身是以<SP>(空格)结尾的文字字符,或者当没有参数时以Telnet行末符结尾。命令代码和命令的语义在本章描述;详细的命令语法在命令一章描述,响应序列在命令和响应一章中描述,命令用法的情景演示说明在典型FTP情景一章中给出。

FTP命令分为访问控制命令、数据传输参数命令、FTP服务请求命令三种。某些命令(例如,ABOR,STAT,QUIT)可以在数据传输过程中,通过控制连接发送。一些服务器可能不能同时监控控制连接和数据连接,此时就要发出一些特殊的动作来引起服务器的注意。下面的指令格式是试验性建议:

1.用户系统在Telnet流中插入Telnet"中断过程"(Interrupt Process-IP)信号

2.用户系统发出Telnet “同步”(Synch)信号

3.用户系统在Telnet流中插入命令(例如,ABOR)

4.服务器PI,在接收到"IP"后,扫描telnet流,寻找FTP命令

(对于其它服务器,这些操作可能没有必要,但并不会引起意外的后果。)

4.2. FTP响应

文件传输协议命令的响应,用来确保在文件传输过程中的请求和正在执行的动作保持一致,保证用户程序总是可以得到服务器的状态信息。每一个命令必须产生至少一个响应,也可能产生多个响应;多重的响应必须是可以简单区分的。另外,一些命令是有一定顺序的组合。比如USER、PASS和ACCT,或者RNFR和RNTO。此时的响应表示一种中间状态,说明前面的命令是成功的。顺序组合中出现任何错误都会导致需要从头开始整个命令序列。

命令-响应序列的细节,将由下面一组状态图表明确表示。

FTP响应由3位数字组成(以3个数字字符传递)后面跟着一些文本。数字用来自动的判断当前的状态,文本内容提供给人类用户。三位数字应该包含足够的信息,使用户PI不需要检查文本内容,而将其忽略或返回给用户。文本内容可能是与特定服务器相关的,所以每一个响应的文本内容很可能不同。

响应包含的3位数字,后面跟着空格<SP>,然后是一行文本(已指定一行最大的长度),以Telnet行末符结尾。有可能出现文本长度大于一行的情况。在这种情况下,文本全文必须在两端加以标识,使用户进程知道什么时候应该停止读取响应(也就是,停止从控制连接读取输入),去做别的事情。这要求第一行文本需要一种特殊的格式,来标识传来的文本内容有多行,并在文本最后一行指明这是最后。必需要包含适当的响应代码,以指明当前文本的状态。为了满足这些功能,第一行和最后一行的代码应该是一样的。

因此,多行回应的格式是:第一行以正常的响应代码开始,后接连字符“-”(也就是那个减号)后面跟着文本。最后一行需要以相同的代码开始,后面跟空格<SP>分隔的可选文本,然后是Telnet行末符

例如:
123-第一行
第二行
234 以数字开始的一行
123 最后一行

用户进程只需要简单地寻找一行开始时后面跟随(空格)的相同响应代码,并忽略掉中间的文本。如果中间文本的某一行首出现了3位数字,服务器必须在前面填充,以避免混淆。

添加“人工的”第一行和最后一行标志的这种方案,允许使用标准系统例行程序产生响应信息(例如,产生STAT响应)。少数情况下,如果例行程序必须在某一行行首生成3位数字后跟空格,文本的每一行行首应该填充一些空文本,例如空格。

这个方案假定多行的响应不能被嵌套。

3位数字的每一位都有特定的意义。允许用户进程将复杂的响应简化。第一位数字标识了响应是好,坏或者未完成。(参见状态图),简单的用户进程可以通过检查第一位数字,决定它的下一个动作(依计划处理,重试,放弃等等)。用户进程如果希望知道大概是发生了什么错误(比如,文件系统错误,语法错误),可以通过检查第二位数字来完成。第三位数字指示信息顺序是否有误(例如,RNTO前没有RNFR命令)。

响应的第一位数字可能有以下五个值:

1yz,预备状态

请求的动作已经启动;在下一个新命令之前,期望一个回应。(用户进程在接收到完成响应前就发送另一条命令是违返协议的。但服务器FTP进程在处理前面命令的过程中应该将后续收到的命令存入队列。)这种类型的响应用来表明命令已被接受,对于不能同时监视数据和控制连接的用户进程来说,它可能要开始关注数据的连接了。服务器FTP进程最多每个命令发送一个1yz代码。

2yz,完成状态

请求动作被成功的完成。一个新的请求可以开始。

3yz,中间状态

命令被接受,但是请求动作暂时没有被执行,等侍收到进一步的信息。用户应该发送另一个命令来指定这个信息。这个回应用在命令组合中。

4yz,暂时拒绝状态

命令没有被接受,请求动作没有发生,但是这个错误状态是暂时的,动作可以被再次请求。用户应该重新回到命令队列的开始。说明“暂时”的具体意思是很困难的,尤其在两个截然不同的站点(服务器和用户进程)间要达成解释的一致更是不易。每个4yz号响应可能都有一个稍不同的时间值,但总体思想都是鼓励用户进程再一次重试。判断一个响应应该属于4yz号还是5yz号的一个规则是看这个命令是否可以不加修改并在相同的用户、服务器状态下(比如,命令使用同样的拼写使用同样的参数;用户不改变文件访问权限;服务器不产生新的实现。)再次重复。

5yz,永久拒绝状态

命令不被接受,请求动作不会发生。用户进程不能重复同样的请求(包括同样的命令顺序)。一些“永久的”错误状态可以被修正,因此人类用户也许希望控制用户进程在将来的某点上重新开始命令队列。(比如在拼写改变之后,或目录状态改变之后。)

下面为第二位数字的功能:

x0z 语法 - 这种响应指出了语法错误。给出的命令不存在、没有被实现、或多余。

x1z 信息 - 对于请求信息的响应,比如对状态或帮助的请求。

x2z 连接 - 关于控制连接和数据连接的响应。

x3z 身份验证和帐户 - 对登陆过程和帐户处理的响应。

x5z 目前还未使用。

x5z 文件系统 - 请求传输时服务器文件系统的状态或其他文件系统动作状态。

第三位数字为第二位数字指定的状态提供了更详细的意义。下面的响应列表会说明这一点。注意,每一个响应的对应文本只是推荐的,而非强制性的,可依照相应的命令而更改。另一方面,响应代码,必须严格的遵守最后部分的规范,也就是说,服务器实现不应该为与上面所描述的只有微小区别的状态发明新的代码,而应该使用已经定义的代码。

类似TYPE或ALLO这样的成功执行也不会给用户进程新信息的命令将产生200号响应。如果命令因为与本计算机系统无关而不必被服务器FTP进程支持的,例如ALLO在TOPS20站点上,应该回复一个完成状态的响应,来通知用户进程可以继续它的动作请求。202号响应用来处理这种情况,例如,响应文本为“No storage allocation necessary”(无需分配存储)。另外,如果,如果请求了一个并没有被实现的命令,将返回502。504,表明实现了此命令,但是请求的参数并未被实现。

 

4.2.1. 按功能分组的响应代码

200 Command okay. (命令OK)

500 Syntax error, command unrecognized. (语法错误,命令不能被识别)
可能包含因为命令行太长的错误。

501 Syntax error in parameters or arguments. (参数语法错误)

202 Command not implemented, superfluous at this site. (命令没有实现,对本站点冗余)

502 Command not implemented. (命令没有实现)

503 Bad sequence of commands. (命令顺序错误)

504 Command not implemented for that parameter. (没有实现这个命令参数)

110 Restart marker reply. (重新开始标记响应)
对于这种情况,文本应该是明确的,无需进行特殊实现;必须形如:
MARK yyyy = mmmm
yyyy是用户进程数据流标记,mmmm服务器的等效标记(注意,标记间的空格和“=“)

211 System status, or system help reply. (系统状态,或者系统帮助响应。)

212 Directory status. (目录状态)

213 File status. (文件状态)

214 Help message. (帮助信息)
关于如何如使用服务器,或者特殊的非标准的命令的意义。只对人类用户有用。

215 NAME system type. (系统类型名称)
这里的NAME指在Assigned Numbers文档中列出的正式名称。

120 Service ready in nnn minutes. (服务将在nnn分钟后准备完成)

220 Service ready for new user. (接受新用户服务准备完成)

221 Service closing control connection. (服务关闭控制连接)
已注消

421 Service not available, closing control connection. (服务不可用,关闭控制连接)
如果服务器知道它必须关闭,应该以421作为任何命令的响应代码。

125 Data connection already open; transfer starting. (数据连接已打开,传输开始)

225 Data connection open; no transfer in progress. (数据连接打开,没有传输)

425 Can't open data connection. (不能打开数据连接)

226 Closing data connection. (关闭数据连接)
请求文件动作成功(例如,文件传输或者放弃)

426 Connection closed; transfer aborted. (连接关闭,放弃传输)

227 Entering Passive Mode (h1,h2,h3,h4,p1,p2). (进入被动模式)

230 User logged in, proceed. (用户成功登录,继续)

530 Not logged in. (没有登录成功)

331 User name okay, need password. (用户名OK,需要密码)

332 Need account for login. (需要帐户才能登录)

532 Need account for storing files. (需要帐户来存储文件)

150 File status okay; about to open data connection. (文件状态OK,将打开数据连接)

250 Requested file action okay, completed. (请求文件动作OK,完成)

257 "PATHNAME" created. (创建了“PATHNAME”)

350 Requested file action pending further information. (请求文件动作需要进一步的信息)

450 Requested file action not taken. (请求文件动作没有执行)
文件不可使用(例如,文件忙)

550 Requested action not taken. (请求的动作没有执行)
文件不可用(例如,没有找到文件,没有访问权限)

451 Requested action aborted. Local error in processing. (请求动作放弃,处理中发生本地错误)

551 Requested action aborted. Page type unknown. (请求动作放弃,未知的页面类型)

452 Requested action not taken. (请求动作未执行)
系统存储空间不足。

552 Requested file action aborted. (请求文件动作被放弃)
超出存储分配空间(当前的路径或者数据集)

553 Requested action not taken. (请求动作未获得)
文件名不允许。

4.2.2. 按数字顺序排列的响应代码

110 Restart marker reply. (重新开始标记响应)
对于这种情况,文本应该是明确的,无需进行特殊实现;必须形如:
MARK yyyy = mmmm
yyyy是用户进程数据流标记,mmmm服务器的等效标记(注意,标记间的空格和“=“)

120 Service ready in nnn minutes. (服务将在nnn分钟后准备完成)

125 Data connection already open; transfer starting. (数据连接已打开,传输开始)

150 File status okay; about to open data connection. (文件状态OK,将打开数据连接)

200 Command okay. (命令OK)

202 Command not implemented, superfluous at this site. (命令没有实现,对本站点冗余)

211 System status, or system help reply. (系统状态,或者系统帮助响应。)

212 Directory status. (目录状态)

213 File status. (文件状态)

214 Help message. (帮助信息)
关于如何如使用服务器,或者特殊的非标准的命令的意义。只对人类用户有用。

215 NAME system type. (系统类型名称)
这里的NAME指在Assigned Numbers文档中列出的正式名称。

220 Service ready for new user. (接受新用户服务准备完成)

221 Service closing control connection. (服务关闭控制连接)
已注消

225 Data connection open; no transfer in progress. (数据连接打开,没有传输)

226 Closing data connection. (关闭数据连接)
请求文件动作成功(例如,文件传输或者放弃)

227 Entering Passive Mode (h1,h2,h3,h4,p1,p2). (进入被动模式)

230 User logged in, proceed. (用户成功登录,继续)

250 Requested file action okay, completed. (请求文件动作OK,完成)

257 "PATHNAME" created. (创建了“PATHNAME”)

331 User name okay, need password. (用户名OK,需要密码)

332 Need account for login. (需要帐户才能登录)

350 Requested file action pending further information. (请求文件动作需要进一步的信息)

421 Service not available, closing control connection. (服务不可用,关闭控制连接)
如果服务器知道它必须关闭,应该以421作为任何命令的响应代码。

425 Can't open data connection. (不能打开数据连接)

426 Connection closed; transfer aborted. (连接关闭,放弃传输)

450 Requested file action not taken. (请求文件动作没有执行)
文件不可使用(例如,文件忙)

451 Requested action aborted. Local error in processing. (请求动作放弃,处理中发生本地错误)

452 Requested action not taken. (请求动作未执行)
系统存储空间不足。

550 Requested action not taken. (请求的动作没有执行)
文件不可用(例如,没有找到文件,没有访问权限)

501 Syntax error in parameters or arguments. (参数语法错误)

502 Command not implemented. (命令没有实现)

503 Bad sequence of commands. (命令顺序错误)

504 Command not implemented for that parameter. (没有实现这个命令参数)

530 Not logged in. (没有登录成功)

532 Need account for storing files. (需要帐户来存储文件)

550 Requested action not taken. (请求的动作没有执行)
文件不可用(例如,没有找到文件,没有访问权限)

551 Requested action aborted. Page type unknown. (请求动作放弃,未知的页面类型)

552 Requested file action aborted. (请求文件动作被放弃)
超出存储分配空间(当前的路径或者数据集)

553 Requested action not taken. (请求动作未获得)
文件名不允许。

posted @ 2007-05-18 16:43 郭大伟 阅读(842) | 评论 (0)编辑 收藏

FTP协议的内部命令
2006-12-29 10:46

FTP协议的内部命令
FTP命令是Internet用户使用最频繁的命令之一,不论是在DOS还是UNIX操作系统下使用FTP,都会遇到大量的FTP内部命令。 
熟悉并灵活应用FTP的内部命令,可以大大方便使用者,并收到事半功倍之效。 
FTP的命令行格式为: ftp -v -d -i -n -g [主机名] ,其中 
-v 显示远程服务器的所有响应信息; 
-n 限制ftp的自动登录,即不使用; 
.n etrc文件; 
-d 使用调试方式; 
-g 取消全局文件名。 
ftp使用的内部命令如下(中括号表示可选项): 
1.![cmd[args]]:在本地机中执行交互shell,exit回到ftp环境,如:!ls*.zip. 
2.$ macro-ame[args]:执行宏定义macro-name. 
3.account[password]:提供登录远程系统成功后访问系统资源所需的补充口令。 
4.append 
local-file[remote-file]:将本地文件追加到远程系统主机,若未指定远程系统文件名,则使用本地文件名。 
5.ascii:使用ascii类型传输方式。 
6.bell:每个命令执行完毕后计算机响铃一次。 
7.bin:使用二进制文件传输方式。 
8.bye:退出ftp会话过程。 
9.case:在使用mget时,将远程主机文件名中的大写转为小写字母。 
10.cd remote-dir:进入远程主机目录。 
11.cdup:进入远程主机目录的父目录。 
12.chmod mode file-name:将远程主机文件file-name的存取方式设置为mode,如: chmod 777 
a.out 。 
13.close:中断与远程服务器的ftp会话(与open对应)。 
14.cr:使用asscii方式传输文件时,将回车换行转换为回行。 
15.delete remote-file:删除远程主机文件。 
16.debug[debug-value]:设置调试方式, 显示发送至远程主机的每条命令,如: deb up 
3,若设为0,表示取消debug。 
17.dir[remote-dir][local-file]:显示远程主机目录,并将结果存入本地文件local-file。 

18.disconnection:同close。 
19.form format:将文件传输方式设置为format,缺省为file方式。 
20.get remote-file[local-file]: 
将远程主机的文件remote-file传至本地硬盘的local-file。 
21.glob:设置mdelete,mget,mput的文件名扩展,缺省时不扩展文件名,同命令行的-g参数。 
22.hash:每传输1024字节,显示一个hash符号(#)。 
23.help[cmd]:显示ftp内部命令cmd的帮助信息,如:help get。 
24.idle[seconds]:将远程服务器的休眠计时器设为[seconds]秒。 
25.image:设置二进制传输方式(同binary)。 
26.lcd[dir]:将本地工作目录切换至dir。 
27.ls[remote-dir][local-file]:显示远程目录remote-dir, 并存入本地文件local-file。 
28.macdef macro-name:定义一个宏,遇到macdef下的空行时,宏定义结束。 
29.mdelete[remote-file]:删除远程主机文件。 
30.mdir remote-files local-file:与dir类似,但可指定多个远程文件,如: mdir 
*.o.*.zipoutfile 。 
31.mget remote-files:传输多个远程文件。 
32.mkdir dir-name:在远程主机中建一目录。 
33.mls remote-file local-file:同nlist,但可指定多个文件名。 
34.mode[modename]:将文件传输方式设置为modename, 缺省为stream方式。 
35.modtime file-name:显示远程主机文件的最后修改时间。 
36.mput local-file:将多个文件传输至远程主机。 
37.newer file-name: 如果远程机中file-name的修改时间比本地硬盘同名文件的时间更近,则重传该文件。 
38.nlist[remote-dir][local-file]:显示远程主机目录的文件清单,并存入本地硬盘的local-file。 
39.nmap[inpattern outpattern]:设置文件名映射机制, 使得文件传输时,文件中的某些字符相互转换,如:nmap 
$1.$2.$3[$1,$2].[$2,$3],则传输文件a1.a2.a3时,文件名变为a1,a2。该命令特别适用于远程主机为非UNIX机的情况。 
40.ntrans[inchars[outchars]]:设置文件名字符的翻译机制,如ntrans 1R,则文件名LLL将变为RRR。 
41.open host[port]:建立指定ftp服务器连接,可指定连接端口。 
42.passive:进入被动传输方式。 
43.prompt:设置多个文件传输时的交互提示。 
44.proxy ftp-cmd:在次要控制连接中,执行一条ftp命令, 
该命令允许连接两个ftp服务器,以在两个服务器间传输文件。第一条ftp命令必须为open,以首先建立两个服务器间的连接。 


45.put local-file[remote-file]:将本地文件local-file传送至远程主机。 


46.pwd:显示远程主机的当前工作目录。 


47.quit:同bye,退出ftp会话。 


48.quote arg1,arg2...:将参数逐字发至远程ftp服务器,如:quote syst. 


49.recv remote-file[local-file]:同get。 


50.reget remote-file[local-file]:类似于get,但若local-file存在,则从上次传输中断处续传。 


51.rhelp[cmd-name]:请求获得远程主机的帮助。 


52.rstatus[file-name]:若未指定文件名,则显示远程主机的状态,否则显示文件状态。 


53.rename[from][to]:更改远程主机文件名。 


54.reset:清除回答队列。 


55.restart marker:从指定的标志marker处,重新开始get或put,如:restart 130。 


56.rmdir dir-name:删除远程主机目录。 


57.runique:设置文件名唯一性存储,若文件存在,则在原文件后加后缀..1,.2等。 


58.send local-file[remote-file]:同put。 


59.sendport:设置PORT命令的使用。 


60.site arg1,arg2...:将参数作为SITE命令逐字发送至远程ftp主机。 


61.size file-name:显示远程主机文件大小,如:site idle 7200。 


62.status:显示当前ftp状态。 


63.struct[struct-name]:将文件传输结构设置为struct-name, 缺省时使用stream结构。 


64.sunique:将远程主机文件名存储设置为唯一(与runique对应)。 


65.system:显示远程主机的操作系统类型。 


66.tenex:将文件传输类型设置为TENEX机的所需的类型。 


67.tick:设置传输时的字节计数器。 


68.trace:设置包跟踪。 


69.type[type-name]:设置文件传输类型为type-name,缺省为ascii,如:type 
binary,设置二进制传输方式。 


70.umask[newmask]:将远程服务器的缺省umask设置为newmask,如:umask3。 

71.user 
user-name[password][account]:向远程主机表明自己的身份,需要口令时,必须输入口令,如:user 
anonymous hnlywjy@email。 


72.verbose:同命令行的-v参数,即设置详尽报告方式,ftp服务器的所有响应都将显示给用户,缺省为on. 


73.?[cmd]:同help

posted @ 2007-05-16 17:16 郭大伟 阅读(256) | 评论 (0)编辑 收藏

Qt&Kdevelop技巧集(原创)
发表:2004-1-21 13:39:23 出处:你的博客网(yourblog.org)
1. 如何在Qt程序中加入OpenGL支持。
在QT程序中加入OpenGL支持很简单,只需要在Kdevelop连接的库中加入“-lGL -lGLU”即可,如果需要glut支持,还可以加入“-lglut”。具体操作是在kdevelop集成编译环境中按下”F7”,在弹出的对话框中选择 “Linker”一项,在输入栏输入你想添加的库即可,写法与gcc/g++一致。
一般在类QGLWidget中使用OpenGL,调用此类的头文件是qgl.h,具体写法请参考qt例程中的gear,texture,box等程序(在RedHat7.2中,它们在/usr/lib/qt-2.3.1/doc/examples下).

2. 检验linux/Unix环境是否支持OpenGL.
Qt中的QGLFormat类可以帮助我们轻易检验系统是否支持OpenGL,载入头文件(#include <qgl.h>)后,我们就可以使用QGLFormat的静态函数hasOpenGL来检验,具体写法如下例:
if (!QGLFormat::hasOpenGL()) //Test OpenGL Environment
{
qWarning( "This system has no OpenGL support. Exiting." );//弹出警告对话框
return -1;
}

3.获得屏幕的高和宽.
一般我们可以通过QT的Qapplication类来获得系统的一些信息,载入头文件(#include <qapplication.h>)我们就可以调用它,下例是使主程序充满整个屏幕的代码:
Gui_MainForm gui_mainform;
a.setMainWidget( &gui_mainform );
gui_mainform.resize( QApplication::desktop()->width(), QApplication::desktop()->height() ); gui_mainform.show();

4.关于信号和槽.
信号和槽机制是QT库的重要特性,可以说不了解它就不了解Qt.此机制能在各类间建立方便快捷的通信联系,只要类中加载了Q_OBJECT宏并用 connect函数正确连接在一起即可,具体写法这里就不赘述了.但本人在使用过程中发现使用此机制容易破坏程序的结构性和封装性,速度也不是很让人满意,尤其是在跨多类调用时.鄙人的一孔之见是: 信号和槽机制不可不用,但不可多用.

5.QT程序中界面的设计.
尽管Kdevelop是一个优秀的集成编译环境,可遗憾的是它不是一个可视化的编译环境,好在有Qdesigner来帮助我们完成界面设计,该程序的使用很简单,使用过VB,VC和Delphi的程序员能很快其操作方式,操作完成后存盘会生成一个扩展名为”ui”的文件,你接下来的任务就是把它解析成 cpp和h文件,假设文件名为myform.ui,解析方法如下:
$uic myform.ui –I myform.h –o myform..cpp //这句生成cpp文件
$uic myform.ui –o myform.h //这句生成h文件.

6.由pro文件生成Makefile.
对于Linux/Unix程序员来说编写Makefile文件是一项令人烦恼的任务,而qt程序员就没有这样的烦恼,一句$qmake –o Makefile myprogram.pro就可以轻松愉快的完成任务,而pro文件的编写也很容易,其核心是h和cpp文件的简单列表.具体写法请参考一下qt自带的样例和教程吧(在RedHat7.2中,它在/usr/lib/qt-2.3.1/doc/examples下),相对Makefile文件简直没有什么难度.

7.主组件的选择.
一般我们在编程是使用继承Qwidget类的类作为主组件,这当然未可厚非.但在制作典型的多文档和单文档程序时我们有更好的选择— QmainWindow类,它可以方便的管理其中的菜单工具条主窗口和状态条等,在窗体几何属性发生变化时也能完美的实现内部组件缩放,这比用传统的几何布局类来管理要方便得多,而且不用写什么代码.关于它的具体细节请查阅QT的帮组文档,这里就不赘述了.

8.菜单项中加入Checked项.
在QT中,菜单项中加入Checked有点麻烦,具体写法如下:
1> 定义int型成员变量,并在创建菜单项中写:
displayGeometryMode=new QPopupMenu(this); //这里创建弹出菜单组displayGeometryMode
m_menuIDWire=displayGeometryMode->insertItem("Wire",this,SLOT(slt_Change2WireMode()));.//创建弹出菜单子项
displayGeometryMode->setItemChecked(m_ menuIDWire,true);//设定此子项为选择状态

2> 再在槽函数中写:
displayGeometryMode->setItemChecked(m_menuIDWire,false);//这里设定此子项为非选择状态

9.截获程序即将退出的信号.
有些时候我们需要在程序即将退出时进行一些处理,如保存文件等等.如何截获程序退出的信号呢?还是要用到Qapplication类的aboutToQuit()信号,程序中可以这样写:
connect(qApp,SIGNAL(aboutToQuit()),this,SLOT(Slot_SaveActions()));
在槽函数Slot_SaveActions()就可以进行相关处理了,注意,使用全局对象qApp需要加载头文件(#include <qapplication.h>).

10.弹出标准文件对话框.
在程序中弹出文件对话框是很容易处理的,举例如下:
QString filter="Txt files(*.txt)\n" //设置文件过滤,缺省显示文本文件
"All files(*)" ; //可选择显示所有文件
QString Filepathname=QFileDialog::getOpenFileName(" ",filter,this);//弹出对话框,这句需要加载头文件(#include < qfiledialog.h >)


11.将当前日期时间转化为标准Qstring.
QDateTime currentdatetime =QDateTime::currentDateTime();//需要加载头文件(#include < qdatetime.h >)
QString strDateTime=currentdatetime.toString();

12.设置定时器
所有Qobject的子类在设置定时器时都不必加载一个Qtimer对象,因为这样造成了资源浪费且需要书写多余的函数,很不方便.最好的办法是重载timerEvent函数,具体写法如下:
class Gui_DlgViewCtrlDatum : public QDialog
{
Q_OBJECT
public:
Gui_DlgViewCtrlDatum( QWidget* parent = 0, const char* name = 0, bool modal = FALSE, WFlags fl = 0 );
~Gui_DlgViewCtrlDatum();
protected:
void timerEvent( QTimerEvent * );
};
void Gui_DlgViewCtrlDatum::timerEvent( QTimerEvent *e )
{
//statements
}
再在Gui_DlgViewCtrlDatum的构造函数中设置时间间隔:
startTimer(50);//单位为毫秒

这样,每隔50毫秒,函数timerEvent便会被调用一次.

13.最方便的几何布局类QGridLayout
在QT的几何布局类中,笔者认为QgridLayout使用最为方便,举例如下:
QGridLayout* layout=new QGridLayout(this,10,10);//创建一个10*10的QgridLayout实例
layout->addMultiCellWidget(gui_dlgslab_glwnd,1,8,0,7);//将OpenGL窗口固定在QgridLayout中的(1,0)单元格到(8,7)单元格中
layout->addMultiCellWidget(Slider1,0,9,8,8);//将一个slider固定在单元格(0,8)到(9,8)中
layout->addWidget(UpLimitLbl,1,9);//将一个label(UpLimitLbl)固定在单元格(1,9)中
这样,无论窗体大小如何改变,它们的布局方式都不会发生改变,这比反复使用QvboxLayout和QhboxLayout要方便快捷许多.
注:使用几何布局类需要调用头文件(#include <qlayout.h>)

14.字符串类Qstring和字符串链表类QstringList.
Qstring是Qt中标准字符串类,下面列出它的一些常用函数:
toInt():将字符串转化成int类型.
ToFloat():将字符串转化成float类型.
ToDouble():将字符串转化成double类型.
Left(n):从左起取n个字符
Right(n):从右起取n个字符
SetNum(n):将实数n(包括int,float,double等)转化为Qsting型.

QstringList是大家比较少使用的类,它可以看成Qstring组成的链表(QT中标准链表类Qlist的函数对它都适用,它的单个节点是Qstring类型的),特别适合与处理文本,下面一段代码就可见其方便快捷:
Qstring strtmp=”abc|b|c|d”;
QstringList strlsttmp;
Strlsttmp =QStringList::split("|", strtmp);
For(unsigned int I=0;I< Strlsttmp.count();I++)
{
cout<< Strlsttmp.at(I);
}
结果输出为:abc b c d,也就是说,通过一个函数split,一行文本就被符号”|”自动分割成了单个字符串.这在文本处理时特别省力.(请参考c语言大全第四版中用”strtok”函数分割文本的例程,将双方比较一下)

15. QGLWidget类如何加入鼠标支持.
QGLWidget类加入鼠标支持需要重载以下函数:
void mousePressEvent(QMouseEvent*);
void mouseMoveEvent(QMouseEvent*);
void mouseReleaseEvent(QMouseEvent*);
请具体看一个实例:
class Gui_WgtMain_GLWnd : public QGLWidget {
Q_OBJECT
public:
Gui_WgtMain_GLWnd(QWidget *parent=0, const char *name=0);
~Gui_WgtMain_GLWnd();
protected:
void initializeGL();
void paintGL();
void resizeGL( int w, int h );
void mousePressEvent(QMouseEvent*);
void mouseMoveEvent(QMouseEvent*);
void mouseReleaseEvent(QMouseEvent*);
private:
int m_nCnt;
};
void Gui_WgtMain_GLWnd::mousePressEvent(QMouseEvent* e)
{
//statements
}
void Gui_WgtMain_GLWnd:: mouseMoveEvent (QMouseEvent* e)
{
//statements
}
void Gui_WgtMain_GLWnd:: mouseReleaseEvent (QMouseEvent* e)
{
//statements
}
其中, e->x();e->y();可以获得鼠标的位置, e->button()可以取得鼠标按键的状态(左中右键以及ctrl,alt,shift等组合键),灵活使用他们就可以在用鼠标操作OpenGL画面了.

16.由ui文件生成.h和.cpp文件
生成.cpp文件
$uic myform.ui -i myform.h -o myform.cpp

生成.h文件
$uic myform.ui -o myform.h

posted @ 2007-05-10 16:17 郭大伟 阅读(908) | 评论 (0)编辑 收藏

引言

  想使用Linux已经很长时间了,由于没有硬性任务一直也没有系统学习,近日由于工作需要必须使用Linux下的MySQL。本以为有Windows下使用SQL Server的经验,觉得在Linux下安装MySql应该是易如反掌的事,可在真正安装和使用MySQL时走了很多弯路,遇见很多问题,毕竟Linux 和Windows本身就有很大区别。为了让和我一样的初学者在学习的过程中少走弯路,尽快入门,写了此文,希望对您有所帮助。本文的Linux环境是 Red Hat 9.0,MySQL是4.0.16。

   二、安装Mysql

   1、下载MySQL的安装文件
   安装MySQL需要下面两个文件:
   MySQL-server-4.0.16-0.i386.rpm   
   MySQL-client-4.0.16-0.i386.rpm
   下载地址为:http://www.mysql.com/downloads/mysql-4.0.html,打开此网页,下拉网页找到“Linux x86 RPM downloads”项,找到“Server”和“Client programs”项,下载需要的上述两个rpm文件。

   2、安装MySQL
   rpm文件是Red Hat公司开发的软件安装包,rpm可让Linux在安装软件包时免除许多复杂的手续。该命令在安装时常用的参数是 –ivh ,其中i表示将安装指定的rmp软件包,V表示安装时的详细信息,h表示在安装期间出现“#”符号来显示目前的安装过程。这个符号将持续到安装完成后才停止。
   1)安装服务器端
   在有两个rmp文件的目录下运行如下命令:
   [root@test1 local]# rpm -ivh MySQL-server-4.0.16-0.i386.rpm
   显示如下信息。
    warning: MySQL-server-4.0.16-0.i386.rpm: V3 DSA signature: NOKEY, key ID 5072e1f5
   Preparing...       ########################################### [100%]
   1:MySQL-server     ########################################### [100%]
    。。。。。。(省略显示)
   /usr/bin/mysqladmin -u root password 'new-password'
   /usr/bin/mysqladmin -u root -h test1 password 'new-password'
    。。。。。。(省略显示)
   Starting mysqld daemon with databases from /var/lib/mysql
   如出现如上信息,服务端安装完毕。测试是否成功可运行netstat看Mysql端口是否打开,如打开表示服务已经启动,安装成功。Mysql默认的端口是3306。
   [root@test1 local]# netstat -nat
   Active Internet connections (servers and established)
   Proto Recv-Q Send-Q Local Address      Foreign Address     State   
   tcp  0  0 0.0.0.0:3306     0.0.0.0:*      LISTEN   
   上面显示可以看出MySQL服务已经启动。
   2)安装客户端
   运行如下命令:
   [root@test1 local]# rpm -ivh MySQL-client-4.0.16-0.i386.rpm
   warning: MySQL-client-4.0.16-0.i386.rpm: V3 DSA signature: NOKEY, key ID 5072e1f5
   Preparing...    ########################################### [100%]
   1:MySQL-client  ########################################### [100%]
   显示安装完毕。
   用下面的命令连接mysql,测试是否成功。
  三、登录MySQL

   登录MySQL的命令是mysql, mysql 的使用语法如下:
   mysql [-u username] [-h host] [-p[password]] [dbname]
   username 与 password 分别是 MySQL 的用户名与密码,mysql的初始管理帐号是root,没有密码,注意:这个root用户不是Linux的系统用户。MySQL默认用户是root,由于初始没有密码,第一次进时只需键入mysql即可。
   [root@test1 local]# mysql
   Welcome to the MySQL monitor. Commands end with ; or \g.
   Your MySQL connection id is 1 to server version: 4.0.16-standard
   Type 'help;' or '\h' for help. Type '\c' to clear the buffer.
   mysql>
   出现了“mysql>”提示符,恭喜你,安装成功!
   增加了密码后的登录格式如下:
   mysql -u root -p
   Enter password: (输入密码)
   其中-u后跟的是用户名,-p要求输入密码,回车后在输入密码处输入密码。

   注意:这个mysql文件在/usr/bin目录下,与后面讲的启动文件/etc/init.d/mysql不是一个文件。

   四、MySQL的几个重要目录

   MySQL安装完成后不象SQL Server默认安装在一个目录,它的数据库文件、配置文件和命令文件分别在不同的目录,了解这些目录非常重要,尤其对于Linux的初学者,因为 Linux本身的目录结构就比较复杂,如果搞不清楚MySQL的安装目录那就无从谈起深入学习。

   下面就介绍一下这几个目录。

   1、数据库目录
   /var/lib/mysql/

   2、配置文件
   /usr/share/mysql(mysql.server命令及配置文件)

   3、相关命令
   /usr/bin(mysqladmin mysqldump等命令)

   4、启动脚本
   /etc/rc.d/init.d/(启动脚本文件mysql的目录)
  五、修改登录密码

   MySQL默认没有密码,安装完毕增加密码的重要性是不言而喻的。

   1、命令
   usr/bin/mysqladmin -u root password 'new-password'
   格式:mysqladmin -u用户名 -p旧密码 password 新密码

   2、例子
   例1:给root加个密码123456。
   键入以下命令 :
   [root@test1 local]# /usr/bin/mysqladmin -u root password 123456
   注:因为开始时root没有密码,所以-p旧密码一项就可以省略了。

   3、测试是否修改成功
   1)不用密码登录
   [root@test1 local]# mysql
   ERROR 1045: Access denied for user: 'root@localhost' (Using password: NO)
   显示错误,说明密码已经修改。
   2)用修改后的密码登录
   [root@test1 local]# mysql -u root -p
   Enter password: (输入修改后的密码123456)
   Welcome to the MySQL monitor. Commands end with ; or \g.
   Your MySQL connection id is 4 to server version: 4.0.16-standard
   Type 'help;' or '\h' for help. Type '\c' to clear the buffer.
   mysql>
   成功!
   这是通过mysqladmin命令修改口令,也可通过修改库来更改口令。

   六、启动与停止

   1、启动
   MySQL安装完成后启动文件mysql在/etc/init.d目录下,在需要启动时运行下面命令即可。
   [root@test1 init.d]# /etc/init.d/mysql start

   2、停止
   /usr/bin/mysqladmin -u root -p shutdown

   3、自动启动
   1)察看mysql是否在自动启动列表中
   [root@test1 local]# /sbin/chkconfig –list
   2)把MySQL添加到你系统的启动服务组里面去
   [root@test1 local]# /sbin/chkconfig – add mysql
   3)把MySQL从启动服务组里面删除。
   [root@test1 local]# /sbin/chkconfig – del mysql
七、更改MySQL目录

   MySQL默认的数据文件存储目录为/var/lib/mysql。假如要把目录移到/home/data下需要进行下面几步:

   1、home目录下建立data目录
   cd /home
   mkdir data

   2、把MySQL服务进程停掉:
   mysqladmin -u root -p shutdown

   3、把/var/lib/mysql整个目录移到/home/data
   mv /var/lib/mysql /home/data/
   这样就把MySQL的数据文件移动到了/home/data/mysql下

   4、找到my.cnf配置文件
   如果/etc/目录下没有my.cnf配置文件,请到/usr/share/mysql/下找到*.cnf文件,拷贝其中一个到/etc/并改名为my.cnf)中。命令如下:
   [root@test1 mysql]# cp /usr/share/mysql/my-medium.cnf /etc/my.cnf

   5、编辑MySQL的配置文件/etc/my.cnf
   为保证MySQL能够正常工作,需要指明mysql.sock文件的产生位置。 修改socket=/var/lib/mysql/mysql.sock一行中等号右边的值为:/home/mysql/mysql.sock 。操作如下:
   vi  my.cnf    (用vi工具编辑my.cnf文件,找到下列数据修改之)
   # The MySQL server
    [mysqld]
    port   = 3306
    #socket  = /var/lib/mysql/mysql.sock(原内容,为了更稳妥用“#”注释此行)
    socket  = /home/data/mysql/mysql.sock   (加上此行)

   6、修改MySQL启动脚本/etc/rc.d/init.d/mysql
   最后,需要修改MySQL启动脚本/etc/rc.d/init.d/mysql,把其中datadir=/var/lib/mysql一行中,等号右边的路径改成你现在的实际存放路径:home/data/mysql。
   [root@test1 etc]# vi /etc/rc.d/init.d/mysql
   #datadir=/var/lib/mysql    (注释此行)
   datadir=/home/data/mysql   (加上此行)

   7、重新启动MySQL服务
   /etc/rc.d/init.d/mysql start
   或用reboot命令重启Linux
   如果工作正常移动就成功了,否则对照前面的7步再检查一下。

   八、MySQL的常用操作

   注意:MySQL中每个命令后都要以分号;结尾。

   1、显示数据库
   mysql> show databases;
   +----------+
   | Database |
   +----------+
   | mysql  |
   | test   |
   +----------+
   2 rows in set (0.04 sec)
   Mysql刚安装完有两个数据库:mysql和test。mysql库非常重要,它里面有MySQL的系统信息,我们改密码和新增用户,实际上就是用这个库中的相关表进行操作。

   2、显示数据库中的表
   mysql> use mysql; (打开库,对每个库进行操作就要打开此库,类似于foxpro )
   Database changed

   mysql> show tables;
   +-----------------+
   | Tables_in_mysql |
   +-----------------+
   | columns_priv  |
   | db       |
   | func      |
   | host      |
   | tables_priv   |
   | user      |
   +-----------------+
   6 rows in set (0.01 sec)

   3、显示数据表的结构:
   describe 表名;

   4、显示表中的记录:
   select * from 表名;
   例如:显示mysql库中user表中的纪录。所有能对MySQL用户操作的用户都在此表中。
   Select * from user;

   5、建库:
   create database 库名;
   例如:创建一个名字位aaa的库
   mysql> create databases aaa;
6、建表:
   use 库名;
   create table 表名 (字段设定列表);
   例如:在刚创建的aaa库中建立表name,表中有id(序号,自动增长),xm(姓名),xb(性别),csny(出身年月)四个字段
   use aaa;
   mysql> create table name (id int(3) auto_increment not null primary key, xm char(8),xb char(2),csny date);
   可以用describe命令察看刚建立的表结构。
   mysql> describe name;

   +-------+---------+------+-----+---------+----------------+
   | Field | Type  | Null | Key | Default | Extra     |
   +-------+---------+------+-----+---------+----------------+
   | id  | int(3) |   | PRI | NULL  | auto_increment |
   | xm  | char(8) | YES |   | NULL  |        |
   | xb  | char(2) | YES |   | NULL  |        |
   | csny | date  | YES |   | NULL  |        |
   +-------+---------+------+-----+---------+----------------+

   7、增加记录
   例如:增加几条相关纪录。
   mysql> insert into name values('','张三','男','1971-10-01');
   mysql> insert into name values('','白云','女','1972-05-20');
   可用select命令来验证结果。
   mysql> select * from name;
   +----+------+------+------------+
   | id | xm  | xb  | csny    |
   +----+------+------+------------+
   | 1 | 张三 | 男  | 1971-10-01 |
   | 2 | 白云 | 女  | 1972-05-20 |
   +----+------+------+------------+

   8、修改纪录
   例如:将张三的出生年月改为1971-01-10
   mysql> update name set csny='1971-01-10' where xm='张三';

   9、删除纪录
   例如:删除张三的纪录。
   mysql> delete from name where xm='张三';

   10、删库和删表
   drop database 库名;
   drop table 表名;

   九、增加MySQL用户

   格式:grant select on 数据库.* to 用户名@登录主机 identified by "密码"
例1、增加一个用户user_1密码为123,让他可以在任何主机上登录,并对所有数据库有查询、插入、修改、删除的权限。首先用以root用户连入MySQL,然后键入以下命令:

   mysql> grant select,insert,update,delete on *.* to user_1@"%" Identified by "123";
例1增加的用户是十分危险的,如果知道了user_1的密码,那么他就可以在网上的任何一台电脑上登录你的MySQL数据库并对你的数据为所欲为了,解决办法见例2。

  例2、增加一个用户user_2密码为123,让此用户只可以在localhost上登录,并可以对数据库aaa进行查询、插入、修改、删除的操作(localhost指本地主机,即MySQL数据库所在的那台主机),这样用户即使用知道user_2的密码,他也无法从网上直接访问数据库,只能通过 MYSQL主机来操作aaa库。

   mysql>grant select,insert,update,delete on aaa.* to user_2@localhost identified by "123";

   用新增的用户如果登录不了MySQL,在登录时用如下命令:

   mysql -u user_1 -p -h 192.168.113.50 (-h后跟的是要登录主机的ip地址)

   十、备份与恢复

   1、备份

   例如:将上例创建的aaa库备份到文件back_aaa中

   [root@test1 root]# cd /home/data/mysql (进入到库目录,本例库已由val/lib/mysql转到/home/data/mysql,见上述第七部分内容)
   [root@test1 mysql]# mysqldump -u root -p --opt aaa > back_aaa

   2、恢复

   [root@test mysql]# mysql -u root -p ccc < back_aaa

Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=229637

posted @ 2007-03-30 10:30 郭大伟 阅读(218) | 评论 (0)编辑 收藏

 深拷贝与浅拷贝探析
1.         深拷贝是指源对象与拷贝对象互相独立,其中任何一个对象的改动都不会对另外一个对象造成影响。举个例子,一个人名叫张三,后来用他克隆(假设法律允许)了另外一个人,叫李四,不管是张三缺胳膊少腿还是李四缺胳膊少腿都不会影响另外一个人。比较典型的就是Value(值)对象,如预定义类型Int32Double,以及结构(struct),枚举(Enum)等。

考虑以下写法

       int source = int.MaxValue;//(1)初始化源对象为整数的最大值2,147,483,647

            int dest = source;//(2)赋值,内部执行深拷贝

            dest = 1024;//(3)对拷贝对象进行赋值

            source = 2048;//(4)对源对象进行赋值

       首先(2)中将source赋给dest,执行了深拷贝动作,其时destsource的值是一样的,都是int.MaxValue;(3)对dest进行修改,dest值变为1024,由于是深拷贝,因此不会运行sourcesource仍然是int.MaxValue;(4)对source进行了修改,同样道理,dest仍然是1024,同时int.MaxValue的值也不变,仍然是2,147,483,647;只有source变成了2048

       再考虑以下写法

        structPoint

        {

            publicint X;

            publicint Y;

            public Point(int x, int y)

            {

                X = x;

                Y = y;

            }

        }

 

        Point source = newPoint(10, 20);

        Point dest = source;

 

        dest.X = 20

     dest.X属性变成20后,sourceX属性仍然是10

2.         浅拷贝是指源对象与拷贝对象共用一份实体,仅仅是引用的变量不同(名称不同)。对其中任何一个对象的改动都会影响另外一个对象。举个例子,一个人一开始叫张三,后来改名叫李四了,可是还是同一个人,不管是张三缺胳膊少腿还是李四缺胳膊少腿,都是这个人倒霉。比较典型的就有Reference(引用)对象,如Class(类)。

考虑以下写法

        classPoint

        {

            publicint X;

            publicint Y;

            public Point(int x, int y)

            {

                X = x;

                Y = y;

            }

        }

 

        Point source = newPoint(10, 20);

        Point dest = source;

   dest.X = 20;

由于Point现在是引用对象,因此Point dest=source的赋值动作实际上执行的是浅拷贝,最后的结果应该是sourceX字段值也变成了20。即它们引用了同一个对象,仅仅是变量明sourcedest不同而已。

3.         引用对象的浅拷贝原理

引用对象之间的赋值之所以执行的是浅拷贝动作,与引用对象的特性有关,一个引用对象一般来说由两个部分组成

1)一个具名的Handle,也就是我们所说的声明(如变量)

2)一个内部(不具名)的对象,也就是具名Handle的内部对象。它在Manged Heap(托管堆)中分配,一般由新增引用对象的New方法是进行创建

       如果这个内部对象已被创建,那么具名的Handle就指向这个内部对象在Manged Heap中的地址,否则就是null(从某个方面来讲,如果这个具名的handle可以被赋值为null,说明这是一个引用对象,当然不是绝对)。两个引用对象如果进行赋值,它们仅仅是复制这个内部对象的地址,内部对象仍然是同一个,因此,源对象或拷贝对象的修改都会影响对方。这也就是浅拷贝

4.         引用对象如何进行深拷贝

由于引用对象的赋值仅仅是复制具名Handle(变量)指向的地址,因此要对引用对象进行深拷贝就要重新创建一份该对象的实例,并对该对象的字段进行逐一赋值,如以下写法

        classPoint

        {

            publicint X;

            publicint Y;

            public Point(int x, int y)

            {

                X = x;

                Y = y;

            }

        }

 

        Point source = newPoint(10, 20);

        Point dest = newPoint(source.X, source.Y);

        //或以下写法

        //Point dest = new Point()

        //dest.X = source.X

   //dest.Y = source.Y

       其时,sourcedest就是两个互相独立的对象了,两者的修改都不会影响对方

 

5.一些需要注意的东西

       1):String字符串对象是引用对象,但是很特殊,它表现的如值对象一样,即对它进行赋值,分割,合并,并不是对原有的字符串进行操作,而是返回一个新的字符串对象

       2):Array数组对象是引用对象,在进行赋值的时候,实际上返回的是源对象的另一份引用而已;因此如果要对数组对象进行真正的复制(深拷贝),那么需要新建一份数组对象,然后将源数组的值逐一拷贝到目的对象中

posted @ 2007-03-29 11:24 郭大伟 阅读(375) | 评论 (0)编辑 收藏

关于typedef的用法

一.基本概念剖析

int* (*a[5])(int, char*);     //#1
void (*b[10]) (void (*)()); //#2
double(*)() (*pa)[9];        //#3


1.C语言中函数声明和数组声明。函数声明一般是这样:
int fun(int, double);
对应函数指针(pointer to function)的声明是这样:
int (*pf)(int, double);
可以这样使用:
pf = &fun;     //赋值(assignment)操作
(*pf)(5, 8.9);//函数调用操作
也请注意,C语言本身提供了一种简写方式如下:
pf = fun;      // 赋值(assignment)操作
pf(5, 8.9);    // 函数调用操作
不过我本人不是很喜欢这种简写,它对初学者带来了比较多的迷惑。
数组声明一般是这样:
int a[5];
对于数组指针(pointer to array)的声明是这样:
int (*pa)[5];
可以这样使用:
pa = &a;           // 赋值(assignment)操作
int i = (*pa)[2]; // 将a[2]赋值给i;

2.有了上面的基础,我们就可以对付开头的三只纸老虎了!:) 这个时候你需要复习一下各种运算符的优先顺序和结合顺序了,顺便找本书看看就够了。
#1:int* (*a[5])(int, char*);
首先看到标识符名a,“[]”优先级大于“*”,a与“[5]”先结合。所以a是一个数组,这个数组有5个元素,每一个元素都是一个指针,
指针指向“(int, char*)”,对,指向一个函数,函数参数是“int, char*”,返回值是“int*”。完毕,我们干掉了第一个纸老虎。:)
#2:void (*b[10]) (void (*)());
b是一个数组,这个数组有10个元素,每一个元素都是一个指针,指针指向一个函数,函数参数是“void (*)()”【注1】,返回值是“void”。完毕!
注1:这个参数又是一个指针,指向一个函数,函数参数为空,返回值是“void”。
#3:double(*)()(*pa)[9];
pa是一个指针,指针指向一个数组,这个数组有9个元素,每一个元素都是“double(*)()”【也即一个指针,指向一个函数,函数参数为空,返回值是“double
”】。(注意typedef int* p[9]与typedef int(*p)[9]的区别,前者定义一个数组,此数组包含9个int*类型成员,而后者定义一个指向数组的指针,被指向的数组包含9个int类型成员)。
现在是不是觉得要认识它们是易如反掌,工欲善其事,必先利其器!我们对这种表达方式熟悉之后,就可以用“typedef”来简化这种类型声明。
#1:int* (*a[5])(int, char*);
typedef int* (*PF)(int, char*);//PF是一个类型别名【注2】。
PF a[5];//跟int* (*a[5])(int, char*);的效果一样!
注2:很多初学者只知道typedef char* pchar;但是对于typedef的其它用法不太了解。Stephen Blaha对typedef用法做过一个总结:“建立一个类型别名的方法
很简单,在传统的变量声明表达式里用类型名替代变量名,然后把关键字typedef加在该语句的开头”。
#2:void (*b[10])(void (*)());
typedef void (*pfv)();
typedef void (*pf_taking_pfv)(pfv);
pf_taking_pfv b[10]; //跟void (*b[10]) (void (*)());的效果一样!
#3. double(*)()(*pa)[9];
typedef double(*PF)();
typedef PF (*PA)[9];
PA pa; //跟doube(*)()(*pa)[9];的效果一样!

posted @ 2007-03-28 11:32 郭大伟 阅读(275) | 评论 (1)编辑 收藏

ls -l | grep ^d
ls -l | grep '^[^d]'
ls -l | grep '^d.....x..x'

grep "garland" /etc/password
grep -s "garland" /etc/password

ps ax | grep "named"

#!/bin/sh
echo "first name:\c"
read name
echo "second name:\c"
read sexname
echo "third name:\c"
read manname

cp justis.txt garland.txt && echo "if you are seeing this cp OK"
cp wopper.txt garland.txt || echo "if you are seeing this cp failed"
echo &name

echo please input buddy count:
count =
read count

if count == 5
{
        echo galrnad 5
}

posted @ 2006-10-27 12:02 郭大伟 阅读(285) | 评论 (0)编辑 收藏

#!/bin/sh
echo "first name:\c"
read name
echo "second name:\c"
read sexname
echo "third name:\c"
read manname

cp justis.txt garland.txt && echo "if you are seeing this cp OK"
cp wopper.txt garland.txt || echo "if you are seeing this cp failed"
echo &name

posted @ 2006-10-27 11:25 郭大伟 阅读(366) | 评论 (0)编辑 收藏

仅列出标题
共2页: 1 2 

posts - 11, comments - 1, trackbacks - 0, articles - 0

Copyright © 郭大伟