Network Sockets

De la WikiLabs
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

Generally, a network connection is established from a host called client to another host called server. For a client to connect to a server, it needs two pieces of information: the address of the server and the port on which the service application listens to. The class which implements the client functionality in Java is called java.net.Socket. Any object of type Socket, after instantiation, has two associated streams:

  • the InputStream from which you can read data coming from the other side of the connection, accessible by method Socket.getInputStream();
  • the OutputStream on which you can write data to the other side of the connection, accessible by method 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());
    }
}

}

Server Socket

The role of a server socket is to accept connections from one or more clients. The class that implements this behavior is java.net.ServerSocket. For a ServerSocket to function, it needs only one information: the port on which to listen to. To actually accept connections, the class contains a method called accept().

Attention: Method ServerSocket.accept() is blocking! That means that once the method called, the program is blocked until a client initiates a connection. Only in that moment will the accept() method return an object of type Socket associated with that client. At the same time, a client can not connect as long as the server isn't blocked in the method 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());
    }
}

}

You can check if this server is working by modifying the client above to connect to "localhost", on port 9000 and running both programs on the same computer, in different consoles.

In order to program a service to administer multiple client at the same time, it must be implemented using multiple execution threads, or otherwise said, by using multithreading.