文件下载


1.得到文件路径

从$_GET['file']得到文件路径

$path_parts = pathinfo($_GET['file']);
$file_name  = $path_parts['basename'];
$file_path  = '/mysecretpath/' . $file_name;

务必使用上面这种方法得到路径,不能简单的字符串拼接得到路径
$mypath = '/mysecretpath/' . $_GET['file'];
如果输入的是../../,就可以访问任何路径

2. 设置header信息

header('Content-Description: File Transfer'); //描述页面返回的结果
header('Content-Type: application/octet-stream'); //返回内容的类型,此处只知道是二进制流。具体返回类型可参考http://tool.oschina.net/commons
header('Content-Disposition: attachment; filename='.basename($file));//可以让浏览器弹出下载窗口
header('Content-Transfer-Encoding: binary');//内容编码方式,直接二进制,不要gzip压缩
header('Expires: 0');//过期时间
header('Cache-Control: must-revalidate');//缓存策略,强制页面不缓存,作用与no-cache相同,但更严格,强制意味更明显
header('Pragma: public');
header('Content-Length: ' . filesize($file));//文件大小,在文件超过2G的时候,filesize()返回的结果可能不正确

3. 输出文件之file_get_contents()方法

file_get_contents()把文件内容读取到字符串,也就是要把文件读到内存中,再输出内容

$str = file_get_contents($file);
echo $str;

这种方式,只要文件稍微一大,就会超过内存限制

4. 输出文件之file()方法

与file_get_contents()差不多,只不过是file()会把内容按行读取到数组中,也是需要占用内存

$f = file($file);
while(list($line, $cnt) = each($f)) {
   echo $cnt;
}

文件大的时候也会超出内存限制

5. 输出文件之readfile()方法

readfile()方法:读入一个文件并写入到输出缓冲
这种方式可以直接输出到缓冲,不会整个文件占用内存
前提要先清空缓冲,先要让用户看到下载文件的对话框

while (ob_get_level()) ob_end_clean();
//设置完header以后
ob_clean();
flush();  //清空缓冲区
readfile($file);

这种方法可以输出大文件,读取单个文件不会超出内存限制,但下面的情况除外。
readfile()在多人读取文件的时候同样会造成PHP内存耗尽:http://stackoverflow.com/questions/6627952/why-does-readfile-exhaust-php-memory

PHP has to read the file and it writes to the output buffer. So, for 300Mb file, no matter what the implementation you wrote (by many small segments, or by 1 big chunk) PHP has to read through 300Mb of file eventually.
If multiple user has to download the file, >there will be a problem. (In one server, hosting providers will limit memory given to each hosting user. With such limited memory, using buffer is not going to be a good idea. )
I think using the direct link to download a file is a much better approach for big files.

大意:PHP需要读文件,再输出到缓冲。对于一个300M的文件,PHP最终还是要读300M内存。因此在多个用户同时下载的时候,缓冲也会耗尽内存。(不对还请指正)

例如100个用户在下载,就需要100*buffer_size大小的内存

6. 输出文件之fopen()方法

set_time_limit(0);
$file = @fopen($file_path,"rb");
while(!feof($file))
{
    print(@fread($file, 1024*8));
    ob_flush();
    flush();
}

fopen()可以读入大文件,每次可以指定读取一部分的内容。在操作大文件的时候也很有用

7. 总结

利用PHP下载文件时,应该要注重场景。如果本身只是几个小文件被下载,那么使用PHP下载比较好;但是如果PHP要承受大量下载请求,这时下载文件就不该交给PHP做。

对于Apache,有mod_xsendfile可以帮助完成下载任务,更简单也更快速

当文件是远程文件时,filesize,file_exist函数不能正确获取文件的存在。

原文


初学者理解生成器


理解php生成器并不是一个艰巨的事情。希望你能在看完这篇文章之后完全理解他。那么,让我们开始吧。

什么是PHP生成器?

我们都知道内存管理是所有编程模块当中最难处理的。小巧,轻便和快速的应用总是人们所喜爱的。在PHP5.5当中,我们引入PHP生成器的概念。这个概念非常有用他能是我们的应用运行的更快。总的来说,这个概念就是不在内存当中创建数组。首先让我们用一个例子来解释它。创建一个generator.php在你本地,粘贴如下代码,执行代码并查看。

