socket实现简陋远程文件管理

By money at 2022-01-18 • 1人收藏 • 483人看过

最近需要对多台远程设备文件统一管理,简单撸了一套CS代码。

server端

import win.ui;
/*DSG{{*/
var winform = win.form(text="aardio form";right=759;bottom=469)
winform.add()
/*}}*/

import web.view;
import fsys;
import fsys.file;
import win.dlg.message;

import hpsocket.httpServerEx;
var hpHttpServer = hpsocket.httpServerEx();
hpHttpServer.threadGlobal = {
	winform = winform;
	rpcexternal ={
		$callWorker = function($, data){
			data.socket=$;
			thread.command.post(data.action, data)
		}
	}
}
//监听线程被创建后触发
hpHttpServer.onThreadCreated = function(){
	import thread.command;
	import time.performance;
	import string.xxtea;
	import console;
	
}

//监听线程退出前触发此事件
hpHttpServer.onThreadEnd = function(){}

//允许升级为WebSocket协议
hpHttpServer.onUpgrade = function(hpHttpServer,connId,upgradeType){
	if(upgradeType!=1) return -1;
	hpHttpServer.sendWsSwitchingProtocols(connId);
	
	hpHttpServer.notify(connId, "rpcClientId", connId)
}

hpHttpServer.onWsMessageHeader = function(hpHttpServer,connId,final,reserved,opCode,mask,bodyLen){
	hpHttpServer.reallocString(connId,bodyLen);//初始化缓冲区
}

//接收到WebSocket请求数据
hpHttpServer.onWsMessageBody = function(hpHttpServer,connId,pData,len){
	hpHttpServer.appendString(connId,pData,len);
}

