Программирование сокетов на Java

 

Вы хотите разработать сетевую программу на Джаве – игрушку, чат, или то и другое вместе… Вы нашли правильную статью – здесь вы сможете ознакомиться с захватывающим миром сокетов в Java. Прочитав эту статью, вам будет виден свет в конце туннеля – станет очевидным предназначение сокетов и то, как разработать простую программу с использованием сокетов на языке программирования Джава.

Что такое сокет?

На сегодняшний день использование клиентов служб мгновенного обмена сообщениями (instant messanger) стало незаменимым средством для всех пользователей Интернета. Существует множество различных протоколов и клиентов (MSN, ICQ, Skype и т. д.), о которых каждый слышал и которые мы все ежедневно используем. Но не все знают, что положено в основу их роботы – собственно для этого статья и написана. Предположим, вы установили один из клиентов служб мгновенного обмена сообщениями на ваш компьютер. После его запуска и введения имени и пароля вашего пользователя, программа пытается подключиться к серверу. Что именно означает слово "подключиться"?

Каждый компьютер в сети имеет IP-адрес. Этот адрес похож на ваш домашний адрес – он однозначно идентифицирует ваш компьютер и позволяет другим общаться с вашим компьютером. Не будем вдаваться в подробности IP-адреса, так как эта статья не о них, хочу только отметить что IP-адрес это набор номеров разделенных точками (например, 64.104.137.158). Хотя существует и другой способ идентификации компьютеров в сети – доменное имя, которое более удобное и нагляднее идентифицирует компьютер, чем простой набор чисел (например, www.quizful.net). В Интернете существуют специальные компьютеры, которые осуществляют преобразование доменного имени в IP-адрес и наоборот.

На одном компьютере может параллельно исполняется несколько программ. Предположим, что вы запустили 10 программ на своем компьютере, и все они ожидают, чтоб другие компьютеры связались с ними. Можете представить себе это так: вас 10 человек в большом офисе с 1 телефоном и все ждут звонков от их собственных клиентов. Как вы это решите? Можно конечно назначить ответственного работника, и он будет приносить телефон соответственному человеку, которому звонят, но тогда другие клиенты не смогут дозвониться к другим людям. Кроме того, это очень трудно и нелепо иметь ответственного работника за маршрутизацию звонков к нужным людям. Вы должно быть, уже догадались, к чему я веду – если все эти программы, исполняющиеся на одном компьютере, с гордостью просят своих клиентов связаться с ними по определенному IP-адресу, то их клиенты не будут довольны. Идея состоит в следующем … иметь отдельный IP-адрес для каждой программы, верно? НЕ ВЕРНО! Суть вопроса не правильная – это также как спрашивать об отдельном офисе для каждого из вас. Ну, тогда … может отдельных телефонных номеров будет достаточно? ДА! На сетевом жаргоне "отдельные телефонные номера" имеют название порты. Порт – это просто число и каждая из программ, которая исполняется на определенном компьютере, может выбрать уникальное число порта, чтоб определить себя для внешнего мира. ЗАПОМНИТЕ – эти порты вы не сможете найти среди аппаратных средств компьютера (даже не старайтесь их искать). Эти числа – логические. Теперь все прояснилось: существует IP-адрес, с помощью которого другие компьютеры могут распознавать определенный компьютер в сети, и порт-число, которое определяет некую программу, работающую на компьютере. Также становиться понятным и то, что две программы на разных компьютерах могут использовать один и тот же порт (два дома на разных улицах тоже могут иметь один и тот самый номер, или нет?). Ну что же, мы практически у цели, только чтоб немного вас попугать, давайте выведем формулу:

IP-адрес = уникально определяет компьютер в сети.
Порт-число = уникально определяет программу, которая исполняется на соответствующем компьютере.

Если сложить вместе выше описанные уравнения, то получим:

IP-адрес + порт-число = _____

Другими словами:

_____ = уникально определяет программу в сети.

