Главная » Java, Web » Потоки и работа с сетью

0

Программирование и работа с сетью невозможны без использования одновременного выполнения нескольких потоков. Потоки полезны в клиентских программах. Но еще более необходимы они в серверных приложениях. Для осуществления связи с новым клиентом сервер создает новый поток. При этом второму клиенту не придется ждать, когда первый клиент завершит работу с сервером. Если рассмотреть предыдущий пример, то здесь пользователь (с любой стороны) будет вынужден ждать до тех пор, пока он не получит ответное сообщение. Хорошо было бы иметь возможность, не дожидаясь ответа с противоположной стороны, работать с текстом, создавая следующее сообщение. Такой подход неприемлем в командном интерфейсе, но графический интерфейс пользователя вполне приспособлен для решения такого рода задач. Тут как раз и могут пригодиться потоки. Но есть и еще одна проблема: если наш сервер занят, то мы не сможем подсоединить к нему еще один клиент. Хорошо было бы знать, какие клиенты ждут очереди на соединение. Такая задача также может быть решена с применением потоков.

Рассмотрим программу общения, использующую графический интерфейс пользователя. Когда один пользователь соединяется с другим, открывается окно с областью для ввода текста. В это окно пользователь может ввести текст, который будет передан другому пользователю. Пользователь должен иметь возможность отправить созданное сообщение в любое время. Программа также должна быть готова получить сообщение в любой момент, а полученные сообщения должны быть отображены и видимы пользователю сразу после их поступления. Сервер может общаться с несколькими клиентами одновременно. Такой сервер можно поместить в общедоступное место, где он будет поддерживать список участников чата (сетевого общения в реальном времени). Клиент может связаться с сервером из любого места Интернета, зарегистрироваться и участвовать в чате. Сервер представлен в файле ConnectionBroker.java (листинг 2.12).

Листинг 2.12. Файл ConnectionBroker.java

ConnectionBroker — это сетевой сервер, устанавливающий связь с клиентами. Клиенты регистрируют себя на сервере, тем самым делая себя доступными для участия в чате. Каждый, кто соединяется с сервером, должен выполнить одну из следующих команд:

REGISTER: Пользователь добавляется в список доступных клиентов, все символы после команды REGISTER сохраняются как дополнительная информация о пользователе. Пользователю присваивается идентификатор ID. Клиент может иметь только одного партнера. Клиент не может посылать данные до тех пор, пока он не имеет партнера. Исключение – команда close. Когда пользователь получает партнера, к нему направляется сообщение, которое начинается со слова CONNECTED.

CONNECT: Данные в строке после команды CONNECT должны быть

идентификатором ID. Соединение устанавливается между клиентом, пославшим команду CONNECT, и клиентом, чей идентификатор был послан в строке после команды CONNECT. После этого сервер устанавливает связь между клиентами, которая существует до тех пор, пока один из них не закроет соединение. Сообщение, начинающееся с символа закрытия связи, служит для передачи информации о закрытии соединения. Если соединение не может быть установлено, то сервер отвечает строкой, которая в качестве своего первого символа содержит символ NOT_AVAILABLE. Если соединение установлено, то обоим клиентам посылается сообщение, начинающееся символом CONNECTED.

SEND_CLIENT_LIST: Сервер отвечает, посылая информацию о всех клиентах, ожидающих партнера. Каждая строка начинается с символа CLIENT_INЕО, за которой следует идентификатор клиента ID и информация о клиенте. Строка, начинающаяся символом END_CLIENT_INFO служит признаком конца списка клиентов, находящихся в состоянии ожидания партнера.

Порт, который прослушивается сервером, может быть задан в качестве пара^ метра в командной строке. Если номер порта в командной строке

не указан, то используется порт, установленный по умолчанию в качестве

значения для DEFAULT_PORT. В качестве клиентов могут выступать апплеты.

*/

