应用程序卡在 serverSocket.accept() 并提供已在线程的第二次调用中使用的绑定异常地址

app get stuck at serverSocket.accept() and gives bind exception address already in use on the second call for the thread

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

第一个问题:有没有办法让serverSocket.accept();非阻塞或者设置超时?我试过 serverSocket.setTimeSoOut() 方法,但没有用。也许这个问题不是来自 UDP 消息?

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

以下是我正在使用的线程: 这个在我按下按钮后被调用:

 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();
         }
        }


       }

      }



     }

这是两个非常常见的问题。这两个我倒序回答。

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

    创建新的 ServerSocket 之前关闭现有的 ServerSocket
  2. 如果您阅读the documentation, it clearly states what ServerSocket.accept() does: "[...] The method blocks until a connection is made." This is the "getting stuck" that you described. The thread that executes that code is put into a waiting position and continues only when a connection is made, then returns that new connection. The classic approach is to start a new thread that waits for incoming connections so that your main thread continues to execute and your whole application does not "freeze". Another approach would be a non-blocking framework that encapsulates all that overhead away from you, one of those is Apache MINA

我强烈建议查看处理基本 client/server 行为的小型示例项目,因为您很可能会在这里处理线程。

第一个问题:您的应用很可能没有收到UDP包。如果 serverSocket.accept() 没有得到任何客户端,它将无限期地等待有人连接。您可以通过使用另一个只接受连接的线程来避免这种情况,以避免冻结您的应用程序。另一种方法是使用 Java 的 NIO classes 为几乎任何东西提供非阻塞 IO。这将要求您使用 ServerSocketChannel and related classes. (Quick googling also gave me this guide,这似乎很容易理解)。

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