[英]Client-Server Pair: Ephemeral Port
我正在用Java對客戶端服務器對進行編碼,我希望系統在運行時分配端口。 從服務器方面,這可以通過API輕松完成,但是客戶端如何知道服務器正在偵聽哪個端口?
客戶端不知道服務器正在偵聽哪個端口,除非您將其指定給客戶端
您可以考慮使用其他體系結構,例如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);
}
一般來說,您需要使用其他目錄或發現服務來讓客戶端知道服務器所處的端口。 沒有廣泛使用的通用機制來詢問服務器“服務器進程X正在監聽的端口是什么”
如果您只是想靈活地分配端口(例如,能夠在不同的安裝中使用不同的端口,或者使服務器的多個實例在不同的端口上運行),請考慮使用簡單的環境變量或命令行覆蓋以將端口提供給服務器。 例如,
./start-server -p 4000
./start-server -p 4001
當然,您仍然需要使客戶端可配置以找到服務器,但是至少您將具有可預測性(並且不會依賴於系統動態分配的端口)。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.