Voip UDP adding a sequence number (preparing for packetloss) - java

Note: I'm currently learning UDP and how effective a VoIP system would be in comparison with a TCP system, I've already done TCP so please no one comment TCP is better etc..
So I'm trying to add a sequence number to the packets so I can order them on the server end and prepare for any lost packets, by repeating the previous for example
Problem: I read a stackoverflow that said using DataOutputStreams is a good way to go about this, so I implemented it. however when using the code dos.writeInt(sequenceNumber++); I get a horrible repetitive crackle. I was thinking maybe the problem is the amount of bytes that im sending.
Thank you in advance, any pointers will be great
boolean running = true;
try {
AudioRecorder recorder = new AudioRecorder();
int sequenceNumber = 0;
while (running) {
byte[] tempBuffer = recorder.getBlock();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
DataOutputStream dos = new DataOutputStream(baos);
dos.write(tempBuffer);
dos.writeInt(sequenceNumber++);
dos.flush();
DatagramPacket sendPacket = new DatagramPacket(baos.toByteArray(), baos.size(), clientIP, PORT);
sending_socket.send(sendPacket);
}
recorder.close();
} catch (Exception e) {
System.out.println("Error" + e);
Server end:
byte[] buffer = new byte[512];
byte[] temp = new byte[512];
try {
// CODE TO RECEIVE THE AUDIO
AudioPlayer player = new AudioPlayer();
int expectedValue = 0;
while (running) {
//Vector used to store audio blocks (32ms/512bytes each)
Vector<byte[]> voiceVector = new Vector<>();
// creates a new udp packet to receive the audio
DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
receiving_socket.receive(packet);
// creates a byte array from the data
byte[] udpPacketBytes = packet.getData();
ByteArrayInputStream baos = new ByteArrayInputStream(udpPacketBytes);
DataInputStream dos = new DataInputStream(baos);
int receivedValue = dos.readInt();
if (receivedValue == expectedValue) {
byte[] filteredByteArray = Arrays.copyOfRange(udpPacketBytes, 4, udpPacketBytes.length - 4);
voiceVector.add(filteredByteArray);
Iterator<byte[]> voiceItr = voiceVector.iterator();
while (voiceItr.hasNext()) {
player.playBlock(voiceItr.next());
}
} else {
// play the previous again
byte[] filteredByteArray = Arrays.copyOfRange(temp, 4, temp.length - 4);
voiceVector.add(filteredByteArray);
Iterator<byte[]> voiceItr = voiceVector.iterator();
while (voiceItr.hasNext()) {
player.playBlock(voiceItr.next());
}
// play the current one
byte[] fba = Arrays.copyOfRange(udpPacketBytes, 4, udpPacketBytes.length - 4);
voiceVector.add(fba);
Iterator<byte[]> vItr = voiceVector.iterator();
while (vItr.hasNext()) {
player.playBlock(vItr.next());
}
}
System.out.println(receivedValue + " " + expectedValue);
expectedValue = receivedValue + 1;
temp = packet.getData();
}
} catch (Exception e) {
System.out.println("yo");
}
//Close the socket
receiving_socket.close();

You are writing the sequence number but you are not reading or removing it so your sequence number ends up in the audio.
I suggest you read the same format you write.

Related

Java Socket consumes 100% CPU when started receiving client request

I have a Socket that consumes 100% CPU, Single client always sends the data to server around 4096 bytes, at the server side I want to get client data & convert into actual form & store into database. Client sending data after every 3 Seconds. I did the following code to get client request.
ServerSocket waiting = new ServerSocket(18800, 50, InetAddress.getByName("192.20.50.102"));
while(true) {
Socket socket = waiting.accept();
new SessionHandler(socket).start();
}
Client code written in C so datatypes are different than Java because of that I need to convert received bytes into actual form & insert into database. Thread class code as per follows:
public class SessionHandler extends Thread {
private Socket socket;
public SessionHandler(Socket socket) {
this.socket = socket;
}
public void run() {
DataInputStream dataInputStream;
try {
dataInputStream = new DataInputStream(socket.getInputStream());
int tcCode = dataInputStream.readInt();
int length = dataInputStream.readInt();
if (tcCode == 1001) {
System.out.println("in 1001");
byte[] messageByte = new byte[length];
int totalBytesRead = 0;
while (totalBytesRead < length) {
int currentBytesRead = dataInputStream.read(messageByte, totalBytesRead, length - totalBytesRead);
totalBytesRead = currentBytesRead + totalBytesRead;
}
ByteBuffer buffer = ByteBuffer.wrap(messageByte);
ShortBuffer shortBuffer = buffer.asShortBuffer();
short[] values = new short[length / 2];
shortBuffer.get(values);
TCCodeOneOOOne tcCodeOneOOOne = new TCCodeOneOOOne(values);
tcCodeOneOOOne.main(null);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
try it as follow:
ServerSocket waiting = new ServerSocket(18800, 50, InetAddress.getByName("192.20.50.102"));
Socket socket = waiting.accept();
new SessionHandler(socket).start();

How to create an input stream whose source is the received packet (ByteArrayInputStream and DataInputStream)?

This is a portion of my code (Multicast subscriber to images)
public void subscribe() throws IOException {
byte[] b = new byte[100];
DatagramPacket datagram = new DatagramPacket(b, b.length, groupAddress, localPort);
MulticastSocket socket = new MulticastSocket(localPort);
socket.joinGroup(groupAddress);
socket.send(datagram);
while(true) {
socket.receive(datagram);
System.err.println("Received " + datagram.getLength() +
" bytes from " + datagram.getAddress());
datagram.setLength(b.length);
socket.leaveGroup(groupAddress);
socket.close();
}
Here is the task:
Create an input stream whose source is the received packet (e.g. ByteArrayInputStream and DataInputStream).
this is publisher:
private void castOneImage(DatagramSocket socket, String imageFn, SocketAddress groupSocketAddress) {
byte[] imageBytes = null;
try (InputStream in = getClass().getClassLoader().getResourceAsStream(imageFn);
ByteArrayOutputStream byteOut = new ByteArrayOutputStream()) {
if (in == null) {
throw new FileNotFoundException("Cannot open image file " + imageFn);
}
int b;
while ((b = in.read()) != -1) {
byteOut.write(b);
}
imageBytes = byteOut.toByteArray();
byteOut.reset();
ByteBuffer byteBuffer = ByteBuffer.allocate(Integer.SIZE / 8);
byteBuffer.putInt(imageBytes.length);
byteOut.write(byteBuffer.array());
byteOut.write(imageBytes);
imageBytes = byteOut.toByteArray();
DatagramPacket packet = new DatagramPacket(imageBytes, imageBytes.length, groupSocketAddress);
socket.send(packet);
} catch (IOException e) {
// if there is an error for this image, we soldier on hoping other images may be good
LOGGER.warn("I/O error: " + e.getMessage(), e);
}
}
Use a ByteArrayInputStream:
DataInputStream dis = new DataInputStream(new ByteArrayInputStream(datagram.getData(), datagram.getOffset(), datagram.getLength()));
And move the close() outside the loop. You don't have to leave the group explicitly if you're about to close the socket.
EDIT To reconstitute the image data:
int length = dis.readInt();
byte[] image = new byte[length];
dis.readFully(image);
But of course as datagrams know their own length, you didn't really need to send or receive the length word at all.

Bandwidth speed testing

Faced with the following problem:
I need to determine bandwidth for a given ip and depending on it my task will be done in different ways.
I've written a simple implementation
Client:
public void send(Socket socket, File file) throws IOException {
FileInputStream inputStream = null;
DataOutputStream outputStream = null;
try {
inputStream = new FileInputStream(file);
int fileSize = (int) file.length();
byte[] buffer = new byte[fileSize];
outputStream = new DataOutputStream(socket.getOutputStream());
outputStream.writeUTF(file.getName());
int recievedBytesCount = -1;
while ((recievedBytesCount = inputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, recievedBytesCount);
}
} catch (IOException e) {
System.out.println(e);
} finally {
inputStream.close();
outputStream.close();
socket.close();
}
Server:
public void recieve() throws IOException {
ServerSocket server = new ServerSocket (port);
Socket client = server.accept();
DataInputStream dataInputStream = new DataInputStream(client.getInputStream());
DataInputStream inputStream = new DataInputStream(client.getInputStream());
String fileName = dataInputStream.readUTF();
FileOutputStream fout = new FileOutputStream("D:/temp/" + fileName);
byte[] buffer = new byte[65535];
int totalLength = 0;
int currentLength = -1;
while((currentLength = inputStream.read(buffer)) != -1){
totalLength += currentLength;
fout.write(buffer, 0, currentLength);
}
}
Test class:
public static void main(String[] args) {
File file = new File("D:\\temp2\\absf.txt");
Socket socket = null;
try {
socket = new Socket("127.0.0.1", 8080);
} catch (IOException e) {
e.printStackTrace();
}
ClientForTransfer cl = new ClientForTransfer();
long lBegin = 0;
long lEnd = 0;
try {
lBegin = System.nanoTime();
cl.send(socket, file);
lEnd = System.nanoTime();
} catch (IOException e) {
e.printStackTrace();
}
long lDelta = lEnd - lBegin;
Double result = ( file.length() / 1024.0 / 1024.0 * 8.0 / lDelta * 1e-9 ); //Mbit/s
System.out.println(result);
}
The problem is that using different sizes of the input files I get different speeds.
Tell me, please, how to solve this problem.
The problem is the TCP slow-start. http://en.wikipedia.org/wiki/Slow-start
Try to first transfer something like 10KBs, followed by the real measurement transfer. Make sure to use the same connection for both transfers.
This is not an easily "solved" problem. It is completely normal to get different speeds for different size files, as the "bandwidth" depends on many, many factors, including raw connection speed, quality (dropped packets) and latency, and can vary even from moment to moment.
You need to try several different file sizes, starting with small files and moving to larger files until the transfer takes 10-20 seconds in order to judge average bandwidth.

Invalid Stream Header - Socket Transfer in Java

I have another problem.
This is a part of my client:
Socket socket = new Socket("127.0.0.1", 3000);
OutputStream out = socket.getOutputStream();
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutput oo = null;
try {
oo = new ObjectOutputStream(bos);
oo.writeObject(mp3dataStrings);
byte[] serializedMP3 = bos.toByteArray();
out.write(serializedMP3);
out.flush();
} finally {
oo.close();
bos.close();
}
this is a part of my server:
ServerSocket clientConnect = new ServerSocket(port);
System.out.println("SimpleServer running on port" + port);
Socket clientSock = clientConnect.accept();
InputStream is = clientSock.getInputStream();
byte[] buffer = new byte[1024];
for (int i = 0; i < buffer.length; i++) {
int b = is.read();
buffer[i] = (byte) b;
if (b == -1 | b == 0) break;
}
ObjectInputStream stream = new ObjectInputStream(new ByteArrayInputStream(buffer));
String[][] songs = (String[][]) stream.readObject();
stream.close();
When I send my object (a String[][]) I get the exception invalid stream header: ACED0000.
I can't find what this means and what I have to do.
greets
Alex
You have made it far more complicated than you need to.
Socket socket = new Socket("127.0.0.1", 3000);
try {
ObjectOutputStream oo = new ObjectOutputStream(socket.getOutputStream());
oo.writeObject(mp3dataStrings);
oo.close();
} finally {
socket.close();
}
and
ServerSocket clientConnect = new ServerSocket(port);
System.out.println("SimpleServer running on port" + port);
Socket clientSock = clientConnect.accept();
try {
ObjectInputStream stream = new ObjectInputStream(clientSock.getInputStream());
String[][] songs = (String[][]) stream.readObject();
} finally {
clientSock.close();
}
I agree with Peter Lawrey's answer, however the problem in your original code stems from the exit condition in the byte buffer population code
byte[] buffer = new byte[1024];
for (int i = 0; i < buffer.length; i++) {
int b = is.read();
// THIS ARE PROBLEM LINES
buffer[i] = (byte) b;
if (b == -1 | b == 0) break;
}
ObjectInputStream stream =
new ObjectInputStream(new ByteArrayInputStream(buffer));
You should only exit this loop when you've detected End-Of-Stream condition. In other words you should never consider b==0, as it is a valid part of the ObjectInputStream.
Second, you should not assign the byte to the buffer before checking for break condition.
Third, if you initialize ByteArrayInputStream, you should pass only the number of bytes that contain input, not the whole buffer itself.
Corrected block should be like this:
// How do you know if 1024 is enough to get all data?
// For the sake of this example, assume it's enough
byte[] buffer = new byte[1024];
int count = 0;
for (; count < buffer.length; count++) {
int b = is.read();
if ( b == -1 )
{
// exit only on End-Of-Stream, and do not record
// this result into the buffer
break;
}
buffer[count] = (byte) b;
}
ObjectInputStream stream =
new ObjectInputStream(
// Note, that we are now passing the number of 'active' bytes in the buffer
new ByteArrayInputStream(buffer, 0, count)
);

Java Socket synchronization behavior

I tried to solve the problem in many ways but without success and I have also looked for information in this forum but with same results, so here we go.
I am actually doing a server daemon that accepts client requests and then it (the server) transfers all the files contained in a specific folder. I'm going to post the code of the sendFileData (on the server) and the receiveFileData (on the client).
The server uses:
public static void sendFileData(File file, Socket socket) throws FileNotFoundException, IOException, SocketException {
byte[] auxiliar = new byte[8192];
byte[] mybytearray = new byte[(int) file.length()];
int longitud = mybytearray.length;
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file));
bis.read(mybytearray, 0, longitud);
DataOutputStream os = new DataOutputStream(socket.getOutputStream());
int paquetes = longitud / 8187;
int resto = longitud % 8187;
int i = 0;
while(i<paquetes){//The length goes on the first 4 bytes and the 5th tells if there are more packets to send (8192 bytes or less).
byte[] bytes = ByteBuffer.allocate(4).putInt(8187).array();
auxiliar[0] = bytes[0];
auxiliar[1] = bytes[1];
auxiliar[2] = bytes[2];
auxiliar[3] = bytes[3];
auxiliar[4] = 1;
for(int j = 5; j < 8192; j++){
auxiliar[j] = mybytearray[i*8187+(j-5)];
}
os.write(auxiliar, 0, 8192);
i+=1;
}
if(resto > 0){
byte[] bytes = ByteBuffer.allocate(4).putInt(resto).array();
auxiliar[0] = bytes[0];
auxiliar[1] = bytes[1];
auxiliar[2] = bytes[2];
auxiliar[3] = bytes[3];
auxiliar[4] = 0;
for(int j = 5; j < resto+5; j++){
auxiliar[j] = mybytearray[i*8187+(j-5)];
}
os.write(auxiliar, 0, resto+5);
}
os.flush();
}
And in the client side:
public static void receiveFileData(String nombreFichero, Socket s) throws IOException{
File monitored = new File(nombreFichero);
if(monitored.exists() == false){
monitored.createNewFile();
}
byte[] mybytearray;
DataInputStream is = new DataInputStream(s.getInputStream());
FileOutputStream fos = new FileOutputStream(monitored);
BufferedOutputStream bos = new BufferedOutputStream(fos);
int bytesRead = 0;
int hasNext = 1;
do {
bytesRead = is.readInt();//Leo longitud
try {
Thread.sleep(1);// HERE!!!!
} catch (InterruptedException e) {
}
// System.out.println("Bytes read "+bytesRead );
if(bytesRead <= 8187 && bytesRead > 0){
// System.out.println("Bytes leídos "+bytesRead );
hasNext = is.readByte();//Leo si hay más datos por enviar
mybytearray = new byte[bytesRead];
is.read(mybytearray);
if(monitored.exists()){
synchronized(monitored){
bos.write(mybytearray, 0, mybytearray.length);
}
}
mybytearray = null;
}else{
System.out.println("Fuera de rango "+bytesRead);
}
}while(hasNext == 1);
bos.close();
mybytearray = null;
System.out.println("Fichero recibido: "+monitored.getAbsolutePath());
}
In the receiveFileData code, if I do not put a Thread.sleep(1) or a System.out.println() or whatever who takes time to execute, I am not receiving the data in the correct way on the client, because readInt() returns a very high number randomly negative or positive (which implies Heap out of memory and other exceptions).
Sure it's something about synchronization but I think the transfering schema between the two methods is correct (maybe the client is too slow and server too fast).
What is happening?? Because I do not want to put a Thread.sleep, this is not good programming here I think.
Thank you so much!
is.read(bytes) is not guaranteed to fill the supplied byte array. You need to check its return value to see how many bytes were read or (better) use readFully().
The sleep() probably just allows time for all bytes to have been returned from the socket.

Categories