Создаем клиент-сервер на сокетах
Содержание
Что такое сокеты?
Для начала давайте определим что такое сервер и клиент. Итак, сервер - это специальная программа, обычно запущенная на отдельном компьютере (хосте, от слова host(eng.) - хозяин), и выполняющая некий круг задач. Клиент, в свою очередь - программа, которая запрашивает сервер выполнить то или иное действие (задачу) и вернуть полученные данные клиенту. На хосте для работы сервера обычно выделяется порт (port). К этому порту и должен будет обращаться клиент. Клиент для связи с портом хоста, который соединен в свою очередь с нужным сервером (программой), создает сокет.
В целом алгоритм работы системы клиент-сервер выглядит следующим образом:
- Сервер подключается к порту на хосте и ждет соединения с клиентом;
- Клиент создает сокет и пытается соединить его с портом на хосте;
- Если создание сокета прошло успешно, то сервер переходит в режим ожидания команд от клиента;
- Клиент формирует команду и передает ее серверу, переходит в режим ожидания ответа;
- Сервер принимает команду, выполнеят ее и пересылает ответ клиенту.
- и т.д.
Теперь попробуем создать сервер и клиент.
Создаем сервер
Для создания сокетов и управления ими в Java есть специальные классы java.net.Socket и java.net.ServerSocket. Первый для клиента, второй для сервера. Так же нам будут необходимы два класса из пакета java.io.*: BufferedReader и PrintWriter для чтения/записи в сокет (думаю, что читатель уже знаком с этими классами).
Для начала подключимся к порту хоста. Сделать это можно с помощью конструктора класса ServerSocket. Обратите внимание, что конструктор выбрасывает исключение типа IOException, т.е. нам понадобится блок try - catch:
ServerSocket servers; try { servers = new ServerSocket(4444); } catch (IOException e) { System.out.println("Couldn't listen to port 4444"); System.exit(-1); }
После успешного подключения к порту сервер должен ждать подключения от клиента. Сделать это можно так:
Socket fromclient; try { System.out.print("Waiting for a client..."); fromclient= servers.accept(); System.out.println("Client connected"); } catch (IOException e) { System.out.println("Can't accept"); System.exit(-1); }
Рассмотрим этот блок подробнее. Метод servers.accept() позволяет серверу следить за портом, или иначе говоря ждать подключения клиента. Как только клиент подключается - сокет для клиента сразу же создается. В противном случае выбрасывается исключение IOException.
Как только клиент подключился к серверу, сервер должен создать потоки ввода и вывода для связи с ним. Это можно сделать следующим образом:
BufferedReader in; PrintWriter out; in=new BufferedReader(new InputStreamReader(fromclient.getInputStream())); out = new PrintWriter(fromclient.getOutputStream(),true);
И далее можно просто считывать данные из потока in и записывать данные в out.
Исходный код сервера:
import java.io.*; import java.net.*; public class Server { public static void main(String[] args) throws IOException { System.out.println("Welcome to Server side"); BufferedReader in = null; PrintWriter out= null; ServerSocket servers = null; Socket fromclient = null; // create server socket try { servers = new ServerSocket(4444); } catch (IOException e) { System.out.println("Couldn't listen to port 4444"); System.exit(-1); } try { System.out.print("Waiting for a client..."); fromclient= servers.accept(); System.out.println("Client connected"); } catch (IOException e) { System.out.println("Can't accept"); System.exit(-1); } in = new BufferedReader(new InputStreamReader(fromclient.getInputStream())); out = new PrintWriter(fromclient.getOutputStream(),true); String input,output; System.out.println("Wait for messages"); while ((input = in.readLine()) != null) { if (input.equalsIgnoreCase("exit")) break; out.println("S ::: "+input); System.out.println(input); } out.close(); in.close(); fromclient.close(); servers.close(); } }
Создаем клиент
Для создания клиента достаточно класса Socket и двух классов для ввода/вывода (см. Создаем сервер). Также необходимо знать имя компьютера (хоста), на котором запущен сервер и номер порта.
Конструктор сокета имеет два параметра: имя хоста и номер порта. Опять таки, конструктор выбрасывает исключение типа IOException.
Socket fromserver = null; fromserver = new Socket("localhost",4444);
Далее аналогичным образом, как для сервера, создаем потоки ввода вывода. И можно записывать/считывать данные.
Исходный код клиента:
import java.io.*; import java.net.*; public class client { public static void main(String[] args) throws IOException { System.out.println("Welcome to Client side"); Socket fromserver = null; if (args.length==0) { System.out.println("use: client hostname"); System.exit(-1); } System.out.println("Connecting to... "+args[0]); fromserver = new Socket(args[0],4444); BufferedReader in = new BufferedReader(new InputStreamReader(fromserver.getInputStream())); PrintWriter out = new PrintWriter(fromserver.getOutputStream(),true); BufferedReader inu = new BufferedReader(new InputStreamReader(System.in)); String fuser,fserver; while ((fuser = inu.readLine())!=null) { out.println(fuser); fserver = in.readLine(); System.out.println(fserver); if (fuser.equalsIgnoreCase("close")) break; if (fuser.equalsIgnoreCase("exit")) break; } out.close(); in.close(); inu.close(); fromserver.close(); } }
Замечания
Клиент-сервер организован на примере Echo server (Эхо сервер). Клиент получает обратно строку переданную серверу.Несколько замечаний по кодам:
Обратите внимание на конструкцию методов main :
public static void main(String[] args) throws IOException {}
throws IOException позволит не использовать блоки try-catch для ловки исключения IOException в самом методе, что достаточно удобно.
В клиентской части используется параметр командной строки для указания имени хоста. Например, если Вы запускаете сервер и клиент на одном компьютере, то клиент надо запускать так:
java client localhost