Работа с сетью

В этой статье я рассмотрю методы работы с сетью, используя язык Java, а так же приведу теоретические выкладки по теории сетей (если такая есть).

Теория

Структура:
  • Стартовая строка (англ. Starting line) — определяет тип сообщения;
  • Заголовки (англ. Headers) — характеризуют тело сообщения, параметры передачи и прочие сведения;
  • Тело сообщения (англ. Message Body) — непосредственно данные сообщения. Обязательно должно отделяться от заголовков пустой строкой.

Протокол HTTP устроен следующим образом: клиент отправляет запрос, после чего сервер отправляет ответ клиенту. При этом клиент не может узнать список файлов в папке (как это можно сделать в ftp протоколе).
Отправляя запрос, клиент описывает себя через заголовок запроса, указывая свой язык, кодировку, браузер. Эти данные позволяют серверу правильно составить ответ. (см. ниже пример запроса от клиента)

GET /wiki/страница HTTP/1.1
Host: edunow.su
User-Agent: Mozilla/5.0 (X11; U; Linux i686; ru; rv:1.9b5) Gecko/2008050509 Firefox/3.0b5
Accept: text/html
Connection: close

В ответ, клиент получает header, в котором описывается сервер, и body, содержащий саму страницу, содержащую html размету (см. пример ответа от сервера).

HTTP/1.1 200 OK
Date: Wed, 11 Feb 2009 11:20:59 GMT
Server: Apache
X-Powered-By: PHP/5.2.4-2ubuntu5wm1
Last-Modified: Wed, 11 Feb 2009 11:20:59 GMT
Content-Language: ru
Content-Type: text/html; charset=utf-8
Content-Length: 1234
Connection: close
<html><body><a href="http://example.com/about.html#contacts">Click here</a></body></html>

Класс, который реализует вышеуказанные бесчинства, вы можете скачать с нашего сайта.

Готовый сниппет

Если вас интересует только готовый код, который позволит загрузить произвольную веб-страницу, то ниже написан именно он:

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URL;
import java.net.URLConnection;

public class Main {
    public static void main(String[] args) throws Exception{
        try{
            System.out.println(getPage("http://edunow.su"));
        }
        catch (IOException e){
            System.out.println(e.getMessage());
        }
    }

    /**
     * Загружает веб-страницу
     * @param page_url URL веб-страницы
     * @return исходный код страницы
     * @throws IOException
     */
    static String getPage(String page_url) throws IOException {
        URL url = new URL(page_url);
        URLConnection urlConnection = url.openConnection();
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(urlConnection.getInputStream()));

        StringBuilder lines = new StringBuilder();
        String line;
        while ( (line=bufferedReader.readLine()) != null){
            lines.append(line).append("
");
        }
        return lines.toString();
    }
}

Ну а теперь обратимся к более энциклопедическому повествованию, для этого стоит изучить java.net. О нем и пойдем речь дальше...

Описание Package java.net

Следующие классы обеспечивают реализацию сетевых приложений. Все packege можно разделить на две категории:
A Low Level API (низкоуровневый API), который имеет дело со следующими абстракциями:

  • Addresses, которые являются сетевыми идентификаторами, пример IP addresses
  • Sockets, которые являются основным двунаправленным механизмом передачи данных
  • Interfaces, которые описывают сетевые интерфейсы
A High Level API (высокоуровневый API), который имеет дело со следующими абстракциями:
  • URIs, которые представляют Universal Resource Identifiers. Как следует из названия, это просто идентификатор и не обеспечивает непосредственно средств для получения доступа к ресурсу.
  • URLs, представляют Universal Resource Locators, который является одновременно старше концепции URI, и среднее, чтобы получить доступ к ресурсам.
  • Connections, который представляет подключение к ресурсу, на который указывает URLs. Этот абстрактный класс делегирует большую часть работы к базовым обработчикам протоколов, таким как HTTP или FTP.
  • HttpURLConnection подкласс URLConnection, который предоставляет некоторые дополнительные функциональные возможности, характерные для протокола HTTP.

Low Level API (Низкоуровневый API)

Addresses

Addresses используются повсеместно в java.net APIs как идентификатор хостов или сокетов. InetAddress класс представляющий IP (Internet Protocol) адрес, у него существует два подкласса:

  • Inet4Address для IPv4 адресов
  • Inet6Address for IPv6 адресов
Но в большинстве случаев, нет нужны указывать конкретно subclasses, так как InetAddress абстракция покрывает все требуемую функциональность.

Sockets

Ниже описано приложение, состоящие из двух частей "сервера" (AppServer) и "клиента" (ClientApp). Вначале требуется запустить сервер (AppServer), который будет реагировать на обращение к указанному в настройках порту (в примере 9090), затем, каждый раз запуская "клиент" мы будем подключаться к порту и получать в ответ текущее время.

AppServer.java
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Date;

public class AppServer {
    /* описываем порт, который будем "слушать" */
    static final int PORT = 9090;
    public static void main(String[] args) throws Exception{
        System.out.println("сервер запущен");
        //
        ServerSocket listener = new ServerSocket(PORT);
        try {
            while (true) {
                Socket socket = listener.accept();
                try {
                    PrintWriter out =
                            new PrintWriter(socket.getOutputStream(), true);
                    out.println(new Date().toString());
                } finally {
                    socket.close();
                }
            }
        }
        finally {
            listener.close();
        }
    }
}
ClientApp.java
import java.io.*;
import java.net.Socket;
import java.net.UnknownHostException;

public class ClientApp {
    /* куда будем подключаться */
    static final String serverIP = "127.0.0.1";
    static final int PORT = 9090;
    public static void main(String[] args) {
        try {
            Socket s = new Socket(serverIP, PORT);
            BufferedReader input =
                    new BufferedReader(new InputStreamReader(s.getInputStream()));
            String answer = input.readLine();
            System.out.println(answer);
        }
        catch (UnknownHostException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

Пример приложения: "Чат"

AppServer.java
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashSet;

public class AppServer {
    private static final int PORT = 9001;
    private static HashSet names = new HashSet();
    private static HashSet writers = new HashSet();

    public static void main(String[] args) throws Exception {
        System.out.println("Чат-сервер запущен.");
        ServerSocket listener = new ServerSocket(PORT);
        try {
            while (true) {
                new Handler(listener.accept()).start();
            }
        } finally {
            listener.close();
        }
    }

    private static class Handler extends Thread {
        private String name;
        private Socket socket;
        private BufferedReader in;
        private PrintWriter out;

        public Handler(Socket socket) {
            this.socket = socket;
        }

        public void run() {
            try {

                in = new BufferedReader(new InputStreamReader(
                        socket.getInputStream()));
                out = new PrintWriter(socket.getOutputStream(), true);

                while (true) {
                    out.println("SUBMITNAME");
                    name = in.readLine();
                    if (name == null) {
                        return;
                    }
                    synchronized (names) {
                        if (!names.contains(name)) {
                            names.add(name);
                            System.out.println("New user " + name);
                            break;
                        }
                    }
                }

                out.println("NAMEACCEPTED");
                writers.add(out);

                while (true) {
                    String input = in.readLine();
                    if (input == null) {
                        return;
                    }
                    for (PrintWriter writer : writers) {
                        writer.println("MESSAGE " + name + ": " + input);
                    }
                }
            } catch (IOException e) {
                System.out.println(e);
            } finally {

                if (name != null) {
                    names.remove(name);
                }
                if (out != null) {
                    writers.remove(out);
                }
                try {
                    socket.close();
                } catch (IOException e) {
                }
            }
        }
    }
}
ClientApp.java
import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.*;
import java.net.Socket;
import java.net.UnknownHostException;

public class ClientApp {
    BufferedReader in;
    PrintWriter out;
    JFrame frame = new JFrame("ChatApp");
    JTextField textField = new JTextField(40);
    JTextArea messageArea = new JTextArea(8, 40);

    public ClientApp() {

        // GUI
        textField.setEditable(false);
        messageArea.setEditable(false);
        frame.getContentPane().add(textField, "North");
        frame.getContentPane().add(new JScrollPane(messageArea), "Center");
        frame.pack();

        // Add Listeners
        textField.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                out.println(textField.getText());
                textField.setText("");
            }
        });
    }

    private String getServerAddress() {
        return JOptionPane.showInputDialog(
                frame,
                "Enter IP Address of the Server:",
                "Welcome to the Chatter",
                JOptionPane.QUESTION_MESSAGE);
    }

    private String getName() {
        return JOptionPane.showInputDialog(
                frame,
                "Choose a screen name:",
                "Screen name selection",
                JOptionPane.PLAIN_MESSAGE);
    }