<?php
    function getLimit ($max = 10) {
        $array = [];

        for ($i = 1; $i < $max; $i++) {
            $array[] = $i;
        }

        return $array;
    }

    foreach (getLimit(15) as $range) {
        echo "Dataset {$range} <br>";
    }

输入结果


php编码技巧


  1. 如果一个方法能被定义成静态的static,那么定义成静态的速度可快25%
  1. echo的效率高于print,因为echo没有返回值
  1. 在循环之前设置最大的次数,而不是在循环之中
  1. 销毁变量去释放内存,特别是大的数组
    a) unset()只能销毁超过256个字节的变量内存,否则空间是不被释放的
    b) 结论一、unset()函数只能在变量值占用内存空间超过256字节时才会释放内存空间。
    c) 结论二、只有当指向该变量的所有变量(如引用变量)都被销毁后,才会释放内存。
  1. 避免使用__construct,__destruct,__call,__callStatic,__get,__set,__isset,__unset,__sleep,__wakeup,__toString,__set_state,__cloneand__autoload等魔术方法
  1. 避免require_once()比较耗资源
  1. 在include,或者是require中尽量使用绝对路径,减少分析时间
  1. 如果你需要得到脚本的执行时间那么用$_SERVER['REQUEST_TIME']要优于time();
  1. 用@掩盖错误会降低运行速度
  1. 养成对数组健加引号的习惯,这样速度会快7倍如:$_row['id']与$_row[id];
  1. 错误信息很有用
  1. 在循环里别用函数,如count()在外面先计算
  1. 在方法里面建立局部变量速度快
  1. 全局变量几乎要比局部变量慢2倍
  1. 对象属性要双局部变量慢3倍
  1. 初始化的局部变量要比未定义的局部变量快9-10倍
  1. 子类的方法性能优于基类方法
  1. 只调用一个参数并且函数体为空的函数运行花费的时间等于7-8次$localvar++运算,而一个类似的方法(类里的函数)运行等于大约15次$localvar++运算;
  1. 当用echo输出字符串时用逗号代替点运行快些
  1. 在apache服务器里一个php脚本页面比相应的HTML静态页面生成至少要多花2-10倍的时间,建议多用些静态HTML页面和少量的脚步;
  1. 除非你的安装了缓存,不然你的php脚本每次被访问都需要被重编译。建议安装个php缓存程序,这样通过去除一些重复的编译来很明显的提高你20-100%的性能;
  1. 建议用memcached,高性能的分布式内存对象缓存系统,提高动态网络应用程序性能,减轻数据库的负担;
  1. 使用long2ip()或者是ip2long()函数将IP地址转换成整型存入数据库,能降低25%的存储空间,同时也可以很容易对IP进行排序和快速查找
  1. 尽量优先使用三元运算符(a?a:b;)
  1. 使用gzcompress()和gzuncompress()对容量大的字符串进行压缩和解压再存进和取出数据库时,这种内置函数使用gzip算法能压缩到90%;\
  1. 如果你想查看文件的完整路径那么可以用realpath();
  1. 魔术常量

    __LINE__ __FILE__ __DIR__ __FUNCTION__ __CLASS__ __METHOD__ __NAMESPACE__

  2. left(text,20)只取出text文本的前20个字

  1. 在mysql插入语句中如果是自增字段要用NULL代替
  1. 尽量使用PHP内置函数
  1. str_replace函数比preg_replace函数快,但strtr函数的效率是str_replace函数的四倍。
  1. PHP在windows和LINUX下的路径分隔符DIRECTORY_SEPARATOR(windows下的是\和/,而LINUX下的是/)

ORM相关概念


今天学习了几个概念,之前一直不是很清楚,如有错误,还请指正!

ORM

ORM (object relational mapping) 对象关系映射,ORM也是一种对数据库操作的封装,但ORM不像DAO只是一种软件设计的指导原则,其强调的是系统应该层次分明,更像是一种工具。好处是能将程序中的数据对象自动转化为关系型数据库中对应的表和列,数据对象间的引用也可以通过这个工具转化为表之间的join。

使用ORM,可以在开发中不用接触SOL语句:创建一张表,声明一个对应的类即可,然后只用和这个类的实例进行交互,而不用关系对象里的数据如何存储等细节。

采用ORM后,还是需要DAO层的,只不过这是DAO层只和ORM框架打交道,而由ORM跟数据库打交道。

DAO

DAO (data access object)

DAO是一个软件设计的指导原则,在核心J2EE模式中是这样介绍DAO模式的:为了建立一个健壮的J2EE应用,应该将所有对数据源的访问操作抽象封装在一个公共API中。用程序设计的语言来说,就是建立一个接口,接口中定义了此应用程序中将会用到的所有事务方法。在这个应用程序中,当需要和数据源进行交互的时候则使用这个接口,并且编写一个单独的类来实现这个接口在逻辑上对应这个特定的数据存储。

顾名思义就是与数据库打交道,夹在业务逻辑与数据库资源中间。

简单的说 dao层 就是对数据库中数据的增删改查等操作封装在专门的类里面,在业务逻辑层中如果要访问数据的时候,直接调用该dao类(包括了如何访问数据库和数据的增删改查等等代码),就可以返回数据,而不需要再在业务逻辑层中写这些代码。

Active Record

Active Record是一种ORM模式,它是把负责持久化的代码也集成到数据对象中,即这个数据对象知道如何把自己存到数据库里。

传统的ORM会把数据对象和负责持久化(存储到数据库中)的代码分开,数据对象只是一个单纯包含数据的结构体,在模型层和ORM层中传递。

Active Record中,模型层集成了ORM的功能,它们既代表实体,包含业务逻辑,又是数据对象,负责把自己存储到数据库中。

yii2.0 Active Record

DataMaper

DataMaper也是ORM的一种,他和Active Record的区别就是,Active Record将数据操作类,数据库都写在了一起,而DataMaper会将两者拆分。这样的好处是不继承数据库操作类,是的代码更轻巧。


Wireshark抓包常见问题解析(转)


原文地址(http://www.xianren.org/net/wireshark-q.html)

1. tcp out-of-order(tcp有问题)

解答:

1)、 应该有很多原因。但是多半是网络拥塞,导致顺序包抵达时间不同,延时太长,或者包丢失,需要重新组合数据单元 因为他们可能是通过不同的路径到达你电脑上面的。

2)、 CRM IT 同仁上礼拜来跟我反应一个问题,由他们客服系统藉由邮件主机要寄送给客户的信件,常常会有寄送失败的问题,查看了一下 Log,发现正常的信件在主机接收 DATA 完成后会记录收到的邮件大小,然后开始进行后续寄送出去的处理,但这些有问题的寄送,都会发生 DATA 没有传送完,Server 就记录已读取到 EOF,然后结束连线,也因此这封信就不算顺利的送到 Server 上来。

初步看了一下排除是 Timeout 问题,因为连线断的时间都还未达设定的连线 Timeout 时间,由于 CRM 系统是外面厂商写的,为了厘清问题我只好抓封包来看是不是用户端送出来结束传送的指令的。

抓了一下结果如下:

整封邮件的传送过程,包含了大量的 TCP Retransmission 或是 Segment Lost,到后来还有跑出 TCP Out-Of-Order,看起来是网路的问题,网路上对于 TCP Out-Of-Order 的建议是说,有些 Packet 可能 Lost,所以重新传送造成,另一个可能是因为 Client 到 Server 间有两条网路路径,像是 Load Balance 之类的架构,因此若两个封包走不同路径,晚送的封包却比早送的到达,就会发生 Out-Of-Order。

因此在断定有可能是网路造成,加上 CRM 系统上的网卡同事是把两张做成一张 Virtual,再请他拿掉 Bonding 只用单一张跑以后,问题就不存在了,观察流量还跑的比原本两张合起来的 Virtual 单张跑的高,所以 M$ 在 Bonding 网卡上是不是还有什么需要调整的就不得而之了,至少找出造成大量寄送失败的原因就好。

2. tcp segment of a reassembled PDU

解答:1)在连个连接建立的时候,SYN包里面会把彼此TCP最大的报文段长度,在局域网内一般都是1460.如果发送的包比最大的报文段长度长的话就要分片了,被分片出来的包,就会被标记了“TCP segment of a reassembled PDU”,可以参考下图,看一下,被标记了的包的SEQ和ACK都和原来的包一致:

2)上周在公司里遇到一个问题,用wireshark抓系统给网管上报的数据发现里面有好多报文被标识为“TCP segment of a reassembled PDU”,并且每一段报文都是180Byte,当时看到这样的标识,觉得是IP报文分片,以为系统的接口MTU值为设置小了,通过命令查询发现是1500,没有被重设过,当时有点想不通。

回来查了一下,发现自己的理解是错的,“TCP segment of a reassembled PDU”指的不是IP层的分片,IP分片在wireshark里用“Fragmented IP protocol”来标识。详细查了一下,发现“TCP segment of a reassembled PDU”指TCP层收到上层大块报文后分解成段后发出去。于是有个疑问,TCP层完全可以把大段报文丢给IP层,让IP层完成分段,为什么要在TCP层分呢?其实这个是由TCP的MSS(Maximum Segment Size,最大报文段长度)决定的,TCP在发起连接的第一个报文的TCP头里通过MSS这个可选项告知对方本端能够接收的最大报文(当然,这个大小是TCP净荷的大小),以太网上这个值一般设置成1460,因为1460Byte净荷+20Byte TCP头+20Byte IP头= 1500字节,正好符合链路层最大报文的要求。

至于收到一个报文后如何确定它是一个”TCP segment”?如果有几个报文的ACK序号都一样,并且这些报文的Sequence Number都不一样,并且后一个Sequence Number为前一个Sequence Number加上前一个报文大小再加上1的话,肯定是TCP segment了,对于没有ACK标志时,则无法判断。

既然收到的TCP报文都是180Byte的segment,那么应该是协商的时候PC端告知了MSS为180Byte,至于为什么这样,只能等抓包后确认是MSS的问题再排查了。另外,有一种情况也可能导致这个问题:被测系统因为MTU为220Byte而设置MSS为180Byte,但是这种情况现在可以排除,因为前面讲过,已经查询过MTU值为1500。

3. Tcp previous segment lost(tcp先前的分片丢失)

解答:

(1)、“TCP Previous segment lost” errors are not “fatal” errors. They simply indicate that the sequence number in the arriving packet is higher than the next-expected sequence number, indicating that at least one segment was dropped/lost. The receiving station remedies this situation by sending duplicate ACKs for each additional packet it receives until the sender retransmits the missing packet(s). TCP is designed to recover from this situation, which is why the image is downloaded correctly despite having a (briefly) missing packet.

If you are getting a large number of lost packets, then there is likely a communication problem between the sender and receiver. A common cause of this is un-matched duplex settings between the PC and the switch.

We (our lab) recently upgraded to Ethereal 0.10.14 with WinPCap 3.1. If I remember correctly, we had previously been using 0.10.2 with WinPCap 3.0. However, since the upgrade we have been noticing several issues.

The first issue is with “TCP Previous segment lost” and “TCP CHECKSUM INCORRECT” messages appearing in the Packet Listing window. We do not remember seeing these in the previous version of Ethereal, or at least not nearly as many as we are seeing now. For example, one task for the student instructional part of the lab involves visiting a website containing two images and observing the network activity. After the two GET requests are sent for the images, it is not uncommon for one image to be returned with a typical 200 OK response packet, but the response packet for the other image will be displayed as “TCP Previous segment lost.” However, both images are downloaded and displayed perfectly fine in the browser. I would think that the segment lost error would mean the object wasn’t returned correctly and shouldn’t be able to be displayed, but apparently that is not the case. (The cache had been cleared when this was performed, so it was not defaulting to a local copy of the image.)

Another problem we’ve been noticing is that some packets simply aren’t displayed in the Packet Listing window, even when they are obviously received. Using the same example as above, after the two GET requests are sent for the images, it is not uncommon for one image to be returned with a typical 200 OK response, but the other response will not appear. Yet both images are successfully displayed in the browser. Is this a problem with Ethereal not detecting the packets?

I’m not sure how typical this is, but we seem to be experiencing these issues often with 0.10.14 while we never did with 0.10.2. Could it also be an issue with WinPCap, and not necessarily Ethereal? I’m just trying to find some answers as to why we are seeing a sudden abundance of TCP related errors and uncaptured packets. Thanks.

(2)、I have a network client application that runs fine while I am debugging (no TCP errors),

but when I run the release version, it runs incredibly slow. It runs as a series of

transactions, where each transaction is a separate connection to the server. Wireshark

analysis has determined that about 50% of all transactions involve the series:

TCP Previous Segment Lost

TCP Dup ACK

RST

The RST consumes 3 seconds per transaction, which is a Big Deal. So to prevent it, I must

prevent the initial “TCP Previous Segment Lost” (which seems, on the surface, to merely be

a time-out on a particular segment).

In the following clip, the SYN packet suffers from the “TCP Previous Segment Lost” condition.

0.000640 seconds seems like too short of a time to declare this condition, as many previous

