Подтвердить что ты не робот

Запись звука на сервер через TCP-сокет

Я пытаюсь передать микрофонную запись в реальном времени на сервер через TCP-сокет и сервер для записи потока ввода в файл. Соединение установлено, но через некоторое время я получаю сообщение об отказе от отказа на моих клиентах.

Код сервера:

    public class auServer extends Thread{
    private static ServerSocket serverSocket;
    private static int port = 3333; 

    public void run()
    {

        System.out.println("init success");
       while(true)
       {

          try
          {
              serverSocket = new ServerSocket(port);
              serverSocket.setSoTimeout(10000);
              Socket clientSoc = serverSocket.accept();
             System.out.println("Waiting for client on port " +serverSocket.getLocalPort() + "...");
             System.out.println("Just connected to " + clientSoc.getRemoteSocketAddress());
             InputStream in = clientSoc.getInputStream();
             while(in!=null)
             {
                 writeToFile(in);
             }
             System.out.println("socket");

             clientSoc.close();
          }catch(SocketTimeoutException s)
          {
             System.out.println("Socket timed out!");
             break;
          }catch(IOException e)
          {
             e.printStackTrace();
                     System.out.println("some io");
             break;
          } catch (Exception e) {
                    System.out.println("some e");
            e.printStackTrace();
        }
       }
    }

    private void writeToFile(InputStream in) throws IOException {
        // Write the output audio in byte
        String filePath = "8k16bitMono1.wav";
        short sData[] = new short[1024];
        byte[] bData = IOUtils.toByteArray(in);
        FileOutputStream os = null;
        try {
         os = new FileOutputStream(filePath);
        } catch (FileNotFoundException e) {
         e.printStackTrace();
        }
         System.out.println("Short wirting to file" + sData.toString());
         try {
          os.write(bData, 0, 2048);
         } catch (IOException e) {
          e.printStackTrace();
         }
        try {
         os.close();
        } catch (IOException e) {
         e.printStackTrace();
        }

    }


    public static void main(String[] args) {
        // TODO Auto-generated method stub
      try
      {
        Thread serverThread = new auServer();
        serverThread.run();
        System.out.println("runing");
       }catch(IOException e){
         e.printStackTrace();
      }
    }
}

и клиент:

private void streamData(byte[] bData) throws UnknownHostException, IOException, InterruptedException {  //bData is byte array to transmit
    Thread.sleep(500);
    Socket client = new Socket("10.221.40.41",3333);
    OutputStream outToServer = client.getOutputStream();
    outToServer.write(bData);
    if(!isRecording)
        client.close();
}

В чем может быть проблема? Спасибо заранее.

4b9b3361

Ответ 1

Я прокомментирую ваш код по частям.

private static ServerSocket serverSocket;

Нет причин для статичности.