hpHttpServer.afterJsonStringify = function(jsonData){
	console.dump("afterJsonStringify", #jsonData)
	return jsonData; 
}

hpHttpServer.beforeJsonParse = function(connId, jsonData){
	return jsonData; 
}

//WebSocket请求数据接收完成
hpHttpServer.onWsMessageComplete = function(hpHttpServer,connId){
	var data = hpHttpServer.getString(connId);
	if( data ){
		hpHttpServer._translateMessage(connId, data);
	}
}

hpHttpServer.onClose = function(hpHttpServer,connId,enOperation,errCode){
	hpHttpServer.reallocString(connId,0);//释放缓冲区
	thread.command.post("onClientClose", connId);
}
sleep(1000)
import wsock;
var port = wsock.getFreePort()
port=8999
hpHttpServer.start(/*IP*/,port);

import console
console.dump("已启动:",hpHttpServer.getWsUrl())

var wb = web.view(winform);

var dirs={};

newFileReader = function(id, f){
	var port = wsock.getFreePort()
	thread.create(function(id, port, f){
		import wsock.tcp.server;
		import wsock.tcp.client;
		import thread.command;
		import fsys;
		import math;
		import console;
		
		console.dump("文件线程已经启动,端口:", port)
		var tcpServer = wsock.tcp.server("0.0.0.0",port)
		tcpServer.forever(
			function(acceptSocket){
				console.dump("客户端连接")
				var client = wsock.tcp.client(,acceptSocket);
				
				var file = io.open(f,"w+b");//注意io.open默认是文本方式写入的,b指定二进制模式
				var size = math.size64();
				var buffer = raw.buffer(0x4000);
				for(readSize,remainSize in client.eachReadBuffer(buffer) ){  
					size.add(readSize);
					console.dump( size.format())
    				thread.command.post("downloadSize", id, size.format())
					file.writeBuffer(buffer,readSize);	
				}
				file.close();
				
				client.close();
				tcpServer.close();
			}
		)
		console.dump("文件线程关闭")
	},id, port, f)
	return port; 
}

var download={}
wb.export ({
	enumDir = function(path){
		hpHttpServer.publish("enumDir", path)
	};
	fileInfo = function(id, path){
		console.dump(id)
		hpHttpServer.publish("fileInfo", id, path)
	};
	enumParent = function(path){
		path = fsys.getParentDir(path);
		hpHttpServer.publish("enumDir", path)
	}
	download = function(id, path){
		var f = io.fullpath("/user_download/"+string.replace(path,"@@:",''))
		if(io.exist(f)){
			if(!winform.msgAsk("已经存在此文件,是否重新下载?")){
				process.exploreSelect(f);
				return ; 
			}
		}
		//先创建一个空文件及对应的完整目录
		string.save(f, '')
		var port = newFileReader(id, f)
		download[path] = f;
		hpHttpServer.publish("download", id, path, port)
	}
})

wb.html = /**
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>文件浏览</title>
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <!-- Bootstrap -->
    <link href="https://cdn.staticfile.org/twitter-bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet" media="screen">
    <style>
      #fork{position:fixed;top:0;right:0;_position:absolute;z-index: 10000;}
      .bottom{margin: 20px auto; width: 100%; text-align: center;}
      .container{width: 1080px; margin: 50px auto;}
    </style>
    <script src="http://libs.baidu.com/jquery/2.0.0/jquery.min.js"></script>
    <script src="https://layui.11dz.cn/layui/release/layer/dist/layer.js"></script>
  <head>
  <body>
  <script>
  	parentPath="";
  	layerIndex=0;
	setVolume = function(data){
		var html = "文件浏览";
		data.forEach(function(item){
			html += '<a class="driver" path="'+item+'" href="javascript:void(0);">'+item+'/</a> '
		})
		document.getElementById("h1").innerHTML = html;
		$(".driver").off("click");
		$(".driver").on("click", function(){
			loading()
			parentPath = $(this).attr("path")
			enumDir($(this).attr("path"))
		});
	}
	bindDownClick = function(cls){
		setTimeout(function(){
			$(".down").off("click");
			$(".down").on("click", function(){
				var file = parentPath + $(this).attr("path")
				var id = $(this).data("id")
				download(id, file);
			});
		},100)
	}
	downloadSize = function(id, size){
		var s = $("#s_"+id)
		s.text(s.data("size")+"/"+size)
	}
	downEnd = function(id){
		var s = $("#d_"+id)
		s.text("已下载")
	}
	setFileInfo = function(id, time, size){
		loadend()
		var path = $("#"+id).attr("path")
		var txt = $("#"+id).text()
		document.getElementById(id).outerHTML = '<a id="'+id+' class="file" path="'+path+'" href="javascript:void(0);">'+txt+'</a>   '+time+'    <span id="s_'+id+'" data-size='+size+'>'+size+'</span>       <a id="d_'+id+'" data-id="'+id+'" path="'+path+'" class="down" href="javascript:void(0);" >下载</a>'
		bindClick('file')
		bindDownClick()
	}
	loading = function(){
		layerIndex = layer.load(1, {
  			shade: [0.1,'#fff'] //0.1透明度的白色背景
		});
	}
	loadend = function(){
		layer.close(layerIndex)
	}
	bindClick = function(cls){
		setTimeout(function(){
			$("."+cls).off("click");
			$("."+cls).on("click", function(){
				loading()
				if(cls=="dir"){
					if($(this).text()!=".."){
						parentPath += $(this).attr("path")
					}
					enumDir(parentPath)
				}else if(cls=="file"){
					var file = parentPath + $(this).attr("path")
					fileInfo($(this).attr("id"), file)
				}
			});
			$(".parent").off("click");
			$(".parent").on("click", function(){
				loading()
				enumParent(parentPath)
			});
		},100)
	}
	
	setRow = function(parent, data, cls, clear){
		parentPath = parent;
		loadend()
		var html = document.getElementById("pre").innerHTML;
		if(clear){
			html = "";
		}
		if(parentPath.length>3 && cls=="dir"){
			html = '<a class="parent" href="javascript:void(0);">..</a> \r\n';
		}
		data.forEach(function(item,id){
			if(cls=="dir"){
				html += '<a class="'+cls+'" path="/'+item+'" href="javascript:void(0);">/'+item+'</a> \r\n'
			}else {
				html += '<a id="f_'+id+'" class="'+cls+'" path="/'+item+'" href="javascript:void(0);">/'+item+'</a> \r\n'
			}
		})
		document.getElementById("pre").innerHTML = html;
		bindClick(cls)
		document.getElementById("h2").innerHTML ="路径:"+parentPath;
	}
	</script>
    <div class="container">
      <h1 id="h1">文件浏览</h1>
	  <h2 id="h2"></h2>
<pre id="pre">

</pre>
	</div>
	<div class="bottom"> </div>

  </body>
</html>

**/

import thread.command;
import process;

var cmd = thread.command(winform);
cmd.setVolume = function(data){
	var json = web.json.stringify(data.data)
	wb.doScript("setVolume("+json+")")
}
cmd.enumDir = function(data){
	var json = web.json.stringify(data.dir)
	wb.doScript("setRow('"+data.path+"',"+json+", 'dir', true)")
	json = web.json.stringify(data.file)
	wb.doScript("setRow('"+data.path+"',"+json+", 'file', false)")
}
cmd.fileInfo = function(data){
	if(data.sc){
		var js = string.format("setFileInfo('%s','%s','%s')", data.id, data.writeTime, data.size)
		wb.doScript(js)
	}else {
		win.msgbox(data.msg);
		wb.doScript("loadend()")
	}
}
cmd.downloadSize = function(id, size){
	var js = string.format("downloadSize('%s','%s')", id, size)
	//console.dump(js)
	wb.doScript(js)
}

cmd.download = function(data){
	if(!data.sc){
		win.msgbox(data.msg);
	}else {
		process.exploreSelect(download[data.path]);
	}
	var js = string.format("downEnd('%s')", data.id)
	console.dump(js)
	wb.doScript(js)
}


winform.show();
win.loopMessage();
return winform;

代码中有自定义库hpsocket.httpServerEx,作用与web.socket.jsonServer功能一致,可替换掉运行。


启动后这样,等待client连接:


image.png

client端:

import win
import sys.volume;
import web.socket.jsonClient;
import fsys;
import fsys.file;

if(_STUDIO_INVOKED){
	import console
	console.open()
}

var dyws = web.socket.jsonClient();
var commonHost = "*********";
if(_STUDIO_INVOKED){
    commonHost = "127.0.0.1:8999";
}
var wsServer = "ws://"+commonHost+"/jsonrpc"
connectServer = function(){
	win.setTimeout(
		function(){
			//console.dump(wsServer)
			if(!userClash){
				dyws.connect(wsServer);	
			}
		},1000
	)
}

dyws.on("open",function(){
	//console.dump(open)
	var json = {}
	var drives = sys.volume.getLogicalDrives()
	for(i,drv in drives){
    	var info = sys.volume.getInfo( drv)  ;
    	if(info){
    	    table.push(json, info.drive)
		}
	}
	dyws.$callWorker({
		action="setVolume";
        data=json
    });
})

dyws.on("enumDir",function(path){
	//console.dump("path", path)
	var file, dir = fsys.list(path,,"*.*")
	dyws.$callWorker({
		action="enumDir";
        file=file;
        dir=dir;
        path=path;
    });
})
dyws.on("fileInfo",function(id, path){
	//console.dump("path", id, path)
	var file = fsys.file(path)
	if(file){
		dyws.$callWorker({
			action="fileInfo";
        	writeTime=tostring(file.getTime().write);
        	size=fsys.formatSize(file.size64());
        	id=id;
        	sc=true
    	});
	}else {
		dyws.$callWorker({
			action="fileInfo";
        	writeTick=tonumber(file.getTime().write);
        	id=id;
        	sc=false;
        	msg="打开文件失败"
    	});
	}
})
dyws.on("download",function(id, path, port){
	var ret,err = thread.invokeAndWait(
		function(path, port){
			import wsock.tcp.client;
			var socket = wsock.tcp.client()
			var server = "*********";
			if(_STUDIO_INVOKED){
    			server = "127.0.0.1";
			}
			if(socket.connect(server,port) ){
				var file,err = io.open(path,"rb") 
				if( file ){  
					while( 
		    			var buf;
		    			buf,readSize = file.read(4096);
		    			buf
					) {
						socket.writeBuffer(buf,readSize) ; 
					} 
					file.close(); 
					return true, "下载完成"; 
				}else {
					return false, "打开文件失败"; 
				}
			}else {
				return false, "连接失败"; 
			}
		}, path, port
	)
	
	dyws.$callWorker({
		action="download";
        path=path;
        id=id;
        sc=ret;
        msg=err
    });
})

connectServer()

win.loopMessage()

clinet启动后,连接到server,并枚举盘符发送给服务端

image.png

点击盘符,开始枚举目录及文件
image.png

点击目录,继续向下枚举,两个小点返回上级,跟其它网页版文件管理器大致相同

点击文件,获取文件的信息(只取了修改时间和文件大小)

image.png

点击下载,server新建一条tcp通道,并通知client连接新端口,连接上后自动传输文件,文件传输完成后,自动打开所在路径

image.png


此代码仅供学习讨论,请勿用于非法用途,后果自负,与本人无关

2 个回复 | 最后更新于 2022-01-21
2022-01-18   #1

很实用,赞一个

2022-01-21   #2

感谢分享

登录后方可回帖

登 录
信息栏
公告:
个人博客
专注分享
可在分享中适当提问
谢绝纯提问
否则不再提醒一律
删帖
谢谢合作!



本站域名:HtmLayout.Cn
aardio可以快速开发上位机,本站主要记录了学习过程中遇到的问题和解决办法及aardio代码分享

这里主要专注于aardio学习交流和经验分享.
纯私人站,当笔记本用的,学到哪写到哪.

Aardio 官方站:Aardio官方
Aardio最新功能:Aardio官方更新日志
本 站 主 站:Stm32cube中文网
Sciter中文在线文档Sciter在线学习文档
空间赞助:才仁机械
Loading...