In a traditional blocking-thread server, I would do something like this
class ServerSideThread {
ObjectInputStream in;
ObjectOutputStream out;
Engine engine;
public ServerSideThread(Socket socket, Engine engine) {
in = new ObjectInputStream(socket.getInputStream());
out = new ObjectOutputStream(socket.getOutputStream());
this.engine = engine;
}
public void sendMessage(Message m) {
out.writeObject(m);
}
public void run() {
while(true) {
Message m = (Message)in.readObject();
engine.queueMessage(m,this); // give the engine a message with this as a callback
}
}
}
Now, the object can be expected to be quite large. In my nio loop, I can't simply wait for the object to come through, all my other connections (with much smaller workloads) will be waiting on me.
How can I only get notified that a connection has the entire object before it tells my nio channel it's ready?
You can write the object to a ByteArrayOutputStream allowing you to give the length before an object sent. On the receiving side, read the amount of data required before attempting to decode it.
However, you are likely to find it much simpler and more efficient to use blocking IO (rather than NIO) with Object*Stream
Edit something like this
public static void send(SocketChannel socket, Serializable serializable) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
for(int i=0;i<4;i++) baos.write(0);
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(serializable);
oos.close();
final ByteBuffer wrap = ByteBuffer.wrap(baos.toByteArray());
wrap.putInt(0, baos.size()-4);
socket.write(wrap);
}
private final ByteBuffer lengthByteBuffer = ByteBuffer.wrap(new byte[4]);
private ByteBuffer dataByteBuffer = null;
private boolean readLength = true;
public Serializable recv(SocketChannel socket) throws IOException, ClassNotFoundException {
if (readLength) {
socket.read(lengthByteBuffer);
if (lengthByteBuffer.remaining() == 0) {
readLength = false;
dataByteBuffer = ByteBuffer.allocate(lengthByteBuffer.getInt(0));
lengthByteBuffer.clear();
}
} else {
socket.read(dataByteBuffer);
if (dataByteBuffer.remaining() == 0) {
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(dataByteBuffer.array()));
final Serializable ret = (Serializable) ois.readObject();
// clean up
dataByteBuffer = null;
readLength = true;
return ret;
}
}
return null;
}
Inspired by the code above I've created a (GoogleCode project)
It includes a simple unit test:
SeriServer server = new SeriServer(6001, nthreads);
final SeriClient client[] = new SeriClient[nclients];
//write the data with multiple threads to flood the server
for (int cnt = 0; cnt < nclients; cnt++) {
final int counterVal = cnt;
client[cnt] = new SeriClient("localhost", 6001);
Thread t = new Thread(new Runnable() {
public void run() {
try {
for (int cnt2 = 0; cnt2 < nsends; cnt2++) {
String msg = "[" + counterVal + "]";
client[counterVal].send(msg);
}
} catch (IOException e) {
e.printStackTrace();
fail();
}
}
});
t.start();
}
HashMap<String, Integer> counts = new HashMap<String, Integer>();
int nullCounts = 0;
for (int cnt = 0; cnt < nsends * nclients;) {
//read the data from a vector (that the server pool automatically fills
SeriDataPackage data = server.read();
if (data == null) {
nullCounts++;
System.out.println("NULL");
continue;
}
if (counts.containsKey(data.getObject())) {
Integer c = counts.get(data.getObject());
counts.put((String) data.getObject(), c + 1);
} else {
counts.put((String) data.getObject(), 1);
}
cnt++;
System.out.println("Received: " + data.getObject());
}
// asserts the results
Collection<Integer> values = counts.values();
for (Integer value : values) {
int ivalue = value;
assertEquals(nsends, ivalue);
System.out.println(value);
}
assertEquals(counts.size(), nclients);
System.out.println(counts.size());
System.out.println("Finishing");
server.shutdown();
Related
I have to transfer a file using SCTP protocol. I have written the code in java but the code is not working when I am using 4G hotspot network. So I came across this RFC which talks about UDP encapsulation of SCTP. I want to know if there is an implementation which I can use to encapsulate SCTP packet in UDP and send it over UDP channel so that it can traverse heavily NATted network. My current code for sending the data packet is as follows:
import java.io.*;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.util.*;
import com.sun.nio.sctp.MessageInfo;
import com.sun.nio.sctp.SctpChannel;
import com.sun.nio.sctp.SctpServerChannel;
public class Main {
SctpChannel connectionChannelPrimary;
SctpChannel connectionChannelSecondary;
InetSocketAddress serverSocketAddressPrimary;
InetSocketAddress serverSocketAddressSecondary;
String directoryPath;
public Main() {
serverSocketAddressPrimary = new InetSocketAddress(6002);
serverSocketAddressSecondary = new InetSocketAddress(6003);
}
public void setDirectoryPath(String directoryPath) {
this.directoryPath = directoryPath;
}
public String getDirectoryPath() {
return directoryPath;
}
public void establishConnection(int connId) throws IOException {
SctpServerChannel sctpServerChannel = SctpServerChannel.open();
if (connId == 0) {
sctpServerChannel.bind(serverSocketAddressPrimary);
connectionChannelPrimary = sctpServerChannel.accept();
System.out.println("connection established for primary");
} else {
sctpServerChannel.bind(serverSocketAddressSecondary);
connectionChannelSecondary = sctpServerChannel.accept();
System.out.println("connection established for helper");
}
}
ArrayList<String> getAllFiles() {
File directory = new File(this.directoryPath);
ArrayList<String> fileNames = new ArrayList<>();
for (File fileEntry : Objects.requireNonNull(directory.listFiles())) {
if (fileEntry.isFile()) {
fileNames.add(fileEntry.getName());
}
}
Collections.sort(fileNames);
return fileNames;
}
public byte[] readFile(String filename) throws IOException {
String extraString = "\n\n\n\nNRL\n\n\n";
File file = new File(filename);
FileInputStream fl = new FileInputStream(file);
ByteBuffer finalBuffer = ByteBuffer.allocate((int) (file.length() + extraString.length()));
byte[] arr = new byte[(int) file.length()];
int res = fl.read(arr);
if (res < 0) {
System.out.println("Error in reading file");
fl.close();
return null;
}
fl.close();
finalBuffer.put(arr);
finalBuffer.put(extraString.getBytes());
byte[] tmp = new byte[extraString.length()];
finalBuffer.position((int) (file.length() - 1));
finalBuffer.get(tmp, 0, tmp.length);
return finalBuffer.array();
}
public void sendBytes(String filename, int connId) throws IOException {
byte[] message = readFile(filename);
assert message != null;
System.out.println(message.length);
int tmp = 0;
int cntIndex = 60000;
int prevIndex = 0;
boolean isBreak = false;
while (!isBreak) {
byte[] slice;
if (prevIndex + 60000 >= message.length) {
slice = Arrays.copyOfRange(message, prevIndex, message.length);
isBreak = true;
} else {
slice = Arrays.copyOfRange(message, prevIndex, cntIndex);
prevIndex = cntIndex;
cntIndex = cntIndex + 60000;
}
final ByteBuffer byteBuffer = ByteBuffer.allocate(64000);
final MessageInfo messageInfo = MessageInfo.createOutgoing(null, 0);
byteBuffer.put(slice);
byteBuffer.flip();
tmp += slice.length;
try {
if (connId == 0) connectionChannelPrimary.send(byteBuffer, messageInfo);
else connectionChannelSecondary.send(byteBuffer, messageInfo);
} catch (Exception e) {
e.printStackTrace();
}
}
System.out.println(tmp);
}
public static void main(String[] args) throws IOException {
String bgFilePath = "/home/iiitd/Desktop/background/";
String fgFilePath = "/home/iiitd/Desktop/foreground/";
Main myObj = new Main();
myObj.setDirectoryPath("/home/iiitd/Desktop/tmp/");
myObj.establishConnection(1);
myObj.establishConnection(0);
ArrayList<String> files = myObj.getAllFiles();
for (String tmpFile : files) {
String cntFilePath = myObj.getDirectoryPath() + tmpFile;
myObj.sendBytes(cntFilePath,0);
}
}
}
RFC Link: https://datatracker.ietf.org/doc/html/draft-ietf-tsvwg-sctp-udp-encaps-09
In C, I think that usrsctp is a popular implementation of SCTP over UDP. If I understand correctly, it was used by Google Chrome at some point (though I see they mentioned moving to "dcsctp" at some point). Also I have seen it in a mirror of the Firefox sources in 2016, not sure what's the state today.
So one solution would be to wrap usrsctp with JNI. And it appears that this is exactly what jitsi-sctp is doing. I haven't used it, but I would have a look.
When sending file, you can do ctx.writeAndFlush(new ChunkedFile(new File("file.png")));.
how about a List<Object>?
The list contains String and bytes of image.
from the documentation there's ChunkedInput() but I'm not able to get the use of it.
UPDATE
let's say in my Handler, inside channelRead0(ChannelHandlerContext ctx, Object o) method where I want to send the List<Object> I've done the following
#Override
protected void channelRead0(ChannelHandlerContext ctx, Object o) throws Exception {
List<Object> msg = new ArrayList<>();
/**getting the bytes of image**/
byte[] imageInByte;
BufferedImage originalImage = ImageIO.read(new File(fileName));
// convert BufferedImage to byte array
ByteArrayOutputStream bAoS = new ByteArrayOutputStream();
ImageIO.write(originalImage, "png", bAoS);
bAoS.flush();
imageInByte = baos.toByteArray();
baos.close();
msg.clear();
msg.add(0, "String"); //add the String into List
msg.add(1, imageInByte); //add the bytes of images into list
/**Chunk the List<Object> and Send it just like the chunked file**/
ctx.writeAndFlush(new ChunkedInput(DONT_KNOW_WHAT_TO_DO_HERE)); //
}
Just implement your own ChunkedInput<ByteBuf>. Following the implementations shipped with Netty you can implement it as follows:
public class ChunkedList implements ChunkedInput<ByteBuf> {
private static final byte[] EMPTY = new byte[0];
private byte[] previousPart = EMPTY;
private final int chunkSize;
private final Iterator<Object> iterator;
public ChunkedList(int chunkSize, List<Object> objs) {
//chunk size in bytes
this.chunkSize = chunkSize;
this.iterator = objs.iterator();
}
public ByteBuf readChunk(ChannelHandlerContext ctx) {
return readChunk(ctx.alloc());
}
public ByteBuf readChunk(ByteBufAllocator allocator) {
if (isEndOfInput())
return null;
else {
ByteBuf buf = allocator.buffer(chunkSize);
boolean release = true;
try {
int bytesRead = 0;
if (previousPart.length > 0) {
if (previousPart.length > chunkSize) {
throw new IllegalStateException();
}
bytesRead += previousPart.length;
buf.writeBytes(previousPart);
}
boolean done = false;
while (!done) {
if (!iterator.hasNext()) {
done = true;
previousPart = EMPTY;
} else {
Object o = iterator.next();
//depending on the encoding
byte[] bytes = o instanceof String ? ((String) o).getBytes() : (byte[]) o;
bytesRead += bytes.length;
if (bytesRead > chunkSize) {
done = true;
previousPart = bytes;
} else {
buf.writeBytes(bytes);
}
}
}
release = false;
} finally {
if (release)
buf.release();
}
return buf;
}
}
public long length() {
return -1;
}
public boolean isEndOfInput() {
return !iterator.hasNext() && previousPart.length == 0;
}
public long progress() {
return 0;
}
public void close(){
//close
}
}
In order to write ChunkedContent there is a special handler shipped with Netty. See io.netty.handler.stream.ChunkedWriteHandler. So just add to your downstream. Here is the quote from documentation:
A ChannelHandler that adds support for writing a large data stream
asynchronously neither spending a lot of memory nor getting
OutOfMemoryError. Large data streaming such as file transfer requires
complicated state management in a ChannelHandler implementation.
ChunkedWriteHandler manages such complicated states so that you can
send a large data stream without difficulties.
I've been trying to apply the followed code in a wider scheme. Right now I am just trying to understand the concept of ObjectInput/outputStreams and how they handle Array of objects.
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
public class WritFile {
private FileInputStream output;
private ObjectInputStream outPut;
private FileOutputStream input;
private ObjectOutputStream inPut;
private Haha obj[];
public static void main(String args[]) throws Exception {
WritFile obj = new WritFile();
obj.Shazam("src\\Aloha.txt");
}
public void Shazam(String path) {
obj = new Haha[30];
for (int i = 0; i < obj.length; i++) {
obj[i] = new Haha();
}
for (int i = 0; i < 5; i++) {
obj[i].q1 = " Name" + i;
obj[i].x = i;
}
int No = 5;
saveToFile(obj, path);
int counter = 0;
try {
for (int i = 0; i < ReadFromFile(path).length; i++) {
if (ReadFromFile(path)[i].q1 != null) {
counter++;
}
}
for (int i = 0; i < counter; i++) {
if (ReadFromFile(path)[i] != null) {
obj[No++].q1 = ReadFromFile(path)[i].q1;
}
}
} catch (Exception e) {
System.out.printf("error1 : %s", e.getMessage());
e.printStackTrace();
}
try {
for (int i = 0; i < ReadFromFile(path).length; i++) {
if (ReadFromFile(path)[i] != null) {
System.out.println(ReadFromFile(path)[i].q1);
}
}
} catch (Exception e) {
System.out.printf("error2 : %s", e.getMessage());
}
System.out.printf("%s %n", "*******************");
for (int i = 0; i < obj.length; i++) {
System.out.println(obj[i].q1);
}
saveToFile(obj, path);
}
public void saveToFile(Haha arr[], String path) {
try {
input = new FileOutputStream(path, true);
inPut = new ObjectOutputStream(input);
inPut.writeObject(arr);
inPut.close();
} catch (Exception e) {
System.out.printf("Crashd : %s", e.getMessage());
}
}
public Haha[] ReadFromFile(String path) throws Exception {
output = new FileInputStream(path);
outPut = new ObjectInputStream(output);
return ((Haha[]) outPut.readObject());
}
}
My goal with this code is to store some data into an array of objects (OBJ) then save that array to a file using the function I created. Then read the data from the file and store it in the First empty index in the same array and so on.
For some reason it doesn't seem to write the data to the file after the first run :( !
Help!
public class SerializableArray implements Serializable, AutoCloseable{
/**
*
*/
private static final long serialVersionUID = 1L;
private static File ardatei;
transient Integer[] currentarray;
private int cntoverwrite;
FileInputStream fi;
BufferedReader br;
ObjectInputStream in;
int cnt;
public SerializableArray(Integer[] integers, String filename) throws IOException {
ardatei = new File(filename);
currentarray = integers;
if (!ardatei.exists())
{
try {
ardatei.createNewFile();
System.out.println("your file is created....");
} catch (IOException e) {
// TODO Auto-generated catch block
System.out.println("could not create File");
System.exit(-1);
}
FileOutputStream fos = new FileOutputStream(ardatei);
ObjectOutputStream o = new ObjectOutputStream(fos);
o.writeObject(integers);
o.flush();
o.close();
} else if (ardatei.exists() || zustand > 0) {
FileOutputStream fos =
new FileOutputStream(ardatei);
ObjectOutputStream o =
new ObjectOutputStream(fos);
o.writeObject(integers);
o.writeObject("\n " + "your file was overwrite.");
System.out.println("your file exists and become overwritten");
o.flush();
o.close();
}
}
method to read data from given file
public SerializableArray(String filename) {
File f = new File (filename);
fi = null;
br= null;
in = null;
if (f.exists()){
try {
try {
fi = new FileInputStream(filename);
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
throw new FileNotFoundException("Loading file failed...");
}
in = new ObjectInputStream(fi);
System.out.println("Read data....");
//Cast to integer because readObject returns a object
currentarray =(Integer[]) in.readObject();
} catch (IOException | ClassNotFoundException e) {
System.out.println("could not read file....);
}
} else {
throw new RuntimeException ("this file doesnt exist yet");
}
}
that code works in my case ^^
I am using the following code to connect with java socket from an Applet client. I store Client's IP Address and some random number in every NEW connection happening in addNewClient() function in the below code. I store this info in HashMap. I add more client info in the ArrayList of this HashMap.
If there is already some client info in ArrayList, I need to read through it. I am trying that in SocketConnection class below using Iterator.
The problem I see is, I am adding some 3 client info into the ArrayList. But, when i read through it using Iterator, it can get only last added Client info, and other KEYS are just getting empty. But, at the same time, its giving the ArrayList size correctly as 3
Could some experts please refer my below complete code, and advise me what could be the problem in there?
public class DataSharingSocketListner {
public static void main(String[] args) {
System.out.println("client trying to connect before thread creation");
Thread thr = new Thread(new SocketThread());
thr.start();
}
}
class SocketThread implements Runnable {
HashMap<String, ClientInfo> clientInfo = new HashMap<String, ClientInfo>();
ArrayList<HashMap<String, ClientInfo>> myList = new ArrayList<HashMap<String, ClientInfo>>();
#Override
public void run() {
try {
System.out.println("client trying to connect after thread creation");
ServerSocket server = new ServerSocket(8080);
while (true) {
SocketConnection client = new SocketConnection(server.accept(), clientInfo, myList);
client.start();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
class SocketConnection extends Thread {
InputStream input;
PrintWriter output;
Socket socket;
ObjectOutputStream out = null;
OutputStream clientOutput;
Scanner scannerObj;
HashMap<String, byte[]> hm;
InetAddress addr;
HashMap<String, ClientInfo> clientinfo;
ArrayList<HashMap<String, ClientInfo>> clientList;
public SocketConnection(Socket socket, HashMap<String, ClientInfo> clientInfo, ArrayList<HashMap<String, ClientInfo>> myList) {
super("Thread 1");
this.socket = socket;
//this.hm = dataHashMap;
this.clientinfo = clientInfo;
this.clientList = myList;
try {
// IT IS PRINTING TOTAL SIZE 3 SUCCESSFULLY HERE
int totalClientList = clientList.size();
System.out.println("totalClientList: " + totalClientList);
if ( totalClientList>0 )
{
for (int i=0; i<totalClientList; i++)
{
System.out.println("client list reading " + i);
HashMap<String, ClientInfo> tmpData = (HashMap<String, ClientInfo>) clientList.get(i);
// IT IS GETTING ONLY THE LAST KEY, OTHER KEYS ARE SHOWING EMPTY
Set<String> key = tmpData.keySet();
Iterator it = key.iterator();
while (it.hasNext()) {
System.out.println("hasNexthasNext");
String hmKey = (String)it.next();
ClientInfo hmData = (ClientInfo) tmpData.get(hmKey);
System.out.println("Key: "+hmKey +" & Data: "+hmData.getRandomNo());
it.remove(); // avoids a ConcurrentModificationException
}
}
// TO ADD NEW CLIENT EVERY TIME
addNewClient();
}
else {
System.out.println("Client List shows empty");
// TO ADD NEW CLIENT EVERY TIME
addNewClient();
}
// Not used yet, will be used
input = socket.getInputStream();
scannerObj = new Scanner(socket.getInputStream());
clientOutput = socket.getOutputStream();
} catch (IOException e) {
e.printStackTrace();
}
}
public int genRandomNumber() {
Random r = new Random( System.currentTimeMillis() );
return 10000 + r.nextInt(20000);
}
String getLocalIP () {
InetAddress inetAddress = null;
String ipAddress = null;
try {
inetAddress = InetAddress.getLocalHost();
ipAddress = inetAddress.getHostAddress();
} catch (UnknownHostException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("ipAddress : " + ipAddress);
return ipAddress;
}
void addNewClient () {
String ipAddress = getLocalIP();
if ( ipAddress!=null )
{
ClientInfo clientobj = new ClientInfo();
clientobj.setIPAdd(ipAddress);
int randno = genRandomNumber();
System.out.println("genRandomNumber() : " + randno);
clientobj.setRandomNo(randno);
String key = String.valueOf(randno);
clientinfo.put(key, clientobj);
clientList.add(clientinfo);
}
}
#Override
public void run() {
System.out.println("Going to Read client data");
//do something
{
String hostIP = addr.getHostAddress() ;
System.out.println("hostIP: " + hostIP);
//do something
}
}
}
class ClientInfo {
private String IPAddress;
private long RandomNumber;
private byte[] data;
public static void main(String []args) {
System.out.println("Client info Main");
}
//Setter
void setIPAdd (String ip) {
System.out.println("setIPAdd called");
IPAddress = ip;
}
void setRandomNo (long randomno) {
RandomNumber = randomno;
}
void setImageData (byte[] imgData) {
data = imgData;
}
//Getter
String getIPAdd () {
return IPAddress;
}
long getRandomNo () {
return RandomNumber;
}
byte[] getImageData () {
return data;
}
}
UPDATE: As per Amrish suggestion, changed the following code, it solved the issue.
int totalClientList = clientList.size();
System.out.println("totalClientList: " + totalClientList);
if ( totalClientList>0 )
{
for (int i=0; i<totalClientList; i++)
{
System.out.println("client list reading " + i);
HashMap<String, ClientInfo> tmpData = (HashMap<String, ClientInfo>) clientList.get(i);
Set<String> key = tmpData.keySet();
System.out.println("key: " + key);
}
addNewClient();
}
else {
System.out.println("Client List shows empty");
addNewClient();
}
void addNewClient () {
String ipAddress = getLocalIP();
if ( ipAddress!=null )
{
// CREATE NEW OBJECT EVERY TIME WHEN STORING
HashMap<String, ClientInfo> clientInfo = new HashMap<String, ClientInfo>();
ClientInfo clientobj = new ClientInfo();
//System.out.println("Test log 1" + clientobj);
clientobj.setIPAdd(ipAddress);
// System.out.println("Test log 2");
int randno = genRandomNumber();
System.out.println("genRandomNumber() : " + randno);
clientobj.setRandomNo(randno);
String key = String.valueOf(randno);
//System.out.println("key: " + key);
clientInfo.put(key, clientobj);
clientList.add(clientInfo);
}
}
Your Arraylist has 3 hashmap but each hashmap has only one object. Hence , you are getting size as 3 but only one object is returned when you iterate over the hashmap.
Problem here is you are comparing two different things:
int totalClientList = clientList.size();
this will give you the number of clients you have in your list:
HashMap<String, ClientInfo> tmpData = (HashMap<String, ClientInfo>) clientList.get(i);
Set<String> key = tmpData.keySet();
This will give you the keys of the i'th client. which is different than the total clients.
What you should do is to drop List of hashmaps and just use a single HashMap to keep track of all the clients.
Hope this helps.
Change your SocketThread to remove list
class SocketThread implements Runnable {
HashMap<String, ClientInfo> clientInfo = new HashMap<String, ClientInfo>();
#Override
public void run() {
try {
System.out.println("client trying to connect after thread creation");
ServerSocket server = new ServerSocket(8080);
while (true) {
SocketConnection client = new SocketConnection(server.accept(), clientInfo);
client.start();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
change your SockectConnection to remove list
HashMap<String, ClientInfo> clientinfo;
public SocketConnection(Socket socket, HashMap<String, ClientInfo> clientInfo) {
super("Thread 1");
this.socket = socket;
//this.hm = dataHashMap;
this.clientinfo = clientInfo;
try {
// IT IS PRINTING TOTAL SIZE 3 SUCCESSFULLY HERE
int totalClientList = clientInfo.size();
System.out.println("totalClientList: " + totalClientList);
if ( totalClientList>0 )
{
// IT IS GETTING ONLY THE LAST KEY, OTHER KEYS ARE SHOWING EMPTY
Set<String> key = clientInfo.keySet()
Iterator it = key.iterator();
while (it.hasNext()) {
System.out.println("hasNexthasNext");
String hmKey = (String)it.next();
ClientInfo hmData = (ClientInfo) tmpData.get(hmKey);
System.out.println("Key: "+hmKey +" & Data: "+hmData.getRandomNo());
it.remove(); // avoids a ConcurrentModificationException
}
// TO ADD NEW CLIENT EVERY TIME
addNewClient();
}
else {
System.out.println("Client List shows empty");
// TO ADD NEW CLIENT EVERY TIME
addNewClient();
}
// Not used yet, will be used
input = socket.getInputStream();
scannerObj = new Scanner(socket.getInputStream());
clientOutput = socket.getOutputStream();
} catch (IOException e) {
e.printStackTrace();
}
}
Change Add New Client:
void addNewClient () {
String ipAddress = getLocalIP();
if ( ipAddress!=null )
{
ClientInfo clientobj = new ClientInfo();
clientobj.setIPAdd(ipAddress);
int randno = genRandomNumber();
System.out.println("genRandomNumber() : " + randno);
clientobj.setRandomNo(randno);
String key = String.valueOf(randno);
clientinfo.put(key, clientobj);
}
}
I just edited your code, it should give you a start. Hope this helps.
You are talking about two different things. One is the size of a list, the other one is the size of a hashMap.
Try this:
int mapSize = tmpData.size();
You can get the size of the map and check if it's right.
Is there any way InputStream wrapping a list of UTF-8 String? I'd like to do something like:
InputStream in = new XyzInputStream( List<String> lines )
You can read from a ByteArrayOutputStream and you can create your source byte[] array using a ByteArrayInputStream.
So create the array as follows:
List<String> source = new ArrayList<String>();
source.add("one");
source.add("two");
source.add("three");
ByteArrayOutputStream baos = new ByteArrayOutputStream();
for (String line : source) {
baos.write(line.getBytes());
}
byte[] bytes = baos.toByteArray();
And reading from it is as simple as:
InputStream in = new ByteArrayInputStream(bytes);
Alternatively, depending on what you're trying to do, a StringReader might be better.
You can concatenate all the lines together to create a String then convert it to a byte array using String#getBytes and pass it into ByteArrayInputStream. However this is not the most efficient way of doing it.
In short, no, there is no way of doing this using existing JDK classes. You could, however, implement your own InputStream that read from a List of Strings.
EDIT: Dave Web has an answer above, which I think is the way to go. If you need a reusable class, then something like this might do:
public class StringsInputStream<T extends Iterable<String>> extends InputStream {
private ByteArrayInputStream bais = null;
public StringsInputStream(final T strings) throws IOException {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
for (String line : strings) {
outputStream.write(line.getBytes());
}
bais = new ByteArrayInputStream(outputStream.toByteArray());
}
#Override
public int read() throws IOException {
return bais.read();
}
#Override
public int read(byte[] b) throws IOException {
return bais.read(b);
}
#Override
public int read(byte[] b, int off, int len) throws IOException {
return bais.read(b, off, len);
}
#Override
public long skip(long n) throws IOException {
return bais.skip(n);
}
#Override
public int available() throws IOException {
return bais.available();
}
#Override
public void close() throws IOException {
bais.close();
}
#Override
public synchronized void mark(int readlimit) {
bais.mark(readlimit);
}
#Override
public synchronized void reset() throws IOException {
bais.reset();
}
#Override
public boolean markSupported() {
return bais.markSupported();
}
public static void main(String[] args) throws Exception {
List source = new ArrayList();
source.add("foo ");
source.add("bar ");
source.add("baz");
StringsInputStream<List<String>> in = new StringsInputStream<List<String>>(source);
int read = in.read();
while (read != -1) {
System.out.print((char) read);
read = in.read();
}
}
}
This basically an adapter for ByteArrayInputStream.
You can create some kind of IterableInputStream
public class IterableInputStream<T> extends InputStream {
public static final int EOF = -1;
private static final InputStream EOF_IS = new InputStream() {
#Override public int read() throws IOException {
return EOF;
}
};
private final Iterator<T> iterator;
private final Function<T, byte[]> mapper;
private InputStream current;
public IterableInputStream(Iterable<T> iterable, Function<T, byte[]> mapper) {
this.iterator = iterable.iterator();
this.mapper = mapper;
next();
}
#Override
public int read() throws IOException {
int n = current.read();
while (n == EOF && current != EOF_IS) {
next();
n = current.read();
}
return n;
}
private void next() {
current = iterator.hasNext()
? new ByteArrayInputStream(mapper.apply(iterator.next()))
: EOF_IS;
}
}
To use it
public static void main(String[] args) throws IOException {
Iterable<String> strings = Arrays.asList("1", "22", "333", "4444");
try (InputStream is = new IterableInputStream<String>(strings, String::getBytes)) {
for (int b = is.read(); b != -1; b = is.read()) {
System.out.print((char) b);
}
}
}
In my case I had to convert a list of string in the equivalent file (with a line feed for each line).
This was my solution:
List<String> inputList = Arrays.asList("line1", "line2", "line3");
byte[] bytes = inputList.stream().collect(Collectors.joining("\n", "", "\n")).getBytes();
InputStream inputStream = new ByteArrayInputStream(bytes);
You can do something similar to this:
https://commons.apache.org/sandbox/flatfile/xref/org/apache/commons/flatfile/util/ConcatenatedInputStream.html
It just implements the read() method of InputStream and has a list of InputStreams it is concatenating. Once it reads an EOF it starts reading from the next InputStream. Just convert the Strings to ByteArrayInputStreams.
you can also do this way create a Serializable List
List<String> quarks = Arrays.asList(
"up", "down", "strange", "charm", "top", "bottom"
);
//serialize the List
//note the use of abstract base class references
try{
//use buffering
OutputStream file = new FileOutputStream( "quarks.ser" );
OutputStream buffer = new BufferedOutputStream( file );
ObjectOutput output = new ObjectOutputStream( buffer );
try{
output.writeObject(quarks);
}
finally{
output.close();
}
}
catch(IOException ex){
fLogger.log(Level.SEVERE, "Cannot perform output.", ex);
}
//deserialize the quarks.ser file
//note the use of abstract base class references
try{
//use buffering
InputStream file = new FileInputStream( "quarks.ser" );
InputStream buffer = new BufferedInputStream( file );
ObjectInput input = new ObjectInputStream ( buffer );
try{
//deserialize the List
List<String> recoveredQuarks = (List<String>)input.readObject();
//display its data
for(String quark: recoveredQuarks){
System.out.println("Recovered Quark: " + quark);
}
}
finally{
input.close();
}
}
catch(ClassNotFoundException ex){
fLogger.log(Level.SEVERE, "Cannot perform input. Class not found.", ex);
}
catch(IOException ex){
fLogger.log(Level.SEVERE, "Cannot perform input.", ex);
}
I'd like to propose my simple solution:
public class StringListInputStream extends InputStream {
private final List<String> strings;
private int pos = 0;
private byte[] bytes = null;
private int i = 0;
public StringListInputStream(List<String> strings) {
this.strings = strings;
this.bytes = strings.get(0).getBytes();
}
#Override
public int read() throws IOException {
if (pos >= bytes.length) {
if (!next()) return -1;
else return read();
}
return bytes[pos++];
}
private boolean next() {
if (i + 1 >= strings.size()) return false;
pos = 0;
bytes = strings.get(++i).getBytes();
return true;
}
}