AS3 socket 连接 FTP 历程

这篇博客详细记录了使用AS3通过Socket连接FTP服务的步骤和注意事项,包括FTP命令交互、Flash安全沙箱、文件选择、端口授权、AS与JS交互,以及解决丢包问题的策略。虽然无法提供完整源码,但提供了关键实现思路。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

AS3 socket 连接 FTP 历程

本文主要记录AS3(flash)基于socket连接ftp服务的处理过程,过程比较艰难,坑很多,记录下来,也许能够帮到有缘人。限于公司相关政策,未能放出全部源码请见谅,主要提供解决思路,以及相关问题的处理。

相关技术

  1. AS3 socket ,主要用于ftp连接处理;
  2. ftp命令以及参数响应 ,进行数据交互的过程理解;
  3. flash FileReference 类 ,文件选择;
  4. flash 安全沙箱,以及端口授权
  5. AS,js交互
  6. ftp 被动模式传输的丢包问题,如果有拆包处理,并且发送的文件不完整;

实现过程简述

基本结构如下图所示:

在这里插入图片描述

  1. flash客户端编写
    实现思路

使用FileReference来进行文件选择,注册监听
fileReference.addEventListener(Event.SELECT, onFileSelect);//文件选择触发
fileReference.addEventListener(ProgressEvent.PROGRESS, progressHandle);//文件加载监听,用于刷新百分比进度
fileReference.addEventListener(Event.COMPLETE, completeHandle);//文件加载完成事件
在completeHandle中调用我们实现的ftp上传服务处理。
其中过程注册的js交互方法就不列举了。

Socket交互处理

private function connect():void{
   		Security.loadPolicyFile("xmlsocket://"+serverIP+":"+_securityPort);
   		ftpSocket = new Socket();
   		ftpSocket.addEventListener(ProgressEvent.SOCKET_DATA, ftpSocketDataHandle);
   		ftpSocket.addEventListener(SecurityErrorEvent.SECURITY_ERROR,ftpSocketSecurityErrorHandle);
   		ftpSocket.addEventListener(IOErrorEvent.IO_ERROR,ftpIOErrorHandle);
   		ftpSocket.addEventListener(Event.CONNECT,onConnect);
   		ftpSocket.connect(serverIP, serverPort);
}  

其中Security.loadPolicyFile方法为授权检查,我们访问的端口需要通过flashplayer的安全沙箱检查,flashplayer会在访问之前调用843端口,这里我们可以明确定义要访问的端口,例如:1234。在ftp服务器需要创建一个socket服务,此处我使用java实现了一个。

package org.jod.web.center.flashUpload;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;

public class FTPUploadAuthPortListener extends Thread{
	
	private String authxml = "<cross-domain-policy><allow-access-from domain=\"*\" to-ports=\"*\" /></cross-domain-policy>";
	
	private int port;
	
	private boolean keepRunning = true;
	
	private ServerSocket serverSocket;
	
	public FTPUploadAuthPortListener(int port) {
		this.port = port;
	}
	
