I receive a lenght of bytes - java

InputStream in = socket.getInputStream();
byte[] content = new byte[2048];
int received = in.read(content, 0, content.length);
System.out.println(received);
Using this code, I would like to know how I retrieve only the number of bytes that the server sends me.
I was told to use a loop using a Buffer, but as I am new to this area, I didn't quite understand what it means, could someone give me a hand?

You are storing the number of bytes that the server sends you in the received variable. If you want to convert the data that the server sent into a string for debugging purposes, this is how you can do it:
int received = in.read(content, 0, content.length);
String messageFromServer = new String(content, 0, received);
Note that in general you need to call read multiple times in order to receive all the data from the server, just like with any InputStream. You can find tutorials on using Input and OutputStreams here:
https://docs.oracle.com/javase/tutorial/essential/io/bytestreams.html - specific example for reading and writing files, but sockets are no different.
http://tutorials.jenkov.com/java-io/inputstream.html - more general and thorough tutorial

Related

How to send ISO8583 message using Java

I'am really sorry if this is a duplicate question but I tried many answers in other threads and none of them worked for me.
I'am trying to send an ISO8583 message to a remote server through an SSLSocket using the TLSv1.2 protocol, I configured the certificate with the Keystore and attempted to send a sample ISO8583 message : 08002220010000800000900000011312115000000180105000003
0800: MTI
2220010000800000: Binary Hex Encoded Bitmap (Only fields 3, 7, 11, 24, 41 are present)
900000: Process code
0113121150: Transmission Date&Time
000001: STAN
801: Function code
05000003: Terminal ID
I then converted the message to an array of bytes and sent it with the socket OutputStream but no response came from the server and it is freezing when attempting to read the InputStream.
For the purpose of this question, I chose to test a manually-set sample message and not use any packaging method.
I'm very new to the ISO8583 so I don't exactly know what I'm doing wrong.
Here is the code I tried so far and thank you so much to who ever that will try to help me.
Thread thread = new Thread(() -> {
try {
X509TrustManager[] tmm;
KeyStore ks = KeyStore.getInstance("BKS");
InputStream is = getResources().openRawResource(R.raw.tunrootca2);
ks.load(is, KEY_PASSWORD.toCharArray());
tmm=tm(ks);
SSLContext ctx = SSLContext.getInstance("TLSv1.2");
ctx.init(null, tmm, null);
SSLSocketFactory SocketFactory = ctx.getSocketFactory();
SSLSocket socket = (SSLSocket) SocketFactory
.createSocket(REMOTE_ENDPOINT, REMOTE_ENDPOINT_PORT);
String sampleMessage = "080022200100008000009000000113120000000180105000003";
byte[] bytesMessage = sampleMessage.getBytes(StandardCharsets.UTF_16LE);
byte[] bytes = packData(bytesMessage);
OutputStream out = socket.getOutputStream();
out.write(bytes);
byte[] buffer = new byte[256];
InputStream in = socket.getInputStream();
int read;
while((read = in.read(buffer)) != -1) {
String output = new String(buffer, 0, read);
Log.v("SOCKET_OUTPUT", output);
}
} catch (Exception e) {
e.printStackTrace();
}
});
thread.start();
});
PackData Function
static byte[] packData(byte[] data) {
int len = data.length;
byte buf[] = new byte[len + 2];
buf[0] = (byte) (len >> 8 & 255);
buf[1] = (byte) (len & 255);
System.arraycopy(data, 0, buf, 2, len);
return buf;
}
Settuing up socket communication can be tricky. If possible, I'd advise using WebSockets for communication, as they're already set up with how the communications protocols connect.
But if you are going to stick with plain sockets:
After your write call, call flush();
out.write(bytes);
out.flush();
If 1 didn't work, things get trickier. Since you don't control the server side part of this, it's hard to know what you need to send them in order to get them to send you something back. You might try sending a newline character. But otherwise, there's a mismatch between what you are sending and what the server on the other end is expecting
--- Edit ---
I looked up ISO 8583 and have a better idea of what you are trying to do. You can ignore my previous suggestion on using WebServer sockets.
You are missing the basics. You can't build ISO messages using string concatenation. You have to set correct bitmaps for those enable fields. Maybe try to follow the below sample. It will guide you with the basics.
https://kodejava.org/how-do-i-pack-an-iso-8583-message/
Your code looks ok. What sort of backend are you connecting to? 8583 is a bit like xml, it's a format description, but every processor uses it to build their own specific protocol from it, so you really need to ask the vendor you are connecting to for protocol documentation.
Some things that may be the matter:
flush the OutputStream when you are done writing, the message may still be hanging in your OS buffer
check the vendor documentation whether you need some sort of framing you may need to append a checksum to the message or ...
you may need to prepend a length header to the message. 8583 was originally built on top of relay protocols where the transport handled message length. A lot of parsers haven't caught up yet with the transition to TCP/IP :)

Sending a string after a file on the same socket

I'm sending a string over the socket I previously sent a file to, but the recipient reads it as part of the file itself, is there a way to send a sort of EOF before sending the string?
To send the file I'm using
byte[] buffer = new byte[1024];
int count;
while ((count = fis.read(buffer)) >= 0) os.write(buffer, 0, count);
os.flush();
(and almost the same to receive it)
To send the string I'm using OutputStreamWriter
(Here you are my code: hatebin)
I've also read here that I should send a SOH character, but which one should I send and how?
Thanks in advance.
No there's no way to send an "eof" and then send something afterwards.
If you don't want to open a new connection, there are basically two ways to solve this.
You can modify the client so it recognizes some special byte sequence as a "delimiter", and stops writing to the file when it reads the delimiter from the socket. In this case you need to have some strategy to deal with the possibility that the file actually contains the delimiter.
You can send the size of the file in bytes before sending the file, and modify the client so it counts the number of bytes it reads from the socket. When the client has read enough, it should stop writing to the file.

How to read a DataInputStream twice or more than twice?

I have a Socket connection to an application that I hosted elsewhere. Once I connected I made a OutputStream and DataInputStream.
Once the connection has been made, I use the OutputStream to send out a handshake packet to the application. Once this handshake has been approved, it returns a packet through the DataInputStream (1).
This packet is processed and is returned to the application with the OutputStream.
If this returned data is valid, I get another packet from the DataInputStream (2). However, I have not been able to read this packet through the DataInputStream.
I have tried to use DataInputStream.markSupported() and DataInputStream.mark() but this gave me nothing (except for an empty Exception message).
Is it possible to read the input stream for a second time? And if so, can someone please point me out what I'm doing wrong here?
EDIT: Here is my solution:
// First define the Output and Input streams.
OutputStream output = socket.getOutputStream();
BufferedInputStream bis = new BufferedInputStream(socket.getInputStream());
// Send the first packet to the application.
output.write("test"); // (not actual data that I sent)
// Make an empty byte array and fill it with the first response from the application.
byte[] incoming = new byte[200];
bis.read(incoming); //First packet receive
//Send a second packet to the application.
output.write("test2"); // (not actual data that I sent)
// Mark the Input stream to the length of the first response and reset the stream.
bis.mark(incoming.length);
bis.reset();
// Create a second empty byte array and fill it with the second response from the application.
byte[] incoming2 = new byte[200];
bis.read(incoming2);
I'm not sure if this is the most correct way to do this, but this way it worked for me.
I would use ByteArrayInput stream or something that you can reset. That would involve reading the data into another type of input stream and then creating one.
InputStream has a markSupported() method that you could check on the original and the byte array one to find one that the mark will work with:
https://docs.oracle.com/javase/7/docs/api/java/io/InputStream.html#markSupported()
https://docs.oracle.com/javase/7/docs/api/java/io/ByteArrayInputStream.html
The problem here is not re-reading the input. I don't see anything in the question that requires you to read the input twice. The problem is the BufferedInputStream, which will read everything that is available to be read, including the second message, if it has already arrived.
The solution is not to use a buffered stream until you have completed the handshake. Just issue a read on the socket input stream for exactly the length of the first message, do the handshake, and then proceed to construct and read the buffered stream.

Receiving .mp4 over TCP socket (C++/Java)

I'm working on a project which requires transmitting a MP4 video from a Java (Android) server to a C++ (Visual studio) client.
Everything works fine if I use a Java Client, but with the C++ client I receive a file 1 byte larger that the file sent. As a result, the file won't obviously open. I don't know if the problem is just with that extra bit or there is something else wrong.
Here's the relevant code of my C++ Client:
FILE *myFile=std::fopen("scan.mp4","wb");
char *recVideoBuffer=new char[videoSize];//videoSize previously received
int written;
totalBytesRcvd = 0;
while (totalBytesRcvd < videoSize)
{
if ((bytesRcvd = recv(sock, recVideoBuffer, videoSize, 0)) <= 0)
DieWithError("recv() failed or connection closed prematurely");
totalBytesRcvd += bytesRcvd; /* Keep tally of total bytes */
if ((written = fwrite(recVideoBuffer, 1, bytesRcvd, myFile)) != bytesRcvd)
DieWithError("fwrite() failed");
printf("Filesize: %d\nReceived: %d\Written %d\nTotal Received: %d\n\n", videoSize, bytesRcvd, written, totalBytesRcvd);
}
fclose(myFile);
closesocket(sock);
Java Server:
File source = new File (VIDEO_FILE);
byte byteArray[] = new byte [(int)source.length()];
BufferedInputStream bStream = new BufferedInputStream(new FileInputStream(source));
bStream.read(byteArray, 0, byteArray.length);
OutputStream outStream = s.getOutputStream();
outStream.write(byteArray, 0, byteArray.length);
outStream.flush();
outStream.close();
bStream.close();
s.close();
server.close();
Any ideas on what the problem(s) might be and how to fix them?
I've looked around and found some related questions but those were either unanswered or not relevant...
Thanks.
EDIT:
I tried sending a 'abcde' .asc file, and the client receives just 'abcd'. In this case both the files are 5 byte large though.
===
I looked into the byte values of the MP4 files.
The received one has 2 extra bytes, 0a, at the beginning, and is missing the last 2B. Manually changing these values makes the video play correctly.
Where does 0a come from? Obviously because of those 2B the last 2 are not written...
SOLVED
Quite a silly issue really... I would send the video size using println(), which recv() couldn't read entirely, apparently leaving those 0a bytes over the socket, ready to be received by the next recv() along with the file bytes.
That explains why everything worked with the Java client, as I would read the videoSize using readLine() .
How annoying having wasted a day on this, Lol.

