TCP-соединение
Основой TCP является сокет. В TCP-соединении мы будем использовать ServerSocket и Socket. После того, как клиент и сервер установят соединение, остальное — это, по сути, контроль ввода-вывода.
Давайте сначала посмотрим на простую TCP-связь, которая разделена на клиентскую и серверную.
Клиентский код выглядит следующим образом:
public static void main(String[] args) выдает IOException
{
Сокет сокет = ноль;
BufferedReader br = ноль;
PrintWriter pw = ноль;
BufferedReader brTemp = null;
пытаться
{
сокет = новый сокет (InetAddress.getLocalHost (), 5678);
br = новый BufferedReader (новый InputStreamReader (socket.getInputStream ()));
pw = новый PrintWriter(socket.getOutputStream());
brTemp = новый BufferedReader (новый InputStreamReader (System.in));
пока (правда)
{
Строковая строка = brTemp.readLine();
pw.println(строка);
pw.flush();
if (line.equals("end")) сломать;
System.out.println(br.readLine());
}
}
поймать (исключение ex)
{
System.err.println(ex.getMessage());
}
окончательно
{
if (socket != null) socket.close();
если (br != null) br.close();
if (brTemp != null) brTemp.close();
если (pw != null) pw.close();
}
}
}
public static void main(String[] args) выдает IOException
{
Сервер ServerSocket = ноль;
Клиент сокета = ноль;
BufferedReader br = ноль;
PrintWriter pw = ноль;
пытаться
{
сервер = новый ServerSocket (5678);
клиент = server.accept();
br = новый BufferedReader (новый InputStreamReader (client.getInputStream ()));
pw = новый PrintWriter(client.getOutputStream());
пока (правда)
{
Строковая строка = br.readLine();
pw.println("Ответ:" + строка);
pw.flush();
if (line.equals("end")) сломать;
}
}
поймать (исключение ex)
{
System.err.println(ex.getMessage());
}
окончательно
{
if (server != null) server.close();
если (клиент != null) client.close();
если (br != null) br.close();
если (pw != null) pw.close();
}
}
}
Приведенный выше код в основном описывает основную структуру клиента и сервера во время процесса связи TCP. Мы видим, что в приведенном выше коде сервер может обрабатывать только один запрос от клиента в любой момент времени. Да, это последовательная обработка. не может быть распараллелен. Это не то же самое, что серверный метод обработки в нашем представлении. Мы можем добавить на сервер многопоточность. Когда поступает клиентский запрос, мы создаем поток для обработки соответствующего запроса.
Улучшенный серверный код выглядит следующим образом:
класс ServerThread расширяет поток
{
частный сокет сокета = ноль;
общедоступный ServerThread (сокет сокета)
{
this.socket = сокет;
}
общественный недействительный запуск () {
BufferedReader br = ноль;
PrintWriter pw = ноль;
пытаться
{
br = новый BufferedReader (новый InputStreamReader (socket.getInputStream ()));
pw = новый PrintWriter(socket.getOutputStream());
пока (правда)
{
Строковая строка = br.readLine();
pw.println("Ответ:" + строка);
pw.flush();
if (line.equals("end")) сломать;
}
}
поймать (исключение ex)
{
System.err.println(ex.getMessage());
}
окончательно
{
если (сокет != ноль)
пытаться {
сокет.закрыть();
} поймать (IOException e1) {
e1.printStackTrace();
}
если (бр != ноль)
пытаться {
бр.закрыть();
} catch (IOException e) {
е.printStackTrace();
}
если (pw != null) pw.close();
}
}
}
В процессе программирования у нас есть понятие «ресурс». Например, соединение с базой данных является типичным ресурсом. Чтобы повысить производительность, мы обычно не уничтожаем соединение с базой данных напрямую, а используем пул соединений с базой данных для управления несколькими. Базы данных управляются и используются повторно. Для Socket-соединений это также ресурс. Когда нашей программе требуется большое количество Socket-соединений, это будет очень неэффективный подход, если каждое соединение необходимо будет устанавливать заново.
Подобно пулу соединений с базой данных, мы также можем спроектировать пул TCP-соединений. Идея здесь заключается в том, что мы используем массив для поддержки нескольких соединений Socket, а также другой массив состояния, чтобы описать, используется ли каждое соединение Socket. Соединение сокета. Мы просматриваем массив состояния и удаляем первое неиспользуемое соединение сокета. Если все соединения используются, выдается исключение. Это очень интуитивно понятная и простая «стратегия планирования». Во многих платформах с открытым исходным кодом или коммерческих (Apache/Tomcat) существуют аналогичные «пулы ресурсов».
Код пула TCP-соединений выглядит следующим образом:
частный адрес InetAddress = ноль;
частный международный порт;
частный Socket [] arrSockets = null;
частное логическое значение [] arrStatus = null;
частный счетчик int;
public TcpConnectionPool (адрес InetAddress, внутренний порт, количество целых чисел)
{
этот.адрес = адрес;
this.port = порт;
это .count = count;
arrSockets = новый сокет [количество];
arrStatus = новое логическое значение [количество];
инициализация();
}
частная недействительная инициализация()
{
пытаться
{
for (int i = 0; i < count; i++)
{
arrSockets[i] = новый сокет(address.getHostAddress(), порт);
arrStatus[i] = ложь;
}
}
поймать (исключение ex)
{
System.err.println(ex.getMessage());
}
}
общедоступный сокет getConnection()
{
если (arrSockets == null) init();
интервал я = 0;
for(я = 0; я <счет; я++)
{
если (arrStatus[i] == false)
{
arrStatus[i] = правда;
перерыв;
}
}
if (i == count) throw new RuntimeException("на данный момент нет доступного соединения.");
вернуть arrSockets[i];
}
public void ReleaseConnection (сокет сокета)
{
если (arrSockets == null) init();
for (int i = 0; i < count; i++)
{
если (arrSockets[i] == сокет)
{
arrStatus[i] = ложь;
перерыв;
}
}
}
публичная недействительная перестройка()
{
инициализация();
}
общественная пустота уничтожить()
{
если (arrSockets == null) return;
for(int я = 0; я <счет; я++)
{
пытаться
{
arrSockets[i].close();
}
поймать (исключение ex)
{
System.err.println(ex.getMessage());
продолжать;
}
}
}
}
UDP — это метод подключения, отличный от TCP. Обычно он используется в ситуациях, когда требуется высокая производительность в реальном времени и низкие требования к точности, например, при онлайн-видео. UDP будет иметь «потерю пакетов». В TCP, если Сервер не запущен, при отправке сообщения Клиентом будет сообщено об исключении, но для UDP исключение не будет сгенерировано.
Два класса, используемые для связи UDP, — это DatagramSocket и DatagramPacket, последний хранит содержимое связи.
Ниже приведен простой пример связи по протоколу UDP. Как и TCP, он также разделен на две части: клиент и сервер. Код клиента выглядит следующим образом.
public static void main(String[] args)
{
пытаться
{
Хост InetAddress = InetAddress.getLocalHost();
внутренний порт = 5678;
BufferedReader br = новый BufferedReader (новый InputStreamReader (System.in));
пока (правда)
{
Строковая строка = br.readLine();
byte[] сообщение = line.getBytes();
Пакет DatagramPacket = новый DatagramPacket(сообщение, message.length, хост, порт);
Сокет DatagramSocket = новый DatagramSocket();
сокет.отправить (пакет);
сокет.закрыть();
if (line.equals("end")) разрыв;
}
бр.закрыть();
}
поймать (исключение ex)
{
System.err.println(ex.getMessage());
}
}
}
public static void main(String[] args)
{
пытаться
{
внутренний порт = 5678;
DatagramSocket dsSocket = новый DatagramSocket (порт);
буфер байт[] = новый байт[1024];
Пакет DatagramPacket = новый DatagramPacket(buffer, buffer.length);
пока (правда)
{
dsSocket.receive(пакет);
Строковое сообщение = новая строка (буфер, 0, package.getLength());
System.out.println(packet.getAddress().getHostName() + ":" + сообщение);
if (message.equals("end")) сломать;
package.setLength(buffer.length);
}
dsSocket.close();
}
поймать (исключение ex)
{
System.err.println(ex.getMessage());
}
}
}
В многоадресной рассылке используется метод, аналогичный UDP. В ней используются IP-адреса класса D и стандартные номера портов UDP. Это адреса между 224.0.0.0 и 239.255.255.255, исключая 224.0.0.0.
Класс, используемый в многоадресной рассылке, — MulticastSocket, у которого есть два метода, на которые следует обратить внимание: joinGroup и LeaveGroup.
Ниже приведен пример многоадресной рассылки. Клиентский код выглядит следующим образом:
public static void main(String[] args)
{
BufferedReader br = новый BufferedReader (новый InputStreamReader (System.in));
пытаться
{
Адрес InetAddress = InetAddress.getByName("230.0.0.1");
внутренний порт = 5678;
пока (правда)
{
Строковая строка = br.readLine();
byte[] сообщение = line.getBytes();
Пакет DatagramPacket = новый DatagramPacket(сообщение, длина сообщения, адрес, порт);
MulticastSocket multicastSocket = новый MulticastSocket();
multicastSocket.send(пакет);
if (line.equals("end")) разрыв;
}
бр.закрыть();
}
поймать (исключение ex)
{
System.err.println(ex.getMessage());
}
}
}
public static void main(String[] args)
{
внутренний порт = 5678;
пытаться
{
MulticastSocket multicastSocket = новый MulticastSocket (порт);
Адрес InetAddress = InetAddress.getByName("230.0.0.1");
multicastSocket.joinGroup(адрес);
буфер байт[] = новый байт[1024];
Пакет DatagramPacket = новый DatagramPacket(buffer, buffer.length);
пока (правда)
{
multicastSocket.receive(пакет);
Строковое сообщение = новая строка (буфер, пакет.getLength());
System.out.println(packet.getAddress().getHostName() + ":" + сообщение);
if (message.equals("end")) сломать;
package.setLength(buffer.length);
}
multicastSocket.close();
}
поймать (исключение ex)
{
System.err.println(ex.getMessage());
}
}
}
NIO — это новый набор API-интерфейсов ввода-вывода, представленный в JDK1.4. Он имеет новые конструкции в управлении буфером, сетевой связи, доступе к файлам и операциях с наборами символов. Для сетевой связи NIO использует концепции буферов и каналов.
Ниже приведен пример NIO, который сильно отличается от стиля кодирования, упомянутого выше.
public static void main(String[] args)
{
Строка хост="127.0.0.1";
внутренний порт = 5678;
Канал SocketChannel = ноль;
пытаться
{
Адрес InetSocketAddress = новый InetSocketAddress (хост, порт);
Кодировка charset = Charset.forName("UTF-8");
Декодер CharsetDecoder = charset.newDecoder();
Кодировщик CharsetEncoder = charset.newEncoder();
Буфер ByteBuffer = ByteBuffer.allocate(1024);
CharBuffer charBuffer = CharBuffer.allocate(1024);
канал = SocketChannel.open();
канал.connect (адрес);
Строковый запрос = "GET / /r/n/r/n";
Channel.write(encoder.encode(CharBuffer.wrap(запрос)));
while((channel.read(буфер)) != -1)
{
буфер.флип();
decoder.decode(буфер, charBuffer, ложь);
charBuffer.flip();
System.out.println(charBuffer);
буфер.очистить();
charBuffer.clear();
}
}
поймать (исключение ex)
{
System.err.println(ex.getMessage());
}
окончательно
{
если (канал!= ноль)
пытаться {
канал.закрыть();
} catch (IOException e) {
// TODO Автоматически сгенерированный блок catch
е.printStackTrace();
}
}
}
}