RandomNumberGenerator-Implementierung in C# für random.org 👍 👎

In einem früheren Beitrag zum Thema (Pseudo-)Zufallszahlen in C# hatte ich bereits die Klassen Random und RNGCryptoServiceProvider vorgestellt. Letztere leitet dabei von der abstrakten Klasse RandomNumberGenerator ab, welche wir im Folgenden ebenfalls als Basis für eine Klasse verwenden werden.

Unsere Klasse soll die Werte jedoch nicht selbst erzeugen, sondern vom Anbieter random.org über dessen Schnittstelle auf Basis von JSON-RPC beziehen. Der Dienst ist – zumindest derzeit und in gewissen Grenzen – kostenlos, wobei ein API-Schlüssel benötigt wird, den ihr dem Konstruktor der Klasse übergeben müsst.

Hinweis: Ich möchte vor der eigentlichen Implementierung noch darauf hinweisen, dass dieser Beitrag weder die Güte der darüber ermittelten (Pseudo-)Zufallszahlen noch die Vertrauenswürdigkeit des Anbieters bewerten soll. Dieser Beitrag soll lediglich eine sehr einfache und beispielhafte Implementierung für den Fall aufzeigen, dass ihr euch eben für diese Nutzung (beispielsweise für weniger kritische Daten oder aus reinem Interesse) entschieden habt.
Klasse implementieren
010203040506070809101112131415161718192021222324252627282930313233343536373839404142434445
public class RandomOrg : RandomNumberGenerator {    private const string ApiMethod = "generateIntegers";    private const string ApiUrl = "https://api.random.org/json-rpc/1/invoke";    private const string ApiVersion = "2.0";
private readonly string apiKey;

public RandomOrg(string apiKey) { this.apiKey = apiKey; }
public override void GetBytes(byte[] data) { Array.Copy(this.SendRequest(data.Length), data, data.Length); }
public override void GetNonZeroBytes(byte[] data) { Array.Copy(this.SendRequest(data.Length, 1), data, data.Length); }

private byte[] SendRequest(int n, byte min = 0) { using(WebClient webClient = new WebClient()) { JavaScriptSerializer js = new JavaScriptSerializer();
dynamic data = js.Deserialize<dynamic>(webClient.UploadString(ApiUrl, js.Serialize(new { jsonrpc = ApiVersion, method = ApiMethod, @params = new { apiKey = this.apiKey, n = n, min = min, max = 255 }, id = DateTime.Now.Ticks })));
if(data.ContainsKey("error")) { throw new Exception(data["error"]["message"]); }
return Array.ConvertAll((object[]) data["result"]["random"]["data"], Convert.ToByte); } }}
Die eigentliche Verwendung gestaltet sich nun kompatibel zur Basisklasse RandomNumberGenerator und entsprechend einfach. Um beispielsweise ein byte-Feld mit fünf Zahlen zu befüllen, kann folgender Aufruf dienen:
Klasse verwenden
01020304
using(RandomOrg numberGenerator = new RandomOrg("@ToDo")) {  // API-Schlüssel eintragen    byte[] randomNumbers = new byte[5];    numberGenerator.GetBytes(randomNumbers);}

Zeitserver mit C# abfragen 👍 👎

Im Folgenden möchte ich drei Möglichkeiten vorstellen, wie man mit C# über ein Netzwerk eine Zeitangabe abrufen kann. Es gilt zu beachten, dass es sich um sehr reduzierte Beispiele handelt und beispielsweise keine umfassende Fehlerbehandlung berücksichtigt wird. Wir verwenden standardmäßig jeweils einen Server des NIST.

Time Protocol

Beim Time Protocol handelt es sich um ein sehr einfaches (und etwas betagtes) Protokoll. Der Dienst steht für gewöhnlich unter Port 37 zur Verfügung. Details zum Protokoll können in RFC 868 nachgelesen werden.

Methode zur Zeitabfrage per Time Protocol
010203040506070809101112131415161718
public static DateTime GetTime(string host = "time.nist.gov", int port = 37) {    using(TcpClient client = new TcpClient(host, port)) {        using(NetworkStream networkStream = client.GetStream()) {            // Antwort lesen            byte[] timeBuffer = new byte[4];            networkStream.Read(timeBuffer, 0, timeBuffer.Length);
// Byte-Reihenfolge ggf. umkehren if(BitConverter.IsLittleEndian) { Array.Reverse(timeBuffer); }
return (new DateTime(1900, 1, 1, 0, 0, 0, DateTimeKind.Utc)) .AddSeconds(BitConverter.ToUInt32(timeBuffer, 0)) .ToLocalTime(); } }}

Daytime Protocol

Beim Daytime Protocol verhält es sich ähnlich, ist jedoch textbasiert. Der Dienst steht für gewöhnlich unter Port 13 zur Verfügung. Details zum Rückgabeformat können in RFC 867 und der Dienstbeschreibung des NIST nachgelesen werden – wir werden uns auf den minimalen Part (Datum und Uhrzeit) beschränken.

Methode zur Zeitabfrage per Daytime Protocol
010203040506070809101112131415161718
public static DateTime GetDaytime(string host = "time.nist.gov", int port = 13) {    using(TcpClient client = new TcpClient(host, port)) {        using(StreamReader streamReader = new StreamReader(client.GetStream())) {            // Antwort lesen und auftrennen            string[] response = streamReader.ReadToEnd().Split(' ');
// Datum und Uhrzeit ermitteln int[] date = Array.ConvertAll(response[1].Split('-'), Int32.Parse); int[] time = Array.ConvertAll(response[2].Split(':'), Int32.Parse);
return new DateTime( CultureInfo.CurrentCulture.Calendar.ToFourDigitYear(date[0]), date[1], date[2], time[0], time[1], time[2], DateTimeKind.Utc ).ToLocalTime(); } }}

Network Time Protocol (NTP)

Beim Network Time Protocol handelt es sich um einen weit verbreiteten Standard zur Zeitsynchronisierung in Computernetzwerken über Port 123. Details zum Protokoll können in RFC 5905 nachgelesen werden – wir werden uns wieder auf einen minimalen Teil beschränken. Als Server bietet sich hier natürlich auch die PTB an.

Methode zur Zeitabfrage per NTP
010203040506070809101112131415161718192021222324252627282930313233343536
public static DateTime GetNetworkTime(string host = "time.nist.gov", int port = 123) {    using(UdpClient client = new UdpClient()) {        client.Connect(host, port);
// Anfrage senden const byte header = ( (0 << 6) // LI = 0 (keine Warnung; zur Verdeutlichung ausformuliert) | (4 << 3) // VN = 4 (Version) | (3 << 0) // Mode = 3 (Client) ); // 00_100_011
byte[] request = new byte[48];
request[0] = header;
client.Send(request, request.Length);
// Antwort lesen IPEndPoint endPoint = new IPEndPoint(IPAddress.Any, 0); byte[] response = client.Receive(ref endPoint);
// Daten ab Offset aus ggf. umgekehrter Byte-Reihenfolge ermitteln const int offset = 40;
if(BitConverter.IsLittleEndian) { Array.Reverse(response, offset, 4); Array.Reverse(response, (offset + 4), 4); }
ulong integerPart = BitConverter.ToUInt32(response, offset); ulong fractionalPart = BitConverter.ToUInt32(response, (offset + 4));
return (new DateTime(1900, 1, 1, 0, 0, 0, DateTimeKind.Utc)) .AddMilliseconds((integerPart * 1000) + ((fractionalPart * 1000) / UInt32.MaxValue)) .ToLocalTime(); }}
Insbesondere bei diesem letzten Beispiel möchte ich noch einmal explizit darauf hinweisen, dass es sich um eine sehr reduzierte Implementierung handelt. Die Rückgabe erfolgt bei allen Beispielen jeweils bereits in lokaler Zeit.

Neujahr 👍 👎

Ich bedanke mich für das bisherige Interesse und wünsche ein glückliches und erfolgreiches neues Jahr 2017.

Ich würde mich natürlich sehr darüber freuen, dich auch im nächsten Jahr wieder hier begrüßen zu dürfen.

CCC-Fahrplan 👍 👎

Pünktlich zum aktuellen Chaos Communication Congress des CCC möchte ich ein kleines Projekt vorstellen.

Meine Variante des CCC-Fahrplans greift auf die XML- und JSON-Dateien des offiziellen Zeitplans zurück, stellt dadurch alle Veranstaltungen (seit 2006) und Sprecher (seit 2013) einheitlich dar und enthält einige Querverweise. Darüber hinaus ermöglichen die Flaggen eine besonders schnelle Sprachzuordnung direkt auf der Startseite.

Zur Schonung der Infrastruktur des CCC werden die Daten zwischengespeichert, sodass die angezeigten Informationen u. U. veraltet sein können. Bitte informiere dich für verbindliche Informationen direkt beim CCC.

Parameter einer Java-Methode per Reflexion ermitteln 👍 👎

Für ein kleines Projekt (mehr dazu in einem separaten Beitrag) war es notwendig, die Parameter von Methoden per Reflexion in Java zu ermitteln. Die entsprechende Funktionalität findet sich im Paket java.lang.reflect.
Parameter einer Java-Methode per Reflexion ermitteln
0102030405060708091011121314151617181920
public static List<Map<String,String>> GetMethodParameterList(    String typeName,    String methodName) throws ClassNotFoundException {    List<Map<String,String>> methodList = new ArrayList<>();
for(Method method : Class.forName(typeName).getMethods()) { if(method.getName().equals(methodName)) { HashMap<String,String> parameterList = new HashMap<>();
for(Parameter parameter : method.getParameters()) { parameterList.put(parameter.getName(), parameter.getType().getCanonicalName()); }
methodList.add(parameterList); } }
return methodList;}
Ähnlich zu LINQ bei C# gibt es für Java sogenannte Streams; wir können obige Implementierung damit beispielsweise wie folgt unter einem funktionalem Paradigma schreiben:
Implementierung mit Java-Streams
010203040506070809101112
public static List<Map<String,String>> GetMethodParameterList(    String typeName,    String methodName) throws ClassNotFoundException {    return Stream.of(Class.forName(typeName).getMethods())        .filter(m -> m.getName().equals(methodName))        .map(m -> Stream.of(m.getParameters()).collect(Collectors.toMap(            k -> k.getName(),            v -> v.getType().getCanonicalName()        )))        .collect(Collectors.toList());}
Die Verwendung gestaltet sich jeweils identisch:
Methode verwenden und Ergebnis auf der Standardausgabe ausgeben
01020304050607
List<Map<String,String>> methodList = GetMethodParameterList(typeName, methodName);
for(Map<String,String> parameterList : methodList) { for(Map.Entry<String,String> parameter : parameterList.entrySet()) { System.out.println(parameter.getValue() + " " + parameter.getKey()); }}
Es gilt schließlich noch eine kleine Besonderheit zu berücksichtigen: Standardmäßig stehen die Bezeichner der Parameter unter Java nicht zur Verfügung, sondern würden schlicht durchnummeriert. Um die tatsächlichen Bezeichner zu erhalten, muss mit der Option -parameters kompiliert werden. Verbreitete Entwicklungsumgebungen wie Eclipse oder NetBeans bieten hierfür entsprechende Einstellungen.

Wir bereits früher angekündigt werde ich zur Reflexion in C# noch einen etwas ausführlicheren Beitrag schreiben und dann dort eine entsprechende Implementierung ähnlicher Funktionalität vorstellen.

Projektverweise

Kategorien / Archiv  |  Übersicht RSS-Feed

Schlagworte

Suche