Read JPEG bytes from TCP socket (Android) - java

I have an application that show CCTV feed from mobile. I successfully develop iOS application and now my client wants me to port to Android. The porting was fine until I'm stuck at this part of code.
This part of code, I have to connect to TCP socket server. When connected, I don't have to send server any thing, instead, server will send me a JPEG image. So, after connected, I'll have to keep reading until I received JPEG end marker (0xFF, 0xD9) and then close the connection.
What I plan to do is
Socket s = new Socket("Server IP Addreess", SERVER_PORT);
DataInputStream dis = new DataInputStream(socket.getInputStream());
ArrayList<Byte> bytes = new ArrayList<Byte>();
boolean err = false;
while (true) {
byte b = dis.readByte();
if (b == -1) {
err = true;
break;
}
bytes.add(b);
if ((bytes.get(bytes.size() - 1) == (byte) 0xFF) &&
(bytes.get(bytes.size() - 2) == (byte) 0xD9)) {
break;
}
}
socket.close();
if (!err) {
// create bitmap from byte array and show in ImageView
}
But I'm not sure that this is correct or not. The other solution I'm thinking about is
Socket s = new Socket("Server IP Addreess", SERVER_PORT);
BitmapFactory.decodeStream(s.getInputSteam());
But, also, I don't know how to close socket when server send 0xFF, 0xD9. Or will the BitmapFactory will detect that marker and close socket connection for me?
Any suggestion are welcome. Thank you!
P.S. I don't have test environment as they took it back when I delivered iOS app. So, I have to develop and then deliver to them to test (by playing with the app). Then if it's not working, they will tell me to correct it but I won't be able to access LogCat or other useful information. This is just like trying to sew a button in dark room, and I can't turn on the light.
: |
EDIT
More information about this server
Server won't send length of file, so, number of bytes for each file is unknown.
I have to detect 0xFF, 0xD9 that indicate end of file.
Problem is, I have to terminate socket connection from my side, server won't do it.
I can't change the way server works, it's hardware that my client purchase.
Also, I'm getting one image for each TCP connection, I'm not getting multiple images for single TCP connection.