difference between Java TCP Sockets and C TCP Sockets while trying to connect to JDBC

My problem is that C sockets look to act differently than Java sockets. I have a C proxy and I tested it between a workload generator (oltp benchmark client written in Java) and the JDBC connector of the Postgres DB.
This works great and forwards data from one to other, as it should. We need to make this proxy work in Java, so I used plain ServerSocket and Socket classes from java.net and I cannot make it work. The Postgres returns an authentication error message, assuming that the client did not send the correct password.
Here is how the authentication at the JDBC protocol works:
-client sends a requests to connect to a database specifying the database name and the username
-server responds back with a one time challenge message (13 byte message with random content)
-client concatenates this message with the user password and performs a md5 hash
-server compares the hash got from the client with the hash he computes
[This procedure is performed in order to avoid replay attacks (if client would send only the md5 hash of its password then an attacker could replay this message, pretending he is the client)]
So I inspected the packets with tcpdump and they look correct! The size is exactly as it should, so maybe the content is corrupted (??)
Sometimes though the DB server responds ok for the authentication (depending on the value of the challenge message)!! And then the oltp client sends a couple of queries, but it crashes in a while…
I guess that maybe it has to do with the encoding, so I tried with the encoding that C uses (US-ANSII), but still the same.
I send the data using fixed size character or byte arrays both in C and in Java!
I really don't have any more ideas, as I tried so many cases...
What is your guess of what would be the problem?
Here is a representative code that may help you have a more clear view:
byte [] msgBuf;
char [] msgBufChars;
while(fromInputReader.ready()){
msgBuf = new byte[1024];
msgBufChars = new char[1024];
// read data from one party
int read = fromInputReader.read(msgBufChars, 0, 1024);
System.out.println("Read returned : " + read);
for(int i=0; i<1024; i++)
msgBuf[i] = (byte) msgBufChars[i];
String messageRead = new String(msgBufChars);
String messageToWrite = new String(msgBuf);
System.out.println("message read : "+messageRead);
System.out.println("message to write : "+new String(messageToWrite));
// immediatelly write data to other party (write the amount of data we read (read value) )
// there is no write method that takes a char [] as a parameter, so pass a byte []
toDataOutputStream.write(msgBuf, 0, read);
toDataOutputStream.flush();
}
There are a couple of message exchanges in the beginning and then Postgres responds with an authentication failure message.
Thanks for your time!
What is your guess of what would be the problem?
It is nothing to do with C versus Java sockets. It is everything to do with bad Java code.
I can see some problems:
You are using a Reader in what should be a binary stream. This is going to result in the data being converted from bytes (from the JDBC client) to characters and then back to bytes. Depending on the character set used by the reader, this is likely to be destructive.
You should use plain, unadorned1 input streams for both reading and writing, and you should read / write to / from a preallocated byte[].
This is terrible:
for(int i=0; i<1024; i++)
msgBuf[i] = (byte) msgBufChars[i];
If the characters you read are not in the range 0 ... 255 you are mangling them when you stuff them into msgBuf.
You are assuming that you actually got 1024 characters.
You are using the ready() method to decide when to stop reading stuff. This is almost certainly wrong. Read the javadoc for that method (and think about it) and you should understand why it is wrong. (Hint: what happens if the proxy can read faster than the client can deliver?)
You should use a while(true), and then break out of the loop if read tells you it has reached the end of stream; i.e. if it returns -1 ...
1 - Just use the stream objects that the Socket API provides. DataXxxStream is unnecessary because the read and write methods are simply call-throughs. I wouldn't even use BufferedXxxStream wrappers in this case, because you are already doing your own buffering using the byte array.
Here's how I'd write that code:
byte [] buffer = new byte[1024]; // or bigger
while(true) {
int nosRead = inputStream.read(buffer);
if (nosRead < 0) {
break;
}
// Note that this is a bit dodgy, given that the data you are converting is
// binary. However, if the purpose is to see what embedded character data
// looks like, and if the proxy's charset matches the text charset used by
// the client-side JDBC driver for encoding data, this should achieve that.
System.out.println("Read returned : " + nosRead);
System.out.println("message read : " + new String(buffer, 0, nosRead));
outputStream.write(buffer, 0, nosRead);
outputStream.flush();
}
C sockets look to act differently than Java sockets.
Impossible. Java sockets are just a very thin layer over C sockets. You're on the wrong track with this line of thinking.
byte [] msgBuf;
char [] msgBufChars;
Why are you reading chars when you want to write bytes? Don't use Readers unless you know that the input is text.
And don't call ready(). There are very few correct uses, and this isn't one of them. Just block.

Categories