I want to read from a socket till a specific String sequence like <END>. The problem is the server sometimes returns me the data in chunks that is when the text (JSON) is too long to be sent in packets. I want to make a logic where the socket keeps appending a String Builder until the stream ends at <END> and after that continue appending another message.
InputStreamReader inr = new InputStreamReader(socket.getInputStream());
BufferedReader br = new BufferedReader(inr);
byte[] resultBuff = new byte[0];
byte[] buff = new byte[99999];
int k;
StringBuilder sb = new StringBuilder();
while ((k = in .read(buff, 0, buff.length)) > -1) {
byte[] tbuff = new byte[resultBuff.length + k]; // temp buffer size = bytes already read + bytes last read
System.arraycopy(resultBuff, 0, tbuff, 0, resultBuff.length); // copy previous bytes
System.arraycopy(buff, 0, tbuff, resultBuff.length, k); // copy current lot
resultBuff = tbuff; // call the temp buffer as your result buff
System.out.println(resultBuff.length + " bytes read.");
String s = new String(resultBuff);
sb.append(s);
if (s.endsWith(DELIMITER)) {
String response = sb.toString().replace(DELIMITER, "").replace("\n", "").replace("\r", "");
System.out.println(response);
if (listener != null) {
listener.onMessageReceived(params[0], response);
} else {
Log.e(TAG, "Response :: listener.onMessageReceived null ");
}
resultBuff = new byte[0];
sb = new StringBuilder();
} else {
sb.append(s);
}
}
The goal here is to keep the while loop running so that the socket can keep reading whenever something comes in stream.
You can do something like this.
String str = "";
byte[] b = new byte[1024];
for(int r = 0; (r = is.read(b))!=-1;){
str += new String(b,0,r);
if(str.contains(DELIMITER)){
String message = str.substring(0,str.indexOf(DELIMITER));
str = str.substring(str.indexOf(DELIMITER)+DELIMITER.length());
//process message.
}
}
if(!str.isEmpty()){
//the JSON object was sent in a single chunk, porcess it
}
What this does is to store all the data read from the InputStream into the str String and when the str contains the DELIMITER it will extract the message from the str so it can be processed. This will fail if the buffer contains more then one message. You can use String[] messages = str.split(DELIMITER) to deal with that.
A better solution will be to use a Scanner, here is an example:
Scanner sc = new Scanner(in);
sc.useDelimiter(DELIMITER);
while(sc.hasNext()){
String message = sc.next();
// process message.
}
Related
I'm currently working on Android Studio.
I want to read a file from asset folder but it gives me this error. does anyone encountered this problem ?
java.lang.IllegalArgumentException: n <= 0: 0
here's my code:
private String getRandomDataFromCategory(String name/* #param for the name of text file*/){
readings = "";
StringBuffer stringBuffer = new StringBuffer();
String[] temp;
try{
//getting file from asset folder
InputStream inputStream = getAssets().open(name + ".txt",AssetManager.ACCESS_BUFFER);
int size = inputStream.available();
byte[] buffer = new byte[size];
inputStream.read(buffer);
inputStream.close();
stringBuffer.append(new String(buffer));
// to check if stringBuffer isn't empty
if (stringBuffer.length() != 0){
//spliting stringBuffer
temp = stringBuffer.toString().split("\\`");
Random random = new Random();
// to create random index of array temp
int a = random.nextInt(temp.length - 1);
readings = temp[a];
}
}catch (Exception e) {
Toast.makeText(gaming.this, e.toString(),Toast.LENGTH_SHORT).show();
}
return readings;
}
this post is somehow similar to this post
Android Random Number llegalArgumentException: n <= 0: 0
yet different
You can read your text file from assets this way:
public String getContentFromFile(String filename)
{
StringBuilder s=new StringBuilder();
try
{
InputStream is=getAssets().open(filename);
BufferedReader br=new BufferedReader(new InputStreamReader(is,Charset.forName("UTF-8")));
String line;
while((line=br.readLine())!=null)
{
s.append(line).append("\n");
}
}catch (Exception | Error e){e.printStackTrace();}
return s.toString();
}
You can customize it to fit your need and if doesn't work you can still ask for clarifications in comments.
I am trying to build a manual HTTP client (using sockets) along with a cache and I cant seem to figure out why the files are not saving to disk properly. It works pretty good for HTML files, but cant seem to work for other files types that re not text based like .gif. Could anyone tell me why? I am quite new to HTTP protocol and Socket programming in general.
The loop to grab the response.
InputStream inputStream = socket.getInputStream();
PrintWriter outputStream = new PrintWriter(socket.getOutputStream());
ArrayList<Byte> dataIn = new ArrayList<Byte>();
ArrayList<String> stringData = new ArrayList<String>();
//Indices to show the location of certain lines in arrayList
int blankIndex = 8;
int lastModIndex = 0;
int byteBlankIndex = 0;
try
{
//Get last modified date
long lastMod = getLastModified(url);
Date d = new Date(lastMod);
//Construct the get request
outputStream.print("GET "+ "/" + pathName + " HTTP/1.1\r\n");
outputStream.print("If-Modified-Since: " + ft.format(d)+ "\r\n");
outputStream.print("Host: " + hostString+"\r\n");
outputStream.print("\r\n");
outputStream.flush();
//Booleans to prevent duplicates, only need first occurrences of key strings
boolean blankDetected = false;
boolean lastModDetected = false;
//Keep track of current index
int count = 0;
int byteCount = 0;
//While loop to read response
String buff = "";
byte t;
while ( (t = (byte) inputStream.read()) != -1)
{
dataIn.add(t);
//Check for key lines
char x = (char) t;
buff = buff + x;
//For the first blank line (signaling the end of the header)
if(x == '\n')
{
stringData.add(buff);
if(buff.equals("\r\n") && !blankDetected)
{
blankDetected = true;
blankIndex = count;
byteBlankIndex = byteCount + 2;
}
//For the last modified line
if(buff.contains("Last-Modified:") && !lastModDetected)
{
lastModDetected = true;
lastModIndex = count;
}
buff = "";
count++;
}
//Increment count
byteCount++;
}
}
The the code to parse through response and write file to disk.
String catalogKey = hostString+ "/" + pathName;
//Get the directory sequence to make
String directoryPath = catalogKey.substring(0, catalogKey.lastIndexOf("/") + 1);
//Make the directory sequence if possible, ignore the boolean value that results
boolean ignoreThisBooleanVal = new File(directoryPath).mkdirs();
//Setup output file, and then write the contents of dataIn (excluding header) to the file
PrintWriter output = new PrintWriter(new FileWriter(new File(catalogKey)),true);
for(int i = byteBlankIndex + 1 ; i < dataIn.size(); i++)
{
output.print(new String(new byte[]{ (byte)dataIn.get(i)}, StandardCharsets.UTF_8));
}
output.close();
byte t;
while ( (t = (byte) inputStream.read()) != -1)
The problem is here. It should read:
int t;
while ( (t = inputStream.read()) != -1)
{
byte b = (byte)t;
// use b from now on in the loop.
The issue is that a byte of 0xff in the input will be returned to the int as 0xff, but to the byte as -1, so you are unable to distinguish it from end of stream.
And you should use a FileOutputStream, not a FileWriter, and you should not accumulate potentially binary data into a String or StringBuffer or anything to do with char. As soon as you've got to the end of the header you should open a FileOutputStream and just start copying bytes. Use buffered streams to make all this more efficient.
Not much point in any of these given that HttpURLConnection already exists.
I sent a GET message with socket. And I received response message as string. But I want to receive as hexadecimal. But I didn't accomplish. This is my code block as string. Can you help me ?
dos = new DataOutputStream(socket.getOutputStream());
dis = new BufferedReader(new InputStreamReader(socket.getInputStream()));
dos.write(requestMessage.getBytes());
String data = "";
StringBuilder sb = new StringBuilder();
while ((data = dis.readLine()) != null) {
sb.append(data);
}
when you use BufferedReader you'll get the input into String format..so better way to use InputStream...
here is sample code to achieve this.
InputStream in = socket.getInputStream();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] read = new byte[1024];
int len;
while((len = in.read(read)) > -1) {
baos.write(read, 0, len);
}
// this is the final byte array which contains the data
// read from Socket
byte[] bytes = baos.toByteArray();
after getting the byte[] you can convert it to hex string using the following function
StringBuilder sb = new StringBuilder();
for (byte b : bytes) {
sb.append(String.format("%02X ", b));
}
System.out.println(sb.toString());// here sb is hexadecimal string
reference from java-code-to-convert-byte-to-hexadecimal
Based on the below question,
http://stackoverflow.com/questions/23753342/java-memory-allocation-for-local-variables
I use SerialPortEvent to read data from serial port,
public String logtext = "";
public void serialEvent(SerialPortEvent evt) {
if (evt.getEventType() == SerialPortEvent.DATA_AVAILABLE) {
try {
StringBuilder sBuilder = new StringBuilder();
int length = input.available();
byte[] array = new byte[length];
int numBytes = input.read(array);
for (byte b : array) {
logText = new String(new byte[] {b});
sBuilder.append(logText);
}
//Finally i append the StringBuilder to JtextPane
.......
......
}
Creating new String() every time the serialEvent is called it will create new instance every time which will increase the memory usage.. In certain scenario this serialEvent will be called every second.
Is there any other efficient way to do this with out using new operator??
Please help
You don't need to do this one byte at a time. I would just do all the bytes at once.
int length = input.available();
byte[] array = new byte[length];
String logText = new String(array, 0); // assume ISO-8859-1 encoding
is there any possibility my following BufferedReader is able to put the input directly into a byte[]?
public static Runnable reader() throws IOException {
Log.e("Communication", "reader");
din = new DataInputStream(sock.getInputStream());
brdr = new BufferedReader(new InputStreamReader(din), 300);
boolean done = false;
while (!done) {
try {
char[] buffer = new char[200];
int length = brdr.read(buffer, 0, 200);
String message = new String(buffer, 0, length);
btrar = message.getBytes("ISO-8859-1");
int i=0;
for (int counter = 0; counter < message.length(); counter++) {
i++;
System.out.println(btrar[counter] + " = " + " btrar " + i);
}
...
thats the part of the reader, pls have a look.
I want the input directly to btrar,
is there any possibility my following BufferedReader is able to put the input directly into a byte[]?
Any Reader is designed to let you read characters, not bytes. To read binary data, just use an InputStream - using BufferedInputStream to buffer it if you want.
It's not really clear what you're trying to do, but you can use something like:
BufferedInputStream input = new BufferedInputStream(sock.getInputStream());
while (!done) {
// TODO: Rename btrar to something more meaningful
int bytesRead = input.read(btrar);
// Do something with the data...
}