Selamat Datang.
Hari ini saya ingin melihat proses penulisan aplikasi client-server yang menjalankan fungsi utilitas Windows standar, seperti Telnet, TFTP, dan lain-lain, dan lain-lain dalam Java murni. Jelas bahwa saya tidak akan membawa sesuatu yang baru - semua utilitas ini telah bekerja dengan sukses selama lebih dari satu tahun, tetapi saya yakin tidak semua orang tahu apa yang sedang terjadi.
Inilah tepatnya yang akan dibahas dalam pemotongan ini.
Pada artikel ini, agar tidak berlarut-larut, selain informasi umum, saya hanya akan menulis tentang server Telnet, tetapi saat ini ada juga materi tentang utilitas lain - itu akan ada di bagian selanjutnya dari seri ini.
Pertama-tama, Anda perlu memahami apa itu Telnet, apa kegunaannya, dan kegunaannya. Saya tidak akan mengutip sumber kata demi kata (jika perlu, saya akan melampirkan tautan ke materi tentang topik tersebut di akhir artikel), saya hanya akan mengatakan bahwa Telnet menyediakan akses jarak jauh ke baris perintah perangkat. Pada umumnya, di sinilah fungsinya berakhir (saya sengaja bungkam tentang akses ke port server; lebih lanjut tentang itu nanti). Artinya untuk mengimplementasikannya, kita perlu menerima sebuah baris di klien, meneruskannya ke server, mencoba meneruskannya ke baris perintah, membaca respons baris perintah, jika ada, meneruskannya kembali ke klien dan menampilkannya di layar, atau, jika terjadi kesalahan, beri tahu pengguna bahwa ada sesuatu yang salah.
Oleh karena itu, untuk mengimplementasikan hal di atas, kita memerlukan 2 kelas kerja dan beberapa kelas pengujian tempat kita akan meluncurkan server dan melaluinya klien akan bekerja.
Oleh karena itu, saat ini struktur aplikasi meliputi:
- Klien Telnet
- TelnetClientTester
- Server Telnet
- TelnetServerTester
Mari kita bahas masing-masingnya:
Klien Telnet
Yang harus dapat dilakukan oleh kelas ini hanyalah mengirimkan perintah yang diterima dan menunjukkan respons yang diterima. Selain itu, Anda harus dapat menyambung ke port sembarang (seperti yang disebutkan di atas) pada perangkat jarak jauh dan memutuskan sambungannya.
Untuk mencapai hal ini, fungsi-fungsi berikut dilaksanakan:
Sebuah fungsi yang menggunakan alamat soket sebagai argumen, membuka koneksi dan memulai aliran input dan output (variabel aliran dideklarasikan di atas, sumber lengkap ada di akhir artikel).
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());
}
}
Membebani fungsi yang sama, menghubungkan ke port default - untuk telnet ini adalah 23
public void run(String ip)
{
run(ip, 23);
}
Fungsi ini membaca karakter dari keyboard dan mengirimkannya ke soket keluaran - yang biasanya, dalam mode garis, bukan mode karakter:
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());
}
}
Fungsi ini menerima data dari soket dan menampilkannya di layar
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());
}
}
Fungsi ini menghentikan penerimaan dan transmisi data
public void stop()
{
reader.stop();
writer.stop();
}
}
Server Telnet
Kelas ini harus memiliki fungsi menerima perintah dari soket, mengirimkannya untuk dieksekusi, dan mengirimkan respons dari perintah kembali ke soket. Program ini sengaja tidak memeriksa data masukan, karena pertama, bahkan dalam "telnet kotak" dimungkinkan untuk memformat disk server, dan kedua, masalah keamanan dalam artikel ini pada prinsipnya dihilangkan, dan itulah mengapa tidak ada sepatah kata pun tentang enkripsi atau SSL.
Hanya ada 2 fungsi (salah satunya kelebihan beban), dan secara umum ini bukan praktik yang baik, tetapi untuk keperluan tugas ini, tampaknya tepat bagi saya untuk membiarkan semuanya apa adanya.
boolean isRunning = true;
public void run(int port) {
(new Thread(()->{ try {
ServerSocket ss = new ServerSocket(port); // ΡΠΎΠ·Π΄Π°Π΅ΠΌ ΡΠΎΠΊΠ΅Ρ ΡΠ΅ΡΠ²Π΅ΡΠ° ΠΈ ΠΏΡΠΈΠ²ΡΠ·ΡΠ²Π°Π΅ΠΌ Π΅Π³ΠΎ ΠΊ Π²ΡΡΠ΅ΡΠΊΠ°Π·Π°Π½Π½ΠΎΠΌΡ ΠΏΠΎΡΡΡ
System.out.println("Port "+port+" is waiting for connections");
Socket socket = ss.accept();
System.out.println("Connected");
System.out.println();
// ΠΠ΅ΡΠ΅ΠΌ Π²Ρ
ΠΎΠ΄Π½ΠΎΠΉ ΠΈ Π²ΡΡ
ΠΎΠ΄Π½ΠΎΠΉ ΠΏΠΎΡΠΎΠΊΠΈ ΡΠΎΠΊΠ΅ΡΠ°, ΡΠ΅ΠΏΠ΅ΡΡ ΠΌΠΎΠΆΠ΅ΠΌ ΠΏΠΎΠ»ΡΡΠ°ΡΡ ΠΈ ΠΎΡΡΡΠ»Π°ΡΡ Π΄Π°Π½Π½ΡΠ΅ ΠΊΠ»ΠΈΠ΅Π½ΡΡ.
InputStream sin = socket.getInputStream();
OutputStream sout = socket.getOutputStream();
Map<String, String> env = System.getenv();
String wayToTemp = env.get("TEMP") + "tmp.txt";
for (int i :("Connectednnr".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 = "Something gets wrong... Err code: "+ 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("Error: " + e.getMessage());
}
}).start();
System.out.println(buffer);
buffer = "";
}
}
catch(Exception x) {
System.out.println(x.getMessage());
}})).start();
}
Program membuka port server, membaca data darinya hingga menemukan karakter akhir perintah, meneruskan perintah ke proses baru, dan mengarahkan output dari proses ke soket. Semuanya sesederhana senapan serbu Kalashnikov.
Oleh karena itu, ada kelebihan beban untuk fungsi ini dengan port default:
public void run()
{
run(23);
}
Oleh karena itu, fungsi menghentikan server juga sepele, memutus loop abadi, melanggar kondisinya.
public void stop()
{
System.out.println("Server was stopped");
this.isRunning = false;
}
Saya tidak akan memberikan kelas tes di sini, mereka ada di bawah - yang mereka lakukan hanyalah memeriksa fungsionalitas metode publik. Semuanya ada di git.
Untuk meringkas, dalam beberapa malam Anda dapat memahami prinsip-prinsip pengoperasian utilitas konsol utama. Sekarang, ketika kami melakukan telenet ke komputer jarak jauh, kami memahami apa yang terjadi - keajaiban telah hilang)
Jadi, tautannya:
Sumber: www.habr.com