while(true)
{
    try
    {
        serverSocket = new ServerSocket(port);
        serverSocket.setSoTimeout(10000);

Последние две строки должны быть впереди цикла. Это является причиной отказа в подключении, и это также вызовет BindExceptions, о котором вы не упомянули. Непонятно, зачем вам нужен тайм-аут.

             Socket clientSoc = serverSocket.accept();
             System.out.println("Waiting for client on port " +serverSocket.getLocalPort() + "...");

Нет, нет. Он уже подключился. Вы ждали до accept().

             System.out.println("Just connected to " + clientSoc.getRemoteSocketAddress());
             InputStream in = clientSoc.getInputStream();
             while(in!=null)

Оба цикла и теста бесполезны. Первоначально переменная не является нулевой, и она никогда не может стать нулевой. Цикл бесполезен, потому что метод writeToFile() полностью исчерпывает входной поток, поэтому больше ничего не читать. Это приведет к появлению нежелательных данных, о которых вы не упомянули.

             {
                 writeToFile(in);
             }
             System.out.println("socket");

Беспредметное сообщение.

             clientSoc.close();

Весь код из строки после accept() здесь должен быть выполнен в отдельном потоке. Контур принятия должен ничего не делать, кроме как принимать соединения и запускать потоки.

          }catch(SocketTimeoutException s)
          {
             System.out.println("Socket timed out!");

То, что здесь было, было accept(), так как прослушивающий сокет - это единственный сокет, на который вы установили таймаут. Я сомневаюсь, что вам это нужно.

             break;
          }catch(IOException e)
          {
             e.printStackTrace();
                     System.out.println("some io");

Другое бесполезное сообщение.

             break;
          } catch (Exception e) {
                    System.out.println("some e");

Еще один. Когда вы получите исключение, распечатайте исключение. Не какое-то тщетное сообщение о вашем собственном проекте. В противном случае отладка станет простой гадательной игрой.

            e.printStackTrace();
        }
       }
    }

    private void writeToFile(InputStream in) throws IOException {
        // Write the output audio in byte
        String filePath = "8k16bitMono1.wav";
        short sData[] = new short[1024];

Неиспользованный. Удалить.

        byte[] bData = IOUtils.toByteArray(in);

Не используйте это. Он тратит пространство и добавляет латентность. Ниже приведено правильное решение.

        FileOutputStream os = null;
        try {
         os = new FileOutputStream(filePath);
        } catch (FileNotFoundException e) {
         e.printStackTrace();
        }

Плохая техника. Этот catch находится не в том месте. Код, который зависит от успеха кода в блоке try, должен находиться внутри одного и того же блока try. В настоящее время вы проваливаете этот try-catch, как будто исключение никогда не происходило, что приведет к появлению NullPointerException в коде ниже.

         System.out.println("Short wirting to file" + sData.toString());

Другое бессмысленное сообщение. С ошибками в написании; sData ничего не имеет; sData.toString() не печатает ничего полезного вне зависимости от содержимого; и неправильно, поскольку вы вообще не пишете sData.

         try {
          os.write(bData, 0, 2048);

Это записывает ровно 2048 байт, независимо от прочитанной суммы, которая может быть меньше или больше. Если бы он был меньше, он выбрал бы ArrayIndexOutOfBoundsException или аналогичный, который я ожидал бы увидеть во втором вызове, хотя вы его не упомянули, потому что массив должен быть нулевой длиной во втором и последующих вызовах, если они есть. Это должен быть цикл общей формы:

int count;
byte[] buffer = new byte[8192]; // or more if you like
while ((count = in.read(buffer)) > 0)
{
    out.write(buffer, 0, count);
}

Вернуться к вашему коду:

         } catch (IOException e) {
          e.printStackTrace();
         }
        try {
         os.close();
        } catch (IOException e) {
         e.printStackTrace();
        }
    }


    public static void main(String[] args) {
        // TODO Auto-generated method stub
      try
      {
        Thread serverThread = new auServer();
        serverThread.run();

Это запускает метод run() потока. Он не запускает нить. Это должно быть serverThread.start().

        System.out.println("runing");

С ошибками в написании. Пока вы не устраните проблему с запуском/запуском выше, вы не увидите это сообщение до тех пор, пока не выйдет сервер run().

и клиент:

private void streamData(byte[] bData) throws UnknownHostException, IOException, InterruptedException {  //bData is byte array to transmit
    Thread.sleep(500);

Бессмысленно. Удалить. Не помещайте спальные места в сетевой код.

    Socket client = new Socket("10.221.40.41",3333);

Не создавайте новое соединение для каждого буфера. Используйте одно и то же соединение для жизни клиента.

    OutputStream outToServer = client.getOutputStream();
    outToServer.write(bData);
    if(!isRecording)
        client.close();

Это должно быть безусловным, и оно должно быть в другом месте вместе с созданием сокета.

В чем может быть проблема?

Проблемы. Множественное число. Множественный. См. Выше.

Ответ 2

Создание serverocket должно выполняться вне цикла. А для параллельных соединений вам нужно запустить поток для каждого соединения.

Также применяйте тайм-аут к установленным соединениям. Тайм-аут на сервере Server прекратит прием после таймаута, это ничего не нужно на сервере.

...
public void run() {
    ServerSocket serverSocket;
    try {
        serverSocket = new ServerSocket(port);
    } catch (Exception ex) {
        ex.printStackTrace();
        return;
    }

    System.out.println("init success");

    while (true) {
        try {
            System.out.println("Waiting for client on port " + serverSocket.getLocalPort() + "...");
            final Socket clientSoc = serverSocket.accept();
            clientSoc.setSoTimeout(10000);

            System.out.println("Just connected to " + clientSoc.getRemoteSocketAddress());


            new Thread() {
                public void run() {
                    try {
                        InputStream in = clientSoc.getInputStream();
                        writeToFile(in);
                    } catch (Exception e) {
                        e.printStackTrace();
                    } finally {
                        try {
                            clientSoc.close();
                        } catch (Exception ignore) {
                        }
                    }
                }
            }.start();
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }
}
...

Ответ 3

Я предполагаю, что ваша клиентская сторона - приложение для Android. Однажды я попытался прочитать и записать данные в приложение Android через соединение сокета. В конце концов, я сделал это, используя adb (мост отладчика android).

Допустим, есть приложение java, которое создает Socket с номером порта 1992. И есть приложение Android, которое создает ServerSocket с номером порта 1993.

Вот реальная сделка!

Перед запуском приложений вам необходимо выполнить следующую команду в оболочке adb (или вы можете найти свой adb в cmd/terminal и выполнить команду)

adb forward tcp:1992 tcp:1993    

После пересылки порта вы можете записывать и считывать данные из приложения java в приложение Android и, наоборот, используя потоки IO сокетов.

Снимок фрагмента рабочего приложения:

while (flag) {
    try {
        final Socket socket = new Socket("localhost", 1992);
        new Thread(){
            @Override
            public void run() {                        
                while (flag) {                            
                    try {
                        new PrintWriter(socket.getOutputStream(), true).println(new Date().toString());
                    } catch (IOException ex) {
                        break;
                    }
                }
            }                    
        }.start();
    } catch (IOException ex) {
        // need port-forwarding
    }
}    

Фрагмент кода приложения Android:

while (flag) {
    try {
        final Socket socket = new ServerSocket(1993).accept();
        new AsyncTask<Void, Void, Void>() {
            @Override
            public Void doInBackground(Void... params) {
                while (flag) {
                    try {
                        String line = new BufferedReader(new InputStreamReader(socket.getInputStream())).readLine();
                        Log.d("", line);
                    } catch (IOException ex) {
                        return null;
                    }
                }
                return null;
            }
        }.execute();
    } catch (IOException ex) {
        break;
    }
}    

Вы можете преобразовать свой аудио в массив байтов и записать его в выходной поток. Надеюсь, мой ответ будет полезен.

Ответ 4

Я понял решение. На самом деле проблема заключалась в блокировке сетевого ввода-вывода из-за неправильно закодированного потока.

Я пропустил

InputStream in = clientSoc.getInputStream();
DataInputStream DataIn =new DataInputStream(in)
while(DataIn!=null)
    {
        writeToFile(DataIn);
    }

и клиентская сторона.

OutputStream outToServer = client.getOutputStream();
DataOutputStream out = new DataOutputStream(outToServer);
out.write(sData);