Capturing Buffering playing live Audio Streaming - java

I am getting live audio streaming over the network in the form of RTP packets and I have to write a code to Capture, Buffer and play the audio stream.
Problem
Now to solve this problem I have written two threads one for capture the audio and another for playing it. Now when I start both the threads my capture threads running slower than playing thread :(
Buffer Requirement
RTP Audio Packets.
8kHz, 16-bit Linear Samples (Linear PCM).
4 frames of 20ms audio will be sent in each RTP Packet.
Do not play until AudioStart=24 (# of 20ms frames) have arrived.
While playing ... if the # of 20ms frames in buffer reaches 0 ...
stop playing until AudioStart frames are buffered then restart.
While playing ... if the # of 20ms frames in buffer exceeds
AudioBufferHigh=50 then delete 24 frames (in easiest manner -- delete
from buffer or just drop next 6 RTP messages).
What I have done so far..
Code
BufferManager.java
public abstract class BufferManager {
protected static final Integer ONE = new Integer(1);
protected static final Integer TWO = new Integer(2);
protected static final Integer THREE = new Integer(3);
protected static final Integer BUFFER_SIZE = 5334;//5.334KB
protected static volatile Map<Integer, ByteArrayOutputStream> bufferPool = new ConcurrentHashMap<>(3, 0.9f, 2);
protected static volatile Integer captureBufferKey = ONE;
protected static volatile Integer playingBufferKey = ONE;
protected static Boolean running;
protected static volatile Integer noOfFrames = 0;
public BufferManager() {
//captureBufferKey = ONE;
//playingBufferKey = ONE;
//noOfFrames = new Integer(0);
}
protected void switchCaptureBufferKey() {
if(ONE.intValue() == captureBufferKey.intValue())
captureBufferKey = TWO;
else if(TWO.intValue() == captureBufferKey.intValue())
captureBufferKey = THREE;
else
captureBufferKey = ONE;
//printBufferState("SWITCHCAPTURE");
}//End of switchWritingBufferKey() Method.
protected void switchPlayingBufferKey() {
if(ONE.intValue() == playingBufferKey.intValue())
playingBufferKey = TWO;
else if(TWO.intValue() == playingBufferKey.intValue())
playingBufferKey = THREE;
else
playingBufferKey = ONE;
}//End of switchWritingBufferKey() Method.
protected static AudioFormat getFormat() {
float sampleRate = 8000;
int sampleSizeInBits = 16;
int channels = 1;
boolean signed = true;
boolean bigEndian = true;
return new AudioFormat(sampleRate, sampleSizeInBits, channels, signed, bigEndian);
}
protected int getByfferSize() {
return bufferPool.get(ONE).size()
+ bufferPool.get(TWO).size()
+ bufferPool.get(THREE).size();
}
protected static void printBufferState(String flag) {
int a = bufferPool.get(ONE).size();
int b = bufferPool.get(TWO).size();
int c = bufferPool.get(THREE).size();
System.out.println(flag + " == TOTAL : [" + (a + b +c) + "bytes] ");
// int a,b,c;
// System.out.println(flag + "1 : [" + (a = bufferPool.get(ONE).size()) + "bytes], 2 : [" + (b = bufferPool.get(TWO).size())
// + "bytes] 3 : [" + (c = bufferPool.get(THREE).size()) + "bytes], TOTAL : [" + (a + b +c) + "bytes] ");
}
}//End of BufferManager Class.
AudioCapture.java
public class AudioCapture extends BufferManager implements Runnable {
private static final Integer RTP_HEADER_SIZE = 12;
private InetAddress ipAddress;
private DatagramSocket serverSocket;
long lStartTime = 0;
public AudioCapture(Integer port) throws UnknownHostException, SocketException {
super();
running = Boolean.TRUE;
bufferPool.put(ONE, new ByteArrayOutputStream(BUFFER_SIZE));
bufferPool.put(TWO, new ByteArrayOutputStream(BUFFER_SIZE));
bufferPool.put(THREE, new ByteArrayOutputStream(BUFFER_SIZE));
this.ipAddress = InetAddress.getByName("0.0.0.0");
serverSocket = new DatagramSocket(port, ipAddress);
}
#Override
public void run() {
System.out.println();
byte[] receiveData = new byte[1300];
DatagramPacket receivePacket = null;
lStartTime = System.currentTimeMillis();
receivePacket = new DatagramPacket(receiveData, receiveData.length);
byte[] packet = new byte[receivePacket.getLength() - RTP_HEADER_SIZE];
ByteArrayOutputStream buff = bufferPool.get(captureBufferKey);
while (running) {
if(noOfFrames <= 50) {
try {
serverSocket.receive(receivePacket);
packet = Arrays.copyOfRange(receivePacket.getData(), RTP_HEADER_SIZE, receivePacket.getLength());
if((buff.size() + packet.length) > BUFFER_SIZE) {
switchCaptureBufferKey();
buff = bufferPool.get(captureBufferKey);
}
buff.write(packet);
noOfFrames += 4;
} catch (SocketException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} // End of try-catch block.
} else {
//System.out.println("Packet Ignored, Buffer reached to its maximum limit ");
}//End of if-else block.
} // End of while loop.
}//End of run() Method.
}
AudioPlayer.java
public class AudioPlayer extends BufferManager implements Runnable {
long lStartTime = 0;
public AudioPlayer() {
super();
}
#Override
public void run() {
AudioFormat format = getFormat();
DataLine.Info info = new DataLine.Info(SourceDataLine.class, format);
SourceDataLine line = null;
try {
line = (SourceDataLine) AudioSystem.getLine(info);
line.open(format);
line.start();
} catch (LineUnavailableException e1) {
e1.printStackTrace();
}
while (running) {
if (noOfFrames >= 24) {
ByteArrayOutputStream out = null;
try {
out = bufferPool.get(playingBufferKey);
InputStream input = new ByteArrayInputStream(out.toByteArray());
byte buffer[] = new byte[640];
int count;
while ((count = input.read(buffer, 0, buffer.length)) != -1) {
if (count > 0) {
InputStream in = new ByteArrayInputStream(buffer);
AudioInputStream ais = new AudioInputStream(in, format, buffer.length / format.getFrameSize());
byte buff[] = new byte[640];
int c = 0;
if((c = ais.read(buff)) != -1)
line.write(buff, 0, buff.length);
}
}
} catch (IOException e) {
e.printStackTrace();
}
/*byte buffer[] = new byte[1280];
try {
int count;
while ((count = ais.read(buffer, 0, buffer.length)) != -1) {
if (count > 0) {
line.write(buffer, 0, count);
}
}
} catch (IOException e) {
e.printStackTrace();
}*/
out.reset();
noOfFrames -= 4;
try {
if (getByfferSize() >= 10240) {
Thread.sleep(15);
} else if (getByfferSize() >= 5120) {
Thread.sleep(25);
} else if (getByfferSize() >= 0) {
Thread.sleep(30);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
} else {
// System.out.println("Number of frames :- " + noOfFrames);
}
}
}// End of run() method.
}// End of AudioPlayer Class class.
any help or pointer to the helpful link will be appreciable Thanks...

This answer explains a few challenges with streaming.
In a nutshell, your client needs to deal with two issues:
1) The clock (crystals) on the client and server are not perfectly in sync. The server may be a fraction of a Hz faster/slower than the client. The client continuously match the infer the clock rate of the server by examining the rate that rtp packets are delivered. The client then adjusts the playback rate via sample rate conversion. So instead of playing back at 48k, it may play back at 48000.0001 Hz.
2) Packets loss, out of order arrivals, etc. must be dealt with. If you lose packets, you need to still keep a place holder for those packets in your buffer stream otherwise your audio will skip and sound crackly and become unaligned. The simplest method would be to replace those missing packets with silence but the volume of adjacent packets should be adjusted to avoid sharp envelope changes snapping to 0.
Your design seems a bit unorthodox. I have had success using a ring buffer instead. You will have to deal with edge cases as well.
I always state that streaming media is not a trivial task.

Related

Android - Audio Clipping when recording audio (crest/peak clipping and periodic 0 bit values in between)

I am trying to record an audio stream via a Bluetooth device. I am using Bluetooth SCO for getting Bluetooth audio and AudioRecord class to record audio.
I am recording RAW .PCM files with MONO Channel with a sampling rate of 16000
I am calculating BufferSize like this
private static final int BUFFER_SIZE_FACTOR = 2;
private static final int BUFFER_SIZE = AudioRecord.getMinBufferSize(SAMPLING_RATE_IN_HZ,CHANNEL_CONFIG, AUDIO_FORMAT) * BUFFER_SIZE_FACTOR;
This is how I am getting/writing audio currently,
private class RecordingRunnable implements Runnable {
#Override
public void run() {
setFileNameAndPath();
final ByteBuffer buffer = ByteBuffer.allocateDirect(BUFFER_SIZE);
try (final FileOutputStream outStream = new FileOutputStream(mFilePath)) {
while (recordingInProgress.get()) {
int result = recorder.read(buffer, BUFFER_SIZE);
if (result < 0) {
throw new RuntimeException("Reading of audio buffer failed: " +
getBufferReadFailureReason(result));
}
outStream.write(buffer.array(), 0, BUFFER_SIZE);
buffer.clear();
}
} catch (IOException e) {
e.printStackTrace();
throw new RuntimeException("Writing of recorded audio failed", e);
}
}
I did a little research and found that the clipping effect could be because of the wrong Byte order (LITTLE_ENDIAN or BIG_ENDIAN) or Because of poor multithreading. However in this current implementation, I am not able to understand how bytes are being ordered and saved & what can I do to fix the clipping/noise problem.
I am starting my recorder runnable like this
recordingThread = new Thread(new RecordingRunnable(), "Recording Thread");
recordingThread.start();
recordingThread.setPriority(Thread.MAX_PRIORITY);
I got same issue and I resolved this problem with below code.
private byte[] short2byte(short[] sData, int size) {
int shortArrsize = size;
byte[] bytes = new byte[shortArrsize * 2];
for (int i = 0; i < shortArrsize; i++) {
bytes[i * 2] = (byte) (sData[i] & 0x00FF);
bytes[(i * 2) + 1] = (byte) (sData[i] >> 8);
sData[i] = 0;
}
return bytes;
}
......
int bufferSize = AudioRecord.getMinBufferSize(48000, AudioFormat.CHANNEL_IN_STEREO, AudioFormat.ENCODING_PCM_16BIT);
short[] buffer = new short[bufferSize];
int source = MediaRecorder.AudioSource.VOICE_RECOGNITION;
mAudioRecorder = new AudioRecord(source, 48000,
AudioFormat.CHANNEL_IN_STEREO, AudioFormat.ENCODING_PCM_16BIT, bufferSize);
int state = mAudioRecorder.getState();
if (state != AudioRecord.STATE_INITIALIZED) {
Log.e(TAG, "Can not support");
return;
}
mAudioRecorder.startRecording();
while (mIsRecording) {
int bufferReadResult = mAudioRecorder.read(buffer, 0, bufferSize);
if (bufferReadResult < 0) {
continue;
}
try {
byte data[] = short2byte(buffer, bufferReadResult);
fos.write(data, 0, bufferReadResult * 2);
} catch (IOException e) {
e.printStackTrace();
}
}

Using the OutputStream of the Bluetooth connection in an other thread disturbs receiving of messages

I followed the Android Guide to build a Bluetooth connection.
To separate things and make them independent, I decided to take the sending part of the BT to a separated thread. To achieve this, I pass the "OutStream" of the BT-Socket to a separated Thread class. My problem is, as soon as I start this thread, the incoming messages are not well red anymore.
But I don't know why, because I do not use this Thread at the moment. It is started but no messages are written in it.
This is part of the "ConnectedToDevice"-Class which receives the messages. I use a special way to detect that my Messages are received completely.
public void run() {
byte[] buffer = new byte[1024];
int bytes;
sendPW();
int len = 0;
Communication.getInstance().setFrequentSending(OVS_CONNECTION_IN_PROGRESS);
Communication.getInstance().setSendingMessages(mmOutStream); //Passing the OutStream to the sending class.
Communication.getInstance().setReceivingMessages(queueReceivingMsg);
Communication.getInstance().startThreads(); //currently: only start sending thread.
while (true) {
try {
bytes = this.mmInStream.read(buffer, len, buffer.length - len);
len += bytes;
if (len >= 3 && buffer[2] != -1) {
len = 0;
Log.d(TAG, "run: To Short?");
} else if (len >= 5) {
int dataLength = Integer
.parseInt(String.format("%02X", buffer[3]) + String.format("%02X", buffer[4]), 16);
if (len == 6 + dataLength) {
queueReceivingMsg.add(buffer);
Log.d(TAG, "run: Added to queue");
len = 0;
}
Log.d("BSS", "dataLenght: " + Integer.toString(dataLength) + " len " + len);
}
} catch (IOException var5) {
cancel();
Communication.getInstance().interruptThreads();
return;
}
}
}
The important part of sending message Class
public static BlockingQueue<Obj_SendingMessage> sendingMessages = new LinkedBlockingQueue<>();
#Override
public void run() {
while (!isInterrupted()) {
if (bGotResponse){
try{
sendingMessage = sendingMessages.take();
send(sendingMessage.getsData());
bGotResponse = false;
lTime = System.currentTimeMillis();
} catch (InterruptedException e){
this.interrupt();
}
}
if((System.currentTimeMillis()%500 == 0) && System.currentTimeMillis() <= lTime+1000){
if(sendingMessage != null){
send(sendingMessage.getsData());
}
} else {
bGotResponse =true;
}
}
}
//Where the outStream is used
private void write(int[] buffer) {
try {
for (int i : buffer) {
this.mmOutputStream.write(buffer[i]);
}
} catch (IOException var3) {
}
}
To be clear again, the sendingMessages is empty all the time, but still the messages get not Received correctly anymore.
Here's a proposal how robust code for reading messages from the stream could look like. The code can handle partial and multiple messages by:
Waiting for more data if a message is not complete
Processing the first message and saving the rest of the data if data for more than one message is available
Searching for a the marker byte 0xff and retaining the data for the next possibly valid message if invalid data needs to be discard
While writing this code I've noticed another bug in the code. If a message is found, the data is not copied. Instead the buffer is returned. However, the buffer and therefore the returned message might be overwritten by the next message before or while the previous one is processed.
This bug is more severe than the poor decoding of the stream data.
private byte[] buffer = new byte[1024];
private int numUnprocessedBytes = 0;
public void run() {
...
while (true) {
try {
int numBytesRead = mmInStream.read(buffer, numUnprocessedBytes, buffer.length - numUnprocessedBytes);
numUnprocessedBytes += numBytesRead;
processBytes();
} catch (IOException e) {
...
}
}
}
private void processBytes() {
boolean tryAgain;
do {
tryAgain = processSingleMessage();
} while (tryAgain);
}
private boolean processSingleMessage() {
if (numUnprocessedBytes < 5)
return false; // insufficient data to decode length
if (buffer[2] != (byte)0xff)
// marker byte missing; discard some data
return discardInvalidData();
int messageLength = (buffer[3] & 0xff) * 256 + (buffer[4] & 0xff);
if (messageLength > buffer.length)
// invalid message length; discard some data
return discardInvalidData();
if (messageLength > numUnprocessedBytes)
return false; // incomplete message; wait for more data
// complete message received; copy it and add it to the queue
byte[] message = Arrays.copyOfRange(buffer, 0, messageLength);
queueReceivingMsg.add(message);
// move remaining data to the front of buffer
if (numUnprocessedBytes > messageLength)
System.arraycopy(buffer, messageLength, buffer, 0, numUnprocessedBytes - messageLength);
numUnprocessedBytes -= messageLength;
return numUnprocessedBytes >= 5;
}
private boolean discardInvalidData() {
// find marker byte after index 2
int index = indexOfByte(buffer, (byte)0xff, 3, numUnprocessedBytes);
if (index >= 3) {
// discard some data and move remaining bytes to the front of buffer
System.arraycopy(buffer, index - 2, buffer, 0, numUnprocessedBytes - (index - 2));
numUnprocessedBytes -= index - 2;
} else {
// discard all data
numUnprocessedBytes = 0;
}
return numUnprocessedBytes >= 5;
}
static private int indexOfByte(byte[] array, byte element, int start, int end) {
for (int i = start; i < end; i++)
if (array[i] == element)
return i;
return -1;
}

Why doesn't a socket send the last message unless there is a delay before it?

Alright so I am looking at this piece of code that is supposed to get an array of bytes which represents an image and send it piece by piece to a server. The server needs to be told when the image transmission is done, this ending message is "CLOSE". Everything works fine but unless I uncomment Thread.sleep the end message isn't sent. Also the delay needs to be quite big for some reason, 100 ms for example doesn't work. If anyone could provide an explanation for this behaviour I would be grateful since I don't have a very good understanding of java.
private class NetTask extends AsyncTask<Void, Void, Void>
{
private String ip;
private byte[] to_send;
public NetTask(String ip, byte[] to_send)
{
this.ip = ip;
this.to_send = to_send;
}
#Override
protected Void doInBackground(Void...params)
{
try {
Log.i(dTag, "" + to_send.length);
Socket sck = new Socket(ip, 1234);
DataOutputStream dOut = new DataOutputStream(sck.getOutputStream());
dOut.write(ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN).putInt(to_send.length).array());
Log.d(dTag, "" + to_send.length);
int x = 500;
int len = to_send.length;
for (int i = 0; i < len - x + 1; i += x)
dOut.write(Arrays.copyOfRange(to_send, i, i + x));
if (len % x != 0)
dOut.write(Arrays.copyOfRange(to_send, len - len % x, len));
/*try {
Thread.sleep(1000);
}
catch (Exception ex) {
Log.d(dTag, "thread sleep error");
}*/
dOut.write("CLOSE".getBytes());
dOut.flush();
dOut.close();
sck.close();
}
catch (IOException ex) {
Log.d(dTag, ex.getMessage());
}
return null;
}
}
The server is in c#, here is the code:
while (ok)
{
sck.Listen(1000);
Socket accepted = sck.Accept();
buffer = new byte[accepted.SendBufferSize];
int bytesRead = -1;
bool reading = true;
int im_size = -1;
int index = 0;
byte[] image = null;
while (reading)
{
bytesRead = accepted.Receive(buffer);
if (bytesRead == 5)
Console.WriteLine(bytesRead);
string strData = "YADA";
byte[] formatted = new byte[bytesRead];
if (bytesRead == 5)
{
for (int i = 0; i < bytesRead; i++)
{
formatted[i] = buffer[i];
}
strData = Encoding.ASCII.GetString(formatted);
}
if (strData == "CLOSE")
{
Console.WriteLine("GOT CLOSE MESSAGE");
Image im = Image.FromStream(new MemoryStream(image));
im.Save(#"D:\im1.bmp");
}
else
{
if (im_size == -1)
{
im_size = BitConverter.ToInt32(buffer, 0);
image = new byte[im_size];
Console.WriteLine(im_size);
}
else
{
for (int i = 0; i < bytesRead && index < im_size; i++)
{
image[index++] = buffer[i];
}
}
}
}
accepted.Close();
}

Unable to play sounds simultaneously

Im trying to play six audio tracks simultaneously on the click of a jbutton, but upon click it plays the first track and waits until it finishes to play the second track, and so on. Here is my code
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
if (e.getSource() == button) {
System.out.println("Button Pressed");
AudioPlayerExample2 player1 = new AudioPlayerExample2();
AudioPlayerExample2 player2 = new AudioPlayerExample2();
AudioPlayerExample2 player3 = new AudioPlayerExample2();
AudioPlayerExample2 player4 = new AudioPlayerExample2();
AudioPlayerExample2 player5 = new AudioPlayerExample2();
AudioPlayerExample2 player6 = new AudioPlayerExample2();
player1.play(track1);
player2.play(track2);
player3.play(track3);
player4.play(track4);
player5.play(track5);
player6.play(track6);
}
}
});
and the audio player imported
public class AudioPlayerExample2 {
private static final int BUFFER_SIZE = 4096;
public void play(String audioFilePath) {
File audioFile = new File(audioFilePath);
try {
AudioInputStream audioStream = AudioSystem.getAudioInputStream(audioFile);
AudioFormat format = audioStream.getFormat();
DataLine.Info info = new DataLine.Info(SourceDataLine.class, format);
SourceDataLine audioLine = (SourceDataLine) AudioSystem.getLine(info);
audioLine.open(format);
audioLine.start();
System.out.println("Playback started.");
byte[] bytesBuffer = new byte[BUFFER_SIZE];
int bytesRead = -1;
while ((bytesRead = audioStream.read(bytesBuffer)) != -1) {
audioLine.write(bytesBuffer, 0, bytesRead);
}
audioLine.drain();
audioLine.close();
audioStream.close();
System.out.println("Playback completed.");
} catch (UnsupportedAudioFileException ex) {
System.out.println("The specified audio file is not supported.");
ex.printStackTrace();
} catch (LineUnavailableException ex) {
System.out.println("Audio line for playing back is unavailable.");
ex.printStackTrace();
} catch (IOException ex) {
System.out.println("Error playing the audio file.");
ex.printStackTrace();
}
}
public static void main(String[] args) {
String audioFilePath = "";
AudioPlayerExample2 player = new AudioPlayerExample2();
player.play(audioFilePath);
}}
While the track is playing, the button also remains clicked, so I am unable to use my volume jslider as well. Thanks for the help!
The way you've written the play method it will block until a stream has completely played - meaning the streams will play one after the other. One option is to fork a new thread for each stream. This will avoid the blocking problem but introduces another problem and that is the threads will all be in a race to startup. This means the streams will not all necessarily start at the exact same time (although you could use a signal to get them pretty close to synchronized).
A better approach I think is to use read from all of the files and write to one SourceDataLine all in a single thread. This means you have to manually mix the signals together yourself. Assuming all of your files have the same sample rate and bit depth it is not too difficult. I've assumed 16-bit samples. If your files are different then you can figure out how to deal with that.
public void play(String[] audioFilePath) {
int numStreams = audioFilePath.length;
// Open all of the file streams
AudioInputStream[] audioStream = new AudioInputStream[numStreams];
for (int i = 0; i < numStreams; i++)
audioStream[i] = AudioSystem.getAudioInputStream(audioFile);
// Open the audio line.
AudioFormat format = audioStream[0].getFormat();
DataLine.Info info = new DataLine.Info(SourceDataLine.class, format);
SourceDataLine audioLine = (SourceDataLine) AudioSystem.getLine(info);
audioLine.open(format);
audioLine.start();
while (true) {
// Read a buffer from each stream and mix into an array of
// doubles.
byte[] bytesBuffer = new byte[BUFFER_SIZE];
double[] mixBuffer = new double[BUFFER_SIZE/2];
int maxSamplesRead = -1;
for (int i = 0 ; i < numStreams; i++)
{
int bytesRead = audioStream.read(bytesBuffer);
if (bytesRead != -1) {
int samplesRead = bytesRead/2;
if (samplesRead > maxSamplesRead) {
maxSamplesRead = samplesRead;
}
for (int j = 0 ; j < bytesRead/2 ; j++) {
double sample = ((bytesBuffer[j*2] << 8) | bytesBuffer[j*2+1]) / 32768.0;
mixBuffer[j] += sample;
}
}
}
// Convert the mixed samples back into a byte array and play.
if (maxSamplesRead > 0) {
for (int i = 0; i < maxSamplesRead; i++) {
// rescale data between -1 and 1
mixBuffer[i] /= numStreams;
// and now back to 16-bit
short sample16 = (short)(mixBuffer * 32768);
// and back to bytes
bytesBuffer[i*2] = (byte)(sample16 >> 8);
bytesBuffer[i*2+1] = (byte)(sample16);
}
audioLine.write(bytesBuffer, 0, maxSamplesRead*2);
}
else {
// All of the streams are empty so cleanup.
audioLine.drain();
audioLine.close();
for (int i = 0 ; i < numStreams; i++)
audioStream[i].close();
break;
}
}
}
And call it by passing an array of filenames (which I would recommend replacing track1, track2, etc... with)
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
if (e.getSource() == button) {
System.out.println("Button Pressed");
AudioPlayerExample2 player = new AudioPlayerExample2(allTracks);
}
}
});
A third alternative that might be even better is to derive a class from InputStream that supports multiple files and does the mixing internally. With this approach you could use most of your existing AudioPlayerExample2 class but there would be only one instance of it. It would be a bit more involved than I care to get right now.
P.S. I haven't tried compiling any of this. I'm just trying to get across the idea.