This is a bad idea to just look for some magic bytes to determine the end of the data. While these magic bytes should be at the end of the file they can also happen inside the data.
Better would be to either prefix the data with the length or use a separate TCP connection for each jpeg file and just read until the end of connection (that is until you don't get any more data). If this is not possible you have to do more advanced parsing, see Detect Eof for JPG images.

Related

Java TCP Socket Programming: Client and Server communicate well on the same computer, but fail to send data to each other over LAN

I am trying to set up a program where the server can communicate with multiple clients. The program is written in Java. I got it all working on the same machine so I decided to try LAN. I converted the program to JAR files and I tried connecting my laptop to my PC (both are on the same network). The connection is successful but unfortunately only 1 message arrives to the server. As you can see in the code below, I send multiple messages (Meaning that i write multiple times) via DataOutputStream. One defines the datatype (in the following example 0 means that it's a String) and the other sends the actual message data. I also print the size of the packets in bytes and it always matches the size of the DataOutputStream instance.
DataOutputStream dOut = new DataOutputStream(clientSocket.getOutputStream());
String str = "Hello";
//Type
System.out.println("Type output size: 1");
dOut.writeByte(0);
//Message
System.out.println("Message output size: " + (str.getBytes(StandardCharsets.UTF_8).length + 2));
dOut.writeUTF(str);
System.out.println("Length of all: " + (dOut.size()));
dOut.flush();
So now when the data from the client is sent we need to handle it on the server, which the code below does. It retrieves the InputStream from the Socket called client and inserts it into the DataInputStream. This is were it gets weird on LAN as the stream only contains the first message.
InputStream stream = client.getInputStream();
DataInputStream dIn = new DataInputStream(stream);
while(dIn.available() > 0) {
byte type = dIn.readByte();
switch(type) {
case 0:
System.out.println(dIn.readUTF());
break;
case 1:
System.out.println(dIn.readInt());
break;
case 2:
System.out.println(dIn.readByte());
break;
default:
throw new IllegalStateException("Unexpected value: " + type);
}
}
If you run the Client in the IDE on lets say a laptop connected to the same network and then you run the Server on a PC connected to the same network it will work. However, not if the programs are in JARS.
The actual stacktrace is the following:
java.net.SocketException: Connection reset
at java.base/sun.nio.ch.NioSocketImpl.implRead(NioSocketImpl.java:323)
at java.base/sun.nio.ch.NioSocketImpl.read(NioSocketImpl.java:350)
at java.base/sun.nio.ch.NioSocketImpl$1.read(NioSocketImpl.java:803)
at java.base/java.net.Socket$SocketInputStream.read(Socket.java:966)
at java.base/java.net.Socket$SocketInputStream.read(Socket.java:961)
at
java.base/java.io.DataInputStream.readInt(DataInputStream.java:393)
The stacktrace does not tell me anything, but it points at case 0: in the switch case. It can't read the String as the DataInputStream does not contain any data (I guess?).
I would also like to state that the Server is multithreaded! I have one thread that adds the sockets when they are accepted through ServerSocket.accept() and I use the second (main thread) to read the data sent from clients.
I have selected the code above as I believe that the issue lies within it, however I am new to Socket Programming and I know that some of you would like to see other parts of the code. I will add more relevant code when I am asked.
I do not know why it acts like this, does anyone know why?
What have i tried?
I have tried waiting for packets - but that has only resulted in the
Server looping forever. With waiting for packets I mean not going forward until the DataInputStream contains enough bytes.
I have disabled Nagels Algorithm through setTCPNoDelay(false).
Tried to send different datatypes, but that also failed
I tried changing the first packet to a String which resulted in the String showing up in the DataInputStream.
I have tried portforwarding the port used and I have tried disabling the firewall on both computers.
Update 1
I have been taking advice from the comments which has led a to a few discoveries:
Closing the DataOutputStream successfully sends all packets to the client.
It is also possible to build your own buffer and decode it in the server. However, it is still not possible to send any more messages after this.
It worked as a JAR because IntelliJ was being nice (Eclipse threw the same error when running in IDE)
Update 2:
I think this post is relevant. It states that SocketException is sent when a client closes it's socket "ungracefully". And because my Client closes (as it is not in a loop) and I don't close the socket properly - it will close "ungracefully" and the data will be lost. Hence the error.
The issue is now solved, and the solution is quite logical. My client does not operate in a loop, rather it sends the data and closes the program. That sounds fine, but I forgot to properly close the socket of the client.
The reason why the second 'packet' never arrived was due to me doing this tiny mistake. The packet was on it's way through the local network but the client socket improperly closed it's socket before the packet arrived to the server, which is why I got a SocketException error. See this.
I solved the issue by putting socket.close() ,where socket is the client's socket, after I had sent all the messages I wanted to send.

how to keep listening to a an inputstream in java

I am working on an Android app which has an i/o streams opened to a pc app, I want to keep an eye on the inputStream and get notified when new data comes from the pc side. Currently I am while(true)-ing {istream.read()} and once it does not through an error I know that something new just arrived so I do the propper action. Is there a better way to keep listening to the inputStream? Is this a correct way for listening?
//assume we have inputStream instance
while(true) {
val bytes = ByteArray(size)
try {
inputStream.read(bytes, 0, size)
//consume the bytes!
}catch(e: Exception) {
//do nothing because nothing arrived
}
}

mMediaRecorder.setOutputFile(ParcelFileDescriptor.fromSocket(mSocket)) - nothing written to mSocket

I have Service which records video from back camera:
this.mMediaRecorder = new MediaRecorder();
this.mMediaRecorder.setCamera(mCamera);
this.mMediaRecorder.setVideoSource(VideoSource.CAMERA);
this.mMediaRecorder.setOutputFormat(OutputFormat.DEFAULT);
this.mMediaRecorder.setVideoEncoder(VideoEncoder.DEFAULT);
this.mMediaRecorder.setVideoSize(photo_resolution[0], photo_resolution[1]);
this.mParcelFileDescriptor = ParcelFileDescriptor.fromSocket(this.mSocket);
this.mMediaRecorder.setOutputFile(this.mParcelFileDescriptor.getFileDescriptor());
this.mMediaRecorder.setPreviewDisplay(mSurfaceHolder.getSurface());
this.mMediaRecorder.prepare();
this.mMediaRecorder.start();
Video doesn't have a sound. I connecting to the server with following code:
this.mSocket = new Socket();
this.mSocket.connect(new InetSocketAddress(...), 30000);
this.in = new BufferedReader(new InputStreamReader(this.mSocket.getInputStream(), "UTF-8"));
this.out = this.mSocket.getOutputStream();
this.out.write(("some string" + "\n").getBytes("UTF-8"));
this.out.flush();
...
Every needed permissions I have already added to AndroidManifest.xml.
The problem is that video isn't broadcasted to server. I tried to replace this.mParcelFileDescriptor.getFileDescriptor() to "/sdcard/video.mp4", everything was alright - video recorded correclty. But it doesn't sends to socket. Nothing happened clientside (in my app), but serverside thrown me exception java.net.SocketTimeoutException: read timed out. I tried this code serverside:
byte[] bytes = new byte[1024];
int read = mSocket_inputStream.read(bytes, 0, 1024);
System.out.println(read);
which debug me count of read bytes from client (from my app) - nothing was debugged in console, because 0 bytes was sent. Help me solve this problem please. I didn't found solution in google.
File descriptor only works for local server socket in MediaRecorder.
this.mParcelFileDescriptor = ParcelFileDescriptor.fromSocket(this.mSocket);
this.mMediaRecorder.setOutputFile(this.mParcelFileDescriptor.getFileDescriptor();
You can create your own LocalServerSocket and then pass data to actual server socket from your local server socket
To know more about local server socket you can go through https://developer.android.com/reference/android/net/LocalServerSocket.html
The more efficient way will be to use MediaCodec API for recording as it will provide you data frame by frame in format of byte buffer and you can send that data on to server.

How to read the data from datastream

Please help me out on how to read the stream of data in java. My requirement is to make the telnet connection to the router. This part is accomplished. From the router, Have to connect to the xxx remote machine using its ip address and port number through telnet. While making this connection, i am getting some response. But while reading, the program control stops at read() method of InputStream class. Here are the code snippet which i am using to read the stream of data.
buff = new byte[4*1024];
ret_read = 0;
do
{
ret_read = in.read(buff); // Program control gets hanged here. Once all the data are read...
if(ret_read > 0)
{
System.out.println(new String(buff,0,ret_read));
}
}while(ret_read > 0);
What is happening is the read is blocking and waiting for more data to be sent on the stream, it will continue to do that until the stream is closed or more data is sent.
You need to either use a non-blocking read, put a timeout on the read, or close the stream server side after it finishes sending the data.

How to write packets from vpn tunnel to pcap file

I used the Android's ToyVpnClient to set up a tunnel and intercept incoming & outgoing packets. I want to write these packets from this tunnel, which is stored in a ByteBuffer, to a pcap file. I've looked at jpcap and jnetpcap, but they seem to only support creating the pcap file from the packets that were captured by using these libraries to listen to the device's network interfaces in the first place.
Is there any api available that would help me create the pcap file from the tunnel's packet data stored in the ByteBuffer? If I were to create my own pcap writer, how would i go about parsing this packet from the tunnel and put it into the pcap format? (I've looked but haven't found any examples)
Here's the relevant code sample from the ToyVpnClient:
...
DatagramChannel tunnel = DatagramChannel.open();
// Connect to the server.
tunnel.connect(server);
// Packets to be sent are queued in this input stream.
FileInputStream in = new FileInputStream(mInterface.getFileDescriptor());
// Packets received need to be written to this output stream.
FileOutputStream out = new FileOutputStream(mInterface.getFileDescriptor());
// Allocate the buffer for a single packet.
ByteBuffer packet = ByteBuffer.allocate(32767);
// keep forwarding packets till something goes wrong.
while (true) {
// Read the outgoing packet from the input stream.
int length = in.read(packet.array());
if (length > 0) {
// Write the outgoing packet to the tunnel.
packet.limit(length);
tunnel.write(packet);
//How to write to pcap file here?
packet.clear();
}
...
}
Thank you
For anyone looking for similar solution, I got it working using the netutils library's PCapFileWriter and modifying it to include a fake ethernet header for each packet (http://code.google.com/p/netutils/)

Categories

Resources