Gyakorlati alapok III.
A socket
A socket szó informatikai értelemben alapjában véve lefordíthatatlan, hiszen eredeti jelentése dugaszolóaljzat, amely jelentésnek nyilvánvalóan köze nincs tárgykörünkhöz. Az előző fejezetben a standard portszámmal (80) rendelkező HTTP-protokoll alapszintű hálózati kommunikációját tekintettük át. A socket-kommunikáció, mint egyfajta, hálózaton kiépített kommunikációs csatorna is hasonló, kivéve néhány lényeges különbséget:
-
amikor a kliens kommmunikációt kezdeményez a szerver felé (connection request), akkor a protokoll szerint a szerver egy dedikált portját szólítja meg. A szerver azonban a tényleges kommunikációt (connection) egy másik, általa kijelölt portra, socket-re irányítja át, mivel a felszabaduló porton további klienskéréseket fogadhat, illetve a socket-portokon keresztül egyszerre több klienst is kiszolgálhat.
-
A socket-kommunikációért a Java-rendszeren belül természetesen külön osztály felel.
Ahhoz, hogy ezt a kommunkációs formát tesztelni tudjuk, külön kliens-, és szerveroldalt kell implementálnunk (ilyet a honlapon belül még nem tettünk), illetve ezeknek futtatása sem lesz könnyű, még pontosabban: több lesz a hibalehetőségünk is.
A Szerver osztály a következő rutinokért lesz felelős:
-
inicializálja, építse fel működtető rutinjait és egy dedikált (itt a hazafias 1848-as) socket-porton keresztül jelenjen meg, ebben az esetben a belső hálózaton (mindenkori IP-címe 127.0.0.1), várva a kliens hívására,
-
a kliens bejelentkezésekor feléje építse ki a socket-kapcsolatot és a fogadott információkat (itt a kliens begépelt karaktereit) visszhangszerűen jelenítse meg.
-
A Vege! üzenet hatására bontsa a kapcsolatot.
-
Rendelkezzen alapszintű kivételkezeléssel, amely kimeneti-bemeneti és hálózati rutinok esetében egyébként is kötelező.
-
A hibalehetőségeket csökkentendő ne használjon nemzeti ékezetes karaktereket.
import java.net.*;
import java.io.*;
public class Szerver {
private Socket socket = null;
private ServerSocket serverSocket = null;
private DataInputStream inputStream = null;
public Szerver(int portNumber) {
try{
serverSocket = new
ServerSocket(portNumber);
System.out.println("Szerver
elindult...");
System.out.println("Kliens
bejelentkezesere varva...");
socket = serverSocket.accept();
System.out.println("Bejelentkezes
elfogadva...");
inputStream = new DataInputStream(new
BufferedInputStream(socket.getInputStream()));
String line = "";
while (!line.equals("Vege!")) {
try{
line = inputStream.readUTF();
System.out.println(line);
}
catch(IOException ioe) {
System.out.println("Kapcsolodasi vagy egyeb adatolvasasi hiba!" + "\n" + ioe);
}
}
System.out.println("Kapcsolatzaras!");
socket.close();
inputStream.close();
}
catch(IOException ioe) {
System.out.println("Hiba a kapcsolodas lezarasakor!" + "\n" +
ioe);
}
}
public static void main(String args[]) {
Szerver szerver = new Szerver(1848);
}
}
A Kliens osztály a következő rutinokért lesz felelős:
-
inicializálja, építse fel működtető rutinjait és egy dedikált (itt a hazafias 1848-as) socket-porton keresztül kísérelje meg a szerverhez való kapcsolódást, itt a belső hálózaton (mindenkori IP-címe 127.0.0.1),
-
legyen képes a socket-kapcsolaton keresztül információk küldésére (itt begépelt karakterek + ENTER).
-
A Vege! üzenet a kapcsolódás végét jelentse.
-
Rendelkezzen alapszintű kivételkezeléssel, amely kimeneti-bemeneti és hálózati rutinok esetében egyébként is kötelező.
-
A hibalehetőségeket csökkentendő ne használjon nemzeti ékezetes karaktereket.
import java.net.*;
import java.io.*;
public class Kliens {
private Socket socket = null;
private DataInputStream inputStream = null;
private DataOutputStream outputStream = null;
public Kliens(String addressIP, int portNumber) {
try{
socket = new Socket(addressIP,
portNumber);
System.out.println("Szerverhez
kapcsolodva!");
inputStream = new
DataInputStream(System.in);
outputStream = new
DataOutputStream(socket.getOutputStream());
}
catch(UnknownHostException ukhe) {
System.out.println("Ismeretlen
szerver!" + "\n" + ukhe);
}
catch(IOException ioe) {
System.out.println("Kapcsolodasi vagy
egyeb adatolvasasi hiba!" + "\n" + ioe);
}
String line = "";
while (!line.equals("Vege!")) {
try{
line =
inputStream.readLine();
outputStream.writeUTF(line);
}
catch(IOException ioe) {
System.out.println("Kapcsolodasi vagy egyeb adatolvasasi hiba!" + "\n" + ioe);
}
}
try{
inputStream.close();
outputStream.close();
socket.close();
}
catch(IOException ioe) {
System.out.println("Hiba a
kapcsolodas lezarasakor!" + "\n" + ioe);
}
}
public static void main(String args[]) {
Kliens kliens = new Kliens("127.0.0.1", 1848);
}
}
Végeredmény:
A tesztelés során én mindenféle hibával szembesültem, amikor megpróbáltam váltogatni a Szerver és Kliens osztály Eclipse-n belüli, belső konzolai között, ezért a gyorsaság és fájdalommentesség érdekében az alábbi, biztonságosabb futtatási módot javaslom:
1.
Mindkét osztályt exportáljuk futtatható .jar állományként:
-
File - Export - Runnable JAR file
-
Launch configuration: Kliens vagy Szerver
-
Export destination: - az a könyvtár, ahová az állományokat menteni szeretnénk, illetve az állomány névadása is itt történik: Kliens.jar vagy Szerver.jar
2. Nyissuk meg operációs rendszerünk parancssori konzolját (itt Win10)...
...állítsuk be az állományokat tartalmazó könyvtárat (én az egyszerűség kedvéért mindent a C:\Users\User könyvtárba tettem, mert konzolomban ez volt alapértelmezett).
3. Adjuk ki a szervert indító parancsot (a sorrendre vigyázzunk, először a szerver indul!):
java -jar Szerver.jar
4. Adjuk ki a klienst indító parancsot:
java -jar Kliens.jar
5. Korrekt beállítások esetén létrejön a hálózati kapcsolat, a kliens begépelt karaktereket képes küldeni a szerver felé, amelyet az rögtön meg is jelenít. A Vege! üzenet hatására a szerver bontja a kapcsolatot.
Nézzük meg ezt a dinamikus folyamatot az alábbi 2 képernyőfotón, amelyekről jól leolvashatók a mindkétoldali lépések és egyéb kommunikációs üzenetek: