make an udp server send packets to all online clients

Started by
9 comments, last by hplus0603 4 years, 1 month ago

I need my UDP server to send a package to all clients.

Server code:

while (true) {

      byte[] buf = new byte[256];
      DatagramPacket packet = new DatagramPacket(buf, buf.length);

      try {
        socket.receive(packet);
      } catch (Exception e) {
        System.err.println(e.getMessage());
      }
      System.out.print("O servidor recebeu um pacote do endereco " + packet.getAddress() + "\n");

      // Reenviar pro cliente
      InetAddress ed = packet.getAddress();
      int portad = packet.getPort();
      //manda de volta para o cliente.
      packet = new DatagramPacket(buf, buf.length, ed, portad);
      try {
        socket.send(packet);
      } catch (Exception e) {
        System.err.println(e.getMessage());
      }
    }

Client Send:

private void startStreaming() {

        Log.i(TAG, "Starting the background thread to stream the audio data");

        Thread streamThread = new Thread(new Runnable() {

            @Override
            public void run() {
                try {
                    while (falando == true) {
                        record.startRecording();
                        byte lin[] = new byte[BUFFER_SIZE];
                        am.setMode(AudioManager.MODE_IN_COMMUNICATION);
                        // read the data into the buffer
                        record.read(lin, 0, BUFFER_SIZE);
                        InetAddress group = InetAddress.getByName(groupIp); //Keep this as the same multicast ip as in your client
                        DatagramPacket packet = new DatagramPacket(lin, lin.length, group, port);
                        //Envia o audio
                        try {
                            socket.send(packet);
                            Log.d(TAG, "Enviando audio");
                        } catch (Exception e) {
                            System.err.println(e.getMessage());
                        }
                    }
                } catch (Exception e) {
                    Log.e(TAG, "Exception: " + e);
                }
            }
        });

        // start the thread
        streamThread.start();
    }

client listen

private void comecarEscutar() {

        Thread listenThread = new Thread(new Runnable() {

            @Override
            public void run() {
                try {
                    byte lin[] = new byte[BUFFER_SIZE+=4096];
                    while (true) {
                        // read the data into the buffer
                        record.read(lin, 0, BUFFER_SIZE);
                        InetAddress group = InetAddress.getByName(groupIp);
                        DatagramPacket packet = new DatagramPacket(lin, lin.length, group, port);

                        //Recebe o Audio
                        try {
                            socket.receive(packet);
                            track.write(packet.getData(), 0, BUFFER_SIZE);
                            Log.d(TAG, "Reproduzindo Audio");
                            track.play();
                        } catch (Exception e) {
                            System.err.println(e.getMessage());
                        }
                    }
                } catch (Exception e) {
                    Log.e(TAG, "Exception: " + e);
                }
            }
        });

        // start the thread
        listenThread.start();
    }

In addition I would also like tips for optimizing the audio, which runs in real time

Advertisement

Your code doesn't make a lot of sense. You call the IP address a “group." Are you confusing multicast, with UDP unicast?

Multicast only works on a local LAN, it is filtered out on the greater internet.

To send data to a number of UDP endpoints, the server simply keeps a list of all the clients, and loops through that list, calling send() to each of the clients in turn, from the same source data buffer.

It's helpful to know that the kernel generally has a buffer of incoming and outgoing data, so the “send()” call doesn't block until the data is actually sent; instead it just copies the data into the outgoing buffer, assuming there is space. Similarly for recv(), it will typically copy data from the buffer to your process, rather than having to wait (unless the buffer is empty, and your receive is not non-blocking.) You can set the size of the incoming and outgoing buffers using socket control calls, or, seeing as you use a managed framework, the specific properties on the sockets that your framework exposes.

enum Bool { True, False, FileNotFound };

@undefined Okay, any tips to improve the loss of audio packages?

How do you know you have loss of packets with audio in them?

What tools are you using to measure your throughput and loss?

enum Bool { True, False, FileNotFound };

@undefined I did not use any tools, I just know that the audio was going normally and giving playback in real time when speaking and listening on the same thread, after I made a thread just to listen to the audio it almost doesn’t reach the client, hardly to understand the audio .

It sounds to me like you need to work on understanding what your code is actually doing.

Use Wireshark to see what packets are available on the wire.

Print the results of calling send() and recv() so you can see what your program is doing.

Develop a theory about what is actually happening, based on what you see from investigating packets and the program, and then use a breakpoint, or an if statement with printing, to determine whether that theory is correct or not. Make a change, see if it changes the behavior as expected or not. If not, back it out, and investigate some more.

enum Bool { True, False, FileNotFound };

@undefined after many tests, I realized that the customer only receives the package if it is localhost? Can you tell me why this happens? I changed the server and put a thread, it is sending right on localhost, but when the server is run on an external machine (VPS) it is unable to return packages to the client. Am I doing something wrong?

EDIT:

Network tests via wireshark show that the packet is being sent via ssh to the client, so that must be why it is not receiving it.

CODE SERVER


class Server {

    static int port = 50005; // Porta para receber do cliente
    static int listen = 50010; // Porta para registrar o cliente
    static int removePort = 50011; // Porta para remover o cliente
    static int listenerPort = 50015; // Porta para enviar pro cliente
    static DatagramSocket serverSocket, listenSocket, removeSocket, broadcastSocket;
    static byte[] receiveData, listenData, removeData;
    static DatagramPacket receivePacket, listenPacket, removePacket;
    static ArrayList<String> listeners = new ArrayList<String>();
    static boolean active = true;


    public static void main(String args[]) throws Exception {
        //Definir o soquete do datagrama de recebimento
        serverSocket = new DatagramSocket(port);
        listenSocket = new DatagramSocket(listen);
        removeSocket = new DatagramSocket(removePort);

        //Define Broadcasting socket
        broadcastSocket = new DatagramSocket();

        //Define data size, 1400 is best sound rate so far
        receiveData = new byte[1400];
        listenData = new byte[256];
        removeData = new byte[256];

        //Definir o objeto DatagramPacket
        receivePacket = new DatagramPacket(receiveData, receiveData.length);
        listenPacket = new DatagramPacket(listenData, listenData.length);
        removePacket = new DatagramPacket(removeData, removeData.length);

        // Recebendo dados
        Thread t = new Thread() {
            @Override public void run() {
                getPackets();
            }
        };
        t.start();

        // Esperando Conexão
        Thread l = new Thread() {
            @Override public void run() {
                listen();
            }
        };
        l.start();

        // Esperando desconectar
        Thread r = new Thread() {
            @Override public void run() {
                remove();
            }
        };
        r.start();
    }

    /***
     * Function that gets the audio data packets
     * saves them, and outputs the audio to the speakers.
     */
    public static void getPackets() {
        while (active) {
            try {
                //Aguarde até o pacote ser recebido
                serverSocket.receive(receivePacket);
                System.out.println("Receiving Data");
                //Enviar dados
                sendData(receivePacket.getData());
            } catch (IOException e) {
            
            }
        }
    }

    /***
     * Função que escuta se houver alguma conexão feita com
     * a porta do ouvinte e cria um soquete de datagrama para transmitir áudio
     * para quem se conecta
     */
    
    public static void listen() {
        while (active) {
            try {
                //Wait until packet is received
                listenSocket.receive(listenPacket);
                listeners.add(listenPacket.getAddress().getHostAddress());
                System.out.println("Client received");

            } catch (IOException e) {
                if(active) {
                    listen();
                }
            }
        }
    }

    public static void remove() {
        while (active) {
            try {
                //Wait until packet is received
                removeSocket.receive(removePacket);
                listeners.remove(getUser(removePacket.getAddress().getHostAddress()));
                System.out.println("Client logout");

            } catch (IOException e) {
                if(active) {
                    listen();
                }
            }
        }
    }

    public static int getUser(String ed) {
        try {
            for (int i = 0; i < listeners.size(); i++) {
                if(listeners.get(i).equals(ed)) {
                    return i;
                }
            }
        } catch (Exception e) {
            //If it failed to send don't do anything
            e.printStackTrace();
        }
        return -1;
    }

    public static void sendData(byte[] data) {
        try {
            for (int i = 0; i < listeners.size(); i++) {
                InetAddress destination = InetAddress.getByName(listeners.get(i));
                broadcastSocket.send(new DatagramPacket(data, data.length, destination, listenerPort));
                System.out.println("Sending Data to ip " + destination + " port " + listenerPort);
            }
        } catch (Exception e) {
            //If it failed to send don't do anything
            e.printStackTrace();
        }
    }
}

Network tests via wireshark show that the packet is being sent via ssh to the client, so that must be why it is not receiving it.

SSH is a TCP protocol, that can port forward TCP connections. It will not work for UDP connections.

I suggest you build some very small, easy-to-diagnose, UDP application first. Say, a server that echoes back the text of any packet that comes in, to everybody who has sent the server a packet (by saving the addresses seen in recvfrom().)

Then, a small client that just sends a packet to the server when the user enters some text, and that also receives messages from the server and prints those to the screen.

Once you have this working, including seeing packets on multiple different clients (and multiple clients on different hosts,) you might be able to port what you've built in that system, to a more advanced system, that does things like audio codecs.

enum Bool { True, False, FileNotFound };

@undefined There is the problem even though the encoding was done all in UDP, for some reason the server sends it back when the packet is in the VPS via ssh. At localhost it sends the packet normally.

SSH cannot forward UDP packets. You must be seeing something else happening.

enum Bool { True, False, FileNotFound };

This topic is closed to new replies.

Advertisement