I need to send nullbyte as 1 character this code send its as 4 characters so not a nullbyte (\x00) , it can't be sending it as plain text. it's sending to a flash client. I'm using AsynchronousSocketChannel to send the packets.
the nullbyte is to tell the server that the packet has ended.
for example when I send test\x00 it sends it as test\x00 which is wrong.
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.channels.CompletionHandler;
import java.nio.charset.Charset;
import java.util.concurrent.Future;
public class Main {
public static void main(String[] args) throws Exception {
String connect = "gfdg";
System.out.println(connect);
String request = connect;
AsynchronousSocketChannel channel = AsynchronousSocketChannel.open();
SocketAddress serverAddr = new InetSocketAddress("artix.aqw.aq.com", 5588);
Future<Void> result = channel.connect(serverAddr);
result.get();
Attachment attach = new Attachment();
attach.channel = channel;
attach.buffer = ByteBuffer.allocate(2048);
attach.isRead = false;
attach.mainThread = Thread.currentThread();
Charset cs = Charset.forName("UTF-8");
String msg = request;
byte[] data = msg.getBytes(cs);
attach.buffer.put(data);
attach.buffer.flip();
ReadWriteHandler readWriteHandler = new ReadWriteHandler();
channel.write(attach.buffer, attach, readWriteHandler);
attach.mainThread.join();
}
}
class Attachment {
AsynchronousSocketChannel channel;
ByteBuffer buffer;
Thread mainThread;
boolean isRead;
}
class ReadWriteHandler implements CompletionHandler<Integer, Attachment> {
#Override
public void completed(Integer result, Attachment attach) {
if (attach.isRead) {
attach.buffer.flip();
Charset cs = Charset.forName("UTF-8");
int limits = attach.buffer.limit();
byte bytes[] = new byte[limits];
attach.buffer.get(bytes, 0, limits);
String msg = new String(bytes, cs);
String str = new String(bytes,cs).split("\0")[0];
System.out.format("Server Responded: " + str + "\n");
try {
msg = this.getTextFromUser();
} catch (Exception e) {
e.printStackTrace();
}
if (msg.equalsIgnoreCase("bye")) {
attach.mainThread.interrupt();
return;
}
attach.buffer.clear();
byte[] data = msg.getBytes(cs);
attach.buffer.put(data);
attach.buffer.flip();
attach.isRead = false; // It is a write
attach.channel.write(attach.buffer, attach, this);
} else {
attach.isRead = true;
attach.buffer.clear();
attach.channel.read(attach.buffer, attach, this);
}
}
#Override
public void failed(Throwable e, Attachment attach) {
e.printStackTrace();
}
private String getTextFromUser() throws Exception {
System.out.println("Please enter a message:");
BufferedReader consoleReader = new BufferedReader(
new InputStreamReader(System.in));
String msg = consoleReader.readLine() + "\\x00";
return msg;
}
}
You should write a single null byte (0x00) after writing your string to the channel. What you're doing is not it: you're appending the string \x00 instead (a backslash followed by an x and two 0s).
Against my first instincts, it seems it will work if you append the unicode character \u0000 to your string, but the optimal way to do it is simply to put a byte with value 0 into the ByteBuffer after putting your string.
To be clear, I expected the null byte to be doubled when you append \u0000, as Java encodes chars as UTF-16, hence on 2 bytes. But we're explicitly encoding the String to UTF-8 to get it as bytes, so the null char is indeed encoded as a single null byte.
Here's a small demo of this, showing for each method the length of data written to the channel, then its value as bytes:
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.Channels;
import java.nio.channels.WritableByteChannel;
import java.nio.charset.StandardCharsets;
import java.util.Scanner;
public class Buffer {
public static void main(String[] args) throws IOException {
try (Scanner scan = new Scanner(System.in)) {
boolean done = false;
while(!done) {
System.out.println("Enter string to encode, 'bye' to exit:");
String s = scan.nextLine();
if ("bye".equals(s.toLowerCase())) {
done = true;
break;
}
System.out.println("withNullChar");
String withNullChar = s + '\u0000';
ByteBuffer buff = ByteBuffer.allocate(1024);
buff.put(withNullChar.getBytes(StandardCharsets.UTF_8));
System.out.println("Length: " + buff.position());
buff.flip();
byte[] result = readBack(buff);
printArray(result);
System.out.println("withNullCharFaulty");
String withNullCharFaulty = s + "\\x00";
buff = ByteBuffer.allocate(1024);
buff.put(withNullCharFaulty.getBytes(StandardCharsets.UTF_8));
System.out.println("Length: " + buff.position());
buff.flip();
result = readBack(buff);
printArray(result);
System.out.println("with null byte");
buff = ByteBuffer.allocate(1024);
buff.put(s.getBytes(StandardCharsets.UTF_8)).put((byte) 0);
System.out.println("Length: " + buff.position());
buff.flip();
result = readBack(buff);
printArray(result);
}
}
}
public static byte[] readBack(ByteBuffer buff) throws IOException {
try (ByteArrayOutputStream bos = new ByteArrayOutputStream()) {
try (WritableByteChannel channel = Channels.newChannel(bos)) {
channel.write(buff);
return bos.toByteArray();
}
}
}
public static void printArray(byte[] arr) {
StringBuilder sb = new StringBuilder();
for (byte b : arr)
sb.append(String.format("%02X ", b));
System.out.println(sb);
}
}
Related
I need to transfer a List between nodes and replace the existing one with the new one in the new node to achieve this, I'm using Sockets from Java.
I somehow have managed to transfer the data but only when I terminate the process. I need it to continue running, the process but at the same time transfer, the data in case any other new node joins the List.
How can I achieve this? I will have to introduce Threads in the Download along the road.
I got it working with files but now I need to change it to Sync lists, just having this is enough?
private static List<CloudByte> cloudByteList = Collections.synchronizedList(new ArrayList<>());
This is my current code:
import java.io.*;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import static FileData.getCloudByteList;
import static FileData.getFile;
public class FileData {
private static File file;
private static String fileName;
private static List<CloudByte> cloudByteList = Collections.synchronizedList(new ArrayList<>());
public FileData(String fileName) throws IOException {
if (fileName == null) {
this.fileName = "data2.bin";
this.file = new File(this.fileName);
Download.downloadFile();
} else {
this.file = new File(fileName);
this.fileName = fileName;
fillingList();
}
}
public void fillingList() throws IOException {
byte[] fileContents = Files.readAllBytes(file.toPath());
for (int i = 0; i < fileContents.length - 1; i++) {
cloudByteList.add(new CloudByte(fileContents[i]));
}
}
public static List<CloudByte> getCloudByteList() {
return cloudByteList;
}
public static File getFile() {
return file;
}
public String getFileName() {
return fileName;
}
public static void setFile(File file) {
FileData.file = file;
}
/*--------------------------Download--------------------------*/
}
class Download extends Thread {
static ConnectingDirectory connectingDirectory;
#Override
public void run() {
try {
downloadFile();
} catch (IOException e) {
e.printStackTrace();
}
}
public static void downloadFile() throws IOException {
var nodes = ConnectingDirectory.getNodes();
Socket socket = null;
if (getFile().exists()) {
System.out.println("File: " + getFile() + " exists.");
new Upload().uploadFile();
}
FileOutputStream fos = new FileOutputStream(FileData.getFile());
//ObjectOutputStream oos = new ObjectOutputStream(fos);
for (int i = 0; i < nodes.size() - 1; i++) {
if (!(nodes.get(i).getHostPort() == ConnectingDirectory.getHostIP())) {
System.out.println("test33123");
ServerSocket serverSocket = new ServerSocket(nodes.get(i).getHostPort());
System.out.println(serverSocket);
socket = serverSocket.accept();
System.out.println("now socket");
System.out.println(socket);
//socket = new Socket(nodes.get(i).getName(), nodes.get(i).getHostPort());
//System.out.println(socket);
int bytes = 0;
DataInputStream ois = new DataInputStream(socket.getInputStream());
long size = ois.readLong();
System.out.println(size);
byte[] buffer = new byte[100 * 10000];
while (size > 0 && (bytes = ois.read(buffer, 0, (int) Math.min(buffer.length, size))) != -1) {
System.out.println("test3333");
fos.write(buffer, 0, bytes);
size -= bytes;
}
}
}
}
}
/*--------------------------Upload--------------------------*/
class Upload {
public void uploadFile() throws IOException {
int bytes = 0;
var nodes = ConnectingDirectory.getNodes();
FileInputStream fileInputStream = new FileInputStream("data.bin");
DataInputStream ois = new DataInputStream(fileInputStream);
if (!getFile().exists()) {
System.out.println("File doesn't exist." + "\nDownloading the file!");
new Download().downloadFile();
}
System.out.println("hello");
for (int i = 0; i < nodes.size() - 1; i++) {
System.out.println("hello2");
Socket socket = new Socket(nodes.get(i).getName(), nodes.get(i).getHostPort());
DataOutputStream dos = new DataOutputStream(socket.getOutputStream());
dos.writeLong(new File("data.bin").length());
byte[] buffer = new byte[100 * 10000];
while ((bytes = ois.read(buffer)) != -1) {
dos.write(buffer, 0, bytes);
dos.flush();
}
}
}
}
As you can see, I'm using DataInput because if I try to use the ObjectInputStream, I get a Corrupted Header Exception. I have more classes to add to this. My goal is as I said, to transfer the data inside the "data.bin" to a "data2.bin" file. I'm able to create it and delete it but at the same time, no Data is being written/sent to it.
How can I fix the CorruptedHeaderException and get it to send the content?
All help is appreciated.
StorageNode Class:
import java.io.IOException;
import java.util.Scanner;
import java.util.regex.Pattern;
import static FileData.*;
public class StorageNode extends Thread {
private static int serverPort = 8080;
private static int clientPort = 8082;
private static String fileName = null;
private static String addressName = "localhost";
private static ConnectingDirectory connectingDirectory;
private static FileData fileData;
static ErrorInjection errorInjection;
public static void main(String[] args) throws IOException, InterruptedException {
/* if (args.length > 3) {
addressName = args[0];
serverPort = Integer.parseInt(args[1]);
clientPort = Integer.parseInt(args[2]);
fileData = new FileData(args[3]);
} else {
fileName = null;
fileData = new FileData(fileName);
}*/
connectingDirectory = new ConnectingDirectory(addressName, clientPort, serverPort);
fileData = new FileData(fileName);
errorInjection = new ErrorInjection();
errorInjection.start();
if(fileData.getFile().exists()){
new Upload().uploadFile();
}else {
new Download().downloadFile();
}
}
ConnectingDirectory Class
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
public class ConnectingDirectory {
private String hostName;
private static int hostIP;
private int directoryIP;
private InetAddress address;
private InputStream in;
private OutputStream out;
private static List<Nodes> nodes = new ArrayList<>();
List<String> nodess = new ArrayList<>();
private Socket socket;
private String sign = "INSC ";
public ConnectingDirectory(String hostName, int hostIP, int directoryIP) throws IOException {
this.hostName = hostName;
this.hostIP = hostIP;
this.directoryIP = directoryIP;
this.address = InetAddress.getByName(hostName);
this.socket = new Socket(address, directoryIP);
signUp();
askConnectedNodes();
}
public void signUp() throws IOException {
System.out.println("You are connecting to the following address: " + hostIP + "\n");
System.out.println("The port you are connected to: " + socket.getPort() + "\n");
in = socket.getInputStream();
out = socket.getOutputStream();
out.write(generateSignUp(address, hostIP).getBytes());
out.flush();
}
public String generateSignUp(InetAddress address, int hostIP) {
String signUpString = sign + address + " " + hostIP + "\n";
return signUpString;
}
public void askConnectedNodes() throws IOException {
String directoryNodesAvailable;
String a = "nodes\n";
out.write(a.getBytes());
out.flush();
Scanner scan = new Scanner(in);
while (true) {
directoryNodesAvailable = scan.nextLine();
addExistingNodes(directoryNodesAvailable);
//System.out.println("Eco: " + directoryNodesAvailable);
if (directoryNodesAvailable.equals("end")) {
out.flush();
printNodes();
break;
}
}
}
public void addExistingNodes(String sta) throws IOException {
if (sta.equals("end")) return;
if (!(nodess.contains(sta))) {
nodess.add(sta);
nodes.add(new Nodes(nodess.get(nodess.size() - 1)));
}
return;
}
public static List<Nodes> getNodes() {
return nodes;
}
public void printNodes() {
System.out.println("Checking for available nodes: \n");
nodes.forEach((z) -> System.out.println(z.getNode()));
}
public Socket getSocket() {
return socket;
}
public static int getHostIP() {
return hostIP;
}
public InetAddress getAddress() {
return address;
}
}
For all of those that need help in the future:
Sender side:
Socket socket = new Socket("localhost", hostPort);
ObjectOutputStream objectOutputStream = new ObjectOutputStream(socket.getOutputStream());
ByteBlockRequest bbr = new ByteBlockRequest(getStoredData());
objectOutputStream.writeObject(bbr.blocksToSend(j));
Receiver side:
Socket = StorageNode.getServerSocket().accept();
ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());
byte[] bit = (byte[]) ois.readObject();
In my case, I needed to use byte[], so I had to do a few additional functions in the back, to change Cloudbyte[] into byte[]. Once I did that, I was able to send the data using, ObjectInput/ObjectOutput.
I'm trying to implement a SCTP connection, everything works fine from the server's side but when I run the client program I get the following error:
java.net.ConnectException: Connection refused
at sun.nio.ch.SctpNet.connect0(Native Method)
at sun.nio.ch.SctpNet.connect(SctpNet.java:73)
at sun.nio.ch.SctpChannelImpl.connect(SctpChannelImpl.java:372)
at sun.nio.ch.SctpChannelImpl.connect(SctpChannelImpl.java:438)
at com.sun.nio.sctp.SctpChannel.open(SctpChannel.java:221)
at com.eska.sctp.client.SCTPClient.<init>(SCTPClient.java:20)
at com.eska.sctp.client.SCTPClient.main(SCTPClient.java:62)
The Server Code:
import java.io.IOException;
import java.io.PrintStream;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.util.Iterator;
import com.sun.nio.sctp.AbstractNotificationHandler;
import com.sun.nio.sctp.AssociationChangeNotification;
import com.sun.nio.sctp.AssociationChangeNotification.AssocChangeEvent;
import com.sun.nio.sctp.HandlerResult;
import com.sun.nio.sctp.MessageInfo;
import com.sun.nio.sctp.SctpChannel;
import com.sun.nio.sctp.SctpServerChannel;
import com.sun.nio.sctp.ShutdownNotification;
public class SCTPServer {
private final static int SERVER_PORT = 1111;
private final static int BUFFER_SIZE = 1024;
private SctpServerChannel ssc;
public SCTPServer(int port) throws IOException {
this.ssc = SctpServerChannel.open();
// m.alkhader
// this.ssc.bind(new InetSocketAddress(port));
this.ssc.bind(new InetSocketAddress("127.0.0.1", port));
System.out.println("SCTP server started.");
System.out.println("Local addresses :");
Iterator iterator = this.ssc.getAllLocalAddresses().iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
}
public void launch() throws IOException {
ByteBuffer buf = ByteBuffer.allocate(BUFFER_SIZE);
CharBuffer cbuf = CharBuffer.allocate(BUFFER_SIZE);
Charset charset = Charset.forName("ISO-8859-1");
CharsetDecoder decoder = charset.newDecoder();
while (true) {
System.out.println("Waiting for client connection...");
SctpChannel sc = this.ssc.accept();
System.out.println("Client connected");
System.out.println("Remote adresses :");
Iterator iterator = sc.getRemoteAddresses().iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
AssociationHandler assocHandler = new AssociationHandler();
MessageInfo messageInfo = null;
do {
buf.clear();
cbuf.clear();
messageInfo = sc.receive(buf, null, assocHandler);
buf.flip();
messageInfo.unordered(true);
if (messageInfo != null && buf.limit() > 0) {
decoder.decode(buf, cbuf, true);
cbuf.flip();
System.out.print("Stream(" + messageInfo.streamNumber()
+ "):");
System.out.println(cbuf);
}
} while (messageInfo != null && buf.limit() > 0);
System.out.println("Connection closed by peer.");
}
}
public static void main(String[] args) {
try {
SCTPServer server = new SCTPServer(SERVER_PORT);
server.launch();
} catch (IOException e) {
System.out.println("Error : " + e.getMessage());
}
}
static class AssociationHandler extends AbstractNotificationHandler {
public HandlerResult handleNotification(
AssociationChangeNotification not, PrintStream stream) {
if (not.event().equals(AssocChangeEvent.COMM_UP)) {
int outbound = not.association().maxOutboundStreams();
int inbound = not.association().maxInboundStreams();
stream.printf("New association setup with %d outbound streams"
+ ", and %d inbound streams.\n", outbound, inbound);
}
return HandlerResult.CONTINUE;
}
public HandlerResult handleNotification(ShutdownNotification not,
PrintStream stream) {
stream.printf("The association has been shutdown.\n");
return HandlerResult.RETURN;
}
}
}
The Client Code:
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.nio.charset.CharsetEncoder;
import java.util.Scanner;
import com.sun.nio.sctp.MessageInfo;
import com.sun.nio.sctp.SctpChannel;
public class SCTPClient {
private SctpChannel sc;
private final static int SERVER_PORT = 1111;
private final static int BUFFER_SIZE = 1024;
public SCTPClient(String addr, int port) throws IOException {
this.sc = SctpChannel.open(new InetSocketAddress(addr, port), 0, 0);
}
public void start() throws IOException {
MessageInfo messageInfo = MessageInfo.createOutgoing(null, 0);
Charset charset = Charset.forName("ISO-8859-1");
CharsetEncoder encoder = charset.newEncoder();
ByteBuffer buf = ByteBuffer.allocate(BUFFER_SIZE);
CharBuffer cbuf = CharBuffer.allocate(BUFFER_SIZE);
Scanner scan = new Scanner(System.in);
int i = 0;
int max = this.sc.association().maxInboundStreams();
// messageInfo.unordered(true);
while (scan.hasNext()) {
buf.clear();
cbuf.clear();
cbuf.put(scan.nextLine());
cbuf.flip();
encoder.encode(cbuf, buf, true);
buf.flip();
messageInfo.streamNumber(i % max);
this.sc.send(buf, messageInfo);
i++;
}
}
public void stop() throws IOException {
this.sc.close();
}
public static void main(String[] args) {
try {
System.out.println("Client");
SCTPClient client = new SCTPClient("127.0.0.1", SERVER_PORT);
// SCTPClient client = new SCTPClient("192.168.0.1", SERVER_PORT);
System.out.println("Hello Client");
client.start();
client.stop();
} catch (IOException e) {
System.out.println("Error : " + e.getMessage());
e.printStackTrace();
}
}
}
everything works fine from the server's side
I am trying to create a TFTP client using java NIO. I am able to receive first 512 bytes of data from server, but not able to send acknowledgement to server for getting next block of packet. I am new to java NIO and networking. Not able to find solution for it. So can anyone help me on this to find fix for it? Thanks in advance
package app.sdc.business;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.DatagramChannel;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.util.Iterator;
import app.sdc.business.NetworkElementPool.NetworkElement;
public class TftpNioClient {
static byte OP_ERROR = 5, OP_DATAPACKET = 3, OP_ACK = 4, OP_RRQ = 1, OP_WRQ = 2;
static final String LOCALHOST = "localhost";
static InetSocketAddress server = new InetSocketAddress(LOCALHOST, 69);
// main method
public static void main(String[] args) throws IOException {
processDownload();
}
// Will start downloading of all files
public static void processDownload() throws IOException {
Selector sel = Selector.open();
for (NetworkElement ne : NetworkElementPool.getNetworkElements()) {
DatagramChannel channel = DatagramChannel.open();
channel.configureBlocking(false);
channel.connect(server);
channel.register(sel, SelectionKey.OP_READ | SelectionKey.OP_WRITE, ne);
}
int counter = 0;
while (true) {
int n = sel.select(3000);
if (n < 1) {
continue;
}
Iterator<SelectionKey> itr = sel.selectedKeys().iterator();
while (itr.hasNext()) {
SelectionKey key = itr.next();
itr.remove();
if (key.isWritable()) {
counter++;
System.out.println("channel Write...");
downloadUsingTFTPProtocol(key);
} else if (key.isReadable()) {
System.out.println("Channel Read");
}
}
if (counter >= NetworkElementPool.getNetworkElements().size()) {
break;
}
}
}
// method for downloading file
private static void downloadUsingTFTPProtocol(SelectionKey keyy) throws IOException {
ByteArrayOutputStream byteOutOS = new ByteArrayOutputStream();
Selector sel = keyy.selector();
NetworkElement ne = (NetworkElement) keyy.attachment();
String fileName = ne.getFilename();
DatagramChannel channel = DatagramChannel.open();
channel.configureBlocking(false);
channel.register(sel, SelectionKey.OP_READ | SelectionKey.OP_WRITE);
boolean reqSent = false;
ByteBuffer sendBuffer = null;
ByteBuffer receivedBuffer = ByteBuffer.allocate(516);
boolean stop = false;
byte[] dataByte = null;
boolean received = false;
outer: while (true) {
int n = sel.select();
if (n < 1) {
continue;
}
Iterator<SelectionKey> itr = sel.selectedKeys().iterator();
while (itr.hasNext()) {
SelectionKey key = itr.next();
itr.remove();
DatagramChannel dc = (DatagramChannel) key.channel();
if (!received && key.isReadable()) {
System.out.println("receive packet...");
receivedBuffer.clear();
dc.receive(receivedBuffer);
stop = receivedBuffer.position() < 512;
receivedBuffer.flip();
while (receivedBuffer.hasRemaining()) {
System.out.print(receivedBuffer.get());
}
System.out.println();
dataByte = receivedBuffer.array();
received = true;
}
if (key.isWritable()) {
if (!reqSent) {
System.out.println("Sending First Request....");
sendBuffer = createInitialReadRequest("SDCSource/" + fileName);
sendBuffer.flip();
dc.send(sendBuffer, server);
reqSent = true;
} else if (received) {
System.out.println("Send Acknowledgement");
byte[] opCode = new byte[] { dataByte[0], dataByte[1] };
if (opCode[1] == OP_ERROR) {
System.out.println("Error Occured...");
break outer;
} else if (opCode[1] == OP_DATAPACKET) {
byte[] blockNumber = { dataByte[2], dataByte[3] };
sendBuffer = getAcknowledgment(blockNumber, dc, server);
sendBuffer.flip();
dc.send(sendBuffer, server);
DataOutputStream dos = new DataOutputStream(byteOutOS);
dos.write(dataByte, 4, dataByte.length - 4);
}
}
received = false;
}
if (stop) {
break outer;
}
}
}
writeFile(byteOutOS, fileName);
}
// Creates request packet to send request at the beginning
private static ByteBuffer createInitialReadRequest(final String fileName) {
String mode = "octet";
int rrqByteLength = 2 + fileName.getBytes().length + 1 + mode.getBytes().length + 1;
byte[] rrqByteArray = new byte[rrqByteLength];
ByteBuffer reqBuf = ByteBuffer.allocate(rrqByteArray.length);
reqBuf.put((byte) 0).put((byte) OP_RRQ);
reqBuf.put(fileName.getBytes());
reqBuf.put((byte) 0);
reqBuf.put(mode.getBytes());
reqBuf.put((byte) 0);
return reqBuf;
}
// Creating acknowledgement code
private static ByteBuffer getAcknowledgment(byte[] blockNumber, DatagramChannel channel, InetSocketAddress server)
throws IOException {
byte[] acknowledge = { 0, OP_ACK, blockNumber[0], blockNumber[1] };
ByteBuffer buffer = ByteBuffer.allocate(acknowledge.length);
buffer.put(acknowledge);
return buffer;
}
// Create file after all packets have been received
private static void writeFile(ByteArrayOutputStream baoStream, String fileName) {
try {
OutputStream outputStream = new FileOutputStream("SDCTarget/" + fileName);
baoStream.writeTo(outputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
}
package app.sdc.business;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
public class NetworkElementPool {
private static List<NetworkElement> networkElements;
// create a list of network elements by reading files downloading. It is just a hard-coded way to create network elements
static {
networkElements = new ArrayList<NetworkElement>();
File sourceDir = new File("C:/OpenTFTPServer/SDCSource");
if (sourceDir.exists()) {
for (String filename : sourceDir.list()) {
networkElements.add(new NetworkElement("localhost", 8080, filename));
}
} else {
System.err.println("Network Elements couldn't found...");
}
}
public static List<NetworkElement> getNetworkElements() {
return networkElements;
}
// Represents a network element
public static class NetworkElement {
private String host;
private int port;
private String filename;
public String getHost() {
return host;
}
public int getPort() {
return port;
}
public String getFilename() {
return filename;
}
public NetworkElement() {
super();
}
public NetworkElement(String host, int port, String filename) {
super();
this.host = host;
this.port = port;
this.filename = filename;
}
#Override
public String toString() {
return "NetworkElement [host=" + host + ", port=" + port + ", filename=" + filename + "]";
}
}
}
Note: TftpClient class contain one foreach loop to start downloading file from more than one network element.
You're not sending the acknowledgements at the correct time, after a read: you're sending them every time around the loop. You don't need to wait for OP_WRITE to do a write. Just write whenever you need to. You shouldn't even register for OP_WRITE most of the time. See this answer for the correct technique. But in the case of TFTP it's dubious whether you need that at all. Or NIO either.
In your code, if the key.isReadable, then you can read the packet. Then immediately followed by that you can send the ACK back to TFTP server.
} else if (received) {
This block of code can go to the line immediately next to dc.receive(receivedBuffer);
You can refer this example java nio tftp client
At the beginning, I need to apologise for polish names of variables/functions that I've used in my program.
So, there's little dictionary:
Klient - Client
Serwer - Server
wejscie - input
wyjscie - output
klienci - clients
teraz - now
teraz - text
nawiazPolaczenie - establishConnection
czyscBufor - clearBuffer
odbierzDane - receiveData
pakiet - packet
wyslijDane - sendData
The problem is in moment, when the client is receiving data from server - there's ArrayIndexOutOfBoundsException.
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 6
at kontrolerklient.Klient.czyscBuforWejscia(Klient.java:43)
at kontrolerklient.Klient.odbierzDane(Klient.java:48)
at kontrolerklient.Klient.nawiazPolaczenie(Klient.java:33)
at kontrolerklient.Klient.<init>(Klient.java:25)
at kontrolerklient.KontrolerKlient.main (KontrolerKlient.java:11)
Java Result: 1
BUILD SUCCESSFUL (total time: 3 seconds)
Whole server's code:
package kontrolerserwer;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.util.Date;
public class Serwer
{
private DatagramSocket dgSocket;
private byte[] bufferIn;
private byte[] bufferOut;
private InetAddress[] klienci;
public Serwer() throws IOException
{
dgSocket = new DatagramSocket(9998, InetAddress.getByName("192.168.1.100"));
bufferIn = new byte[1024];
bufferOut = new byte[1024];
klienci = new InetAddress[256];
dgSocket.setSoTimeout(1000);
wyslijDane("ready?", InetAddress.getByName("192.168.1.100"));
Date teraz = new Date();
teraz.setTime(teraz.getTime()+10000);
while (teraz.after(new Date()))
{
}
}
public void wyslijDane(String tekst, InetAddress ip) throws IOException
{
bufferOut = tekst.getBytes("ASCII");
dgSocket.send(new DatagramPacket(bufferOut, bufferOut.length, ip, 9999));
}
}
..and client's code:
package kontrolerklient;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.net.UnknownHostException;
class Klient
{
private DatagramSocket dgSocket;
private InetAddress host;
private byte[] wejscie;
private byte[] wyjscie;
public Klient() throws UnknownHostException, SocketException, IOException
{
wejscie = new byte[1024];
wyjscie = new byte[1024];
host = null;
dgSocket = new DatagramSocket(9999, InetAddress.getByName("192.168.1.100"));
nawiazPolaczenie();
}
private void nawiazPolaczenie() throws IOException
{
while (true)
{
if (odbierzDane().equals("ready?"))
{
wyslijDane("ready!", 9998);
}
}
}
private void czyscBuforWejscia()
{
for (int i = 0; i < 1024; i++)
wejscie[i] = 0;
}
public String odbierzDane() throws IOException
{
czyscBuforWejscia();
DatagramPacket pakiet = new DatagramPacket(wejscie, wejscie.length);
System.out.println(pakiet.getLength());
try
{
dgSocket.receive(pakiet);
host = pakiet.getAddress();
// getting packet's data
String s = new String(pakiet.getData(), 0, wejscie.length);
// getting packet's data length
int i;
for (i = 0; (i < 1024) && (wejscie[i] != 0); i++);
// returning packet's data
return s.substring(0, i);
}
catch (Exception e) { }
return "";
}
public void wyslijDane(String dane, int port) throws IOException
{
wejscie = dane.getBytes("ASCII");
dgSocket.send(new DatagramPacket(wyjscie, wyjscie.length, host, port));
}
}
I guess that here:
wejscie = dane.getBytes("ASCII")
you are overwriting the original declaration:
wejscie = new byte[1024];
with some byte array of unknown size. But then you clear the buffer:
for (int i = 0; i < 1024; i++)
with fixed size. It should have been:
for (int i = 0; i < wejscie.length; i++)
when reading the packet, you need to use the length of the data received (DatagramPacket.getLength()), not the length of the byte array you created.
wyslijDane sets wejscie to "ready!".getBytes(), but czyscBuforWejscia expects its size to be 1024
Basically my response headers contain
Transfer-encoding=chunked,
Trailer=[some trailer I want to send say e.g "SomeTrailer"]
Once I'm done writing the data to the Servlet outputstream, I'm writing the trailer
"SomeTrailer:[value]", but this is not being parsed by the httpclient correctly.
The httpclient considers the whole of inputstream (including the trailer) as a single
chunk.
I've also tried writing the trailer in a response header after the data has been written to the outputstream but without success.
Please help
I haven't found any good sources on this.
I ended up writing a simple single threaded webserver for this. Turned out it was quite easy. The server is pretty simple. The code's a bit rough though, but the main idea is there.
What it does it sends the filecontents as the first chunk and the checksum of the file as a footer.
import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.io.IOUtils;
import org.apache.log4j.Logger;
public class ChunkedResponseServer implements Runnable {
private static final Logger LOGGER = Logger.getLogger(ChunkedResponseServer.class);
// Space ' '
static final byte SP = 32;
// Tab ' '
static final byte HT = 9;
// Carriage return
static final byte CR = 13;
// Line feed character
static final byte LF = 10;
final int port;
private volatile boolean cancelled = false;
public ChunkedResponseServer(int port) {
LOGGER.info("Chunked response server running on port " + port);
this.port = port;
}
#Override
public void run() {
ServerSocket serverSocket = null;
try {
serverSocket = new ServerSocket(port);
while (!cancelled) {
final Socket connectionSocket = serverSocket.accept();
handle(connectionSocket);
}
} catch (final IOException e) {
throw new RuntimeException(e);
}
}
public void cancel() {
LOGGER.info("Shutting down Chunked response Server");
cancelled = true;
}
private void handle(Socket socket) throws IOException {
BufferedReader input = null;
DataOutputStream output = null;
try {
input = new BufferedReader(new InputStreamReader(socket.getInputStream()));
output = new DataOutputStream(socket.getOutputStream());
addHeaders(output);
addCRLR(output);
final String filename = readFilename(input);
final byte[] content = readContent(filename);
addContentAsChunk(output, content);
final String checksum = DigestUtils.md5Hex(content);
addLastChunkAndChecksumFooter(output, checksum);
addCRLR(output);
} finally {
IOUtils.closeQuietly(input);
IOUtils.closeQuietly(output);
}
}
private void addLastChunkAndChecksumFooter(DataOutputStream output, String checksum) throws IOException {
output.writeBytes("0");
addCRLR(output);
output.writeBytes("checksum: " + checksum);
addCRLR(output);
}
private void addContentAsChunk(DataOutputStream output, byte[] content) throws IOException {
output.writeBytes(Integer.toHexString(content.length));
addCRLR(output);
output.write(content);
addCRLR(output);
}
private void addCRLR(DataOutputStream output) throws IOException {
output.writeByte(CR);
output.writeByte(LF);
}
private void addHeaders(DataOutputStream output) throws IOException {
output.writeBytes("HTTP/1.1 200 OK");
addCRLR(output);
output.writeBytes("Content-type: text/plain");
addCRLR(output);
output.writeBytes("Transfer-encoding: chunked");
addCRLR(output);
output.writeBytes("Trailer: checksum");
addCRLR(output);
}
private String readFilename(BufferedReader input) throws IOException {
final String initialLine = input.readLine();
final String filePath = initialLine.split(" ")[1];
final String[] components = filePath.split("/");
return components[components.length - 1];
}
private byte[] readContent(String filename) throws IOException {
final InputStream in = Thread.currentThread().getContextClassLoader().getResourceAsStream(filename);
return IOUtils.toByteArray(in);
}
}