我正在编写java中的客户端服务器对,我希望系统在运行时分配端口。从服务器端可以通过API轻松完成,但客户端如何知道服务器正在侦听哪个端口?
答案 0 :(得分:0)
客户端不知道服务器正在侦听哪个端口,除非您将其指定给客户端
您可以考虑使用其他体系结构,例如OGSI,或者您拥有所有客户端都知道的“服务存储库”的技术,以便他们可以查询并获取有关可用服务及其端口的信息。
另一个选择是让系统管理员在您的DNS组织中为您的服务定义SRV记录,然后客户端代码可以调用SRV记录请求,并获取有关该服务的信息,但这可能对您的需求有些过大,需要涉及你的系统管理员
如果您对我提到的DNS选项感兴趣,您应该在oVirt开源检查DnsSrvLocator类。
在oVirt,我们使用它来检测Ldap服务器(安装Active Directory时,系统管理员应该为LDAP服务器配置条目,指示主机名和它侦听的端口,然后,使用此实用程序,您可以提供以下信息: srv记录(域和协议,并获得可用的服务器列表)
这是它的代码 -
/**
*
*/
package org.ovirt.engine.core.dns;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Arrays;
import java.util.Hashtable;
import java.util.InputMismatchException;
import java.util.Random;
import java.util.Scanner;
import java.util.regex.Pattern;
import javax.naming.Context;
import javax.naming.NamingException;
import javax.naming.directory.Attribute;
import javax.naming.directory.Attributes;
import javax.naming.directory.DirContext;
import javax.naming.spi.NamingManager;
import org.ovirt.engine.core.utils.log.Log;
import org.ovirt.engine.core.utils.log.LogFactory;
/**
* Utility class to query DNS SRV records, and return results according to the priority/weights algorithm as specified
* in RFC 2782
*
*/
public class DnsSRVLocator {
private static final String DNS_QUERY_PREFIX = "dns:///";
private static final String SRV_RECORD = "SRV";
private static Pattern SPACE_PATTERN = Pattern.compile(" ");
private static SrvRecord invalidRecord = new SrvRecord(false, false, 0, 0, 0, "");
private int numberOfValidRecords = 0;
private Random random = new Random(System.currentTimeMillis());
public static final String TCP = "_tcp";
public static final String UDP = "_udp";
/**
* Holds information on a retrieved SRV record
*
*/
public static class SrvRecord implements Comparable<SrvRecord> {
private boolean valid;
private int weight;
private int priority;
private int sum;
private String address;
private boolean used;
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("valid: ").append(valid).append(" sum: ").append(sum).append("priority: ").append(priority)
.append(" weight: ").append(weight).append(" hostport: ").append(address);
return sb.toString();
}
public SrvRecord(int priority, int weight, String hostPort) {
this(true, false, 0, priority, weight, hostPort);
}
public SrvRecord(boolean valid, boolean used, int sum, int priority, int weight, String address) {
this.valid = valid;
this.used = used;
this.sum = sum;
this.priority = priority;
this.weight = weight;
this.address = address;
}
public boolean isValid() {
return valid;
}
public void setValid(boolean isValid) {
this.valid = isValid;
}
public int getWeight() {
return weight;
}
public void setWeight(int weight) {
this.weight = weight;
}
public int getPriority() {
return priority;
}
public void setPriority(int priority) {
this.priority = priority;
}
public int getSum() {
return sum;
}
public void setSum(int sum) {
this.sum = sum;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public boolean isUsed() {
return used;
}
public void setUsed(boolean isUsed) {
this.used = isUsed;
}
@Override
public int compareTo(SrvRecord other) {
// Sort in ascending order where invalid (non parsable) records are
// last
// Records with lower priority value come first
// For a group of records with same priority, records with weight 0
// come first
if (valid && !other.valid) {
return -1;
}
if (!valid && other.valid) {
return 1;
}
if (priority < other.priority) {
return -1;
}
if (priority > other.priority) {
return 1;
}
if (weight == 0 && other.weight != 0) {
return -1;
}
if (weight != 0) {
return 1;
}
return 0;
}
}
public static class DnsSRVResult {
private int numOfValidAddresses;
private String[] addresses;
public DnsSRVResult(int numOfValidAddresses, String[] addresses) {
this.numOfValidAddresses = numOfValidAddresses;
this.addresses = addresses;
}
public int getNumOfValidAddresses() {
return numOfValidAddresses;
}
public String[] getAddresses() {
return addresses;
}
}
public DnsSRVResult getService(String service, String protocol, String domain) throws Exception {
StringBuilder dnsQuery = new StringBuilder();
dnsQuery.append(service).append(".").append(protocol).append(".").append(domain);
try {
return getService(dnsQuery.toString());
} catch (Exception ex) {
log.errorFormat("Error: could not find DNS SRV record name: {0}.{1}.{2}.\nException message is: {3}\n" +
"Possible causes: missing DNS entries in the DNS server or DNS resolving" +
" issues from engine-core machine.\nPlease Ensure correct DNS entries exist in the DNS server" +
" and ensure the DNS server is reachable from the engine-core machine.",
service,
protocol,
domain,
ex.getMessage());
throw ex;
}
}
private DnsSRVResult getService(String dnsUrl) throws NamingException {
Context ctx = NamingManager.getURLContext("dns", new Hashtable(0));
if (!(ctx instanceof DirContext)) {
return null; // cannot create a DNS context
}
StringBuilder fullDnsURL = new StringBuilder(DNS_QUERY_PREFIX);
fullDnsURL.append(dnsUrl);
Attributes attrs = ((DirContext) ctx).getAttributes(fullDnsURL.toString(), new String[] { SRV_RECORD });
if (attrs == null) {
return null;
}
Attribute attr = attrs.get(SRV_RECORD);
if (attr == null) {
return null;
}
int numOfRecords = attr.size();
String[] records = new String[numOfRecords];
for (int counter = 0; counter < numOfRecords; counter++) {
records[counter] = (String) attr.get(counter);
}
return getSRVResult(records);
}
public DnsSRVResult getSRVResult(String[] recordsList) {
int numOfRecords = recordsList.length;
if (recordsList == null || numOfRecords == 0) {
return null;
}
// Read records as retrieved from DNS
SrvRecord[] records = new SrvRecord[numOfRecords];
int counter = 0;
for (String recordStr : recordsList) {
SrvRecord srvRecord = parseSrvRecord(recordStr);
records[counter++] = srvRecord;
}
// Sort the records
Arrays.sort(records);
int priority = -1;
int lastPriorityIndex = -1;
int priorityIndex = -1;
int startPriorityIndex = -1;
String[] addresses = new String[numOfRecords];
// Total number of service addresses
int numOfAddreses = -1;
// Iterates over the records, and calculates for each
// priority the index of the first SRV record that contains
// the index of last SRVV record that contains the priority
// For each group of records with same priorities, gets a list of services
for (SrvRecord record : records) {
if (!record.isValid()) {
break;
}
lastPriorityIndex = priorityIndex;
priorityIndex++;
int currentPriority = record.getPriority();
if (currentPriority != priority) {
if (lastPriorityIndex != -1) {
// This means that this is the end of a group of records
// with same
// priority - get their service addresses
numOfAddreses = fillServiceAddress(records, startPriorityIndex, lastPriorityIndex, addresses,
numOfAddreses);
}
startPriorityIndex = priorityIndex;
priority = currentPriority;
}
lastPriorityIndex = priorityIndex;
}
numOfAddreses = fillServiceAddress(records, startPriorityIndex, lastPriorityIndex, addresses, numOfAddreses);
// numOfAddresses points to the last index of valid address in the
// addresses array
// Increase it by 1 in order to truely reflect the number of valid
// addresses
return new DnsSRVResult(numOfAddreses + 1, addresses);
}
private int fillServiceAddress(SrvRecord[] records,
int startPriorityIndex,
int lastPriorityIndex,
String[] addresses,
int numOfAddressess) {
// Run the following algorithm for determining the order of service entries for a
// group of SRV records with same
// priority:
// 1. For each SRV record calculate its sum based on the sum of weights
// of its weight and
// the weight of all preceding SRV records
// 2. Select a random value between 0 and the sum (inclusive)
// 3. Iterate over the group until a record with sum that is greater or
// above
// to the generated random value is encountered
// 4. This will be the next select SRV record - make it not used for
// next round of the algorithm
int numOfRepetitions = (lastPriorityIndex - startPriorityIndex) + 1;
int totalSum = 0;
for (int counter = 0; counter < numOfRepetitions; counter++) {
for (int index = startPriorityIndex; index <= lastPriorityIndex; index++) {
if (!records[index].isUsed()) {
totalSum += records[index].getWeight();
records[index].setSum(totalSum);
}
}
int randResult = random.nextInt(totalSum + 1);
for (int index = startPriorityIndex; index <= lastPriorityIndex; index++) {
boolean found = false;
if (!found && !records[index].isUsed()) {
if (records[index].getSum() >= randResult) {
records[index].setUsed(true);
addresses[++numOfAddressess] = records[index].getAddress();
found = true;
}
}
}
}
return numOfAddressess;
}
private SrvRecord parseSrvRecord(String recordStr) {
// SRV record looks like: PRIORITY WEIGHT PORT HOST
Scanner s = new Scanner(recordStr).useDelimiter(SPACE_PATTERN);
try {
int priority = s.nextInt();
int weight = s.nextInt();
String port = s.next();
String host = s.next();
StringBuilder sb = new StringBuilder(host);
sb.append(":").append(port);
numberOfValidRecords++;
return new SrvRecord(priority, weight, sb.toString());
} catch (InputMismatchException ex) {
log.errorFormat("the record {0} has invalid format", recordStr);
// In case there is a parsing error, the invalid record constant is
// returned
return invalidRecord;
}
}
public URI constructURI(String protocol, String address) throws URISyntaxException {
String[] parts = address.split("\\.:");
if (parts.length != 2) {
throw new IllegalArgumentException("the address in SRV record should contain host and port");
}
StringBuilder uriSB = new StringBuilder(protocol);
uriSB.append("://").append(parts[0]).append(":").append(parts[1]);
return new URI(uriSB.toString());
}
private static Log log = LogFactory.getLog(DnsSRVLocator.class);
}
答案 1 :(得分:0)
一般来说,您需要使用其他目录或发现服务让客户端知道服务器所在的端口。没有广泛可用的通用机制来询问服务器“服务器进程X监听哪个端口”
如果您只是希望在端口分配方面具有灵活性(例如,为了能够在不同的安装中使用不同的端口,或者让多个服务器实例在不同的端口上运行),请考虑使用简单的环境变量或命令行覆盖以将端口提供给服务器。如,
./start-server -p 4000
./start-server -p 4001
当然,您仍然需要使客户端可配置以查找服务器,但至少您将具有可预测性(并且不会依赖于动态系统分配的端口)。