How to make sure that the file passed using java.net is as it was before transmission

I'm studying java.net and trying to transmit som file. here is the sender code
public static final FilesGetter filesGetter = new FilesGetter();
public static Socket s;
public static File[] files;
public static void main(String args[]) throws Exception{
s = new Socket("localhost", 3128);
while (true){
try{
files = filesGetter.getFilesList("/etc/dlp/templates/");
Socket s = new Socket("localhost", 3128);
args[0] = args[0]+"\n"+s.getInetAddress().getHostAddress()
+":"+s.getLocalPort();
if (files != null){
for (int i = 0; i < files.length; i++){
InputStream is = new FileInputStream(files[i]);
byte[] message = IOUtils.toByteArray(is);
s.getOutputStream().write(message);
byte buf[] = new byte[64*1024];
int r = s.getInputStream().read(buf);
String data = new String(buf, 0, r);
System.out.println(data);
}
}
} catch(Exception e){
System.out.println("init error: "+e);
}
}
}
And here is the receiver code:
public class Consumer extends Thread{
public static Socket s;
public String customerId;
int num;
public static final Filter filter = new Filter();
public MimeParser mimeParser = new MimeParser(true);
public Consumer(int num, final Socket s, final String customerId){
this.num = num;
this.s = s;
this.customerId = customerId;
setDaemon(true);
setPriority(NORM_PRIORITY);
start();
}
public static void receive(final String customerId){
try {
int i = 0;
ServerSocket server = new ServerSocket(3128, 0, InetAddress.getByName("localhost"));
System.out.println("server started");
while (true){
new Consumer(i, server.accept(), customerId);
i++;
}
} catch (Exception e){
e.printStackTrace();
}
}
public void run(){
try {
InputStream is = s.getInputStream();
OutputStream os = s.getOutputStream();
byte buf[] = new byte[64*1024];
int r = is.read(buf);
if (r < 0)
return;
ByteArrayInputStream bais = new ByteArrayInputStream(buf, 0, r);
MessageInfo mi = mimeParser.parseMessages(bais);
filter.getMessageAcceptability(mi.getPlainText(), customerId);
s.close();
} catch (Exception e){
System.out.println("init error: " + e);
}
}
}
I'm not sure of data integrity because processing of data on the server-side is not fully successful and don't know if I need to look for mistakes in the processing code(which worked well when I've been Using rabbitmq) or in client-server code. I also don't know what buffer size must be chosen.
At the sender side you can send as much as you want, 64*1024 it's OK, the send method will loop until all the data has been delivery. But at the receiver it could be that read returns before the whole file has been read, you must loop reading until the other side closes the socket.
In these cases it's better to send, in advance, an integer indicating how much data you are going to send, the receiver will loops until that much bytes are read.
For example:
int ret=0;
int offset=0;
int BUFF_LEN=64*1024;
byte[] buffer = new byte[BUFF_LEN];
while ((ret = is.read(buffer, offset, BUFF_LEN - offset)) > 0)
{
offset+=ret;
// just in case the file is bigger that the buffer size
if (offset >= BUFF_LEN) break;
}
You need to compute a checksum like SHA-1 on both the sender and the receiver's side. When they match, you can assume that there was no transmission error. Your question is really more of a protocol question.
A much simpler and more practical solution than to implement your own integrity checks would be to use the SSL protocol. That will provide you with integrity checked transmissions.

Categories

Resources