ซ็อกเก็ต: ค้นหาความพร้อมใช้งานของพอร์ตโดยใช้ Java


123

ฉันจะตรวจสอบความพร้อมใช้งานของพอร์ตในเครื่องที่กำหนดโดยใช้ Java โดยทางโปรแกรมได้อย่างไร

เช่นระบุหมายเลขพอร์ตตรวจสอบว่ามีการใช้งานอยู่แล้วหรือไม่?


2
ในขณะที่คำถามนี้เกี่ยวกับวิธีการทราบว่ามีการใช้พอร์ตที่กำหนดหรือไม่คุณอาจมาที่นี่เพื่อพยายามหาวิธีรับหมายเลขพอร์ตฟรีซึ่งstackoverflow.com/questions/2675362/… (พร้อมลิงก์ไปยังส่วนสำคัญของฉัน.github.com / 3429822 ) ครอบคลุมดีกว่า
vorburger

เพื่อจุดประสงค์อะไร? หากคุณต้องการหาซ็อกเก็ตว่างเพื่อฟังเพียงแค่ระบุศูนย์ เทคนิคการสแกนใด ๆ อาจทำให้เกิดปัญหาเกี่ยวกับกรอบเวลา: พอร์ตอาจว่างเมื่อคุณสแกนและถูกครอบครองเมื่อคุณไปอ้างสิทธิ์
Marquis of Lorne

คำตอบ:


91

นี่คือการใช้งานที่มาจากโครงการอูฐ Apache :

/**
 * Checks to see if a specific port is available.
 *
 * @param port the port to check for availability
 */
public static boolean available(int port) {
    if (port < MIN_PORT_NUMBER || port > MAX_PORT_NUMBER) {
        throw new IllegalArgumentException("Invalid start port: " + port);
    }

    ServerSocket ss = null;
    DatagramSocket ds = null;
    try {
        ss = new ServerSocket(port);
        ss.setReuseAddress(true);
        ds = new DatagramSocket(port);
        ds.setReuseAddress(true);
        return true;
    } catch (IOException e) {
    } finally {
        if (ds != null) {
            ds.close();
        }

        if (ss != null) {
            try {
                ss.close();
            } catch (IOException e) {
                /* should not be thrown */
            }
        }
    }

    return false;
}

พวกเขากำลังตรวจสอบ DatagramSocket ด้วยเพื่อตรวจสอบว่าพอร์ตสามารถใช้ได้ใน UDP และ TCP หรือไม่

หวังว่านี่จะช่วยได้


5
ตัวแปรที่ดีของสิ่งนี้หากคุณมี MINA คือ AvailablePortFinder.getNextAvailable (1024) ซึ่งทำให้คุณได้รับพอร์ตถัดไปที่ไม่ได้รับสิทธิพิเศษ
Alain O'Dea

9
อย่างไรก็ตามสิ่งนี้ไม่ได้จับทุกอย่าง ฉันถูกกัดโดย nginx ที่รันบน TCP 8000 ในขณะที่รูทีนข้างต้นรายงาน 8000 เท่าที่มีอยู่ ไม่รู้ว่าทำไม - ฉันสงสัยว่า nginx ทำบางอย่างส่อเสียด (นี่คือบน OS X) วิธีแก้ปัญหาสำหรับฉันคือทำข้างต้นและเปิด Socket ใหม่ ("localhost", พอร์ต) จากนั้นส่งคืนเท็จหากเราไม่ได้รับข้อยกเว้น คิด?
เมฆบางส่วน

2
ปัญหาเดียวกันกับ Windows ที่พยายามตรวจสอบว่าเซิร์ฟเวอร์ SMTP ของ Papercut กำลังทำงานอยู่หรือไม่ โซลูชันที่คุณเปิดการเชื่อมต่อกับพอร์ตแทนที่จะพยายามเชื่อมโยงกับพอร์ต (ตามที่แนะนำโดยความคิดเห็น Partly Clodys และคำตอบของ Ivan) ดูเหมือนจะทำงานได้น่าเชื่อถือมากขึ้น
stian

4
น่าสนใจ การเรียก setReuseAddress () ไม่มีจุดหมายโดยสิ้นเชิงหลังจากที่เชื่อมต่อซ็อกเก็ตแล้วและยังขัดกับวัตถุประสงค์ของรหัสโดยสิ้นเชิง
Marquis of Lorne

3
รหัสนี้อาจมีปัญหาเกี่ยวกับกรอบเวลา หากวัตถุประสงค์คือการสร้างServerSocketฟังพอร์ตบางพอร์ตนั่นคือสิ่งที่ควรทำและส่งคืนไฟล์ServerSocket. การสร้างแล้วปิดและส่งคืนเป็นการรับประกันเป็นศูนย์ว่าServerSocketสามารถสร้างรายการที่ตามมาได้โดยใช้พอร์ตนั้น Ditto สำหรับDatagramSocket.
Marquis of Lorne

41

สำหรับ Java 7 คุณสามารถใช้ try-with-resource สำหรับโค้ดที่กะทัดรัดมากขึ้น:

private static boolean available(int port) {
    try (Socket ignored = new Socket("localhost", port)) {
        return false;
    } catch (IOException ignored) {
        return true;
    }
}

15
ไม่ได้ทดสอบว่าที่พอร์ตพร้อมใช้งานหรือไม่ จะทดสอบว่าอยู่ในสถานะ LISTEN หรือไม่ที่อยู่ IP สามารถเข้าถึงได้หรือไม่ ฯลฯ
Marquis of Lorne

1
นอกจากนี้การทดสอบนี้ค่อนข้างช้า (เกือบหนึ่งวินาทีต่อพอร์ต)
JMax

ไม่ควรส่งคืนเท็จเมื่อ IOException ถูกจับได้หรือไม่?
Baroudi Safwen

@BaroudiSafwen มันขึ้นอยู่กับว่าจริงๆแล้วข้อยกเว้นคืออะไร สำหรับConnectException: 'connection refused'ใช่มันควรส่งคืนเท็จ สำหรับการหมดเวลาไม่มีสิ่งใดที่สามารถคืนค่าได้เนื่องจากไม่ทราบคำตอบที่แท้จริง นั่นเป็นเหตุผลที่เทคนิคนี้ไม่มีประโยชน์สำหรับจุดประสงค์นี้
Marquis of Lorne

36

ดูเหมือนว่าใน Java 7 คำตอบของ David Santamariaไม่ได้ผลอย่างน่าเชื่อถืออีกต่อไป ดูเหมือนว่าคุณยังสามารถใช้ Socket เพื่อทดสอบการเชื่อมต่อได้อย่างน่าเชื่อถือ

private static boolean available(int port) {
    System.out.println("--------------Testing port " + port);
    Socket s = null;
    try {
        s = new Socket("localhost", port);

        // If the code makes it this far without an exception it means
        // something is using the port and has responded.
        System.out.println("--------------Port " + port + " is not available");
        return false;
    } catch (IOException e) {
        System.out.println("--------------Port " + port + " is available");
        return true;
    } finally {
        if( s != null){
            try {
                s.close();
            } catch (IOException e) {
                throw new RuntimeException("You should handle this error." , e);
            }
        }
    }
}

ฉันต้องการตรวจสอบว่ามีพร็อกซีเซิร์ฟเวอร์ให้โทรในนามของเขาหรือไม่
Dejell

1
คำตอบของ @ DavidSantamaria ไม่เคยได้ผล ไม่มีส่วนเกี่ยวข้องกับ Java 7
Marquis of Lorne

35

หากคุณไม่กังวลกับประสิทธิภาพมากเกินไปคุณสามารถลองฟังบนพอร์ตโดยใช้คลาสServerSocketได้ตลอดเวลา หากมีการใช้อัตราต่อรองข้อยกเว้นแสดงว่ามีการใช้

public static boolean isAvailable(int portNr) {
  boolean portFree;
  try (var ignored = new ServerSocket(portNr)) {
      portFree = true;
  } catch (IOException e) {
      portFree = false;
  }
  return portFree;
}

แก้ไข:หากสิ่งที่คุณพยายามทำคือเลือกพอร์ตที่ว่างจากนั้นnew ServerSocket(0)จะหาพอร์ตให้คุณ


