Вариация приведенной выше программы, читающей файл, расположенный на сервере. В этом случае файл указывается клиентом:
//: c15:Fetcher.java
// <applet code=Fetcher width=500 height=300>
// </applet>
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.net.*;
import java.io.*;
import com.bruceeckel.swing.*;
public class Fetcher extends JApplet {
JButton fetchIt = new JButton("Fetch the Data");
JTextField f = new JTextField("Fetcher.java", 20);
JTextArea t = new JTextArea(10, 40);
public void init() {
Container cp = getContentPane();
cp.setLayout(new FlowLayout());
fetchIt.addActionListener(new FetchL());
cp.add(new JScrollPane(t));
cp.add(f);
cp.add(fetchIt);
}
public class FetchL implements ActionListener {
public void actionPerformed(ActionEvent e) {
try {
URL url = new URL(getDocumentBase(), f.getText());
t.setText(url + "n");
InputStream is = url.openStream();
BufferedReader in = new BufferedReader(
new InputStreamReader(is));
String line;
while ((line = in.readLine()) != null)
t.append(line + "n");
}
catch (Exception ex) {
t.append(ex.toString());
}
}
}
public static void main(String[] args) {
Console.run(new Fetcher(), 500, 300);
}
} // /:~
Создание объекта URL похоже на предыдущий пример -- getDocumentBase( ) является начальной точкой, как и прежде, но в то же время, имя файла читается из JTextField. Как только объект URL создан, его строковая версия помещается в JTextArea, так что вы можем видеть, как он выглядит. Затем из URL'а получается InputStream, который в данном случае может просто производить поток символов из файла. После конвертации в Reader и буферизации, каждая строка читается и добавляется в JTextArea. Обратите внимание, что JTextArea помещается внутрь JScrollPane, так что скроллирование обрабатывается автоматически.
Мультиплексирование, Основанное на Переключении в JDK 1.4
Когда вы читаете из сокета или пишете в него, вам нужно сделать передачу данных рациональной. Давайте рассмотрим сначала операцию записи. Когда вы пишите данные на уровне приложения (TCP или UDP сокет), вы пишите данные в рабочий буфер системы. Эти данные, в конечном счете, формируют (TCP или UDP) пакеты, которые необходимо передать на машину назначения по сети. Когда вы пишите в сокет и, если в буфере нет достаточно доступного места, запись может блокироваться. Если вы читаете из сокета и нет достаточного количества информации для чтения из буфера операционной системы, куда попадают данные после получения из сети, чтение будет блокировано. Если есть нить (поток) для операции чтения или записи, эта нить не может делать ничего и может стать причиной снижения произовдительности вашей программы. До появления JDK 1.4 не было способа вывести такую нить из заблокированного состояния. С помощью каналов вы можете выполнить асинхронную операцию закрытия на канале и нить, блокированная на этом канале примет AsynchronousCloseException.
Асинхронный ввод-вывод в Java достигается тем же способом, который дает вызов метода select( ) в UNIX подобных системах. Вы можете дать список дескрипторов (чтения или записи) в функцию select( ) и она отследит этот дескриптор на возникновение некоторых событий. Для дескриптора, представляющего сокет, из которого вы читаете, данные в буфере операционной системы для этого буфера представляются событием. Для дескрипторов, представляющих сокеты, в которые вы пишите, наличие места для записи во внутреннем буфере операционной системы для этого сокета представляется событием. Поэтому вызов метода select( ) исследует различные дескрипторы для проверки событий.
Что, если вы просто читаете и пишите в дескриптор когда бы вы не захотели? Select может обрабатывать множество дескрипторов, что позволит вам мониторить множество сокетов. Рассмотрим пример чат-сервера, когда сервер имеет соединения с различными клиентами. Тип данных, достигающих сервера, перемежается. Сервер предназначен для чтения данных из сокета и отображения их в GUI, то есть для показа каждому клиенту - чтобы достич этого, вы читаете данные от каджого клиента и пишите эти данные всем остальным клиентам. Например 5 клиентов: 1, 2, 3, 4 и 5. Если сервер запрограммирован на выполнение чтения от 1 и записи в 2, 3, 4 и 5, затем происходит чтения от 2 и запись в 1, 3, 4, 5 и так далее, то может так случиться, что пока нить сервера заблокирована на чтении одного из клиентских сокетов, могут появиться данные на других сокетах. Одно из решений состоит в том, чтобы создавать различные нити для кадого клиента (до JDK1.4). Но это не масштабируемое решение. Вместо этого вы можете иметь селектор, основанный на механизме, следящем за всеми клиентскими сокетами. Он знает какой сокет имеет данные для чтения без блокирования. Но если единственная нить выполняет эту работу (выбор и запись каждому клиенту) он не будет хорошо откликаться. Таким образом в таких ситуациях одна нить мониторит сокеты на чтение, выбирает сокет, из которого можно осуществить чтение, и делегирует остальную ответственность (запись другим клиентам) другой нити (нитям) или пулу нитей.
Этот шаблон называется шаблоном реактора, когда события отсоединяются от действия, ассоциированного с событиями (Pattern Oriented Software Architecture - Doug Schmidt).
В JDK 1.4 вы создаете канал, регестрируете объект Селектора в канале, который (объект) будет следить за событиями в канале. Многие каналы регестрируют один и тот же объект Селектора. Единственная нить, которая вызывает Selector.select(), наблюдает множество каналов. Каждый из классов ServerSocket, Socket и DatagramSocket имеют метод getChannel( ), но он возвращает null за исключением того случая, когда канал создается с помощью вызова метода open( ) (DatagramChannel.open( ), SocketChannel.open( ), ServerSocketChannel.open( )). Вам необходимо ассоциировать сокет с этим каналом.
Вы мультиплексируете несколько каналов (то есть сокеты), используя Селектор. Статический вызов Selector.select( ) блокирует выполнение до возникновения события в одном из каналов. Существует так же и не блокирующая версия этого метода, которая принимает количество милисекунд для засыпания или блокирования до того момента, когда вызов метода завершится.
ByteBuffer используется для копирования данных из канала и в канал. ByteBuffer является потоком октетов и вы декодируете этот поток, как символы. Со стороны клиента в MultiJabberClient.java это выполняется путем использования классов Writer'а и OutputStreamWriter'а. Эти классы конвертируют символы в поток байтов.
Приведенная ниже программа NonBlockingIO.java объясняет, как вы можете использовать Селектор и Канал для выполнения мультиплексирования. Эта программа требует запущенного Сервера. Она может стать причиной исключения на сервере, но ее назначение не в коммуникации с сервером, а в том, чтобы показать, как работает select( ).
//: TIEJ:X1:NonBlockingIO.java
// Сокет и Селектор сконфигурированы для не блокированного
// Соединения с JabberServer.java
// {RunByHand}
import java.net.*;
import java.nio.channels.*;
import java.util.*;
import java.io.*;
/**
* Цель: Показать как использовать селектор. Нет чтения/записи, просто
* показывается готовность к совершению операции.
*
* Алгоритм: -> Создаем селектор. -> Создаем канал -> Связываем сокет,
* ассоциированный с каналом, с <клиентским портом> -> Конфигурируем канал, как
* не блокирующий -> Регестрируем канал в селекторе. -> Вызываем метод select( ),
* чтобы он блокировал выполнение до тех пор, пока канал не будет готов. (как
* это предполагается методом select(long timeout) -> Получаем множество ключей,
* относящихся к готовому каналу для работы, основной интерес состоит в том,
* когда они зарегестрированя с помощью селектора. -> Перебираем ключи. -> Для
* каждого ключа проверяем, что соответствующий канал готов к работе, в которой
* он заинтересован. -> Если он готов, печатаем сообщение о готовности.
*
* Примечание: -> Необходим запущенный MultiJabberServer на локальной машине. Вы
* запускаете его и соединяетесь с локальным MultiJabberServer -> Он может стать
* причиной исключения в MultiJabberServer, но это исключение ожидаемо.
*/
public class NonBlockingIO {
public static void main(String[] args) throws IOException {
if (args.length < 2) {
System.out.println("Usage: java <client port> <local server port>");
System.exit(1);
}
int cPort = Integer.parseInt(args[0]);
int sPort = Integer.parseInt(args[1]);
SocketChannel ch = SocketChannel.open();
Selector sel = Selector.open();
try {
ch.socket().bind(new InetSocketAddress(cPort));
ch.configureBlocking(false);
// Канал заинтересован в выполнении чтения/записи/соединении
ch.register(sel, SelectionKey.OP_READ | SelectionKey.OP_WRITE
| SelectionKey.OP_CONNECT);
// Разблокируем, когда готовы к чтению/записи/соединению
sel.select();
// Ключи, относящиеся к готовому каналу, канал заинтересован
// в работе, которая может быть выполненаin can be
// без блокирования.
Iterator it = sel.selectedKeys().iterator();
while (it.hasNext()) {
SelectionKey key = (SelectionKey) it.next();
it.remove();
// Если связанный с ключом канал готов к соединению?
// if((key.readyOps() & SelectionKey.OP_CONNECT) != 0) {
if (key.isConnectable()) {
InetAddress ad = InetAddress.getLocalHost();
System.out.println("Connect will not block");
// Вы должны проверить возвращаемое значение,
// чтобы убедиться, что он соединен. Этот не блокированный
// вызов может вернуться без соединения, когда
// нет сервера, к которому вы пробуете подключиться
// Поэтому вы вызываете finishConnect(), который завершает
// операцию соединения.
if (!ch.connect(new InetSocketAddress(ad, sPort)))
ch.finishConnect();
}
// Если канал, связанный с ключом, готов к чтению?
// if((key.readyOps() & SelectionKey.OP_READ) != 0)
if (key.isReadable())
System.out.println("Read will not block");
// Готов ли канал, связанный с ключом, к записи?
// if((key.readyOps() & SelectionKey.OP_WRITE) != 0)
if (key.isWritable())
System.out.println("Write will not block");
}
}
finally {
ch.close();
sel.close();
}
}
} // /:~
Как указано выше, вам необходимо создать канал, используя вызов метода open( ). SocketChannel.open( ) создает канал. Так как он наследован от AbstractSelectableChannel (DatagramChannel и SocketChannel), он имеет функциональность для регистрации себя в селекторе. Вызов метода регистрации совершает это. В качестве аргумента он принимает Селектор для регистрации канала, и события, которые интересны для этого канала. Здесь показано, что SocketChannel заинтересован в соединении, чтении и записи - поэтому в вызове метода регистрации указано SelectionKey.OP_CONNECT, SelectionKey.OP_READ и SelectionKey.OP_WRITE наряду с Селектором.
Статический вызов метода Selector.select( ) наблюдает все каналы, зарегистрированные в нем, относительно тех событий, которые указаны (второй аргумент при регистрации). Вы можете иметь каналы, заинтересованные в нескольких событиях.
Следующий пример работает так же, как и JabberClient1.java, но использует Селектор.
//: TIEJ:X1:JabberClient1.java
// Очень простой клиент, которй просто посылает строки на сервер
// и читает строки, посылаемые сервером.
// {RunByHand}
import java.net.*;
import java.util.*;
import java.io.*;
import java.nio.*;
import java.nio.channels.*;
import java.nio.charset.*;
public class JabberClient1 {
public static void main(String[] args) throws IOException {
if (args.length < 1) {
System.out.println("Usage: java JabberClient1 <client-port>");
System.exit(1);
}
int clPrt = Integer.parseInt(args[0]);
SocketChannel sc = SocketChannel.open();
Selector sel = Selector.open();
try {
sc.configureBlocking(false);
sc.socket().bind(new InetSocketAddress(clPrt));
sc.register(sel, SelectionKey.OP_READ | SelectionKey.OP_WRITE
| SelectionKey.OP_CONNECT);
int i = 0;
// По причине ассинхронной природы, вы не знаете
// когда чтение и запись закончены, поэтому вам необходимо
// следить за этим, переменная boolean written используется для
// переключения между чтением и записью. Во время записи
// отосланные назад символы должны быть прочитаны.
// Переменная boolean done используется для проверки, когда нужно
// прервать цикл.
boolean written = false, done = false;
// JabberServer.java, которому этот клиент подсоединяется, пишет с
// помощью
// BufferedWriter.println(). Этот метод выполняет
// перекодировку в соответствии с кодовой страницей по умолчанию
String encoding = System.getProperty("file.encoding");
Charset cs = Charset.forName(encoding);
ByteBuffer buf = ByteBuffer.allocate(16);
while (!done) {
sel.select();
Iterator it = sel.selectedKeys().iterator();
while (it.hasNext()) {
SelectionKey key = (SelectionKey) it.next();
it.remove();
sc = (SocketChannel) key.channel();
if (key.isConnectable() && !sc.isConnected()) {
InetAddress addr = InetAddress.getByName(null);
boolean success = sc.connect(new InetSocketAddress(
addr, JabberServer.PORT));
if (!success)
sc.finishConnect();
}
if (key.isReadable() && written) {
if (sc.read((ByteBuffer) buf.clear()) > 0) {
written = false;
String response = cs
.decode((ByteBuffer) buf.flip()).toString();
System.out.print(response);
if (response.indexOf("END") != -1)
done = true;
}
}
if (key.isWritable() && !written) {
if (i < 10)
sc.write(ByteBuffer.wrap(new String("howdy " + i
+ 'n').getBytes()));
else if (i == 10)
sc.write(ByteBuffer.wrap(new String("ENDn")
.getBytes()));
written = true;
i++;
}
}
}
}
finally {
sc.close();
sel.close();
}
}
} // /:~
Следующий пример показывает простой механизм, основанный на селекторе, для MultiJabberServer, обсуждаемый ранее. Этот сервер работает таким же образом, как и старый сервер, но он более эффективен в том, что он не требует отдельной нити для обработки кадого клиента.
//: TIEJ:X1:MultiJabberServer1.java
// Имеет туж е семантику, что и многопоточный
// MultiJabberServer
// {RunByHand}
import java.io.*;
import java.net.*;
import java.nio.*;
import java.nio.channels.*;
import java.nio.charset.*;
import java.util.*;
/**
* Сервер принимает соединения не блокирующим способом. Когда соединение
* установлено, создается сокет, который регистрируется с селектором для
* чтения/записи. Чтение/запись выполняется над этим сокетом, когда селектор
* разблокируется. Эта программа работает точно так же, как и MultiJabberServer.
*/
public class MultiJabberServer1 {
public static final int PORT = 8080;
public static void main(String[] args) throws IOException {
// Канал будет читать данные в ByteBuffer, посылаемые
// методом PrintWriter.println(). Декодирование этого потока
// байт требует кодовой страницы для кодировки по умолчанию.
String encoding = System.getProperty("file.encoding");
// Инициализируем здесь, так как мы не хотим создавать новый
// экземпляр кодировки каждый раз, когда это необходимо
// Charset cs = Charset.forName(
// System.getProperty("file.encoding"));
Charset cs = Charset.forName(encoding);
ByteBuffer buffer = ByteBuffer.allocate(16);
SocketChannel ch = null;
ServerSocketChannel ssc = ServerSocketChannel.open();
Selector sel = Selector.open();
try {
ssc.configureBlocking(false);
// Локальныйы адрес, на котором он будет слушать соединения
// Примечание: Socket.getChannel() возвращает null, если с ним не
// ассоциирован канал, как показано ниже.
// т.е выражение (ssc.socket().getChannel() != null) справедливо
ssc.socket().bind(new InetSocketAddress(PORT));
// Канал заинтересован в событиях OP_ACCEPT
SelectionKey key = ssc.register(sel, SelectionKey.OP_ACCEPT);
System.out.println("Server on port: " + PORT);
while (true) {
sel.select();
Iterator it = sel.selectedKeys().iterator();
while (it.hasNext()) {
SelectionKey skey = (SelectionKey) it.next();
it.remove();
if (skey.isAcceptable()) {
ch = ssc.accept();
System.out.println("Accepted connection from:"
+ ch.socket());
ch.configureBlocking(false);
ch.register(sel, SelectionKey.OP_READ);
}
else {
// Обратите внимание, что не выполняется проверка, если
// в канал
// можно писать или читать - для упрощения.
ch = (SocketChannel) skey.channel();
ch.read(buffer);
CharBuffer cb = cs.decode((ByteBuffer) buffer.flip());
String response = cb.toString();
System.out.print("Echoing : " + response);
ch.write((ByteBuffer) buffer.rewind());
if (response.indexOf("END") != -1)
ch.close();
buffer.clear();
}
}
}
}
finally {
if (ch != null)
ch.close();
ssc.close();
sel.close();
}
}
} // /:~
Здесь приведена простейшая реализация Пула Нитей. В этой реализации нет полинга (занят-ожидает) нитей. Она полностью основана на методах wait( ) и notify( ).
//: TIEJ:X1:Worker.java
// Instances of Worker are pooled in threadpool
// {Clean: WorkerErr.log, WorkerErr.log.lck}
// {RunByHand}
import java.io.*;
import java.util.logging.*;
public class Worker extends Thread {
public static final Logger logger = Logger.getLogger("Worker");
private String workerId;
private Runnable task;
// Необходима ссылка на пул нитей в котором существует нить, чтобы
// нить могла добавить себя в пул нитей по завершению работы.
private ThreadPool threadpool;
static {
try {
logger.setUseParentHandlers(false);
FileHandler ferr = new FileHandler("WorkerErr.log");
ferr.setFormatter(new SimpleFormatter());
logger.addHandler(ferr);
}
catch (IOException e) {
System.out.println("Logger not initialized..");
}
}
public Worker(String id, ThreadPool pool) {
workerId = id;
threadpool = pool;
start();
}
// ThreadPool, когда ставит в расписание задачу, использует этот метод
// для делегирования задачи Worker-нити. Кроме того для установки
// задачи (типа Runnable) он также переключает ожидающий метод
// run() на начало выполнения задачи.
public void setTask(Runnable t) {
task = t;
synchronized (this) {
notify();
}
}
public void run() {
try {
while (!threadpool.isStopped()) {
synchronized (this) {
if (task != null) {
try {
task.run(); // Запускаем задачу
}
catch (Exception e) {
logger.log(Level.SEVERE,
"Exception in source Runnable task", e);
}
// Возвращает себя в пул нитей
threadpool.putWorker(this);
}
wait();
}
}
System.out.println(this + " Stopped");
}
catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
public String toString() {
return "Worker : " + workerId;
}
} // /:~
Основной алгоритм:
while true:
- Проверить очередь задач.
- Если она пуста, подождать, пока в очередь будет добавлена задача.
(вызов метода addTask( ) добавляет задачу и уведомляет очередь для разблокирования) - Пробуем получить рабочую (Worker) нить из пула нитей.
- Если нет ни одной доступной нити, ожидаем в пуле нитей.
(Когда нить освободится, она уведомит пул нитей для разблокировки) - На этой стадии есть задачи в очереди и есть свободная рабочая нить.
- Делегируем задачу из очереди рабочей нити.
end while:
//: TIEJ:X1:ThreadPool.java
// Пул нитей, которые выполняют задачи.
// {RunByHand}
import java.util.*;
public class ThreadPool extends Thread {
private static final int DEFAULT_NUM_WORKERS = 5;
private LinkedList workerPool = new LinkedList(),
taskList = new LinkedList();
private boolean stopped = false;
public ThreadPool() {
this(DEFAULT_NUM_WORKERS);
}
public ThreadPool(int numOfWorkers) {
for (int i = 0; i < numOfWorkers; i++)
workerPool.add(new Worker("" + i, this));
start();
}
public void run() {
try {
while (!stopped) {
if (taskList.isEmpty()) {
synchronized (taskQueue) {
// Если очередь пустая, подождать, пока будет добавлена
// задача
taskList.wait();
}
}
else if (workerPool.isEmpty()) {
synchronized (workerPool) {
// Если нет рабочих нитей, подождать, пока
// пока не появится
workerPool.wait();
}
}
// Запускаем следующую задачу из расписания задач
getWorker().setTask((Runnable) taskList.removeLast());
}
}
catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
public void addTask(Runnable task) {
taskList.addFirst(task);
synchronized (taskList) {
taskList.notify(); // Если добавлена новая задача, уведомляем
}
}
public void putWorker(Worker worker) {
workerPool.addFirst(worker);
// Здесь может быть случай, когда вы будете иметь пул из 5 нитей,
// а будет требоваться больше. Это происходит тогда, когда требуется
// рабочая нить,
// но ее нет (свободной), тогда просто блокируем пул нитей.
// Это событие, при котором появляется свободная рабочая нить в пуле
// нитей
// Поэтому эта нить посылает уведомление и разблокирует
// нить ThreadPool, ожидающую пул нитей
synchronized (workerPool) {
workerPool.notify();
}
}
private Worker getWorker() {
return (Worer) workerPool.removeLast();
}
public boolean isStopped() {
return stopped;
}
public void stopThreads() {
stopped = true;
Iterator it = workerPool.iterator();
while (it.hasNext()) {
Worker w = (Worker) it.next();
synchronized (w) {
w.notify();
}
}
} // Junit test
public void testThreadPool() {
ThreadPool tp = new ThreadPool();
for (int i = 0; i < 10; i++) {
tp.addTask(new Runnable() {
public void run() {
System.out.println("A");
}
});
}
tp.stopThreads();
}
} // /:~
Следующий пример MultiJabberServer2.java использует пул нитей. Это шаблон Реактора. Как установлено выше, события отделяются от ассоциированных с ними действий. Пул нитей ассинхронно разделяет действия, ассоциированные с событиями. В системах масштаба предприятия такое разделение обычно достигается путем использования Системы Cообщений Java - Java Messaging System (JMS).
//: TIEJ:X1:MultiJabberServer2.java
// Семантика аналогична MultiJabberServer1, с использованием пула нитей.
// {RunByHand}
import java.io.*;
import java.net.*;
import java.nio.*;
import java.nio.channels.*;
import java.nio.charset.*;
import java.util.*;
class ServeOneJabber implements Runnable {
private SocketChannel channel;
private Selector sel;
public ServeOneJabber(SocketChannel ch) throws IOException {
channel = ch;
sel = Selector.open();
}
public void run() {
ByteBuffer buffer = ByteBuffer.allocate(16);
boolean read = false, done = false;
String response = null;
try {
channel.register(sel, SelectionKey.OP_READ | SelectionKey.OP_WRITE);
while (!done) {
sel.select();
Iterator it = sel.selectedKeys().iterator();
while (it.hasNext()) {
SelectionKey key = (SelectionKey) it.next();
it.remove();
if (key.isReadable() && !read) {
if (channel.read(buffer) > 0)
read = true;
CharBuffer cb = MultiJabberServer2.CS
.decode((ByteBuffer) buffer.flip());
response = cb.toString();
}
if (key.isWritable() && read) {
System.out.print("Echoing : " + response);
channel.write((ByteBuffer) buffer.rewind());
if (response.indexOf("END") != -1)
done = true;
buffer.clear();
read = false;
}
}
}
}
catch (IOException e) {
// будет поймано Worker.java и залогировано.
// Необходимо выбросить исключение времени выполнения, так как мы не
// можем
// оставить IOException
throw new RuntimeException(e);
}
finally {
try {
channel.close();
}
catch (IOException e) {
System.out.println("Channel not closed.");
// Выбрасываем это, чтобы рабочая нить могла залогировать.
throw new RuntimeException(e);
}
}
}
}
public class MultiJabberServer2 {
public static final int PORT = 8080;
private static String encoding = System.getProperty("file.encoding");
public static final Charset CS = Charset.forName(encoding);
// Создаем пул нитей с 20 рабочими нитями.
private static ThreadPool pool = new ThreadPool(20);
public static void main(String[] args) throws IOException {
ServerSocketChannel ssc = ServerSocketChannel.open();
Selector sel = Selector.open();
try {
ssc.configureBlocking(false);
ssc.socket().bind(new InetSocketAddress(PORT));
SelectionKey key = ssc.register(sel, SelectionKey.OP_ACCEPT);
System.out.println("Server on port: " + PORT);
while (true) {
sel.select();
Iterator it = sel.selectedKeys().iterator();
while (it.hasNext()) {
SelectionKey skey = (SelectionKey) it.next();
it.remove();
if (skey.isAcceptable()) {
SocketChannel channel = ssc.accept();
System.out.println("Accepted connection from:"
+ channel.socket());
channel.configureBlocking(false);
// Отделяем события и ассоциированное действие
pool.addTask(new ServeOneJabber(channel));
}
}
}
}
finally {
ssc.close();
sel.close();
}
}
} // /:~
Это минимальное обновления для JabberServer.java. Изначально, когда клиент посылает 'END', JabberServer не отправляет его назад. Эта версия JabberServer отсылает строку 'END' назад. Эти изменения были сделаны, чтобы упростить JabberClient1.java.
//: TIEJ:X1:JabberServer.java
// Очень простой сервер, который просто
// отсылает назад то, что получил от клиента.
// {RunByHand}
import java.io.*;
import java.net.*;
public class JabberServer {
// Выбираем порт за пределами диапазона 1-1024:
public static final int PORT = 8080;
public static void main(String[] args) throws IOException {
ServerSocket s = new ServerSocket(PORT);
System.out.println("Started: " + s);
try {
// Блокируем до возникновения соединения:
Socket socket = s.accept();
try {
System.out.println("Connection accepted: " + socket);
BufferedReader in = new BufferedReader(new InputStreamReader(
socket.getInputStream()));
// Вывод автоматически выталкивается PrintWriter'ом:
BufferedWriter out = new BufferedWriter(new OutputStreamWriter(
socket.getOutputStream()));
while (true) {
String str = in.readLine();
System.out.println("Echoing: " + str);
out.write(str, 0, str.length());
out.newLine();
out.flush();
if (str.equals("END"))
break;
}
// Всегда закрываем два сокета...
}
finally {
System.out.println("closing...");
socket.close();
}
}
finally {
s.close();
}
}
} // /:~
Еще о работе с сетью
На самом деле есть очень много тем, касающихся сетевой работы, которые могут быть освещены в этой вводной статье. Сетевая работа Java также предоставляет четкую и всестороннюю поддержку для URL, включая обработчики протоколов для различных типов содержимого, которое может быть доступно на Интернет сайте. Вы можете найти полное и подробное описание других особенностей сетевого взаимодействия Java в книге Elliotte Rusty Harold "Java Network Programming" (O'Reilly, 1997).
← | Простейший сервер и клиент | Упражнения | → |