import java.io.*; impo rt java.net.*; import java.util.Vectors- public class ConneсtionBroker { // первые символы сообщений

static final char REGISTER = ‘[‘; // символы команд static final char CONNECT = ‘ = ‘;        // от клиента серверу

static final char SEND_CLIENT_L1ST = ‘:';

static final char CLOSE = ‘]';                      // закрыть связь

static final char NOT_AVAILABLE = ‘!';              // символы команд

static final char CONNECTED = ‘.';                  //от сервера клиенту

static final char CLIENT_INFO = ‘>'; static final char END_CLIENT_INFO = ‘<‘; /*

если порт не был указан в командной строке, то сервер прослушивает порт DEFAULT_PORT */

static final int DEFAULT_PORT = 3030;

private static Vector clientList = new Vector(); // доступные

// клиенты

private static int nextClientID =1; // следующий идентификатор

// клиента

public static void main(String[] args) {

// Функция main() создает прослушиваемый порт. // После получения запроса на соединение // создается новый сокет в отдельном потоке. int port; // порт для прослушивания ServerSocket listeners- Socket client; if (args.length == 0)

port = DEFAULT_PORT; else { try {

port = Integer.parselnt(args[0]);

}

catch (NumberFormatException е) {

System.out.println(args[0] + " is not a legal port number."); return;

}

}

try {

listener = new ServerSocket(port);

}

catch (IOException e) {

System.out.println("Can’t start server."); System.out.println(e.toString()); return;

}

System.out.println("Listening on port " + listener.getLocalPort()); try {

while (true) {

client = listener.accept (); new ClientThread(client);

}

}

catch (Exception e) {

System.out.println("Server shut down unexpectedly."); System.out.println(e.toString()); System.exit(1);

}

}

/*

Четыре функции для работы со списком пользователей. Используется синхронизация. */

static void addClient(Client client) { // вставка нового клиента synchronized(clientList) { client.ID = nextClientID++; if (client.info.length() == 0)

client.info = "Anonymous" + client.ID; clientList.addElement(client);

System.out.printIn("Added client " + client.ID + " " + client.info);

}

static void removedient(Client client) { // удаление клиента synchronized(clientList) {

clientList.removeElement(client);

}

System.out.println("Removed client " + client.ID);

}

static Client getClient(int ID) {

// удаление клиента по идентификатору synchronized(clientList) {

for (int i = 0; i < clientList.size(); i++) { Client с = (Client)clientList.elementAt(i); if (c.ID == ID) {

clientList.removeElementAt(i);

System.out.println("Removed client " + c.ID);

с.ID = 0;

return c;

}

}

return null;

}

}

static Client[] getClients() { synchronized(clientList) { if (clientList.size() == 0) return null;

Client[] clients = new Client[ clientList.size() ]; for (int i = 0; i < clientList.size(); i++)

clients[i] = (Client)clientList.elementAt(i); return clients;

}

}

private static class Client {

// Данные клиента. Для клиентов, ожидающих соединение, ID > 0. // Для клиентов, запросивших соединение, ID равно 0. int ID; // идентификатор клиента

String info; 11 информация клиента

Socket connection; // связь между сервером и клиентом Reader incoming; // чтение данных Printwriter outgoing; // отправка данных

volatile Client partner; // если не null, то данные могут

// передаваться от клиента другому // клиенту и наоборот private StringBuffer line = new StringBuffer(); // чтение данных private boolean checkLineFeed; // обработка символов конца строки String getIn() throws IOException { // чтение одной строки if (incoming == null)

throw new IOException("Not connected."); int ch;

ch = incoming.read(); if (ch == -1)

throw new IOException("Attempt to read past end-of-stream."); if (ch == ‘\n’ && checkLineFeed)

ch = incoming.read(); 1ine.setLength(0);

while (ch != -1 && ch != ‘\n’ && ch != ‘\r’) { line.append((char)ch) ; ch = incoming.read();

}

checkLineFeed = (ch == ‘\r’); return line.toString();

}

