Tere tulemast.
Täna arutame kliendi-serveri rakenduste kirjutamise protsessi, mis täidavad Windowsi standardsete utiliitide funktsioone, nagu Telnet, TFTP jne jne puhta Javaga. On selge, et ma ei too midagi uut — kõik need utiliidid on juba aastaid edukalt töötanud, kuid arvan, et mitte kõik ei tea, mis nende taga toimub.
Sellest me allpool räägime.
Selles artiklis, et mitte liiga pikaks venida, kirjutan ma lisaks üldisele teabele ainult Telnet serverist, kuid hetkel on olemas ka muud utiliidid — need tulevad järgnevates osades.
Esiteks tasub selgusele jõuda, mis on Telnet, milleks seda vajatakse ja kuidas seda kasutada. Ma ei hakka allikaid sõnasõnalt tsiteerima (kui vajalik, lisan lõpus lingid teema materjalidele), ütlen vaid, et Telnet võimaldab kaugjuurdepääsu seadme käsureale. Suuresti sellega tema funktsionaalsus lõppeb (serveri sadama käsitlemist ma teadlikult ei maininud, sellest hiljem). Seega, selle rakendamiseks peab klient võtma sisendi, edastama selle serverile, proovima seda käsureale edastada, lugema vastuse, kui see on olemas, edastama selle tagasi kliendile ja kuvama ekraanile, või veateate korral andma kasutajale mõista, et midagi on valesti.
Eespool mainitud realiseerimiseks on vajalik kaks töötavat klassi ning üks testimise klass, millest me serverit käivitame ja mille kaudu klient töötab.
Seega, praegune rakenduse struktuur sisaldab järgmist:
- TelnetClient
- TelnetClientTester
- TelnetServer
- TelnetServerTester
Käime neid kõiki läbi:
TelnetClient
Kõik, mida see klass peab oskama, on saadetud käske edastada ja saadud vastuseid näidata. Lisaks tuleb osata ühenduda suvalise (nagu ülal mainitud) kauad ja sellest lahti öelda.
Selleks on rakendatud järgmised funktsioonid:
Funktsioon, mis võtab argumendiks soketi aadressi, avab ühenduse ja käivitab sisendi- ja väljundi vood (voo muutujad on deklareeritud ülal, täielikud allikad on artikli lõpus).
public void run(String ip, int port)
{
try {
Socket socket = new Socket(ip, port);
InputStream sin = socket.getInputStream();
OutputStream sout = socket.getOutputStream();
Scanner keyboard = new Scanner(System.in);
reader = new Thread(()->read(keyboard, sout));
writer = new Thread(()->write(sin));
reader.start();
writer.start();
}
catch (Exception e) {
System.out.println(e.getMessage());
}
}
Sama funktsiooni ülekäik, mis ühendub vaikeporti — telneti puhul on see 23
public void run(String ip)
{
run(ip, 23);
}
Funktsioon loeb klaviatuurilt sümboleid ja saadab need väljund soketisse — mis iseloomulik, rea ja mitte sümbolite režiimis:
private void read(Scanner keyboard, OutputStream sout)
{
try {
String input = new String();
while (true) {
input = keyboard.nextLine();
for (char i : (input + " n").toCharArray())
sout.write(i);
}
}
catch (Exception e) {
System.out.println(e.getMessage());
}
}
Funktsioon võtab andmed socket'ilt ja kuvab need ekraanile
private void write(InputStream sin)
{
try {
int tmp;
while (true){
tmp = sin.read();
System.out.print((char)tmp);
}
}
catch (Exception e) {
System.out.println(e.getMessage());
}
}
Funktsioon peatab andmete vastuvõtmise ja edastamise
public void stop()
{
reader.stop();
writer.stop();
}
}TelnetServer
See klass peab sisaldama funktsionaalsust, mis aktsepteerib käsku socket'ilt, saadab selle täitmiseks ja saadab vastuse tagasi socket'ile. Programmis ei ole tahtlikult sisendandmete kontrolli, sest esiteks, isegi 'karbi telnetis' on võimalik serveri ketas vormindada, ja teiseks, turvaküsimus on selles artiklis põhimõtteliselt kõrvale jäetud, mistõttu ei saagi siin olla sõna krüpteerimise või SSL-i kohta.
Siin on kokku 2 funktsiooni (üks neist on ülekoormatud), ja üldiselt ei ole see väga hea praktika, kuid antud ülesande kontekstis nägin ma sobivana jätta kõik nii, nagu see oli.
boolean isRunning = true;
public void run(int port) {
(new Thread(()->{ try {
ServerSocket ss = new ServerSocket(port); // loome serveri soketi, et siduda see ülalmainitud pordiga
System.out.println("Port " + port + " ootab ühendusi");
Socket socket = ss.accept();
System.out.println("Ühendatud");
System.out.println();
// Saame soketi sisendi ja väljundi voogude kätte, nüüd saame andmeid kliendile saata ja vastu võtta.
InputStream sin = socket.getInputStream();
OutputStream sout = socket.getOutputStream();
Map env = System.getenv();
String wayToTemp = env.get("TEMP") + "tmp.txt";
for (int i : ("Ühendatudnnr".toCharArray()))
sout.write(i);
sout.flush();
String buffer = new String();
while (isRunning) {
int intReader = 0;
while ((char) intReader != 'n') {
intReader = sin.read();
buffer += (char) intReader;
}
final String inputToSubThread = "cmd /c " + buffer.substring(0, buffer.length()-2) + " 2>&1";
new Thread(() -> {
try {
Process p = Runtime.getRuntime().exec(inputToSubThread);
InputStream out = p.getInputStream();
Scanner fromProcess = new Scanner(out);
try {
while (fromProcess.hasNextLine()) {
String temp = fromProcess.nextLine();
System.out.println(temp);
for (char i : temp.toCharArray())
sout.write(i);
sout.write('n');
sout.write('r');
}
}
catch (Exception e) {
String output = "Midagi läks valesti... Viga kood: " + e.getStackTrace();
System.out.println(output);
for (char i : output.toCharArray())
sout.write(i);
sout.write('n');
sout.write('r');
}
p.getErrorStream().close();
p.getOutputStream().close();
p.getInputStream().close();
sout.flush();
}
catch (Exception e) {
System.out.println("Viga: " + e.getMessage());
}
}).start();
System.out.println(buffer);
buffer = "";
}
}
catch(Exception x) {
System.out.println(x.getMessage());
}})).start();
}
Programm avab serveri pordi, loeb sellest andmeid, kuni kohtub käskluse lõpetamise sümboliga, edastab käsu uuele protsessile ja protsessi väljund suunatakse soketisse. Kõik on sama lihtne nagu Kahuri-Kasak.
Seetõttu on selle funktsiooni jaoks olemas ülekoormus vaikimisi pordiga:
public void run()
{
run(23);
}Noh ja seega, funktsioon, mis peatab serveri — ka kõik on triviaalne, see katkestab igavese tsükli, rikkudes selle tingimust.
public void stop()
{
System.out.println("Server was stopped");
this.isRunning = false;
}Ma ei too siia katseklasse, need on all — kõik, mida nad teevad, on kontrollida avalike meetodite töövõimet. Kõik on git'is olemas.
Kokkuvõtteks, paaril õhtul on võimalik mõista peamisi käsklusseadmete toimimise põhimõtteid. Nüüd, kui me telnetime kaugele arvutisse, mõistame, mis toimub — maagia on kadunud)
Nii et, lingid:
Allikas: habr.com