Если вы догадались до этого сами – значит мои усилия не пропали зря. Если нет, тогда прочитайте еще раз все сначала или воспользуйтесь Google для поиска лучшей статьи.

_____ – это и есть … СОКЕТ!

Подведем итог, сокет – это комбинация IP-адреса и порта. Сокет адрес надает возможность другим компьютерам в сети находить определенную программу, которая исполняется на определенном компьютере. Вы можете отображать сокет адрес вот так 64.104.137.58:80, где 64.104.137.58 – IP-адрес и 80 – порт.

Как программировать с использованием сокетов?

Достаточно о теории, давайте перейдем к действиям. Разработаем очень простой и наглядный код на Java, который продемонстрирует возможности использования сокетов. Попробуем реализовать следующий список действий:

1) Одна Джава программа будет пытаться связаться с другой Java программой (которая отчаянно ждет кого-то, чтоб с ней связался). Назовем первую программу Клиентом, а вторую Сервером.

2) После успешного связывания с сервером, клиент ждет ввода данных от вас и отсылает текст серверу.

3) Серверная программа отсылает клиенту назад тот ;t текст (для того чтоб показать, что она умеет делать даже такое полезное действие).

4) Полученный от сервера текст, клиент показывает вам, чтоб показать вам мнение сервера о вас. Приготовились приступить к разработке? Начнем. Отмечу только, что я не буду учить вас программированию на Java с чистого листа, а только объясню код, который относится к сокетам. Создайте 2 новых Джава программы и назовите их Server.java и Client.java. Я привел код ниже, только не пугайтесь, я все объясню.

Server.java


import java.net.*;
import java.io.*;
public class Server {
public static void main(String[] ar) {
int port = 6666; // случайный порт (может быть любое число от 1025 до 65535)
try {
ServerSocket ss = new ServerSocket(port); // создаем сокет сервера и привязываем его к вышеуказанному порту
System.out.println("Waiting for a client...");

Socket socket = ss.accept(); // заставляем сервер ждать подключений и выводим сообщение когда кто-то связался с сервером
System.out.println("Got a client :) ... Finally, someone saw me through all the cover!");
System.out.println();

// Берем входной и выходной потоки сокета, теперь можем получать и отсылать данные клиенту.
InputStream sin = socket.getInputStream();
OutputStream sout = socket.getOutputStream();

// Конвертируем потоки в другой тип, чтоб легче обрабатывать текстовые сообщения.
DataInputStream in = new DataInputStream(sin);
DataOutputStream out = new DataOutputStream(sout);

String line = null;
while(true) {
line = in.readUTF(); // ожидаем пока клиент пришлет строку текста.
System.out.println("The dumb client just sent me this line : " + line);
System.out.println("I'm sending it back...");
out.writeUTF(line); // отсылаем клиенту обратно ту самую строку текста.
out.flush(); // заставляем поток закончить передачу данных.
System.out.println("Waiting for the next line...");
System.out.println();
}
} catch(Exception x) { x.printStackTrace(); }
}
}

Client.java

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

public class Client {
public static void main(String[] ar) {
int serverPort = 6666; // здесь обязательно нужно указать порт к которому привязывается сервер.
String address = "127.0.0.1"; // это IP-адрес компьютера, где исполняется наша серверная программа.
// Здесь указан адрес того самого компьютера где будет исполняться и клиент.

try {
InetAddress ipAddress = InetAddress.getByName(address); // создаем объект который отображает вышеописанный IP-адрес.
System.out.println("Any of you heard of a socket with IP address " + address + " and port " + serverPort + "?");
Socket socket = new Socket(ipAddress, serverPort); // создаем сокет используя IP-адрес и порт сервера.
System.out.println("Yes! I just got hold of the program.");

// Берем входной и выходной потоки сокета, теперь можем получать и отсылать данные клиентом.
InputStream sin = socket.getInputStream();
OutputStream sout = socket.getOutputStream();

// Конвертируем потоки в другой тип, чтоб легче обрабатывать текстовые сообщения.
DataInputStream in = new DataInputStream(sin);
DataOutputStream out = new DataOutputStream(sout);

// Создаем поток для чтения с клавиатуры.
BufferedReader keyboard = new BufferedReader(new InputStreamReader(System.in));
String line = null;
System.out.println("Type in something and press enter. Will send it to the server and tell ya what it thinks.");
System.out.println();

while (true) {
line = keyboard.readLine(); // ждем пока пользователь введет что-то и нажмет кнопку Enter.
System.out.println("Sending this line to the server...");
out.writeUTF(line); // отсылаем введенную строку текста серверу.
out.flush(); // заставляем поток закончить передачу данных.
line = in.readUTF(); // ждем пока сервер отошлет строку текста.
System.out.println("The server was very polite. It sent me this : " + line);
System.out.println("Looks like the server is pleased with us. Go ahead and enter more lines.");
System.out.println();
}
} catch (Exception x) {
x.printStackTrace();
}
}
}

