先规定简单的传输协议
文件名的长度(两个字节) | 文件名(长度不定,由前两个字节决定) | 文件内容的长度(8个字节) | 内容 (长度不定,由前8个字节决定) |
使用nodejs 实现服务端
var net = require('net'); var fs = require( 'fs' ); var server = net.createServer(); server.on('connection', function(client){ console.log('client connected to server。IP:%s,port%s', client.remoteAddress, client.remotePort); var isFirst = true ; var fd ; var contentLength = 0 ; var dataTotalLength =0; var receiveData = Buffer.alloc(0); // data长度不可控,需要自己合并多个data直至指定的要求或者发送的socket发送FIN client.on('data', function(data){ if (isFirst) { receiveData = Buffer.concat([receiveData, data]); if (receiveData.length < 2) { console.error("奇葩,data长度为%s", data.length) return ; }else { var start = 0; var end = 2; var buffer = Buffer.from(receiveData.slice(start, end)); var filenamelength = buffer.readInt16BE(); console.log("文件名长度为:%s" , filenamelength); } if (receiveData.length < filenamelength + 10) { console.error("奇葩,data长度为%s", data.length) return; } var start = end; var end = end + filenamelength; var buffer = Buffer.from(receiveData.slice(start, end)); var filename = buffer.toString(); console.log("文件名为:%s" ,filename); var start = end; var end = end + 8; var buffer = Buffer.from(receiveData.slice(start, end)); contentLength = buffer.readBigInt64BE(); console.log("文件长度为:%s" , contentLength); var newfilename = "data/" + filename; fd = fs.openSync(newfilename, 'w+'); isFirst = false; var var1 = receiveData.slice(end) fs.writeFileSync(fd, var1); dataTotalLength += receiveData.length - end; }else { fs.writeFileSync(fd, data); dataTotalLength += data.length; } if(dataTotalLength == contentLength){ console.log("文件传输完毕"); } }); client.on("end", function () { console.log(dataTotalLength); console.log(contentLength); console.log("关闭socket!") }); client.on("error",function (e) { console.error(e); }) }); server.listen(7251, '0.0.0.0');
虽然不是很喜欢这种弱类型的语言,但是nodejs与生俱来的的多线程特性还是让人震惊,以上实现中没有出现任何与Thread相关的关键词,但这却是一个支持多线程上传的服务。
java 客户端(多线程上传)
package test; import java.io.DataOutputStream; import java.io.File; import java.io.FileInputStream; import java.net.Socket; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.atomic.AtomicInteger; public class FileTransferClient{ public static final String SERVER_IP = "127.0.0.1"; // 服务端IP public static final int SERVER_PORT = 7251; // 服务端端口 public static void main(String[] args) throws Exception{ String datadir = "C:\\Users\\who\\Desktop\\testdata\\data\\face" ; File directory = new File(datadir) ; assert directory.isDirectory() ; ExecutorService executor = Executors.newFixedThreadPool(10); int count = 0 ; for (File file : directory.listFiles()) { if(file.isFile()) { Task task = new Task(file.getAbsolutePath()); executor.submit(task); count ++ ; } } while (true) { if (aCount.get() == count) { System.out.println("end"); executor.shutdown(); break; } Thread.sleep(1000); } } public static AtomicInteger aCount = new AtomicInteger(0); } class Task implements Runnable { private String filename; public Task(String filename){ this.filename = filename ; } @Override public void run() { try { Socket socket = new Socket(FileTransferClient.SERVER_IP, FileTransferClient.SERVER_PORT); FileInputStream fis = null; DataOutputStream dos = null; File file = new File(filename); if (file.exists()) { fis = new FileInputStream(file); dos = new DataOutputStream(socket.getOutputStream()); // 文件名和长度 byte[] namebytes = file.getName().getBytes(); dos.writeShort(namebytes.length); dos.write(namebytes); dos.writeLong(file.length()); dos.flush(); byte[] bytes = new byte[1024]; int length; while ((length = fis.read(bytes, 0, bytes.length)) != -1) { dos.write(bytes, 0, length); dos.flush(); } } if (fis != null) fis.close(); if (dos != null) dos.close(); if (!socket.isClosed()) { socket.close(); } } catch (Exception e) { e.printStackTrace(); } FileTransferClient.aCount.addAndGet(1); } }
nodejs 客户端
var net = require('net'); var fs = require("fs"); function sendfile(filename) { console.log(filename); var socket = new net.Socket(); socket.on("ready",function () { var contentSize = 0 ; fs.stat(filename,function(error,stats){ if(error){ console.error(error) }else{ contentSize = stats.size ; console.log(contentSize) ; } var length = 2 + filename.length + 8 ; var buffer = Buffer.alloc(length) ; buffer.writeInt16BE(filename.length,0); buffer.write(filename,2); var bigInt = BigInt(contentSize); var start = Number(2 + filename.length); buffer.writeBigInt64BE(bigInt,start); socket.write(buffer.slice(0,length)) ; // 大文件应该一次读取不了,不想写了 var data = fs.readFileSync(filename); socket.write(data); }) ; }) ; socket.on("error",function (e) { console.error(e) }) ; socket.connect(7251, 'localhost'); } sendfile("app.js"); sendfile("client.js");
nodejs 的 socket在处理字节流时不能主动的去read, 只能靠data事件来触发回调函数,但是data并不是一次能接收到所有的字节流,因此在处理时需要自己合并多个data才行,以下是一个小小的使用建议。
var net = require('net'); var client = new net.Socket(); var receiveData = Buffer.alloc(0); client.connect(8899, 'localhost'); client.on('data', function (data) { // 这里只是合并data receiveData = Buffer.concat([receiveData, data]); }); client.on('error', function (e) { console.error(e) }); client.on('end', function (e) { // 接收完所有data再处理 console.log(receiveData.toString()); });
来源:oschina
链接:https://my.oschina.net/qidis/blog/3193189