void send(String message) throws IOException { // отправка одной строки if (outgoing == null)

throw new IOException("Not connected."); outgoing.println(message); outgoing.flush(); if (outgoing.checkError())

throw new IOException("Error while sending data.");

private static class ClientThread extends Thread { // Поток создания соединения с сервером. // Поток открывается конструктором. Client client; // клиент ClientThread(Socket connection) {

// Конструктор. Создается объект Client и

// запускается поток создания соединения.

client = new Client();

client.connection = connection;

start();

}

public void run() { try {

client.connection.setSoTimeout(30000); processClient();

}

catch (Exception e) { }

finally { try {

client.connection.close ();

}

catch (Exception e) { }

if (client.ID > 0)

removeClient(client);

}

}

void processClient() throws IOException { client.incoming =

new InputStreamReader(client.connection.getlnputStream()); client.outgoing =

new PrintWriter(client.connection.getOutputStream()); String message = client.getln(); if (message.length() ==0) { throw new IOException(

"Unexpected input to ‘ConnectionBroker’ program.");

client.connection.setSoTimeout(3600000); // 60 минут char cmd = message.charAt(0);

message = message.substring(1); // здесь могут быть данные

// для команды

if (cmd == REGISTER) { client.info = message; addClient(client); int ID = client.IDs- while (true) {

message = client.getln(); if (client.partner != null)

client.partner.send(message); if (message.length() > 0 && message.charAt(0) == CLOSE) { System.out.println("Closing down relay for client " + ID) ;

break;

}

}

}

else if (cmd == CONNECT) { // соединение с клиентом int partnerlD; try {

partnerlD = Integer.parselnt(message);

}

catch (NumberFormatException e) {

client.send(NOT_AVAILABLE + "Client ID is not an

integer.");

throw new IOException("Illegal connect request.");

}

Client partner = getClient(partnerlD) ; if (partner == null) {

client.send(NOT_AVAILABLE + "Unknown client ID."); throw new IOException("Requested connection not found.");

}

client.partner = partner;            // установление партнера

partner .partner = clients-

System, out. print In ("Setting up relay to client " +

partnerlD);

partner.send("" + CONNECTED); // сообщение партнерам client.send("" + CONNECTED); while (true) {

message = client.getln(); partner.send(message);

if (message.length() > 0 && message.charAt(0) == CLOSE) { System.out.println("Closing down relay for client " +

partnerlD);

break;

}

}

}

else

if (cmd == SEND_CLIENT_LIST) {

// Запрашивается список ждущих соединение клиентов. Client[] clients = getClients(); if (clients != null) {

for (int i =0; i < clients.length; i++)

client.send("" + CLIENT_INEO + clients[i].ID + " " +

clients[i].info);

}

client.send("" + END_CLIENT_INEO);

}

else {

throw new IOException("Unexpected input to ‘ConnectionBroker’

program.");

}

}

}

}

Этот сервер работает совместно с клиентскими программами. Клиент представлен в файле BrokeredChat.java (листинг 2.13). После запуска сервера (на компьютере с именем locaihost) с указанием номера порта запускаем Brokeredchat с указанием компьютера и номера порта (пишем в командной строке java Brokeredchat locaihost). Затем регистрируемся и ожидаем

соединения с другим клиентом. После регистрации появляется окно chatwindow, которое описано в файле ChatWindow.java (рис. 2.10).

Рис. 2.10. Регистрация клиента и участие в чате

Листинг 2.13. Файл BrokeredChat.java

Апплет предназначен для чата между двумя пользователями.

Апплет представляет собой клиент для сервера ConnectionBroker.

Сервер устанавливает связь между двумя такими клиентами.

Клиент решает две задачи. Первая состоит в том, что

клиент может зарегистрироваться на сервере

для ожидания соединения с другим клиентом.

Вторая состоит в том, что клиент устанавливает соединение

с другим клиентом, который ожидает соединение.

Апплет может запросить и получить список клиентов,

ожидающих соединение.

*/

impo гt j ava.a wt.*;

import java.awt.event.*;

import java.io.*;

impo rt java.net.*;

import javax.swing.*;

import java.util.ArrayList;

