Создаем клиент-сервер на сокетах
Содержание
- Что такое сокеты?
- Создаем сервер
- Создаем клиент
- Замечания
Что такое сокеты?
Для начала давайте определим что такое сервер и клиент. Итак, сервер - это специальная программа, обычно запущенная на отдельном компьютере (хосте, от слова 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