	public void stopForSafe() {
		keepRunning = false;
		if(!serverSocket.isClosed()) {
			try {
				serverSocket.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
	
	@Override
	public void run() {
		try{ 
			serverSocket = new ServerSocket(port);
			Socket client=null; 
			while(keepRunning){
				System.out.println("服务器"+port+"已启动,等待客户端请求。。。。");
				client = serverSocket.accept();
				BufferedReader br=new BufferedReader(new InputStreamReader(client.getInputStream()));
				char[] ch=new char[22];
				br.read(ch, 0, ch.length);
				StringBuffer sb=new StringBuffer();
				for(int i=0;i< ch.length;i++){
					sb.append(ch[i]);
				}
				String st=sb.toString();
				System.out.println(port+"接受客户端:["+client.getInetAddress().getHostAddress()+"]请求");
				System.out.println("请求信息:"+st);
				if(st.indexOf("<policy-file-request/>")!=-1){
					OutputStream os = client.getOutputStream();
					os.write(authxml.getBytes("utf-8"));
					os.flush();
					os.close();
				}
				if(!client.isClosed()) {
					client.close(); 
				}
				System.out.println("服务响应结束");
			}
			if(!serverSocket.isClosed()) {
				serverSocket.close();
			}
			System.out.println("服务器已关闭。"); 
		}catch(Exception e){ 
			e.printStackTrace(); 
		} 
	}

	public static void main(String args[]){
		new FTPUploadAuthPortListener(843).start();
		new FTPUploadAuthPortListener(1234).start();
	}
}

FTP 状态说明
在监听事件ftpSocketDataHandle中我们使用
var serverResponse:String = socket.readUTFBytes(socket.bytesAvailable);
var stateResponse:String = serverResponse.substr(0, 3);
来获取ftp服务交互的信息
以下状态均来自主socket的交互信息。
"220": //FTP连接就绪
sendCommand("USER "+this.userName);
case “331”: //用户名正确,请继续输入口令
sendCommand("PASS "+this.userPwd);
case “230”: //登入成功
//指定下載文件的類型,I是二進位文件,A是字元文件
sendCommand(“TYPE A”);//設定TYPE為ASCII
sendCommand(“TYPE I”);//設定上傳的編碼為8-bit binary
if(!StringUtil.isEmpty(userDir)) //设定FTP 上传目录
sendCommand("CWD "+userDir);
sendCommand(“PASV”);//passive模式
case “227” : //进入被动模式 返回ip以及端口号 (h1,h2,h3,h4,p1,p2).
解析地址信息创建dataSocket
dataSocket = new Socket(clientIP,clientPort);
dataSocket.addEventListener(ProgressEvent.SOCKET_DATA, receiveData);
dataSocket.addEventListener(IOErrorEvent.IO_ERROR,dataIOErrorHandle);
dataSocket.addEventListener(SecurityErrorEvent.SECURITY_ERROR,dataSocketSecurityErrorHandle);
dataSocket.addEventListener(OutputProgressEvent.OUTPUT_PROGRESS,outputProgress_Handler);
sendCommand("STOR "+this.fileName);//告知要上传的文件
case “125”: //服务器告知可以开始文件传输
curPos = 0; //设定传输的开始位置
beginTime = new Date();//记录开始时间(为解决后续的丢包问题做铺垫)
intervalID = setInterval(sendData,30);//设置定时执行处理,防止flashplayer假死。
sendData时需要处理分包,以及进度控制
case “226”: //关闭数据连接。请求的文件操作成功(比如,文件传输或文件终止)
注意此时只是说明了dataSocket已经关闭

额外用到的状态
case “530”: //530 用户名或者密码有误!
case “421”: //连接超时!

在outputProgress_Handler做特殊处理以解决丢包问题

丢包问题产生原因是因为网络传输时有延时,客户端flush之后还需要一些时间处理,网上的代码都是在处理完成时直接关闭了socket导致数据尚未传输,因此我们需要解决socket提前关闭的问题。
由于ftp文件在写入时不允许访问,size命令无法执行,因此我们只能采用延时处理的方式,先计算得到全局的平均速度,计算传输整包的时间再乘以2得到等待时间

private function outputProgress_Handler(event: OutputProgressEvent): void {
	var bytesLoaded: Number = 0;
	var bytesTotal: Number = 0;
	bytesLoaded = event.bytesTotal;
	bytesTotal = this.fileData.length;
	var fileUploadPercent : uint = bytesLoaded / bytesTotal * 100; 
	if(lastPercent==fileUploadPercent){
		
	}else{
		dispatcher.dispatchEvent(new FileUpDownloadEvent(FileUpDownloadEvent.PROGRESS,fileUploadPercent+"%")); 
		lastPercent = fileUploadPercent;
	}
	if(bytesLoaded>=bytesTotal){
		endTime = new Date();
		var number:Number = Math.round((endTime.time-beginTime.time)/1000);
		var speedByte:Number = bytesTotal/number;
		var willwait:Number = chunk/speedByte*2000;
		if(willwait<200){
			willwait = 200;
		}
		setTimeout(closeDataSocket,willwait);
	}
}
  1. AS JS交互
    讲交互的文章有很多,在此就不细说了。

as调用js
js 代码
function swfInItCallBack(msg){
doSome;
}
as 代码
ExternalInterface.call(“swfInItCallBack”,msg);

js调用as
as 代码
ExternalInterface.addCallback(“getFile”,getFile);//注册方法
js 代码
thisMovie:function(){
return window[this.movieId]?window[this.movieId]:document[this.movieId];
}
thisMovie.getFile();

参数对象是有区别的,传递我使用的json形式的字符串,或者基本类型。

注意事项

flash程序中应含有一个按钮触发文件选择,外部程序调用含有browse方法的方法时会有异常

不同浏览器生成的swf文件引用代码是不同的,最好使用swfobject.js进行处理,否则会取不到swf对象,也就无法进行交互

由于ftp数据传输socket是没有响应的(其他的没有测试,本例使用win7自带ftp服务),因此处理socket的关闭时机尤为重要。

flash中的按钮可能需要自己编写,默认的button应该不能满足html引用时的样式兼容

ftp端口授权处理需要放在服务器,这里可能有人有疑问,很多人都使用过swfupload,为什么它不需要授权处理,其实是有授权处理的,由于http服务器一般都支持flash的授权访问,所以不需要单独进行配置。

flash沙箱问题,这个的确比较恶心,浪费的时间不少。一定要注意本地应用与web插件的区别,本文提到的实现是基于web的需要授权,

在本机测试时使用抓包工具wireshark抓不到授权处理的包,可以使用以下方法
1.以管理员身份运行cmd
2.route add 本机ip mask 255.255.255.255 网关ip
如:route add 172.16.51.115 mask 255.255.255.255 172.16.1.1
使用完毕后用route delete 172.16.51.115 mask 255.255.255.255 172.16.1.1删除,否则所有本机报文都经过网卡出去走一圈回来很耗性能。
此时再利用wireshark进行抓包便可以抓到本机自己同自己的通信包,这样配置的原因是将发往本机的包发送到网关,而此时wireshark可以捕获到网卡驱动的报文实现抓包。
但这样有一个缺点,那就是本地请求的URL的IP只能写本地的IP地址,不能写localhost或127.0.0.1,写localhost或127.0.0.1还是抓不到包。

socket 直连21端口时,无法连接,此时也无授权数据包交互,可能是flashplayer web端屏蔽了吧,具体的原因不明,防火墙,抓包都试了。但是java写socket能连接。

flash有安全访问限制时使用firefox可能导致假死(无法切换页面,flash也不加载,不显示无响应),尝试过网上的解决方案都没好使。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值