public class BrokeredChat extends JApplet implements ActionListener { public static void main(String[] args) { if (args.length ==0) {

System.out.println("Usage: java BrokeredChat <server> [<port>]"); return;

}

BrokeredChat applet = new BrokeredChat(); applet.computer = args[0]; applet.port = DEFAULT_PORT; if (args.length >1) { try {

applet.port = Integer.parselnt(args[1]);

}

catch (NumberFormatException e) { }

}

JFrame window = new JFrame("BrokeredChat") ;

window.setContentPane(applet.makelnterfacePanel());

window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

window.setSize(500,135);

window.setLocation(20, 50);

window.show();

applet.doCommand(SEND_CLIENT_L1ST);

}

// команды для общения с сервером static final char REGISTER = ‘[‘; static final char CONNECT = ‘='; static final char SEND_CLIENT_L1ST = ‘:'; static final char NOT_AVAILABLE = ‘!'; static final char CONNECTED = static final char CLIENT INFO = ‘>';

static final char END_CLIENT_INFO = ‘<‘; static final int DEFAULT_PORT = 3030;

JLabel display; JComboBox clients; JButton connectButton; JButton refreshButton; JButton registerButton; JTextField infolnput; ArrayList clientStrings; String computer; int ports-

public void init() {

setBackground(Color.lightGray) ; setContentPane(makelnterfacePanel()) ; doCommand(SEND_CLIENT_LIST);

}

JPanel makelnterfacePanel() { JPanel panel = new JPanel();

panel.setBackground(Color.lightGray);

panel.setLayout(new GridLayout(4,1,5,5)) ;

panel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)) ;

JPanel top = new JPanel();

top.setBackground(Color.lightGray);

top.setLayout(new BorderLayout(2,2));

JPanel middle = new JPanel();

middle.setBackground(Color.lightGray);

middle.setLayout(new BorderLayout(5,5));

JPanel bottom = new JPanel();

bottom.setBackground(Color.lightGray) ;

bottom.setLayout(new GridLayout(1,2,5,5)) ;

display = new JLabel("",JLabel.CENTER); display.setForeground(new Color(200, 0, 0) ) ;

panel.add(top);

panel.add(display); panel.add(middle) ; panel.add(bottom) ;

clients = new JComboBox(); clients.setBackground(Color.white);

connectButton = new JButton("Connnect to Selected User"); connectButton.addActionListener(this);

refreshButton = new JButton("Refresh User List"); refreshButton.addActionListener(this);

registerButton = new JButton("Register"); registerButton.addActionListener(this);

infolnput = new JTextField(); infolnput.setBackground(Color.white); infolnput.addActionListener(this) ;

top.add(infolnput,BorderLayout.CENTER);

top.add(registerButton,BorderLayout.EAST);

top.add(new JLabel("Register with Server as: "),

BorderLayout.WEST); middle.add(clients,BorderLayout.CENTER); middle.add(new JLabel("List of Available Users: "),

BorderLayout.WEST); bottom.add(refreshButton); bottom, add(connectButton);

if (computer == null) {

String portStr = getParameter("port"); if (portStr == null)

port = DEEAULT_PORT; else { try {

port = Integer.parselnt(portStr);

}

catch (NumberFormatException e) {

port = DEEAULT_PORT;

}

}

computer = getParameter("server"); if (computer == null) {

// используем компьютер, с которого был загружен апплет computer = getCodeBase().getHost();

}

}

return panel;

}

public void actionPerformed(ActionEvent evt) { // ответ на нажатие кнопки пользователем Object source = evt.getSource (); if (source == connectButton) { doCommand(CONNECT);

}

else if (source == registerButton || source == infolnput) { doCommand(REGISTER);

}

else {

doCommand(SEND_CLIENT_LIST);

}

}

void doCommand(char command) {

String message; // команда серверу String name;

if (command == CONNECT) {

int partner = clients.getSelectedlndex(); if (clientStrings == null || partner < 0 || partner >= clientStrings.size()) { display.setText("Can’t connect — no user!"); return;

}

String info = (String)clientStrings.get(partner); int pos = info.indexOf(" ");

if (pos > 0) {

message = "" + CONNECT + info.substring(0,pos); name = info.substring(pos+1);

}

else {

message = "" + CONNECT + info; name = "unknown user";

}

}

else

if (command == REGISTER) { // регистрация на сервере name = infolnput.getText().trim(); if (name.length() ==0) {

display.setText("To register, you must enter a name."); return;

}

infolnput.setText{""); message = REGISTER + name;

display.setText("Contacting server to register you…");

}

else {

// команда получения клиентского списка с сервера clients.removeAllitems(); clients.addltem("(Checking)"); message = "" + SEND_CLIENT_LIST;

display.setText("Contacting server to get user list…"); name = null;

}

registerButton.setEnabled(false); //кнопка не работает до

// завершения операции

