2015-01-26 13 views
5

Tôi có một lớp TcpServer chịu trách nhiệm, hoạt động như một máy chủ tcp. Bạn có thể tìm thấy những lớp dưới đây:Hai thể hiện ổ cắm khác nhau có thể nghe cùng một cổng TCP (Cổng đã được sử dụng) hay không

public class TcpServer { 
    private ServerSocket serverSocket; 
    private Socket socket; 
    private int locallyBoundPort; 

    public TcpServer() { 


    } 

    public TcpServer(int locallyBoundPort) { 
     try { 
      this.serverSocket = new ServerSocket(locallyBoundPort); 
      serverSocket.setReuseAddress(true); 
     } catch (IOException e) { 
      System.out.println("Error at binding to port TCP : " + locallyBoundPort + "...cause : " + e.getMessage()); 
     } 
     socket = null; 

    } 

    public void accept() { 
     try { 
      socket = serverSocket.accept(); 
      socket.setReuseAddress(true); 
     } catch (IOException e) { 
      System.out.println("Error at accept : " + locallyBoundPort); 
     } 
    } 



    public void send(Data data) throws IOException { 
     if(socket != null) { 

      ObjectOutputStream out = new ObjectOutputStream(socket.getOutputStream()); 
      out.writeObject(data); 

     } 
    } 

    public Data receive() throws ClassNotFoundException, IOException { 
     if(socket != null) { 

       ObjectInputStream in = new ObjectInputStream(socket.getInputStream()); 
       return (Data) in.readObject(); 

     } else { 
      return null; 
     } 
    } 

    public boolean bind(int port) throws IOException { 
     try { 
      this.serverSocket = new ServerSocket(port); 
      this.locallyBoundPort = port; 
     } catch(IOException e) { 
      return false; 
     } 
     return true; 

    } 

    public void close() { 
     try { 
      serverSocket.close(); 
      socket.close(); 
     } catch (IOException e) { 
      OzumUtils.print("IOException in close, TcpServer"); 
     } 

    } 

    public int getLocallyBoundPort() { 
     return locallyBoundPort; 
    } 

    public Socket getSocket() { 
     return socket; 
    } 

    public ServerSocket getServerSocket() { 
     return serverSocket; 
    } 
} 

Và tôi có một mảnh mã mà thực hiện điều này:

TcpServer tcpServer = new TcpServer(LocalPort); 
while(1) 
{ 
    tcpServer.accept(); 
    Thread thread = new Thread(new runnable(tcpServer)); 
    thread.start(); 
    tcpServer = new TcpServer(LocalPort); 
} 

Tuy nhiên tôi nhận được một cổng đã do lỗi sử dụng. Tôi nghĩ rằng hai trường hợp ổ cắm khác nhau có thể nghe cùng một cổng như ghép kênh cho phép hai kết nối thông qua cùng một cổng khi kết nối có ip hoặc cổng khác nhau? Tôi đang thiếu gì?

+0

Không, bạn không thể sử dụng cổng đã ở trạng thái nghe. Tuy nhiên, bất kỳ số lượng khách hàng nào cũng có thể kết nối với cùng một cổng này. – m0skit0

Trả lời

5

Bạn không thể liên kết hai cổng máy chủ tcp với cùng một cổng. reuseAddress thực sự là ổ cắm của khách hàng, và nó không hoạt động theo cách bạn nghĩ ... và cách bạn đang sử dụng nó sẽ không làm gì cả (vì bạn đang thiết lập nó sau khi liên kết).

Bạn không thực sự cần phải liên kết hai lần với cùng một cổng. Chỉ cần xóa dòng này tcpServer = new TcpServer(LocalPort); từ cuối của vòng lặp while của bạn và bạn sẽ được cài đặt xong.

Cách này hoạt động là bạn liên kết ổ cắm máy chủ của bạn một lần và lắng nghe cổng. Khi một kết nối đến, nó sẽ cắm một ổ cắm máy khách để bạn giao tiếp với máy khách và ổ cắm máy chủ ban đầu tiếp tục lắng nghe nhiều kết nối hơn.

Về cơ bản, bạn cần xóa thành viên socket (và bất kỳ trạng thái nào khác) từ TcpServer và thực hiện phương thức accept trả lại ổ cắm được chấp nhận. Sau đó, hãy đặt số runnable của bạn nhận số socket làm thông số thay vì TcpServer và sử dụng thông số đó để phục vụ kết nối khách hàng. Sau đó, chỉ cần tiếp tục gọi số accept trong vòng lặp và tạo luồng cho các kết nối mới giống như cách bạn biết, ngoại trừ việc không tạo lại máy chủ mỗi lần.

Hoặc cách khác, loại bỏ các ổ cắm máy chủ và cổng từ TcpServer, tạo ổ cắm ngoài vòng lặp, sau đó while(true) gọi accept vào nó, tạo ra một mới TcpServer với client socket trở lại, và sử dụng nó trong một thread để xử lý kết nối.

Đừng quên đóng các ổ cắm máy khách sau khi bạn đã hoàn tất.

+0

Xin chào. Cảm ơn bạn đã phản hồi chi tiết. Hãy để tôi hỏi bạn một điều nữa để làm rõ. Bạn có nói rằng khi một socket TCP chấp nhận một kết nối, nó có thể đồng thời chấp nhận các kết nối khác, và tiếp tục giao tiếp với các kết nối trước đó? Ohhh, trong khi tôi đang viết này, tôi nhận ra đây là lý do một ổ cắm mới được trả về khi ServerSocket chấp nhận một kết nối. Cảm ơn bạn ! –

1

Không, bạn không thể sử dụng cổng đã ở trạng thái nghe. Tuy nhiên, bất kỳ số lượng khách hàng nào cũng có thể kết nối với cùng một cổng này. Bạn không cần phải nghe lại cổng, bạn chỉ cần tạo ra một luồng mới để xử lý kết nối hiện tại và đợi một kết nối mới. Ví dụ, giả sử bạn có một lớp TcpConnectionHanlder mà thực hiện Runnable và lấy Socket như tham số, vòng lặp sẽ trông như thế

while (true) { //while(1) is not valid Java syntax 
    final Socket tcpSocket = tcpServer.accept(); // Get socket for incoming connection 
    final Thread thread = new Thread(new TcpConnectionHanlder(tcpSocket)); // Create a thread for this socket/client connection 
    thread.start(); // Launch the thread 
    // tcpServer = new TcpServer(LocalPort); <- not needed, port still listening. 
} 

Sau đó, trong TcpConnectionHanlder dụ của bạn, bạn xử lý khách hàng cụ thể này (socket).

Các vấn đề liên quan