簡體   English   中英

java.net.BindException:已在使用的地址:無法綁定

[英]java.net.BindException: Address already in use: Cannot bind

對不起,我已經搜索了但似乎所有答案都沒有解決我的問題。 嘗試創建ServerSocket以回復多個客戶端消息時出現此錯誤。

我的服務器代碼:

package Server;

import java.net.*;
import java.io.*;

public class Server {
    public final static int defaultPort = 7;

    public static void main(String[] args) {
        try {
            ServerSocket ss = new ServerSocket(defaultPort);            
            int i = 0;
            while (true) {
                try {
                    System.out.println("Server is running on port "
                            + defaultPort);
                    Socket s = ss.accept();
                    System.out.println("Client " + i + " connected");
                    RequestProcessing rp = new RequestProcessing(s, i);
                    i++;
                    rp.start();
                } catch (IOException e) {
                    System.out.println("Connection Error: " + e);
                }
            }
        } catch (IOException e) {
            System.err.println("Create Socket Error: " + e);
        } finally {

        }
    }
}

class RequestProcessing extends Thread {
    Socket channel;
    int soHieuClient;

    public RequestProcessing(Socket s, int i) {
        channel = s;
        clientNo = i;
    }

    public void run() {
        try {
            byte[] buffer = new byte[6000];
            DatagramSocket ds = new DatagramSocket(7);          
            while (true) {  
                DatagramPacket incoming = new DatagramPacket(buffer,
                        buffer.length);
                ds.receive(incoming);
                String theString = new String(incoming.getData(), 0,
                        incoming.getLength());
                System.out.println("Client " + clientNo 
                        + " sent: " + theString);
                if ("quit".equals(theString)) {
                    System.out.println("Client " + clientNo 
                            + " disconnected");
                    ds.close();
                    break;
                }
                theString = theString.toUpperCase();
                DatagramPacket outsending = new DatagramPacket(
                        theString.getBytes(), incoming.getLength(),
                        incoming.getAddress(), incoming.getPort());
                System.out.println("Server reply to Client "
                        + clientNo + ": " + theString);
                ds.send(outsending);
            }
        } catch (IOException e) {
            System.err.println(e);
        }

    }
}

和我的客戶代碼:

package Client;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.Socket;

public class Client extends Object {
    public final static int serverPort = 7;

    public static void main(String[] args) {
        try {

            DatagramSocket ds = new DatagramSocket();
            InetAddress server = InetAddress.getByName("192.168.109.128");
            Socket s = new Socket("192.168.109.128", 7);            
            String theString = "";
            do {
                System.out.print("Enter message: ");
                InputStreamReader isr = new InputStreamReader(System.in);
                BufferedReader br = new BufferedReader(isr);
                theString = br.readLine();
                byte[] data = theString.getBytes();
                DatagramPacket dp = new DatagramPacket(data, data.length,
                        server, serverPort);

                ds.send(dp);
                System.out.println("Sent to server server: " + theString);

                byte[] buffer = new byte[6000];

                DatagramPacket incoming = new DatagramPacket(buffer,
                        buffer.length);
                ds.receive(incoming);
                System.out.print("Server reply: ");
                System.out.println(new String(incoming.getData(), 0, incoming
                        .getLength()));

            } while (!"quit".equals(theString));
            s.close();
        } catch (IOException e) {
            System.err.println(e);
        }
    }
}

通過第一個客戶端連接,它可以順利運行。 但是從第二個客戶端,它拋出java.net.BindException:地址已經在使用:無法綁定。 第二個客戶端也可以發送和接收消息,但客戶端號仍為0。

Server is running on port 7
Client 0 connected
Server is running on port 7
Client 0 sent: msg 0
Server reply to Client 0: MSG 0
Client 1 connected
Server is running on port 7
java.net.BindException: Address already in use: Cannot bind
Client 0 sent: msg 1 <<-- this one is sent from client 1 but Client No is 0
Server reply to Client 0: MSG 1

因此,在RequestProcessing.run您決定忽略在構造函數中接收的套接字,並在與您正在偵聽的端口相同的端口上打開DatagramSocket。 你期望它會發生什么?

class RequestProcessing extends Thread {
    Socket channel;
    int soHieuClient;

    public RequestProcessing(Socket s, int i) {
        // *****************
        // The processor should be using this socket to communicate
        // with a connected client *using TCP Streams*
        channel = s;
        clientNo = i;
    }