connectButton.setEnabled(false); refreshButton.setEnabled(false); new ConnectionHandler(message,name);

}

class ConnectionHandler extends Thread { // поток установки соединения String messages-

String name; // имя для окна ChatWindow

ConnectionHandler(String message, String name) { this.message = message; this.name = name; start();

}

public void run() {

Socket connection = null; ArrayList clients = null;

String displayText = "Client list has been received."; try {

if (message.charAt(0) == REGISTER) {

connection = new Socket(computer,port); PrintWriter out = new

PrintWriter(connection.getOutputStream()); out.println(message); out.flush(); if (out.checkError())

throw new IOException("Can’t send command.");

new ChatWindow("Outgoing Connection (" + name + ")",

connection,true);

displayText = "Opening window to wait for connection";

}

else

if (message.charAt(0) == CONNECT) {

connection = new Socket(computer,port);

connection.setSoTimeout(30000); // ожидание 30 секунд

TextReader in = new

TextReader(connection.getInputStream()); PrintWriter out = new

PrintWriter(connection.getOutputStream()) ; out.println(message); // запрос соединения out.flush(); if (out.checkError())

throw new IOException("Can’t send command."); String answer = in.getln(); // чтение ответа сервера if (answer.length() == 0 || answer.charAt(0) !=

CONNECTED)

displayText = "Can’t connect to " + name;

}

else {

connection.setSoTimeout(0);

new ChatWindow("Connection to " + name, connection); displayText = "Opening connection to " + name;

}

}

connection = null;

connection = new Socket(computer,port); connection.setSoTimeout(30000); TextReader in = new

TextReader(connection.getInputStream()); Printwriter out = new

Printwriter(connection.getOutputStream()); out.println("" + SEND_CLIENT_LIST) ; //запрос списка out.flush(); if (out.checkError())

throw new IOException("Can’t send command."); ArrayList clientlnfo = new ArrayList(); while (true) {

String line = in.getln(); if (line, length () > 0) {

if (line.charAt(0) == END_CLIENT_INFO) break;

else if (line.charAt(0) == CLIENT_INFO) clientlnfo.add(line.substring(1)); else

throw new IOException("Bad data received.");

}

}

clients = clientlnfo;

}

catch (Exception e) {

displayText = "Error: " + e.getMessage();

}

finally{

finishConnection(clients,displayText); if (connection != null) {

try {

connection.close ();

}

catch (IOException e) { }

}

}

}

}

void finishConnection(final ArrayList clientList, final String

displayText)

{

SwingUtilities.invokeLater (new Runnable() { public void run() { if (clientList == null) { clients.removeAllitems(); clients.addltem("(Can’t connect)");

}

else

if (clientList.size() == 0) { clients.removeAllitems(); clients.addltem("(None available)") ;

}

else {

makeClientList(clientList); connectButton.setEnabled(true);

}

display.setText(displayText);

refreshButton.setEnabled(true); // включить кнопки registerButton.setEnabled(true); clientStrings = clientList;

}

}) ;

}

void makeClientList(ArrayList clientlnfo) { clientStrings = clientlnfo; clients.removeAllitems();

for (int i = 0; i < clientlnfo.size(); i++) String info = (String)clientlnfo.get(i); int spacePos = infо.indexOf(" "); if (spacePos > 0)

info = info.substring(spacePos+1) ; clients.addltem(info);

}

}

Рис. 2.11. Сервер найден

 

Рис. 2.12. Регистрация пользователя

Приведенный файл является клиентской программой. При его запуске происходит попытка установки связи с сервером ConnectionBroker. Если связь установлена, то в окне появится надпись Client list has been received, (рис. 2.11). Пользователь должен зарегистрироваться, для этого вводится какое-либо имя пользователя (рис. 2.12). После того как пользователь зарегистрирован, он получает диалоговое окно для ввода текста для чата и ожидает соединения. Если соединение устанавливается, пользователи могут переписываться друг с другом.

Листинг 2.14. Файл ChatWindow.java

impo rt j ava.awt.*; impo rt j ava.awt.event.*; import javax.swing. *; impo rt j ava.net.*; import java.io.*;

public class ChatWindow extends JFrame