ใช้ในที่สุดเพื่อล้างข้อมูล (ลอง {... } catch {... } ในที่สุด {if (socket! = null) socket.close ();}
Hosam Aly

คุณยังสามารถตั้งค่า "portTaken = (socket == null);" ในตอนท้ายแทนที่จะทำแบบจับได้
Hosam Aly

1
ฉันไม่รู้สึกว่าอยู่ในขอบเขตของคำถามของผู้เขียน
Spencer Ruport

1
มีวิธีทำโดยไม่ต้องลอง / จับไหม? ฉันไม่รังเกียจที่จะใช้พอร์ตใด ๆ ที่มีอยู่เพื่อจุดประสงค์ของฉัน แต่การทำซ้ำหมายเลขพอร์ตและลอง / จับแต่ละพอร์ตจนกว่าฉันจะพบว่าพอร์ตฟรีนั้นสิ้นเปลืองเล็กน้อย ไม่มีเมธอด 'native boolean available (int)' ที่ใดที่หนึ่งซึ่งเพิ่งตรวจสอบ?
Oren Shalev

27
SeverSocket ใหม่ (0) จะเลือกพอร์ตฟรีโดยอัตโนมัติ
Spencer Ruport

10

โซลูชันต่อไปนี้ได้รับแรงบันดาลใจจากSocketUtilsใช้งานของ Spring-core (ใบอนุญาต Apache)

เมื่อเทียบกับโซลูชันอื่น ๆ ที่ใช้Socket(...)มันค่อนข้างเร็ว (ทดสอบ 1,000 พอร์ต TCP ในเวลาน้อยกว่าหนึ่งวินาที):

public static boolean isTcpPortAvailable(int port) {
    try (ServerSocket serverSocket = new ServerSocket()) {
        // setReuseAddress(false) is required only on OSX, 
        // otherwise the code will not work correctly on that platform          
        serverSocket.setReuseAddress(false);
        serverSocket.bind(new InetSocketAddress(InetAddress.getByName("localhost"), port), 1);
        return true;
    } catch (Exception ex) {
        return false;
    }
}       

3

โซลูชันที่ใช้ซ็อกเก็ต try / catch อาจให้ผลลัพธ์ที่ไม่ถูกต้อง (ที่อยู่ของซ็อกเก็ตคือ "localhost" และในบางกรณีพอร์ตอาจ "ครอบครอง" ไม่ใช่โดยอินเทอร์เฟซแบบย้อนกลับและอย่างน้อยใน Windows ฉันเคยเห็นการทดสอบนี้ล้มเหลวเช่น มีการประกาศอย่างไม่ถูกต้องว่ามีอยู่)

มีห้องสมุดสุดเท่ชื่อSIGARรหัสต่อไปนี้สามารถเชื่อมโยงคุณได้

Sigar sigar = new Sigar();
int flags = NetFlags.CONN_TCP | NetFlags.CONN_SERVER | NetFlags.CONN_CLIENT;             NetConnection[] netConnectionList = sigar.getNetConnectionList(flags);
for (NetConnection netConnection : netConnectionList) {
   if ( netConnection.getLocalPort() == port )
        return false;
}
return true;

ฉันได้รับ: "no sigar-x86-winnt.dll ใน java.library.path" น่าเสียดายที่ต้องดาวน์โหลด dll เพิ่มเติมบางอย่างที่ไม่สามารถทำงานได้จริงในสภาพแวดล้อมขององค์กร (ฉันไม่สามารถควบคุมระบบ CI ได้) แย่จัง .... ขอบคุณต่อไป
uthomas

@uthomas ฉันเดาว่าคุณยังคงสามารถควบคุมแพ็คเกจการปรับใช้แอพของคุณได้หากเป็นกรณีนี้คุณสามารถให้ Sigar Native runtime DLL / SOs ใกล้ JARs ของคุณและตั้งค่าเส้นทาง JAVA lib ในทางปฏิบัติ (ผ่านการแฮ็กง่ายๆคุณสามารถมีอิทธิพลต่อมันแม้หลังจาก แอปได้เปิดตัวโดย JVM)
Shmil The Cat

2

การล้างคำตอบที่ชี้โดย David Santamaria:

/**
 * Check to see if a port is available.
 *
 * @param port
 *            the port to check for availability.
 */
public static boolean isPortAvailable(int port) {
    try (var ss = new ServerSocket(port); var ds = new DatagramSocket(port)) {
        return true;
    } catch (IOException e) {
        return false;
    }
}

สิ่งนี้ยังคงอยู่ภายใต้เงื่อนไขการแข่งขันที่ user207421 ชี้ให้เห็นในความคิดเห็นต่อคำตอบของ David Santamaria (มีบางอย่างที่สามารถคว้าพอร์ตได้หลังจากวิธีนี้ปิดServerSocketและDatagramSocketและส่งคืน)


1

ในกรณีของฉันมันช่วยในการลองและเชื่อมต่อกับพอร์ต - หากมีบริการอยู่แล้วก็จะตอบสนอง

    try {
        log.debug("{}: Checking if port open by trying to connect as a client", portNumber);
        Socket sock = new Socket("localhost", portNumber);          
        sock.close();
        log.debug("{}: Someone responding on port - seems not open", portNumber);
        return false;
    } catch (Exception e) {         
        if (e.getMessage().contains("refused")) {
            return true;
    }
        log.error("Troubles checking if port is open", e);
        throw new RuntimeException(e);              
    }

1
ไม่ใช่สิ่งที่ถูกขอ
Marquis of Lorne

0

ในกรณีของฉันฉันต้องใช้คลาส DatagramSocket

boolean isPortOccupied(int port) {
    DatagramSocket sock = null;
    try {
        sock = new DatagramSocket(port);
        sock.close();
        return false;
    } catch (BindException ignored) {
        return true;
    } catch (SocketException ex) {
        System.out.println(ex);
        return true;
    }
}

อย่าลืมนำเข้าก่อน

import java.net.DatagramSocket;
import java.net.BindException;
import java.net.SocketException;

ใช้ได้กับพอร์ต UDP คำถามดูเหมือนจะเกี่ยวกับพอร์ต TCP
Marquis of Lorne

-2

ฉันได้ลองอะไรแบบนี้แล้วและมันก็ใช้ได้ผลกับฉันมาก

            Socket Skt;
            String host = "localhost";
            int i = 8983; // port no.

                 try {
                    System.out.println("Looking for "+ i);
                    Skt = new Socket(host, i);
                    System.out.println("There is a Server on port "
                    + i + " of " + host);
                 }
                 catch (UnknownHostException e) {
                    System.out.println("Exception occured"+ e);

                 }
                 catch (IOException e) {
                     System.out.println("port is not used");

                 }

1
ไม่ใช่สิ่งที่ถูกขอ
Marquis of Lorne

และยังมีซ็อกเก็ตรั่ว
Marquis of Lorne
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.