Chinaunix首页 | 论坛 | 博客
  • 博客访问: 7446250
  • 博文数量: 1758
  • 博客积分: 18684
  • 博客等级: 上将
  • 技术积分: 16252
  • 用 户 组: 普通用户
  • 注册时间: 2010-06-02 10:28
个人简介

啥也没写

文章分类

全部博文(1758)

文章存档

2024年(4)

2023年(44)

2022年(39)

2021年(46)

2020年(43)

2019年(27)

2018年(44)

2017年(50)

2016年(47)

2015年(15)

2014年(21)

2013年(43)

2012年(143)

2011年(228)

2010年(263)

2009年(384)

2008年(246)

2007年(30)

2006年(38)

2005年(2)

2004年(1)

分类: 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会怎样,可能会边传边写,但数据就不可靠了。

 

阅读(1132) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~