    public void run() {
       try {
           byte[] buffer = new byte[6000];
           // *****************************
           // But, instead of using the this.channel, your code
           // decides to ignore the TCP socket,
           // then open another UDP *"server-side like"* socket.
           // First time it's OK, but the second thread attempting
           // to open another DatagramSocket on the same port will fail.
           // It's like attempting to open two TCP ServerSockets on the
           // same port
           DatagramSocket ds = new DatagramSocket(7);  

[額外]

你需要決定你將使用什么協議:如果你使用ServerSocket / Socket對,那么你可能需要TCP通信,所以沒有DatagramSocket

如果您想要UDP通信, ServerSocket / Socket與您的方法幾乎沒有關系,您需要使用DatagramSocket 構建它:

  1. 在服務器端使用一個port - 並且只執行一次。
  2. 如果客戶端沒有任何端口,則使用服務器地址和端口限定每個DatagramPackets

請參閱有關數據報客戶端/服務器配置的 Oracle站點上的教程。

每次在main服務器套接字上收到新的客戶端TCP連接時,都會啟動RequestProcessing類的另一個實例。 第一次啟動RequestProcessing實例線程時,它成功綁定到UDP端口7.但是第二個客戶端連接並嘗試啟動另一個RequestProcessing實例,而另一個實例已經存在。 那不行。

您可能應該修改協議,以便RequestProcessing類每次都選擇一個新端口,並通過TCP套接字向選擇的端口發送信號。

但如果是我,我會這樣做。 為所有客戶端提供一個RequestProcessing實例。 鑒於您的UDP echo套接字只是發送回來自該數據包來自的地址的響應,您只需要該類的一個實例。

TCP解決方案:

一個實用程序類(我懶得在多個地方編寫相同的代碼):

  public class SocketRW {
    Socket socket;
    BufferedReader in;
    PrintWriter    out;

    public SocketRW(Socket socket)
    throws IOException 
    {
      super();
      this.socket = socket;
      if(null!=socket) {
        this.in=new BufferedReader(new InputStreamReader(socket.getInputStream()));
        this.out=new PrintWriter(socket.getOutputStream());
      }
    }

    public String readLine()
    throws IOException {
      return this.in.readLine();
    }

    public void println(String str) {
      this.out.println(str);
    }

    public Socket getSocket() {
      return socket;
    }

    public BufferedReader getIn() {
      return in;
    }

    public PrintWriter getOut() {
      return out;
    }
  }

服務器代碼 - 不再使用數據報,只使用來自套接字的輸入/輸出流,使用該實用程序包裝為Reader / Writer

  public class TCPServer
  implements Runnable // in case you want to run the server on a separate thread
  {
    ServerSocket listenOnThis;

    public TCPServer(int port)
    throws IOException {
      this.listenOnThis=new ServerSocket(port);
    }

    @Override
    public void run() {
      int client=0;
      while(true) {
        try {
          Socket clientConn=this.listenOnThis.accept();
          RequestProcessing processor=new RequestProcessing(clientConn, client++);
          processor.start();
        } catch (IOException e) {
          break;
        }

      }
    }

    static public void main(String args[]) {
      // port to be provided as the first CLI option
      TCPServer server=new TCPServer(Integer.valueOf(args[0]));
      server.run(); // or spawn it on another thread
    }
  }

  class RequestProcessing extends Thread {
    Socket channel;
    int clientNo;

    public RequestProcessing(Socket s, int i) {
      channel = s;
      clientNo = i;
    }

    public void run() {
      try {
        SocketRW utility=new SocketRW(this.channel);          
        while (true) { 
          String theString=utility.readLine().trim();
          System.out.println("Client " + clientNo 
              + " sent: " + theString);
          if ("quit".equals(theString)) {
            System.out.println("Client " + clientNo 
                + " disconnected");
            this.channel.close();
            break;
          }
          theString = theString.toUpperCase();
          utility.println(theString);
        }
      } catch (IOException e) {
        System.err.println(e);
      }
    }
  }

客戶端代碼 - 不再使用套接字的相同IO流的數據報套接字。

  class TCPClient
  implements Runnable // just in case you want to run multithreaded clients
  {
    Socket socket;

    public TCPClient(InetAddress serverAddr, int port)
     throws IOException {
      this.socket=new Socket(serverAddr, port);
    }

    public void run() {
      String theString="";
      InputStreamReader isr = new InputStreamReader(System.in);
      try {
        SocketRW utility=new SocketRW(this.socket);
        BufferedReader br = new BufferedReader(isr);
        do {
          System.out.print("Enter message: ");
          theString = br.readLine().trim();
          utility.println(theString);
          System.out.println("Sent to server server: " + theString);

          String received=utility.readLine();
          System.out.println("Server reply: "+received);

        } while (!"quit".equals(theString));
      }
      catch(IOException e) {
        e.printStackTrace();
      }
    }

    static public void main(String[] args) {
      int port=Integer.valueOf(args[0]); // will throw if its no OK.

      TCPClient client=new TCPClient(
          InetAddress.getByName("192.168.109.128"),
          port
      );

      client.run();
    }
  }

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM