TCP клиент / сервер
<dependency>
<groupId>xyz.cofe</groupId>
<artifactId>trambda-tcp</artifactId>
<!-- Актуальную версию лучше поискать
на https://oss.sonatype.org/
на https://search.maven.org/
-->
<version>1.0-SNAPSHOT</version>
</dependency>
Будет рассмотрено
- Публикуем свой API
- Написание TCP сервера
- Написание TCP клиента
- Писать свой TCP сервер необязательно
Публикуем свой API
Допустим у нас есть библиотека (или API / набор интерфейсов), которая протестирована и работает локально.
Вот такая простая библиотека:
package xyz.cofe.trambda.tcp.demo;
/** Информация о процессе ОС */
public class OsProc implements Serializable {
// Идентификатор родительского процесса
private final Integer ppid;
public Optional<Integer> getPpid(){ ... }
// Идентификатор процесса
private final Integer pid;
public int getPid(){ return pid; }
// Имя процесса
private final String name;
public String getName(){ return name; }
// Командная строка процесса
private final String cmdLine;
public Optional<String> getCmdline(){ ... }
...
}
/** Интерфейс получения списка процессов */
public interface IEnv {
// получения списка процессов
public List<OsProc> processes();
}
/** Реализация интерфейса для Linux */
public class LinuxEnv implements IEnv { ... }
/** Реализация интерфейса для Windows */
public class WindowsEnv implements IEnv { ... }
Теперь что эта библиотека была клиент-серверной делаем сделаем следующие:
- Напишем сервер
- Напишем клиента
Написание сервера
// Создание TCP сокета
ServerSocket ssocket = new ServerSocket(port);
// Настраиваем сокет
ssocket.setSoTimeout(1000*5);
// Создаем сервер
server = new TcpServer<IEnv>(
// Передаем сокет
ssocket,
// Передаем функцию получения сервиса для новой сессии
s -> new LinuxEnv(),
// Настраиваем безопасность
SecurityFilters.create(s -> {
// Разрешаем вызовы строго - определенных методов
s.allow( a -> {
// Публикуемый API нашего сервиса
a.invoke("demo api", c->
c.getOwner().matches(
"xyz\\.cofe\\.trambda\\.tcp\\.demo\\.([\\w\\d]+)"));
// Работа с коллекциями
a.invoke("java collections api", c->c.getOwner().matches(
"java\\.util\\.(List)|java\\.util\\.stream\\.([\\w\\d]+)"));
// Работа с Java строками
a.invoke("java lang api", c->
c.getOwner().matches("java\\.lang\\.(String)"));
// Методы которые использует компилятор Java
a.invoke("java compiler", c->
c.getOwner().matches(
"java\\.lang\\.invoke\\.(LambdaMetafactory|StringConcatFactory)"));
});
// Все остальное запрещаем
s.deny().any("by default");
})
);
// Указываем что Thread сервера будет запущен как фоновый
server.setDaemon(true);
// Запускаем сервер
server.start();
Написание клиента
Написание клиента еще проще:
var tcpQuery = TcpQuery
// Какой API мы будем использовать
.create(IEnv.class)
// Указываем адрес сервера
.host("localhost").port(port)
// Получаем Query<IEnv>
.build();
// Выполняем запрос к серверу, получим список процессов chrome/java на сервере
var qRegex = "chrome|java";
tcpQuery.apply( env ->
env.processes().stream()
// Фильтруем список процессов
.filter( p -> p.getName().matches("(?is).*("+qRegex+").*") )
// Преобразуем Stream<OsProc> в List<OsProc>
// т.к. List<?> - реализует Serializable
// а Stream<?> - не реализует Serializable
.collect(Collectors.toList())
)
// Отображаем полученный список процессов
.stream().map(OsProc::toString).forEach(log::info);
Писать свой TCP сервер необязательно
Вообще писать свой сервер необязательно, достаточно
- Взять пакет trambda-tcp-serv-cli
- Пакет имеет следующую структуру каталогов/файлов
bin/- каталог shell скриптами, для запуска сервераbin/trambda-tcp-serv- shell скрипт для запуска сервераbin/trambda-tcp-serv.bat- batch скрипт для запуска сервераjars/- Каталог с библиотеками сервера
- Скопировать свою библиотеку (свой API + реализацию его) в каталог
jars/ - Подготовить скрипт запуска, например
my_service.groovy, его код, будет ниже - Запустить сервер с указанием скрипта
$ bin/trambda-tcp-serv -s my_service.groovy
my_service.groovy
// Публикуем наш API, как некий сетевой сервис
app.service(
// Адрес:порт - адрес на котором будет запущен сервис
// адрес может быть например localhost:7890
// тогда сервис будет доступен только локально
// Можно указывать не ip, а dns имя, например i-env.myserver.com:12345
"0.0.0.0:9988",
new xyz.cofe.trambda.demo.api.LinuxEnv()
) {
daemon false
// Указываем настройки безопасности
security {
// Какие методы будут доступныме
allow {
// Методы которые использует компилятор Java
invoke( 'Java compiler' ){
methodOwner ==~ /java\.lang\.invoke\.(LambdaMetafactory|StringConcatFactory)/
}
// Работа с коллекциями
invoke( 'Java collections' ){
methodOwner ==~ /java\.util\.(stream\.(Stream|Collectors)|(List))/
}
// Работа с Java строками
invoke( 'Java lang' ){
methodOwner ==~ /java\.lang\.String/
}
// Публикуемый API нашего сервиса
invoke( 'Api '){
methodOwner ==~ /xyz\.cofe\.trambda\.demo\.api\.(IEnv|OsProc)/
}
}
// Все остальные методы не будут доступны
deny {
any("ban all")
}
}
}
// Запускаем все наши сервисы
start();
