java.net.BindException:已在使用的地址:无法绑定

时间:2016-10-08 00:38:32

标签: java sockets network-programming serversocket

对不起,我已经搜索了但似乎所有答案都没有解决我的问题。尝试创建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

3 个答案:

答案 0 :(得分:2)

因此,在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 s

如果您想进行UDP通信,ServerSocket / Socket与您的方法关系不大,您需要使用DatagramSocket。构建它:

  1. with a port on the serverside - 只做一次。
  2. without any port for the client side然后使用服务器地址和端口限定每个DatagramPackets
  3. Datagram client/server configurations上查看Oracle网站上的教程。

答案 1 :(得分:1)

每次在main服务器套接字上收到新的客户端TCP连接时,都会启动另一个RequestProcessing类实例。第一次启动RequestProcessing实例线程时,它成功绑定到UDP端口7.但是第二个客户端连接并尝试启动另一个RequestProcessing实例,而另一个实例已经存在。那不行。

您可能应该修改协议,以便RequestProcessing类每次都选择一个新端口,并通过TCP套接字向选择端口发送信号。

但如果是我,我会这样做。为所有客户端提供一个RequestProcessing实例。鉴于您的UDP echo套接字只是发送回来自该数据包来自的地址的响应,您只需要该类的一个实例。

答案 2 :(得分:0)

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