啥也没写
分类: LINUX
2010-08-05 17:29:21
最近要搞DR自动化
通过agent方式管理所有DR工作
其中有一项是DR备份
本来我的任务是编写自动更新脚本功能
但由于使用LWP的模拟表单在传送大文件时占用大量内存(传送机制应该是先block到内存里再发送)
那死翘翘了,只能自己实现分块传输,又要我现学现卖
以下是搞了两天的代码
思想是先用wireshark抓包分析IE提交表达的数据
然后自己拼装http head,boundary等数据
最后把文件分块,和http head拼在一起发送
至于接收代码逻辑就比较简单了
可以通过readline一行一行读
处理好boundary就好了
另外文件信息也自己组装发送
这样就完全模拟了IE的表单提交
这里要感谢老大的指导:文件网络传送无非就是数据的拆分和组合
现在我对这句话有深刻体会!
废话少说,公布代码
==========发送方send_file_by_socket.pl==========
#!/usr/bin/perl
use IO::Socket;
use IO::File;
use strict;
my $fileName = @ARGV[0];
if($fileName eq ""){
die("please chose a file!\n");
}
my $target = "/get_file_by_socket.cgi";
my $head;
my $contentLength;
my $fileLength;
my $readSize = 1048576;
my $fh = new IO::File;
$fh->open("<$fileName");
$fileLength = (stat($fh))[7];
my $extLength;
if($readSize >= $fileLength){
$readSize = $fileLength;
$extLength = 1;
}else{
$extLength = int($fileLength/$readSize);
if(($extLength * $readSize) < $fileLength){
$extLength = $extLength + 1;
}
}
#set file info
my $fileInfo;
$fileInfo = "--cqw\n";
$fileInfo = $fileInfo . "--fileinfo--filename=$fileName\n";
$fileInfo = $fileInfo . "--fileinfo--filepath=$fileName\n";
$fileInfo = $fileInfo . "--cqw\n";
#set file info end
$contentLength = $fileLength + length($fileInfo) + (16 * $extLength);#16是两个--cqw\n和一个---\n的长度
#定义http head,\r\n这里和操作系统有关系的,内核不一样编码方式不一样,当初这里用的是\n结果出问题了,找了我一小时
$head = "POST ".$target." HTTP/1.1\r\n";
$head = $head . "Connection: close\r\n";
$head = $head . "Host: 10.11.77.40:30461\r\n";
$head = $head . "User-Agent: libwww-perl/5.79\r\n";
$head = $head . "Content-Length: $contentLength\r\n";#Content-Length一定要准确,少了会丢包,多了会造成等待超时
$head = $head . "Content-Type: multipart/form-data; boundary=cqw\r\n\r\n";
my $sock = IO::Socket::INET->new(PeerAddr => "10.11.77.40", PeerPort => 30461, Type => SOCK_STREAM);
#send head
if(defined($sock)){
print "connected!\n";
print "sending head...\n";
print $sock $head;
}else{
die("cant create socket connention!");
}
#send head end
#send fileinfo
print $sock $fileInfo;
#send fileinfo end
#send filecontent
my $fileContent;
my $line;
my $i = 1;
my $isLast = 0;
my $complated = 0;
while(($i * $readSize) <= $fileLength || $isLast == 1){
if(read($fh, $line, $readSize)){; #这里开始拆文件,每次读$readSize大小,然后加上boundary发出去
$fileContent = "--cqw\n";
$fileContent = $fileContent . $line . "---\n"; #---\n是我加上去的特殊标示,主要作用是产生一个换行,让接收文件的readline可以成功跳行,而---只是为了后期删掉时避免误删,确实会有误删的情况哦,大家要注意
$fileContent = $fileContent . "--cqw\n";
print $sock $fileContent;
$complated = $complated + length($line);
print "sending file,the size of this piece of cake is:".length($line).",complated:$complated bytes,total:$fileLength bytes,complated:". ($complated / $fileLength * 100)."%\n";
}
$i = $i + 1;
if($i * $readSize > $fileLength && ($i - 1) * $readSize <= $fileLength){
$isLast = 1;
}else{
$isLast = 0;
}
}
#send filecontent end
$fh->close;
$sock->close;
============接收方get_file_by_socket.cgi==========
#!/usr/bin/perl
use IO::File;
my $line;
my $isStartRead = 0;
my $isFileInfo = 0;
my $isContent = 0;
my $file;
while(my $line =
if($line eq "--cqw\n" && $isStartRead == 1){ #判断内容是否已经读完
$isStartRead = 0;
$isFileInfo = 0;
$isContent = 0;
next;
}
if($isStartRead == 1){ #判断是文件信息还是文件内容
if(index($line, "--fileinfo--") >= 0){
$isFileInfo = 1;
$isContent = 0;
}else{
$isContent = 1;
$isFileInfo = 0;
}
}
if($isFileInfo == 1){ #读文件信息
chomp($line);
$line =~ s/--fileinfo--//;
my %fileinfo = split(/=/, $line);
if($fileinfo{"filename"} ne ""){
print($fileinfo{"filename"}."\n");
if(!defined($file)){
$file = new IO::File(">>".$fileinfo{"filename"});
}
}
if($fileinfo{"filepath"} ne ""){
print($fileinfo{"filepath"}."\n");
}
}
if($isContent == 1){ #读文件内容并写入文件
$line =~ s/---\n//g;
if(defined($file)){
print $file $line;
}else{
die("create file failed!\n");
}
}
if($line eq "--cqw\n" && $isStartRead == 0){ #判断是否可以开始读内容
$isStartRead = 1;
next;
}
};
$file->close
结束语:
调试时可能会遇到socket成功连接,但连接后就没反应了,但程序没报错,但接收方也不接受文件的现象。
这应该是因为Content-Length设得过大,导致传送完毕后对方仍然等待数据,最后导致超时,所以设好Content-Length是关键,其他就没什么了。
测试结果,传送一个5G的文件观察
传送方CPU20%左右,内存基本为0
接收方先用lighttpd处理文件,CPU50%左右,内存基本为0
传送完毕后启动perl进程写文件,CPU50%左右,内存基本为0
有一点要注意,传送过程中perl会把临时文件写在根目录下,不知能否通过修改环境变量修改,不然又得改代码。
初步想出来的解决方法是组合多个httphead当多个文件发过去,麻烦,不知道把socket的Type改称UDP会怎样,可能会边传边写,但数据就不可靠了。