应用程序卡在serverSocket.accept()并在第二次调用线程时给出已经使用的绑定异常地址

时间:2015-05-12 13:42:09

标签: java android sockets tcp udp

我在为套接字通信构建的应用程序中遇到两个问题,首先我将尝试解释应用程序的功能,然后我将详细介绍这两个问题。 首先,我点击一个按钮,它启动一个线程,通过UDP套接字发送多播按摩“组地址”。一旦任何设备接收到按摩,他们将通过TCP套接字发送响应,我的设备将充当发送响应的设备的服务器。所以在调试之后我发现第一个问题clientSocket = serverSocket.accept();有时会卡住,应用程序会阻塞所有内容并继续执行它,这可能是因为udp按钮可能永远不会到达目的地,这意味着没有客户端我创建的tcp服务器。

第一个问题:有没有办法让serverSocket.accept();无阻塞或设置超时?我尝试了serverSocket.setTimeSoOut()方法,但是没有用。也许这个问题来自UDP消息以外的东西?

第二个问题是,如果我按下调用线程两次的按钮,它将抛出一个已经在使用的BindException地址:由于serverSocket.bind(new InetSocketAddress(4125));的重新执行,会发生这种情况。有没有办法解决/避免这种情况?

以下是我正在使用的主题: 按下按钮后会调用此按钮:

 private class ChatClientThread extends Thread {

     DatagramSocket socket;
     String sentence;
     String modifiedSentence;
     BufferedReader inFromUser;

     DataOutputStream outToServer;
     BufferedReader inFromServer;
     Socket clientSocket;
     ServerSocket serverSocket;
      @Override
      public void run() {
       /*Socket socket = null;
       DataOutputStream dataOutputStream = null;
       DataInputStream dataInputStream=null;*/
          clientSocket=null;


       try {
           String data="NewTask_"+EmpPhoneNumber;

           serverSocket=new ServerSocket();
           serverSocket.setReuseAddress(true);
            serverSocket.bind(new InetSocketAddress(4125));
           socket = new DatagramSocket(52276);
           socket.setBroadcast(true);
           InetAddress group = InetAddress.getByName(
                   "224.0.1.2");
           DatagramPacket packet = new DatagramPacket(data.getBytes(), data.length(),
               group, 52276);

           socket.send(packet);

              while(true){
               clientSocket = serverSocket.accept();

              ConnectThread ct=new ConnectThread(clientSocket);
              ct.start();
              }

       } catch (UnknownHostException e) {
        e.printStackTrace();
        final String eString = e.toString();
        TicketDetails.this.runOnUiThread(new Runnable() {

         @Override
         public void run() {
          Toast.makeText(TicketDetails.this, eString, Toast.LENGTH_LONG).show();
         }

        });
       } catch (IOException e) {
        e.printStackTrace();
        final String eString = e.toString();
        TicketDetails.this.runOnUiThread(new Runnable() {

         @Override
         public void run() {
          Toast.makeText(TicketDetails.this, eString, Toast.LENGTH_LONG).show();
         }

        });
       }  finally {






        TicketDetails.this.runOnUiThread(new Runnable() {

         @Override
         public void run() {

         }

        });
       }

      }


     }

从上面的线程调用这个,你可以看到:

private class ConnectThread extends Thread {

      Socket socket;
      String sentence;
         String modifiedSentence;
         BufferedReader inFromUser;

         DataOutputStream outToServer;
         BufferedReader inFromServer;
      ConnectThread(Socket socket){

       this.socket= socket;

      }

      @Override
      public void run() {
       DataInputStream dataInputStream = null;
       DataOutputStream dataOutputStream = null;
       Socket socket2 = null;
       DataOutputStream dataOutputStream2= null;
       DataInputStream dataInputStream2=null;

       try {
           while(true){


               inFromUser = new BufferedReader( new InputStreamReader(System.in));
               outToServer = new DataOutputStream(socket.getOutputStream());
               inFromServer = new BufferedReader(new InputStreamReader(socket.getInputStream()));
               sentence = inFromUser.readLine();


               modifiedSentence = inFromServer.readLine();
               socket2 = new Socket(socket.getInetAddress().getHostAddress(), 4125);
                dataOutputStream2 = new DataOutputStream(
                  socket2.getOutputStream());

                String[] parts = modifiedSentence.split("_");
                String partGive = parts[0].substring(4); // 004
                String partEmpId = parts[1];
               if(partGive.equals("GiveMeATask")&&Integer.parseInt(partEmpId)==empId){

                   dataOutputStream2.writeUTF("  "+"SolveProblemOrder_2");
                    dataOutputStream2.flush();
               }



               System.out.println("FROM SERVER: " + modifiedSentence);


               if(modifiedSentence!=null) break;}

           outToServer.close();
           inFromServer.close();


       } catch (IOException e) {
        e.printStackTrace();
       } finally {
        if (dataInputStream != null) {
         try {
          dataInputStream.close();
         } catch (IOException e) {
          // TODO Auto-generated catch block
          e.printStackTrace();
         }
        }

        if (dataOutputStream != null) {
         try {
          dataOutputStream.close();
         } catch (IOException e) {
          // TODO Auto-generated catch block
          e.printStackTrace();
         }
        }


       }

      }



     }

2 个答案:

答案 0 :(得分:1)

这是两个非常常见的问题。我会以相反的顺序回答这两个问题。

  1. 您正在谈论的按钮是创建ServerSocket并将其绑定到特定端口。在您的情况下,端口是4125.从查看您的代码,您似乎无法在任何地方关闭该serversocket。当您再次单击该按钮时,ServerSocket的第二个实例尝试绑定到同一个端口 - 但该端口仍由第一个ServerSocket使用。在这种情况下,您将获得绑定异常。一个端口不能被多个ServerSocket使用。解决方案是在使用serverSocket.close();

  2. 创建新的ServerSocket之前关闭现有的ServerSocket
  3. 如果您阅读the documentation,它会清楚地说明ServerSocket.accept()的作用:" [...]方法会阻塞,直到建立连接。"这就是"卡住了#34;你所描述的。执行该代码的线程被置于等待位置,并且仅在建立连接时继续,然后返回该新连接。经典的方法是启动一个等待传入连接的新线程,以便主线程继续执行,整个应用程序不会“冻结”#34;。另一种方法是一个非阻塞框架,它封装了所有远离你的开销,其中一个是Apache MINA

  4. 我强烈建议您查看处理基本客户端/服务器行为的小示例项目,因为您最有可能在这里处理线程。

答案 1 :(得分:0)

第一个问题:您的应用程序很可能没有收到UDP包。如果serverSocket.accept()没有得到任何客户端,它将无限期地等待某人连接。您可以通过使用另一个只接受连接的线程来避免这种情况,以避免冻结您的应用程序。另一种方法是使用Java的NIO类,为几乎任何东西提供非阻塞IO。这将要求您使用ServerSocketChannel和相关的类。 (快速谷歌搜索也给了我this guide,这似乎很容易遵循)。

第二个问题:完成后,您需要关闭ServerSocket。否则,该端口永远不会再空闲以供另一个ServerSocket使用。 或者你可以让Socket保持打开并记住你已经打开它(例如你的类中有一个布尔字段)。