Теперь скомпилируем код:

javac Server.java Client.java

Откроем два командных окна (DOS). В одном окне введем:

java Server

а в другом:

java Client

Обязательно в таком порядке.

Теперь введите строку текста в окне, где запущен клиент, и нажмите кнопку Enter. Наблюдайте за двумя окнами и увидите что случиься. В конце, нажмите Ctrl-C для того чтоб остановить программы.

Объяснение кода работы с сокетами

Углубимся теперь немного в код. Вы уже уловили некоторые идеи, прочитав комментарии, но давайте проанализируем несколько критических строк кода.

Рассмотрим следующую часть кода сервера:

ServerSocket ss = new ServerSocket(port);
Socket socket = ss.accept();

Класс ServerSocket немного отличается от класса Socket. Класс Socket – это и есть сокет. Главное отличие ServerSocket заключается в том, что он умеет заставлять программу ждать подключений от клиентов. Когда вы его создаете, нужно указывать порт, с которым он будет работать, и вызвать его метод accept(). Этот метод заставляет программу ждать подключений по указанному порту. Исполнение программы зависает в этом месте, пока клиент не подключится. После успешного подключения клиентом, создается нормальный Socket объект, который вы можете использовать для выполнения все существующий операций с сокетом. Заметим также, что этот Socket объект отображает другой конец соединения. Если вы хотите отослать данные клиенту, то вы не можете использовать для этого ваш собственный сокет.

Следующим рассмотрим Socket класс. Вы можете создать Socket объект, указав IP-адрес и порт. Вы можете использовать InetAddress класс для отображения IP-адреса (этот способ более предпочтительный). Для создания InetAddress объекта используйте следующий метод:

InetAddress ipAddress = InetAddress.getByName(address);

Заметим, что в нашей программе мы использовали адрес 127.0.0.1. Это специальный адрес называется адрес замыкания – он просто отображает локальный компьютер. Если вы намерены запустить клиент и сервер на разных компьютерах, тогда нужно использовать соответственный IP-адрес сервера.

После того как мы создали InetAddress, то можно создать Socket:

Socket socket = new Socket(ipAddress, serverPort);

После создания Socket объекта, можно взять входной и выходной потоки сокета. Входной поток позволит вам читать с сокета, а выходной поток дает возможность писать в сокет.

InputStream sin = socket.getInputStream();
OutputStream sout = socket.getOutputStream();

Следующие строки просто конвертируют потоки в другие типы потоков. После этого нам легче будет работать с String объектами. Этот код ничего не делает с сетью.

DataInputStream in = new DataInputStream(sin);
DataOutputStream out = new DataOutputStream(sout);

Все остальное очень просто – простые манипуляции с объектами потоков (никаких сокетов). Вы можете использовать ваши любимые потоки, вызывать ваши любимые методы, и удостоверяться в передаче данных во второй конец. Если вы не очень осведомлены в использовании потоков, рекомендую вам найти статью о них и прочесть.

 

http://www.quizful.net/post/java-socket-programming