本文章提供的只有上传的方法,没有下载且不包含服务,只有客户端。
具体的逻辑:告知服务write,然后分块发送内容,块的大小默认512字节,socket.receive(packetRE)中,对packetRE进行了解析,其中有两个参数,一个是ack 一个是当前包的序号,在for循环中做了判断,索要的块数和当前读取文件的块数相同时才会继续。
Java使用DatagramSocket代表UDP协议的Socket,DatagramSocket本身只是码头,不维护状态,不能产生IO流,它的唯一作用就是接收和发送数据报,Java使用DatagramPacket来代表数据报,DatagramSocket接收和发送的数据都是通过DatagramPacket对象完成的。
receive(DatagramPacket p):从该DatagramSocket中接收数据报。
send(DatagramPacket p):以该DatagramSocket对象向外发送数据报。
以下是贴的代码:
Client.java
package com.hisome.ctrl.web.EMaintenance.DatagramSocketTFTP;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
/**
* Client is an incomplete implementation of the client side of TFTP specification
* It is a command line class that takes four arguments.
* 1. host - ipaddress of the server
* 2. GET or PUT - this is the operation that will be made upon the file.
* PUT will send a file to the server.
* GET will retrieve a file from the server.
* 3. fileDir - is the directory in which the file to be sent resides
* or the directory that the file will be created.
* 4. filename - is the file name of the file to be sent or received.
* @author David Latour
*
*/
public class Client
{
private static final String OCTET_STRING = "octet";
private static final String PUT_STRING = "PUT";
private static final int MAX_SIZE = 512;
private final ByteBuffer receiveBuffer;
public int maxSend=5;//块丢失时 最大从发次数
public static boolean inter(String IP,String path,String filename) throws IOException{
//对象绑定到本机默认IP地址、本机所有可用端口中随机选择的某个端口。
socket = new DatagramSocket();
socket.setSoTimeout(3000);
Client client = new Client(IP,PUT_STRING , path, filename);
boolean success = client.doPutFile();
if(success){
System.out.println("成功");
}else{
System.out.println("失败");
}
return success;
}
public static void main(String[] args) throws IOException
{
//对象绑定到本机默认IP地址、本机所有可用端口中随机选择的某个端口。
socket = new DatagramSocket();
socket.setSoTimeout(3000);
Client client = new Client("192.168.156.150",PUT_STRING , "D:\\mqtt.bin", "V2.03_E");
client.doPutFile();
}
private final String host;
private final String fileDir;
private final String fileName;
private static DatagramSocket socket;
public Client(String host, String operation, String fileDir, String fileName)
{
this.host = host;
this.fileDir = fileDir;
this.fileName = fileName;
receiveBuffer = ByteBuffer.allocate(1024);
}
private boolean doPutFile() throws IOException
{
File fileParent = new File(fileDir);
File fileToSend = new File(fileName);
boolean successW = sendWriteRequest(fileToSend);
boolean successC = false;
if(successW != false){
successC = sendFile(fileParent);
}
return successC;
}
private boolean sendWriteRequest(File file) throws IOException
{ //kmy
// ByteBuffer buffer = ByteBuffer.allocate(2 + file.getName().length() + 1 + OCTET_STRING.length() + 1);
boolean success = true;
ByteBuffer buffer = ByteBuffer.allocate(100);
buffer.putShort(OpCode.WRQ.getValue());
ByteBufferUtil.putString(buffer, file.getName());
ByteBufferUtil.putString(buffer, OCTET_STRING);
//kmy
FileInputStream fileInputTftp=null;
fileInputTftp=new FileInputStream(fileDir);
String fileSize =String.valueOf( fileInputTftp.available());
ByteBufferUtil.putString(buffer, "blksize");
String blksize = String.valueOf(MAX_SIZE);
ByteBufferUtil.putString(buffer, blksize);
ByteBufferUtil.putString(buffer, "tsize");
ByteBufferUtil.putString(buffer, fileSize);
buffer.flip();
// channel.write(buffer);
byte[] bt1 = new byte[buffer.remaining()];
InetSocketAddress ipAddress = new InetSocketAddress(host, 69);
DatagramPacket packet = new DatagramPacket(bt1, bt1.length, ipAddress.getAddress(), 69);
buffer.get(bt1, 0, bt1.length);
socket.send(packet);
byte[] by = new byte[receiveBuffer.remaining()];
DatagramPacket packetRE=new DatagramPacket(by,by.length);
try {
socket.receive(packetRE);
} catch (Exception e) {
success = false;
return success;
// TODO: handle exception
}
return success;
}
private boolean sendFile(File fileToSend) throws IOException
{
boolean success = true;
byte[] buffer = new byte[MAX_SIZE];
DataInputStream inputStream = new DataInputStream(new FileInputStream(fileToSend));
maxSend = 1;
int reNum = 0;
DatagramPacket packet=null;
for (short i = 1; inputStream.available() > 0; i++)
{
int length = MAX_SIZE;
//判断发送的文件块和内容的是否一致
if(reNum == (i-1)){
if (inputStream.available() < MAX_SIZE)
{
length = inputStream.available();
}
inputStream.readFully(buffer, 0, length);
ByteBuffer byteBuffer = ByteBuffer.allocate(length + 4); // opCode and block #
byteBuffer.putShort(OpCode.DATA.getValue());
byteBuffer.putShort(i);
byteBuffer.put(buffer, 0, length);
byteBuffer.flip();
byte[] bt1 = new byte[byteBuffer.remaining()];
InetSocketAddress ipAddress = new InetSocketAddress(host, 69);
packet = new DatagramPacket(bt1, bt1.length, ipAddress.getAddress(), 69);
byteBuffer.get(bt1, 0, bt1.length);
}else{// 如果不一致 就发送上一条文件内容
i = (short) (i-1);
}
socket.send(packet);
// 创建接受类型的数据报
byte[] by = new byte[receiveBuffer.remaining()];
DatagramPacket packetRE=new DatagramPacket(by,by.length);
try {
socket.receive(packetRE);
byte[] getBuf = new byte[1024];
getBuf = packetRE.getData();
StringBuffer sb = new StringBuffer(getBuf.length);
String sTemp;
for (int a = 0; a < getBuf.length; a++) {
sTemp = Integer.toHexString(0xFF & getBuf[a]);
if (sTemp.length() < 2)
sb.append(0);
sb.append(sTemp.toUpperCase());
}
reNum = Integer.parseInt(sb.toString().substring(4, 8),16);
} catch (Exception e) {
// tryS(packet,packetRE);
// TODO: handle exception
}
}
socket.close();
inputStream.close();
return success;
}
private void tryS(DatagramPacket packet, DatagramPacket packetRE) throws IOException{
maxSend = maxSend+1;
if(maxSend>10){
return;
}
socket.send(packet);
try {
socket.receive(packetRE);
} catch (Exception e2) {
tryS(packet,packetRE);
}
}
}
ByteBufferUtil.java
package com.latour.tftp;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
/**
* ByteBufferUtil handles null terminated strings
* @author David Latour
*
*/
public class ByteBufferUtil
{
private static final Charset asciCharset = Charset.forName("US-ASCII");
public static String readString(ByteBuffer receiveBuffer)
{
int startPos = receiveBuffer.position();
byte[] array = receiveBuffer.array();
int stringLen = 0;
for (int i = startPos; i < array.length && array[i] != 0; stringLen++, i++)
{
}
String str = new String(array, startPos, stringLen, asciCharset);
for (int i = 0; i <= stringLen; i++)
{
receiveBuffer.get();
}
return str;
}
public static void putString(ByteBuffer buffer, String inString)
{
byte[] stringBytes = inString.getBytes(asciCharset);
buffer.put(stringBytes);
buffer.put((byte)0);
}
}
OpCode.java
package com.latour.tftp;
/**
* OpCode is an enum to identify the different types of messages that can be sent.
*
* @author David Latour
*
*/
public enum OpCode
{
RRQ((short)1), // Read request
WRQ((short)2), // Write request
DATA((short)3), // Data
ACK((short)4), // Acknowledge
ERROR((short)5); // Error
private final short opCode;
private OpCode(short opCode)
{
this.opCode = opCode;
}
public short getValue()
{
return opCode;
}
public static OpCode valueOf(short inValue)
{
OpCode returnValue = ERROR;
for (OpCode code : OpCode.values())
{
if (code.opCode == inValue)
{
returnValue = code;
break;
}
}
return returnValue;
}
}
了解DatagramSocket内容的可参考该链接 https://www.cnblogs.com/blog-yuesheng521/p/6230441.html
下面有测试压缩包,亲测有效。