implements ActionListener, WindowListener, Runnable {

static final String HANDSHAKE = "cindependent Connection Window>"; static final int DEFAULT_PORT = 17171; public static final void main(String[] args) { int port;

Socket connection; TextReader in; PrintWriter out; String message; if (args.length ==0) {

System.out.println("Usage: java ChatWindow <server-computer> [<port>] ") ;

System.out.println(" or java ChatWindow -s [<port>]"); return;

}

if (args.length == 1)

port = DEFAULT_PORT; else { try {

port = Integer.parselnt(args[1]);

catch (NumberFormatException е) {

System.out.println(args[1] + " is not a legal port number."); return;

}

}

try {

if (args[0].equalsIgnoreCase("-s")) {

ServerSocket listener = new ServerSocket(port); System.out.println("Listening on port " +

listener.getLocalPort()); connection = listener.accept(); listener.close();

}

else {

connection = new Socket(args[0],port);

}

out = new PrintWriter(connection.getOutputStream()); out.printIn(HANDSHAKE); out.flush();

in = new TextReader(connection.getlnputStream());

message = in.getln();

if (! message.equals(HANDSHAKE))

throw new IOException("Connected program is not ChatWindow"); System.out.println("Connected.");

}

catch (Exception e) {

System.out.println("Error opening connection.");

System.out.println(e.toString() ) ;

return;

}

ChatWindow w = new ChatWindow("ChatWindow", connection); w.standalone = true;

}

static final char MESSAGE = ‘0’; static final char CLOSE = ‘]'; JTextArea transcript; JTextField messagelnput; Socket connection; TextReader incoming;

Printwriter outgoings- Thread reader; JButton clearButton; JButton sendButton; JButton closeButton; boolean waitFor; boolean standalone = falser-

public ChatWindow(String title, Socket connection) { this(title,connection,false);

}

public ChatWindow(String title, Socket connection, boolean waitFor) { super(title);

this.connection = connection; addWindowListener(this) ; transcript = new JTextArea(); transcript.setBackground(Color.white); transcript.setEditable(false);

transcript.setFont(new Font("Monospaced", Font.PLAIN, 12));

messagelnput = new JTextField();

messagelnput.addActionListener(this);

messagelnput.setBackground(Color.white);

closeButton = new JButton("Close") ;

closeButton.addActionListener(this);

clearButton = new JButton("Clear Transcript");

clearButton.addActionListener(this);

sendButton = new JButton("Send");

sendButton.addActionListener(this) ;

// компоненты

JPanel bottom = new JPanel();

bottom.setLayout(new GridLayout(2,1));

JPanel inputBar = new JPanel();

inputBar.setLayout(new BorderLayout());

inputBar.add(messagelnput, BorderLayout.CENTER);

inputBar.add(new JLabel("Enter text to send: "), BorderLayout.WEST);

inputBar.setBorder(BorderFactory.createEtchedBorder());

JPanel buttonBar = new JPanel();

buttonBar.setLayout(new GridLayout(1, 3));

buttonBar.add(closeButton);

buttonBar.add(clearButton); buttonBar.add(sendButton); bottom.add(inputBar); bottom, add(buttonBar);

getContentPane().add(bottom, BorderLayout.SOUTH); getContentPane().add(transcript, BorderLayout.CENTER);

try {

incoming = new TextReader(connection.getlnputStream()); outgoing = new PrintWriter(connection.getOutputStream());

}

catch (IOException e) {

transcript.setText("Error opening I/O streams!\n"

+ "Connection can’t be used.\n" + "You can close the window now.\n"); closeButton.setText("Close Window"); sendButton.setEnabled(false); clearButton.setEnabled(false); this.connection = null;

}

if (this.connection != null) { reader = new Thread(this); reader.start () ; if (waitEor) {

sendButton.setEnabled(false);

transcript.setText("Waiting for connection.\n"); this.waitEor = true;

}

}

// размер окна setSize(550,350); setLocation(50, 80) ; show () ;

setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);

}

public void actionPerformed(ActionEvent evt) {

Object source = evt.getSource (); if (source == clearButton)

transcript.setText (""); else

if (source == sendButton || source == messagelnput) doSend();

else

if (source == closeButton) doClose ();

}

