Network Sockets

De la WikiLabs
Versiunea din 23 decembrie 2013 11:51, autor: Rhobincu (discuție | contribuții) (Pagină nouă: The Java language, through the API library, provides native support for network communication. The easiest way to communicate between two machines is by using [http://en.wikipedia.org...)
(dif) ← Versiunea anterioară | Versiunea curentă (dif) | Versiunea următoare → (dif)
Jump to navigationJump to search

The Java language, through the API library, provides native support for network communication. The easiest way to communicate between two machines is by using netowrk sockets. Sockets are network communication mechanisms (developed at The Berkeley University of California) which can use any communication protocol, although usually they're using the ones defined in the TCP/IP stack.

Any computer connected to the Internet needs to have a address by which it is identified, called an IP (internet protocol) address. A IPv4 address (version 4) is made up of 4 bytes and the external representation is done by writing the four bytes separated by dots (E.g.: 192.168.0.1). Besides an IP address, a computer can also have symbolic names, grouped in domains (E.g.: dcae.pub.ro), and the correspondence between IP addresses and symbolic names are stored in a distributed database, accessed through the domain name service (DNS).

In Java, classes and interfaces related to network programming are stored in package java.net. An IP address is represented through class InetAddress, which contains both the IP address (in numerical format) as well as the symbolic corresponding name. This class does not have a public constructor, so an object of this type can be created by calling some of the class's static methods: getByName(), getLocalHost() or getAllByName() which return the reference to the newly created object. For example, the prototype for method getByName() is:

public InetAddress getByName (String host) throws UnknownHostException;

The argument of the method (host), is a String representing the name of the machine as a symbolic name (E.g.: www.upb.ro) or as a textual representation of the IP address ("141.85.94.1").

Sockets can be used in one of the two possible ways:

  • unconnected sockets (UDP protocol);
  • connected sockets (TCP protocol).

Unconnected sockets send packets using the UDP protocol (User Datagram Protocol) between processes which don't have a permanent link.

Connected sockets first establish a connection between sockets belonging to different processes. Connected sockets use the TCP protocol (Transmission Control Protocol) which is a reliable protocol (with error checking and re-transmission), unlike UDP which is not reliable.

In order for multiple applications to access the network at the same time, packages for each application need to be separate. This is achieved by using communication ports. A port is a 16-bit number and every application uses a unique one to communicate. A connection has two ports, one on each side of connection. The client port is allocated by the operating system, when the connection is initiated, and the server port is constant and depends on the service provided. Each standard protocol has an associated port number according to the list maintained by Internet Assigned Numbers Authority. Examples are:

  • port 20 - File Transfer Protocol (data)
  • port 21 - File Transfer Protocol (control)
  • port 21 - Secure Shell (SSH)
  • port 25 - Simple Mail Transfer Protocol (SMTP)
  • port 80 - Hyper Text Transfer Protocol (HTTP)
  • etc.

Here you have the complete list.

In a Linux operating system, ports from 0 to 1023 are reserved by the kernel and are not accessible unless you have root privileges. In Linux, to find out what ports are opened (used), you can use the nmap command, if it's installed:

nmap arh.pub.ro

Client Sockets

În general, o conexiunea de rețea se realizează dinspre o stație (host) numită client, către o altă stație, numită server. Pentru ca un client să se poată conecta la un server, are nevoie de două informații: adresa serverului și portul pe care ascultă aplicația la care se dorește conectarea. Clasa care realizează acest lucru în Java se numește java.net.Socket. Orice obiect de tip Socket, după instanțiere, are asociate două stream-uri:

  • un InputStream de pe care se pot citi date venite de la celălalt capăt al conexiunii, accesibil cu metoda Socket.getInputStream();
  • un OutputStream pe care se pot scrie date pentru a fi trimise la celălalt capăt al conexiunii, accesibil cu metoda Socket.getOutputStream().
import java.net.*;
import java.io.*;

public class Client{

public static void main(String[] _args){
    try{
        //connect to arh.pub.ro on the HTTP port
        Socket _clientSocket = new Socket("arh.pub.ro", 80);
        OutputStream _outputStream = _clientSocket.getOutputStream();
        InputStream _inputStream = _clientSocket.getInputStream();

        _outputStream.write("GET /java.txt\n".getBytes());

        int _char;
        while((_char = _inputStream.read()) != -1){
            System.out.print((char)_char);
        }

        _clientSocket.close();
    }catch(IOException _ioe){
        System.out.println("Communication problem: " + _ioe.getMessage());
    }
}

}

Socket de server

Rolul unui socket de server este de a accepta conexiuni pe la unul sau mai multi clienți. Clasa care implementează acest comportament este java.net.ServerSocket. Pentru ca un ServerSocket să poată funcționa, are nevoie de o singură informație, și anume portul pe care să accepte conexiuni. Pentru a efectua o conectare concretă, clasa conține o metodă numită accept().

Atenție: Metoda ServerSocket.accept() este blocantă! Asta implică faptul că odată intrat în metodă, programul se blochează până în momentul în care un client încearcă să se conecteze. În acel moment abia, metoda accept() întoarce un obiect de tip Socket asociat acelei conexiuni. În același timp, un client nu se poate conecta atâta timp cât serverul nu este blocat în metoda accept().


import java.net.*;
import java.io.*;

public class Server{

public static void main(String[] _args){
    try{
        // start a new server on port 9000 (must be higher or equal to 1024)
        ServerSocket _serverSocket = new ServerSocket(9000);

        // listen to an incoming connection and creating a communication socket
        // accept() is blocking
        System.out.print("Listening for inc. connections... ");
        Socket _socket = _serverSocket.accept();
        System.out.println("connected!");

        // get the streams associated with the socket
        // output one always first
        OutputStream _outputStream = _socket.getOutputStream();
        InputStream _inputStream = _socket.getInputStream();

        // read the input char by char until newline char (\n)
        // we could do the same thing easier using an InputStreamReader
        // and a BufferedReader
        String _command = "";
        char _char;
        while((_char = (char)_inputStream.read()) != '\n'){
            _command = _command + _char;
        }

        // send a response depending on the received string 
        if(_command.equals("GET /java.txt")){
            _outputStream.write("Congratulations, you have received a message from a Java server!\n".getBytes());
        }else{
            _outputStream.write("Congratulations, you still received a message from a Java server, even though you sent the wrong text!\n".getBytes());
        }

        _socket.close();
        _serverSocket.close();
    }catch(IOException _ioe){
        System.out.println("Communication problem: " + _ioe.getMessage());
    }
}

}

Puteți verifica funcționarea acestui server modificând clientul pentru a se conecta la "localhost", pe portul 9000 și rulând ambele programe pe același calculator, în console diferite.

Pentru a putea programa un server care să administreze mai mulți clienți simultan, trebuie obligatoriu create mai multe fire de execuție (thread-uri).