    private void run() throws IOException {

        // Make connection and initialize streams
        String serverAddress = getServerAddress();
        Socket socket = new Socket(serverAddress, 9001);
        in = new BufferedReader(new InputStreamReader(
                socket.getInputStream()));
        out = new PrintWriter(socket.getOutputStream(), true);

        // Process all messages from server, according to the protocol.
        while (true) {
            String line = in.readLine();
            if (line.startsWith("SUBMITNAME")) {
                out.println(getName());
            } else if (line.startsWith("NAMEACCEPTED")) {
                textField.setEditable(true);
            } else if (line.startsWith("MESSAGE")) {
                messageArea.append(line.substring(8) + "
");
            }
        }
    }

    public static void main(String[] args) throws Exception {
        ClientApp client = new ClientApp();
        client.frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        client.frame.setVisible(true);
        client.run();
    }
}

Interfaces

Класс NetworkInterface обеспечивает APIs функциональность всех сетевых интерфейсов (например Подключение к сети Ethernet или конечная точка PPP) к локальным компьютерам. Именно через этот класс вы можете проверить поддержку IPv6 у компьютера.

High Level API (Высокоуровневый API)

URI

Класс позволяет "разобрать" url на составные части.

URIDemo.java
import java.net.URI;
public class URIDemo {
    public static void main(String[] args) throws Exception {
        String path = "http://edunow.su/site/content?cat=1";
        URI uri = new URI(path);
        //
        System.out.println("Input = " + path + "
");
        System.out.println("Scheme = " + uri.getScheme());
        System.out.println("Host = " + uri.getHost());
        System.out.println("Path = " + uri.getPath());
        System.out.println("Query = " + uri.getQuery());
    }
}
Output
Input = http://edunow.su/site/content?cat=1

Scheme = http
Host = edunow.su
Path = /site/content
Query = cat=1

URL

Именно "на базе" URL мы можем открыть соединение с удаленным сервером. Для этого следует вызвать openConnection() или openStream(). Можно так же отметить, что URLConnection поддерживает Proxy.

URLDemo.java
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;
public class URLDemo {
    public static void main(String[] args) throws IOException {
        String path = "http://edunow.su/site/content?cat=1";
        URL url = new URL(path);
        //
        URLConnection urlConnection = url.openConnection();
        InputStream inputStream = url.openStream();
    }
}
Пример использования proxy: ProxyDemo .java
import java.io.BufferedReader;
        import java.io.IOException;
        import java.io.InputStreamReader;
        import java.net.InetSocketAddress;
        import java.net.Proxy;
        import java.net.URL;
        import java.net.URLConnection;
public class ProxyDemo {
    public static void main(String[] args) throws IOException {
        String path = "http://2ip.ru/";
        URL url = new URL(path);
        //
        Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress("192.227.172.145", 8089));
        URLConnection conn = new URL(path).openConnection(proxy);
        //
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(conn.getInputStream()));
        StringBuilder lines = new StringBuilder();
        String line;
        while ( (line=bufferedReader.readLine()) != null){
            lines.append(line).append("
");
        }
        System.out.println(lines.toString());
    }
}

Connection

URLConnection позволяет взаимодействовать с удаленным сервером.

URLConnectionDemo .java
import java.io.*;
import java.net.URL;
import java.net.URLConnection;

public class URLConnectionDemo {
    public static void main(String[] args) throws IOException {
        String path = "http://edunow.su/";
        String charset = "UTF-8";
        //
        URLConnection connection = new URL(path).openConnection();
        connection.setDoOutput(true); // Triggers POST.
        connection.setRequestProperty("Accept-Charset", charset);
        connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded;charset=" + charset);
        connection.setRequestProperty("User-Agent", "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.2.3) Gecko/20100401"); // Do as if you're using Firefox 3.6.3.

        InputStream response = connection.getInputStream();
        // ...
    }
}

Пример: Upload File

Upload File
String param = "value";
File textFile = new File("/path/to/file.txt");
File binaryFile = new File("/path/to/file.bin");
String boundary = Long.toHexString(System.currentTimeMillis()); // Just generate some unique random value.
String CRLF = "
"; // Line separator required by multipart/form-data.
URLConnection connection = new URL(url).openConnection();
connection.setDoOutput(true);
connection.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundary);

try (OutputStream output = connection.getOutputStream()) {
    try (PrintWriter writer = new PrintWriter(new OutputStreamWriter(output, charset), true)) {
        // Send normal param.
        writer.append("--" + boundary).append(CRLF);
        writer.append("Content-Disposition: form-data; name="param"").append(CRLF);
        writer.append("Content-Type: text/plain; charset=" + charset).append(CRLF);
        writer.append(CRLF).append(param).append(CRLF).flush();

        // Send text file.
        writer.append("--" + boundary).append(CRLF);
        writer.append("Content-Disposition: form-data; name="textFile"; filename="" + textFile.getName() + """).append(CRLF);
        writer.append("Content-Type: text/plain; charset=" + charset).append(CRLF); // Text file itself must be saved in this charset!
        writer.append(CRLF).flush();
        Files.copy(textFile.toPath(), output);
        output.flush(); // Important before continuing with writer!
        writer.append(CRLF).flush(); // CRLF is important! It indicates end of boundary.

        // Send binary file.
        writer.append("--" + boundary).append(CRLF);
        writer.append("Content-Disposition: form-data; name="binaryFile"; filename="" + binaryFile.getName() + """).append(CRLF);
        writer.append("Content-Type: " + URLConnection.guessContentTypeFromName(binaryFile.getName())).append(CRLF);
        writer.append("Content-Transfer-Encoding: binary").append(CRLF);
        writer.append(CRLF).flush();
        Files.copy(binaryFile.toPath(), output);
        output.flush(); // Important before continuing with writer!
        writer.append(CRLF).flush(); // CRLF is important! It indicates end of boundary.

        // End of multipart/form-data.
        writer.append("--" + boundary + "--").append(CRLF);
    }
}

HttpURLConnection

HttpURLConnection connection = null;
    try {
        URL url = new URL("www.google.com");
        connection = (HttpURLConnection) url.openConnection();
        connection.connect();
        connection.getInputStream();
                    // do something with the input stream here

    } catch (MalformedURLException e1) {
        e1.printStackTrace();
    } catch (IOException e1) {
        e1.printStackTrace();
    } finally {
        if(null != connection) { connection.disconnect(); }
    }

Дополнительные материалы

↑ Расскажите друзьям о статье


Comments system Cackle

© EduNow.su — материалы подлежат полному/частичному копированию при указании прямой ссылки на источник. (Сегодня 29.06.17)