synchronized void doSend () { if (connection == null) { return;

}

if (waitFor) { return;

}

String message = messagelnput.getText(); outgoing.println(MESSAGE + message); outgoing.flush() ; if (outgoing.checkError()) {

transcript.append("ERROR: An error occured while sending

data.\n");

doForceClose() ;

}

else

transcript.append("SENT:            " + message + ‘\n’);

messagelnput.selectAll(); messagelnput.requestFocus();

}

void doCloseO {

if (connection != null) { outgoing.println(CLOSE) ; outgoing.flush() ; try {

connection.close(); // прервать соединение

catch (IOException е) { }

connection = null; //соединение разорвано

}

dispose (); // окно закрыто

}

void doForceClose() {

if (connection == null) { return;

}

closeButton.setText("Close Window"); sendButton.setEnabled(false); clearButton.setEnabled(false); try {

connection.close ();

}

catch (IOException e) { }

transcript.append("\nCLOSED: Connection has been closed.\n"); transcript.append("     You can now close the window.\n");

connection = null;

}

void addToTranscript(final String message) { Runnable postit = new Runnable() { public void run() {

transcript.append(message + "\n");

}

} ;

SwingUt i1it ies.invokeLater(postit);

}

void closeEromThread() {

Runnable closeit = new Runnable() { public void run() { doForceClose();

};

SwingUt i1it ies.invokeLater(closeit);

}

public void run() { try {

while (true) {

String message = incoming.getIn(); if (connection == null) { break;

}

if (waitFor) {

addToTranscript("Connected.\n"); sendButton.setEnabled(true); waitFor = false;

}

else

if (message.length() >0) {

if (message.charAt(0) == CLOSE) {

addToTranscript("\nConnection closed from other side.");

closeFromThread();

break;

}

message = message.substring(1); addToTranscript("RECEIVED: " + message);

}

}

}

catch (Exception e) { synchronized (this) {

if (connection != null) {

addToTranscript("ERROR: An error occurred while

receiving data.");

closeFromThread();

}

}

}

}

public void windowClosing(WindowEvent evt) {

doClose();

}

public void windowClosed(WindowEvent evt) { if (standalone) System.exit(0);

}

public void windowOpened(WindowEvent evt) { } public void windowlconified(WindowEvent evt) { } public void windowDeiconified(WindowEvent evt) { } public void windowActivated(WindowEvent evt) { }

public void windowDeactivated(WindowEvent evt) { } }

Этот пример (листинг 2.14) вполне работоспособен. Конечно, он не лишен недостатков и вовсе не безупречен, но в нем продемонстрированы основные методы работы с сокетами, используя которые, можно создавать вполне совершенные программы.

После запуска потока обработки соединения, поток может принимать команды и обрабатывать их соответствующим образом. Поток понимает три типа команд. Команда register добавляет клиента в список доступных участников чата. Соединение остается открытым до тех пор, пока кто-либо из участников не обратится к клиенту для установления связи. После установления связи между клиентами участники удаляются из списка доступных клиентов. Команда send_client_list запрашивает копию доступных участников чата. Сервер в ответ посылает список участников чата. Команда connect используется для установления соединения с одним из доступных участников чата. Сервер устанавливает связь и, если не возникли ошибки, посылает сообщение обоим участникам о том, что связь установлена. После этого участники могут обмениваться сообщениями друг с другом. Сообщения пересылаются через сервер, прямая связь остается между сервером и двумя общающимися через сервер клиентами. Сервер передает сообщения ОТ ОДНОГО клиента другому И наоборот. При ЭТОМ сервер ConnectionBroker работает с апплетами, которые являются для него клиентами. Апплеты загружаются с компьютера, на котором работает сервер. При таком подходе прямое общение между двумя клиентами невозможно, так как апплеты не могут устанавливать соединения с ресурсами, расположенными на другом компьютере, отличающемся от того, с которого был загружен апплет.

Чтобы иметь возможность работать с сервером Connect ionBroker, создан апплет, который является для него клиентом. Апплет пытается установить связь с connectionBroker, который располагается на том же компьютере, с которого загружается апплет. Если требуемый сервер не работает, апплет выводит сообщение об ошибке.

Источник: Будилов В. А. Интернет-программирование на Java. — СПб.: БХВ-Петербург, 2003. — 704 е.: ил.

По теме:

  • Комментарии