Сокеты

Введение

Сокет — это программные интерфейс, обеспечивающий обмен данными между процессами. Процессы могут выполняться как на одном компьютере, так и на различных, связанных сетью. Сокеты бывают серверными и клиентскими. Главное их различие в том, что серверный сокет прослушивает выделенный ему порт в ожидании новых клиентов и может работать сразу с несколькими клиентскими сокетами. Инициатором соединения всегда является клиентский сокет. Сокет является основой множества сетевых протоколов, в том числе http/https, ftp, websockets, pop3, smtp, ssh и др.

В пакете java.net есть классы Socket и ServerSocket для создания клиентского и серверного сокетного соединения соответственно.

Клиентский сокет

Класс Socket имеет несколько конструкторов. Рассмотрю пару распространенных:

                        public Socket(String host, int port);
                        public Socket(InetAddress inetAddress, int port);
                

В первый аргумент первого конструктора передаем ip-адрес или DNS-имя машины, с которой хотим осуществить соединение. Второй аргумент - номер порта, к которому будем осуществлять подключение. Во втором конструкторе можем использовать объект класса InetAddress, который обеспечивает работу с DNS-именами и ip-адресами. Вот несколько примеров:

                        Socket s1 = new Socket("icq.com",5190);
                        Socket s2 = new Socket(InetAddress.getByName("icq.com"),5190);

                        byte[] address = new byte[]{(byte)178,(byte)237,23,(byte)237};
                        Socket s3 = new Socket(InetAddress.getByAddress(address), 5190);
                

Разработчику дана возможность управлять таймаутом соединения с помощью метода setSoTimeout(). Если в течении указанного времени с сокетом не произошли никакие действия, то он автоматически закрывается. Запретить автоматическое закрытие сокета можно установив таймаут равным 0.

                        public void setSoTimeout(int timeout)
                

Серверный сокет

Он создается также легко, как и клиентский.

                    public ServerSocket(int port);
                

Этот конструктор создает серверный сокет с прослушкой указанного порта. Важная часть работы с серверным сокетом - метод accept(), вызов которого возвращает сокет, связанный с клиентским. Сразу пример для лучшего понимания:

                    ServerSocket serverSocket = new ServerSocket(5190);//создали серверный сокет
                    Socket socket = serverSocket.accept();//метод accept() ждет клиентов
                

Метод accept() заставляет серверный сокет ждать клиентов и, как только кто-то подключился, возвращает связанный с клиентом сокет в переменную socket. Дальше можно начинать общение клиента с сервером.

Клиент-серверное общение

Работая с экземплярами класса Socket, мы можем получить доступ к входящему и исходящему потокам сокета методами getInputStream() и getOutputStream() соответственно. Сразу приведу пример клиент-серверной связки.

Исходный код сервера:

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

public class ServerSocketExample{

    ServerSocketExample() {
        try {
            ServerSocket ss = new ServerSocket(5524);//Создаем серверный сокет
            Socket s = ss.accept();//Слушаем подключения
            InputStream is = s.getInputStream();//Получаем ссылку на входящий поток
            InputStreamReader isr = new InputStreamReader(is);//Оборачиваем ее в ридер
            BufferedReader br = new BufferedReader(isr);//Оборачиваем ридер в буфер
            String messageFromClient = br.readLine();//Читаем строку
            System.out.println("Сообщение от клиента: " + messageFromClient);
        } catch (IOException ex) {
            System.err.println("Ошибка ввода-вывода на сервере");
        }
    }

    public static void main(String[] args) {
        new ServerSocketExample();
    }
}
                

Исходный код клиента:

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

public class ClientSocketExample() {
    
    ClientSocketExample(){
        Socket s = null;
        try {
            s = new Socket("localhost", 5524);//Подключаемся к серверу
            OutputStream os = s.getOutputStream();//Получаем ссылку на исходящий поток
            OutputStreamWriter osw = new OutputStreamWriter(os);//Оборачиваем ее в писателя
            BufferedWriter bw = new BufferedWriter(osw);//Оборачиваем писателя в буфер
            bw.write("Клиентский сокет установил соединение с сервером\n\t");//Пишем строку
            bw.flush();//Проталкиваем данные из буфера непосредственно в поток
        } catch (UnknownHostException ex) {
            System.out.println("Хост не найден");
        } catch (IOException ex) {
            System.err.println("Ошибка ввода-вывода на клиенте");
        }
        finally{//В случае неудачи необходимо корректно закрыть сокет
            if(s!=null){
                try {
                    s.close();
                } catch (IOException ex) {
                    System.out.println("Ошибка при закрытии клиентского сокета");
                }
            }
        }
    }

    public static void main(String[] args) {
        new ClientSocketExample();
    }
}
                

Код прокомментирован, но, думаю, стоит еще немного добавить. В серверном и клиентском примерах мы оборачивали поток сначала для чтения/записи тектовых данных, затем делали поток буферизованным. Буферизация нужна для того, чтобы поток постоянно не обращался к операционной системе за новыми данными, а сразу бы имел достаточное их количество у себя. Что касается чтения текстовых данных, то мы это обеспечили обертками InputStreamReader и OutputStreamWriter. Если необходимо читать бинарные данные, то лучше потоки оборачивать в BufferedInputStream и BufferedOutputStream.

Заключение

Эта статья всего лишь знакомит читателя с работой с сокетами на языке java. Следующим шагом для реализации клиент-серверных решений будет изучение библиотеки NIO, распололженной в пакете java.nio. С ее помощью можно достичь большего быстродействия, чем с сокетами.

Сайт создан в системе uCoz