successful transactions took much longer to be successfully SYN-ACK’ed.

Can somebody explain “TCP Previous Segment Lost” in this context to help me troubleshoot my

problem?

Any help would be appreciated.

Here is a clip of a problem transaction:

fffgs

4. Tcpacked lost segment(tcp应答丢失)

5. Tcp window update(tcp窗口更新)

6. Tcp dup ack(tcp重复应答)

TCP may generate an immediate acknowledgment (a duplicate ACK) when an out- of-order segment is received. This duplicate ACK should not be delayed. The purpose of this duplicate ACK is to let the other end know that a segment was received out of order, and to tell it what sequence number is expected.

当收到一个出问题的分片,Tcp立即产生一个应答。这个相同的ack不会延迟。这个相同应答的意图是让对端知道一个分片被收到的时候出现问题,并且告诉它希望得到的序列号。

Since TCP does not know whether a duplicate ACK is caused by a lost segment or just a reordering of segments, it waits for a small number of duplicate ACKs to be received. It is assumed that if there is just a reordering of the segments, there will be only one or two duplicate ACKs before the reordered segment is processed, which will then generate a new ACK. If three or more duplicate ACKs are received in a row, it is a strong indication that a segment has been lost. TCP then performs a retransmission of what appears to be the missing segment, without waiting for a retransmission timer to expire.

7. Tcp keep alive(tcp保持活动)

在TCP中有一个Keep-alive的机制可以检测死连接,原理很简单,TCP会在空闲了一定时间后发送数据给对方:

1.如果主机可达,对方就会响应ACK应答,就认为是存活的。

2.如果可达,但应用程序退出,对方就发RST应答,发送TCP撤消连接。

3.如果可达,但应用程序崩溃,对方就发FIN消息。

4.如果对方主机不响应ack, rst,继续发送直到超时,就撤消连接。这个时间就是默认

的二个小时。

uses WinSock2;

procedure TForm1.IdTCPServer1Connect(AThread: TIdPeerThread);

type

TCP_KeepAlive = record

OnOff: Cardinal;

KeepAliveTime: Cardinal;

KeepAliveInterval: Cardinal

end;

var

Val: TCP_KeepAlive;

Ret: DWord;

begin

Val.OnOff:=1;

Val.KeepAliveTime:=6000; //6s

Val.KeepAliveInterval:=6000; //6s

WSAIoctl(AThread.Connection.Socket.Binding.Handle, IOC_IN or IOC_VENDOR or 4,

@Val, SizeOf(Val), nil, 0, @Ret, nil, nil)

end;

——————————————————–

KeepAliveTime值控制 TCP/IP 尝试验证空闲连接是否完好的频率。如果这段时间内没有活动,则会发送保持活动信号。如果网络工作正常,而且接收方是活动的,它就会响应。如果需要对丢失接收方敏感,换句话说,需要更快地发现丢失了接收方,请考虑减小这个值。如果长期不活动的空闲连接出现次数较多,而丢失接收方的情况出现较少,您可能会要提高该值以减少开销。缺省情况下,如果空闲连接 7200000 毫秒(2 小时)内没有活动,Windows 就发送保持活动的消息。通常,1800000 毫秒是首选值,从而一半的已关闭连接会在 30 分钟内被检测到。

KeepAliveInterval值定义了如果未从接收方收到保持活动消息的响应,TCP/IP 重复发送保持活动信号的频率。当连续发送保持活动信号、但未收到响应的次数超出TcpMaxDataRetransmissions的值时,会放弃该连接。如果期望较长的响应时间,您可能需要提高该值以减少开销。如果需要减少花在验证接收方是否已丢失上的时间,请考虑减小该值或TcpMaxDataRetransmissions值。缺省情况下,在未收到响应而重新发送保持活动的消息之前,Windows 会等待 1000 毫秒(1 秒)。

KeepAliveTime根据你的需要设置就行,比如10分钟,注意要转换成MS。

XXX代表这个间隔值得大小

8. Tcp retransmission(tcp重传)

作为一个可靠的传输协议,传输控制协议(TCP)在发送主机需要从目标主机收到一个包时确认。If the sender does not receive that acknowledgment within a certain amount of time, it acts under the assumption that the packet did not reach its destination and retransmits the packet.如果发件人没有收到的时间内一定之金额,确认,它的行为假设下,该数据包没有到达其目的地,以及转发数据包。