28 мая 2009 г.

Сокеты на JAVA(Сервер-клиент с исходным кодом)

Создаем клиент-сервер на сокетах

Содержание

  • Что такое сокеты?
  • Создаем сервер
  • Создаем клиент
  • Замечания
  • Что такое сокеты?

    Для начала давайте определим что такое сервер и клиент. Итак, сервер - это специальная программа, обычно запущенная на отдельном компьютере (хосте, от слова host(eng.) - хозяин), и выполняющая некий круг задач. Клиент, в свою очередь - программа, которая запрашивает сервер выполнить то или иное действие (задачу) и вернуть полученные данные клиенту. На хосте для работы сервера обычно выделяется порт (port). К этому порту и должен будет обращаться клиент. Клиент для связи с портом хоста, который соединен в свою очередь с нужным сервером (программой), создает сокет.

    В целом алгоритм работы системы клиент-сервер выглядит следующим образом:

    1. Сервер подключается к порту на хосте и ждет соединения с клиентом;
    2. Клиент создает сокет и пытается соединить его с портом на хосте;
    3. Если создание сокета прошло успешно, то сервер переходит в режим ожидания команд от клиента;
    4. Клиент формирует команду и передает ее серверу, переходит в режим ожидания ответа;
    5. Сервер принимает команду, выполнеят ее и пересылает ответ клиенту.
    6. и т.д.

    Теперь попробуем создать сервер и клиент.

    Создаем сервер

    Для создания сокетов и управления ими в 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