You've already forked OpenIntegrations
mirror of
https://github.com/Bayselonarrend/OpenIntegrations.git
synced 2025-08-10 22:41:43 +02:00
Пересборка TCP сервера с добавлением класса handler
This commit is contained in:
1
src/addins/tcp_server/Cargo.lock
generated
1
src/addins/tcp_server/Cargo.lock
generated
@@ -96,6 +96,7 @@ version = "0.1.0"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"addin1c",
|
"addin1c",
|
||||||
"dashmap",
|
"dashmap",
|
||||||
|
"once_cell",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@@ -15,4 +15,5 @@ strip = true # Automatically strip symbols from the binary.
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
addin1c = "0.5.0"
|
addin1c = "0.5.0"
|
||||||
serde_json = "1.0.137"
|
serde_json = "1.0.137"
|
||||||
dashmap = "6.1.0"
|
dashmap = "6.1.0"
|
||||||
|
once_cell = "1.20.2"
|
105
src/addins/tcp_server/src/commons/mod.rs
Normal file
105
src/addins/tcp_server/src/commons/mod.rs
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
use std::io::{Read, Write};
|
||||||
|
use std::net::TcpStream;
|
||||||
|
use std::sync::{Arc, Mutex};
|
||||||
|
use std::time::Duration;
|
||||||
|
use dashmap::DashMap;
|
||||||
|
use once_cell::sync::Lazy;
|
||||||
|
use serde_json::json;
|
||||||
|
use crate::commons::getset::ValueType;
|
||||||
|
|
||||||
|
pub mod getset;
|
||||||
|
|
||||||
|
pub static CONNECTIONS: Lazy<DashMap<String, Arc<Mutex<TcpStream>>>> = Lazy::new(DashMap::new);
|
||||||
|
|
||||||
|
pub fn receive_data(connection_id: String, max_size: usize) -> Box<dyn ValueType> {
|
||||||
|
let mut stream = match get_stream(connection_id) {
|
||||||
|
Ok(s) => s,
|
||||||
|
Err(e) => return Box::new(create_error(&e)),
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut buffer = Vec::new();
|
||||||
|
let mut temp_buffer = vec![0; 1024]; // Буфер для чтения данных
|
||||||
|
let mut total_read = 0; // Счетчик прочитанных байт
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let remaining = if max_size > 0 {
|
||||||
|
max_size.saturating_sub(total_read) // Оставшееся место до max_size
|
||||||
|
} else {
|
||||||
|
usize::MAX // Без ограничения по размеру
|
||||||
|
};
|
||||||
|
|
||||||
|
if remaining == 0 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
let to_read = remaining.min(temp_buffer.len());
|
||||||
|
|
||||||
|
match stream.read(&mut temp_buffer[..to_read]) {
|
||||||
|
Ok(0) => {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
Ok(size) => {
|
||||||
|
total_read += size;
|
||||||
|
buffer.extend_from_slice(&temp_buffer[..size]);
|
||||||
|
|
||||||
|
if total_read >= max_size && max_size > 0 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
return Box::new(create_error(&format!("Error reading from stream: {}", e)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Box::new(buffer)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Отправляет данные в соединение по указанному УИД
|
||||||
|
pub fn send_data(connection_id: String, data: Vec<u8>) -> String {
|
||||||
|
let mut stream = match get_stream(connection_id) {
|
||||||
|
Ok(s) => s,
|
||||||
|
Err(e) => return create_error(&e),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Пытаемся отправить данные в поток
|
||||||
|
match stream.write_all(&data) {
|
||||||
|
Ok(_) => create_success(),
|
||||||
|
Err(e) => create_error(&format!("Error writing to stream: {}", e)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Закрывает соединение по указанному УИД
|
||||||
|
pub fn close_connection(connection_id: String) -> String {
|
||||||
|
if CONNECTIONS.remove(&connection_id).is_some() {
|
||||||
|
create_success()
|
||||||
|
} else {
|
||||||
|
create_error("Connection not found")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_stream(connection_id: String) -> Result<TcpStream, String> {
|
||||||
|
// Найти соединение по идентификатору
|
||||||
|
let stream = CONNECTIONS
|
||||||
|
.get(&connection_id)
|
||||||
|
.ok_or_else(|| format!("Connection '{}' not found", connection_id))?;
|
||||||
|
|
||||||
|
// Заблокировать доступ к потоку и клонировать его
|
||||||
|
let stream = stream.lock().map_err(|e| e.to_string())?;
|
||||||
|
match stream.try_clone().map_err(|e| e.to_string()) {
|
||||||
|
Ok(stream) => {
|
||||||
|
stream.set_read_timeout(Some(Duration::from_secs(30))).ok();
|
||||||
|
stream.set_write_timeout(Some(Duration::from_secs(30))).ok();
|
||||||
|
Ok(stream)
|
||||||
|
}
|
||||||
|
Err(e) => Err(e.to_string()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn create_error(message: &str) -> String {
|
||||||
|
json!({ "result": false, "error": message }).to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn create_success() -> String {
|
||||||
|
json!({ "result": true }).to_string()
|
||||||
|
}
|
@@ -1,221 +0,0 @@
|
|||||||
use std::io::{Read, Write};
|
|
||||||
use std::net::{TcpListener, TcpStream};
|
|
||||||
use std::time::{Duration, Instant};
|
|
||||||
use serde_json::{json};
|
|
||||||
use std::sync::{Arc, Mutex};
|
|
||||||
|
|
||||||
use crate::core::getset::ValueType;
|
|
||||||
use crate::component::AddIn;
|
|
||||||
|
|
||||||
/// Запускает сервер на указанном порту
|
|
||||||
pub fn start(tcp: &mut AddIn) -> String {
|
|
||||||
|
|
||||||
match TcpListener::bind(format!("0.0.0.0:{}", tcp.port)) {
|
|
||||||
Ok(listener) => {
|
|
||||||
tcp.listener = Some(listener);
|
|
||||||
json!({ "result": true }).to_string()
|
|
||||||
}
|
|
||||||
Err(e) => create_error(&e.to_string())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Ожидает новое соединение с возможностью указания таймаута (в секундах)
|
|
||||||
pub fn wait_for_connection(tcp: &mut AddIn, timeout: i32) -> String {
|
|
||||||
|
|
||||||
if let Some(listener) = &tcp.listener {
|
|
||||||
|
|
||||||
let start_time = Instant::now();
|
|
||||||
listener.set_nonblocking(true).ok();
|
|
||||||
|
|
||||||
while start_time.elapsed() < Duration::from_secs(timeout as u64) || timeout == 0 {
|
|
||||||
match listener.accept() {
|
|
||||||
Ok((stream, _)) => {
|
|
||||||
|
|
||||||
// Получаем id соединения
|
|
||||||
let id = tcp.next_id.to_string();
|
|
||||||
|
|
||||||
// Получаем адрес клиента
|
|
||||||
let addr = match stream.peer_addr() {
|
|
||||||
Ok(addr) => addr.to_string(),
|
|
||||||
Err(e) => e.to_string(),
|
|
||||||
};
|
|
||||||
|
|
||||||
// Обновляем id
|
|
||||||
tcp.next_id += 1;
|
|
||||||
|
|
||||||
let connection = Arc::new(Mutex::new(stream));
|
|
||||||
|
|
||||||
// Вставляем соединение
|
|
||||||
tcp.connections.insert(id.clone(), connection);
|
|
||||||
|
|
||||||
// Возвращаем успешный ответ
|
|
||||||
return json!({
|
|
||||||
"result": true,
|
|
||||||
"connection": {
|
|
||||||
"id": id,
|
|
||||||
"addr": addr,
|
|
||||||
}
|
|
||||||
}).to_string();
|
|
||||||
}
|
|
||||||
Err(e) if e.kind() == std::io::ErrorKind::WouldBlock => {
|
|
||||||
// Если нет соединений, ожидаем немного и пробуем снова
|
|
||||||
std::thread::sleep(Duration::from_millis(100));
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
return create_error(&e.to_string());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
create_error("Timeout expired")
|
|
||||||
} else {
|
|
||||||
create_error("Listener not initialized")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Получает данные из соединения по указанному УИД
|
|
||||||
pub fn receive_data(tcp: &mut AddIn, connection_id: String, max_size: usize) -> Box<dyn ValueType> {
|
|
||||||
|
|
||||||
let mut stream = match get_stream(tcp, connection_id){
|
|
||||||
Ok(s) => s,
|
|
||||||
Err(e) => return Box::new(create_error(&e))
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut buffer = Vec::new();
|
|
||||||
let mut temp_buffer = vec![0; 1024]; // Буфер для чтения данных
|
|
||||||
let mut total_read = 0; // Счетчик прочитанных байт
|
|
||||||
|
|
||||||
loop {
|
|
||||||
let remaining = if max_size > 0 {
|
|
||||||
max_size.saturating_sub(total_read) // Оставшееся место до max_size
|
|
||||||
} else {
|
|
||||||
usize::MAX // Без ограничения по размеру
|
|
||||||
};
|
|
||||||
|
|
||||||
if remaining == 0 {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
let to_read = remaining.min(temp_buffer.len());
|
|
||||||
|
|
||||||
match stream.read(&mut temp_buffer[..to_read]) {
|
|
||||||
Ok(0) => {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
Ok(size) => {
|
|
||||||
total_read += size;
|
|
||||||
buffer.extend_from_slice(&temp_buffer[..size]);
|
|
||||||
|
|
||||||
if total_read >= max_size && max_size > 0 {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
return Box::new(create_error(&format!("Error reading from stream: {}", e)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Box::new(buffer)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// Отправляет данные в соединение по указанному УИД
|
|
||||||
pub fn send_data(tcp: &mut AddIn, connection_id: String, data: Vec<u8>) -> String {
|
|
||||||
|
|
||||||
let mut stream = match get_stream(tcp, connection_id){
|
|
||||||
Ok(s) => s,
|
|
||||||
Err(e) => return create_error(&e)
|
|
||||||
};
|
|
||||||
|
|
||||||
// Пытаемся отправить данные в поток
|
|
||||||
match stream.write_all(&data) {
|
|
||||||
Ok(_) => create_success(),
|
|
||||||
Err(e) => create_error(&format!("Error writing to stream: {}", e)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Закрывает соединение по указанному УИД
|
|
||||||
pub fn close_connection(tcp: &mut AddIn, connection_id: String) -> String {
|
|
||||||
|
|
||||||
if tcp.connections.remove(&connection_id).is_some() {
|
|
||||||
create_success()
|
|
||||||
} else {
|
|
||||||
create_error("Connection not found")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn list_connections(tcp: &AddIn) -> String {
|
|
||||||
|
|
||||||
let ids: Vec<String> = tcp.connections.iter().map(|entry| entry.key().clone()).collect();
|
|
||||||
json!({ "result": true, "connections": ids }).to_string()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn remove_inactive_connections(tcp: &mut AddIn) -> String {
|
|
||||||
|
|
||||||
let mut inactive_ids = Vec::new();
|
|
||||||
|
|
||||||
for element in tcp.connections.iter() {
|
|
||||||
|
|
||||||
let id = element.key();
|
|
||||||
let stream = element.value();
|
|
||||||
|
|
||||||
let mut temp_buffer = vec![0; 1];
|
|
||||||
|
|
||||||
match stream.lock() {
|
|
||||||
Ok(locked_stream) => match locked_stream.peek(&mut temp_buffer) {
|
|
||||||
Err(e) if e.kind() != std::io::ErrorKind::WouldBlock=> inactive_ids.push(id.clone()),
|
|
||||||
_ => continue
|
|
||||||
},
|
|
||||||
Err(e) => return create_error(&format!("Failed to lock stream: {}", e)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Удаляем неактивные соединения
|
|
||||||
for id in &inactive_ids {
|
|
||||||
match tcp.connections.remove(id) {
|
|
||||||
Some(_) => {}, // Успешное удаление
|
|
||||||
None => return create_error(&format!("Failed to remove connection with id: {}", id)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
json!({ "result": true, "removed_connections": inactive_ids }).to_string()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn stop_server(tcp: &mut AddIn) -> String {
|
|
||||||
|
|
||||||
if let Some(listener) = tcp.listener.take() {
|
|
||||||
drop(listener); // Освобождаем ресурс TcpListener
|
|
||||||
} else {
|
|
||||||
return create_error("Listener not initialized");
|
|
||||||
}
|
|
||||||
|
|
||||||
tcp.connections.clear();
|
|
||||||
|
|
||||||
create_success()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_stream(tcp: &mut AddIn, connection_id: String) -> Result<TcpStream, String> {
|
|
||||||
|
|
||||||
// Найти соединение по идентификатору
|
|
||||||
let stream = tcp.connections.get(&connection_id)
|
|
||||||
.ok_or_else(|| format!("Connection '{}' not found", connection_id))?;
|
|
||||||
|
|
||||||
// Заблокировать доступ к потоку и клонировать его
|
|
||||||
let stream = stream.lock().map_err(|e| e.to_string())?;
|
|
||||||
match stream.try_clone().map_err(|e| e.to_string()) {
|
|
||||||
Ok(stream) => {
|
|
||||||
stream.set_read_timeout(Some(Duration::from_secs(30))).ok();
|
|
||||||
stream.set_write_timeout(Some(Duration::from_secs(30))).ok();
|
|
||||||
Ok(stream)
|
|
||||||
},
|
|
||||||
Err(e) => Err(e.to_string())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn create_error(message: &str) -> String {
|
|
||||||
json!({ "result": false, "error": message }).to_string()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn create_success() -> String {
|
|
||||||
json!({ "result": true }).to_string()
|
|
||||||
}
|
|
85
src/addins/tcp_server/src/handler/component/mod.rs
Normal file
85
src/addins/tcp_server/src/handler/component/mod.rs
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
use addin1c::{name, Variant};
|
||||||
|
|
||||||
|
pub use crate::commons::{getset, CONNECTIONS};
|
||||||
|
use crate::commons;
|
||||||
|
|
||||||
|
// МЕТОДЫ КОМПОНЕНТЫ -------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// Синонимы
|
||||||
|
pub const METHODS: &[&[u16]] = &[
|
||||||
|
name!("Receive"), // 2
|
||||||
|
name!("Send"), // 3
|
||||||
|
name!("Close"), // 4
|
||||||
|
];
|
||||||
|
|
||||||
|
// Число параметров функций компоненты
|
||||||
|
pub fn get_params_amount(num: usize) -> usize {
|
||||||
|
match num {
|
||||||
|
0 => 2,
|
||||||
|
1 => 2,
|
||||||
|
2 => 1,
|
||||||
|
_ => 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Соответствие функций Rust функциям компоненты
|
||||||
|
// Вызовы должны быть обернуты в Box::new
|
||||||
|
pub fn cal_func(_obj: &mut AddIn, num: usize, params: &mut [Variant]) -> Box<dyn getset::ValueType> {
|
||||||
|
|
||||||
|
match num {
|
||||||
|
|
||||||
|
1 => {
|
||||||
|
|
||||||
|
let connection_id = params[0].get_string().unwrap_or("".to_string());
|
||||||
|
let max_size = params[1].get_i32().unwrap_or(0);
|
||||||
|
|
||||||
|
commons::receive_data(connection_id, max_size as usize)
|
||||||
|
},
|
||||||
|
2 => {
|
||||||
|
|
||||||
|
let connection_id = params[0].get_string().unwrap_or("".to_string());
|
||||||
|
let data = params[1].get_blob().unwrap_or(&[]).to_vec();
|
||||||
|
|
||||||
|
Box::new(commons::send_data(connection_id, data))
|
||||||
|
|
||||||
|
},
|
||||||
|
3 => {
|
||||||
|
let connection_id = params[0].get_string().unwrap_or("".to_string());
|
||||||
|
Box::new(commons::close_connection(connection_id))
|
||||||
|
}
|
||||||
|
_ => Box::new(false), // Неверный номер команды
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// ПОЛЯ КОМПОНЕНТЫ ---------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// Синонимы
|
||||||
|
pub const PROPS: &[&[u16]] = &[];
|
||||||
|
|
||||||
|
|
||||||
|
pub struct AddIn {
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AddIn {
|
||||||
|
/// Создает новый объект
|
||||||
|
pub fn new() -> Self {
|
||||||
|
AddIn {}
|
||||||
|
}
|
||||||
|
pub fn get_field_ptr(&self, index: usize) -> *const dyn getset::ValueType {
|
||||||
|
match index {
|
||||||
|
_ => panic!("Index out of bounds"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn get_field_ptr_mut(&mut self, index: usize) -> *mut dyn getset::ValueType { self.get_field_ptr(index) as *mut _ }
|
||||||
|
}
|
||||||
|
// -------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// УНИЧТОЖЕНИЕ ОБЪЕКТА -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// Обработка удаления объекта
|
||||||
|
impl Drop for AddIn {
|
||||||
|
fn drop(&mut self) {}
|
||||||
|
}
|
56
src/addins/tcp_server/src/handler/mod.rs
Normal file
56
src/addins/tcp_server/src/handler/mod.rs
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
pub mod component;
|
||||||
|
|
||||||
|
use addin1c::{name, RawAddin, Variant};
|
||||||
|
|
||||||
|
pub use component::AddIn;
|
||||||
|
|
||||||
|
use component::METHODS;
|
||||||
|
use component::PROPS;
|
||||||
|
use component::get_params_amount;
|
||||||
|
use component::cal_func;
|
||||||
|
use crate::commons::getset;
|
||||||
|
|
||||||
|
|
||||||
|
// Определение класса
|
||||||
|
impl RawAddin for AddIn {
|
||||||
|
|
||||||
|
fn register_extension_as(&mut self) -> &'static [u16] {
|
||||||
|
name!("Handler")
|
||||||
|
}
|
||||||
|
fn get_n_props(&mut self) -> usize {
|
||||||
|
PROPS.len()
|
||||||
|
}
|
||||||
|
fn find_prop(&mut self, name: &[u16]) -> Option<usize> {
|
||||||
|
PROPS.iter().position(|&x| x == name)
|
||||||
|
}
|
||||||
|
fn get_prop_name(&mut self, num: usize, _alias: usize) -> Option<&'static [u16]> { PROPS.get(num).copied() }
|
||||||
|
fn get_prop_val(&mut self, num: usize, val: &mut Variant) -> bool {let field: &dyn getset::ValueType = &self[num]; field.get_value(val) }
|
||||||
|
fn set_prop_val(&mut self, num: usize, val: &Variant) -> bool {let field: &mut dyn getset::ValueType = &mut self[num]; field.set_value(val); true }
|
||||||
|
fn is_prop_readable(&mut self, _num: usize) -> bool { true }
|
||||||
|
fn is_prop_writable(&mut self, _num: usize) -> bool { true }
|
||||||
|
fn get_n_methods(&mut self) -> usize { METHODS.len() }
|
||||||
|
fn find_method(&mut self, name: &[u16]) -> Option<usize> { METHODS.iter().position(|&x| x == name) }
|
||||||
|
fn get_method_name(&mut self, num: usize, _alias: usize) -> Option<&'static [u16]> { METHODS.get(num).copied() }
|
||||||
|
fn get_n_params(&mut self, num: usize) -> usize { get_params_amount(num) }
|
||||||
|
fn get_param_def_value(&mut self, _method_num: usize, _param_num: usize, _value: Variant, ) -> bool { true }
|
||||||
|
fn has_ret_val(&mut self, _num: usize) -> bool { true }
|
||||||
|
fn call_as_proc(&mut self, _num: usize, _params: &mut [Variant]) -> bool { false }
|
||||||
|
fn call_as_func(&mut self, num: usize, params: &mut [Variant], ret_value: &mut Variant, ) -> bool { cal_func(self, num, params).get_value(ret_value) }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::ops::Index<usize> for AddIn {
|
||||||
|
type Output = dyn getset::ValueType;
|
||||||
|
|
||||||
|
fn index(&self, index: usize) -> &Self::Output {
|
||||||
|
unsafe { &*self.get_field_ptr(index) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::ops::IndexMut<usize> for AddIn {
|
||||||
|
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
|
||||||
|
unsafe { &mut *self.get_field_ptr_mut(index) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@@ -1,5 +1,6 @@
|
|||||||
pub mod component;
|
pub mod server;
|
||||||
mod core;
|
pub mod handler;
|
||||||
|
pub mod commons;
|
||||||
|
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
@@ -7,18 +8,25 @@ use std::{
|
|||||||
sync::atomic::{AtomicI32, Ordering},
|
sync::atomic::{AtomicI32, Ordering},
|
||||||
};
|
};
|
||||||
|
|
||||||
use component::AddIn;
|
|
||||||
use addin1c::{create_component, destroy_component, name, AttachType};
|
use addin1c::{create_component, destroy_component, name, AttachType};
|
||||||
|
|
||||||
pub static mut PLATFORM_CAPABILITIES: AtomicI32 = AtomicI32::new(-1);
|
pub static mut PLATFORM_CAPABILITIES: AtomicI32 = AtomicI32::new(-1);
|
||||||
|
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub unsafe extern "C" fn GetClassObject(_name: *const u16, component: *mut *mut c_void) -> c_long {
|
pub unsafe extern "C" fn GetClassObject(name: *const u16, component: *mut *mut c_void) -> c_long {
|
||||||
|
|
||||||
let addin = AddIn::new();
|
|
||||||
create_component(component, addin)
|
|
||||||
|
|
||||||
|
match *name as u8 {
|
||||||
|
b'1' => {
|
||||||
|
let addin = server::AddIn::new();
|
||||||
|
create_component(component, addin)
|
||||||
|
}
|
||||||
|
b'2' => {
|
||||||
|
let addin = handler::AddIn::new();
|
||||||
|
create_component(component, addin)
|
||||||
|
}
|
||||||
|
_ => 0,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
@@ -30,7 +38,7 @@ pub unsafe extern "C" fn DestroyObject(component: *mut *mut c_void) -> c_long {
|
|||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "C" fn GetClassNames() -> *const u16 {
|
pub extern "C" fn GetClassNames() -> *const u16 {
|
||||||
name!("Main").as_ptr()
|
name!("Server|Handler").as_ptr()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
|
118
src/addins/tcp_server/src/server/component/methods.rs
Normal file
118
src/addins/tcp_server/src/server/component/methods.rs
Normal file
@@ -0,0 +1,118 @@
|
|||||||
|
use std::net::{TcpListener};
|
||||||
|
use std::time::{Duration, Instant};
|
||||||
|
use serde_json::{json};
|
||||||
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
|
use crate::commons::{CONNECTIONS, create_error, create_success};
|
||||||
|
use crate::server::component::AddIn;
|
||||||
|
|
||||||
|
/// Запускает сервер на указанном порту
|
||||||
|
pub fn start(tcp: &mut AddIn) -> String {
|
||||||
|
|
||||||
|
match TcpListener::bind(format!("0.0.0.0:{}", tcp.port)) {
|
||||||
|
Ok(listener) => {
|
||||||
|
tcp.listener = Some(listener);
|
||||||
|
json!({ "result": true }).to_string()
|
||||||
|
}
|
||||||
|
Err(e) => create_error(&e.to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Ожидает новое соединение с возможностью указания таймаута (в секундах)
|
||||||
|
pub fn wait_for_connection(tcp: &mut AddIn, timeout: i32) -> String {
|
||||||
|
if let Some(listener) = &tcp.listener {
|
||||||
|
let start_time = Instant::now();
|
||||||
|
listener.set_nonblocking(true).ok();
|
||||||
|
|
||||||
|
while start_time.elapsed() < Duration::from_secs(timeout as u64) || timeout == 0 {
|
||||||
|
match listener.accept() {
|
||||||
|
Ok((stream, _)) => {
|
||||||
|
// Формируем уникальный идентификатор соединения
|
||||||
|
let id = format!("{}:{}", tcp.port, tcp.next_id);
|
||||||
|
|
||||||
|
// Получаем адрес клиента
|
||||||
|
let addr = match stream.peer_addr() {
|
||||||
|
Ok(addr) => addr.to_string(),
|
||||||
|
Err(e) => e.to_string(),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Увеличиваем счётчик ID
|
||||||
|
tcp.next_id += 1;
|
||||||
|
|
||||||
|
let connection = Arc::new(Mutex::new(stream));
|
||||||
|
|
||||||
|
// Вставляем соединение в глобальное хранилище
|
||||||
|
CONNECTIONS.insert(id.clone(), connection);
|
||||||
|
|
||||||
|
// Возвращаем успешный ответ
|
||||||
|
return json!({
|
||||||
|
"result": true,
|
||||||
|
"connection": {
|
||||||
|
"id": id,
|
||||||
|
"addr": addr,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.to_string();
|
||||||
|
}
|
||||||
|
Err(e) if e.kind() == std::io::ErrorKind::WouldBlock => {
|
||||||
|
// Если нет соединений, ожидаем немного и пробуем снова
|
||||||
|
std::thread::sleep(Duration::from_millis(100));
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
return create_error(&e.to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
create_error("Timeout expired")
|
||||||
|
} else {
|
||||||
|
create_error("Listener not initialized")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn list_connections() -> String {
|
||||||
|
let ids: Vec<String> = CONNECTIONS.iter().map(|entry| entry.key().clone()).collect();
|
||||||
|
json!({ "result": true, "connections": ids }).to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn remove_inactive_connections() -> String {
|
||||||
|
let mut inactive_ids = Vec::new();
|
||||||
|
|
||||||
|
for element in CONNECTIONS.iter() {
|
||||||
|
let id = element.key();
|
||||||
|
let stream = element.value();
|
||||||
|
|
||||||
|
let mut temp_buffer = vec![0; 1];
|
||||||
|
|
||||||
|
match stream.lock() {
|
||||||
|
Ok(locked_stream) => match locked_stream.peek(&mut temp_buffer) {
|
||||||
|
Err(e) if e.kind() != std::io::ErrorKind::WouldBlock => inactive_ids.push(id.clone()),
|
||||||
|
_ => continue,
|
||||||
|
},
|
||||||
|
Err(e) => return create_error(&format!("Failed to lock stream: {}", e)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Удаляем неактивные соединения
|
||||||
|
for id in &inactive_ids {
|
||||||
|
match CONNECTIONS.remove(id) {
|
||||||
|
Some(_) => {} // Успешное удаление
|
||||||
|
None => return create_error(&format!("Failed to remove connection with id: {}", id)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
json!({ "result": true, "removed_connections": inactive_ids }).to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn stop_server(tcp: &mut AddIn) -> String {
|
||||||
|
if let Some(listener) = tcp.listener.take() {
|
||||||
|
drop(listener); // Освобождаем ресурс TcpListener
|
||||||
|
} else {
|
||||||
|
return create_error("Listener not initialized");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Удаляем все соединения, связанные с данным сервером
|
||||||
|
let prefix = format!("{}:", tcp.port);
|
||||||
|
CONNECTIONS.retain(|key, _| !key.starts_with(&prefix));
|
||||||
|
|
||||||
|
create_success()
|
||||||
|
}
|
@@ -1,10 +1,10 @@
|
|||||||
mod methods;
|
mod methods;
|
||||||
|
|
||||||
use addin1c::{name, Variant};
|
use addin1c::{name, Variant};
|
||||||
use crate::core::getset;
|
use std::net::{TcpListener};
|
||||||
use std::net::{TcpListener, TcpStream};
|
|
||||||
use std::sync::{Arc, Mutex};
|
pub use crate::commons::{getset, CONNECTIONS};
|
||||||
use dashmap::DashMap;
|
use crate::commons;
|
||||||
|
|
||||||
// МЕТОДЫ КОМПОНЕНТЫ -------------------------------------------------------------------------------
|
// МЕТОДЫ КОМПОНЕНТЫ -------------------------------------------------------------------------------
|
||||||
|
|
||||||
@@ -52,23 +52,23 @@ pub fn cal_func(obj: &mut AddIn, num: usize, params: &mut [Variant]) -> Box<dyn
|
|||||||
let connection_id = params[0].get_string().unwrap_or("".to_string());
|
let connection_id = params[0].get_string().unwrap_or("".to_string());
|
||||||
let max_size = params[1].get_i32().unwrap_or(0);
|
let max_size = params[1].get_i32().unwrap_or(0);
|
||||||
|
|
||||||
methods::receive_data(obj, connection_id, max_size as usize)
|
commons::receive_data(connection_id, max_size as usize)
|
||||||
},
|
},
|
||||||
3 => {
|
3 => {
|
||||||
|
|
||||||
let connection_id = params[0].get_string().unwrap_or("".to_string());
|
let connection_id = params[0].get_string().unwrap_or("".to_string());
|
||||||
let data = params[1].get_blob().unwrap_or(&[]).to_vec();
|
let data = params[1].get_blob().unwrap_or(&[]).to_vec();
|
||||||
|
|
||||||
Box::new(methods::send_data(obj, connection_id, data))
|
Box::new(commons::send_data(connection_id, data))
|
||||||
|
|
||||||
},
|
},
|
||||||
4 => {
|
4 => {
|
||||||
let connection_id = params[0].get_string().unwrap_or("".to_string());
|
let connection_id = params[0].get_string().unwrap_or("".to_string());
|
||||||
Box::new(methods::close_connection(obj, connection_id))
|
Box::new(commons::close_connection(connection_id))
|
||||||
}
|
}
|
||||||
|
|
||||||
5 => Box::new(methods::list_connections(obj)),
|
5 => Box::new(methods::list_connections()),
|
||||||
6 => Box::new(methods::remove_inactive_connections(obj)),
|
6 => Box::new(methods::remove_inactive_connections()),
|
||||||
7 => Box::new(methods::stop_server(obj)),
|
7 => Box::new(methods::stop_server(obj)),
|
||||||
_ => Box::new(false), // Неверный номер команды
|
_ => Box::new(false), // Неверный номер команды
|
||||||
}
|
}
|
||||||
@@ -87,7 +87,6 @@ pub const PROPS: &[&[u16]] = &[
|
|||||||
|
|
||||||
pub struct AddIn {
|
pub struct AddIn {
|
||||||
port: i32,
|
port: i32,
|
||||||
connections: Arc<DashMap<String, Arc<Mutex<TcpStream>>>>,
|
|
||||||
next_id: i32,
|
next_id: i32,
|
||||||
listener: Option<TcpListener>,
|
listener: Option<TcpListener>,
|
||||||
}
|
}
|
||||||
@@ -97,7 +96,6 @@ impl AddIn {
|
|||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
AddIn {
|
AddIn {
|
||||||
port: 0,
|
port: 0,
|
||||||
connections: Arc::new(DashMap::new()),
|
|
||||||
next_id: 1,
|
next_id: 1,
|
||||||
listener: None,
|
listener: None,
|
||||||
}
|
}
|
@@ -1,19 +1,21 @@
|
|||||||
pub mod getset;
|
pub mod component;
|
||||||
|
|
||||||
use addin1c::{name, RawAddin, Variant};
|
use addin1c::{name, RawAddin, Variant};
|
||||||
|
|
||||||
use crate::component::METHODS;
|
pub use component::AddIn;
|
||||||
use crate::component::PROPS;
|
|
||||||
use crate::component::get_params_amount;
|
use component::METHODS;
|
||||||
use crate::component::cal_func;
|
use component::PROPS;
|
||||||
use crate::component::AddIn;
|
use component::get_params_amount;
|
||||||
|
use component::cal_func;
|
||||||
|
use crate::commons::getset;
|
||||||
|
|
||||||
|
|
||||||
// Определение класса
|
// Определение класса
|
||||||
impl RawAddin for AddIn {
|
impl RawAddin for AddIn {
|
||||||
|
|
||||||
fn register_extension_as(&mut self) -> &'static [u16] {
|
fn register_extension_as(&mut self) -> &'static [u16] {
|
||||||
name!("Main")
|
name!("Server")
|
||||||
}
|
}
|
||||||
fn get_n_props(&mut self) -> usize {
|
fn get_n_props(&mut self) -> usize {
|
||||||
PROPS.len()
|
PROPS.len()
|
BIN
src/en/OInt/addins/OPI_TCPServer.zip
vendored
BIN
src/en/OInt/addins/OPI_TCPServer.zip
vendored
Binary file not shown.
Binary file not shown.
BIN
src/ru/OInt/addins/OPI_TCPServer.zip
vendored
BIN
src/ru/OInt/addins/OPI_TCPServer.zip
vendored
Binary file not shown.
@@ -1,4 +1,4 @@
|
|||||||
// OneScript: ./OInt/core/Modules/OPI_TCP.os
|
// OneScript: ./OInt/core/Modules/OPI_TCP.os
|
||||||
// Lib: TCP
|
// Lib: TCP
|
||||||
// CLI: tcp
|
// CLI: tcp
|
||||||
|
|
||||||
@@ -105,6 +105,10 @@
|
|||||||
OPI_ПреобразованиеТипов.ПолучитьЧисло(Таймаут);
|
OPI_ПреобразованиеТипов.ПолучитьЧисло(Таймаут);
|
||||||
OPI_ПреобразованиеТипов.ПолучитьЧисло(МаксимальныйРазмер);
|
OPI_ПреобразованиеТипов.ПолучитьЧисло(МаксимальныйРазмер);
|
||||||
|
|
||||||
|
Если ТипЗнч(Соединение) = Тип("Строка") Тогда
|
||||||
|
Соединение = ОткрытьСоединение(Соединение);
|
||||||
|
КонецЕсли;
|
||||||
|
|
||||||
Если ТипЗнч(Маркер) = Тип("Строка") Тогда
|
Если ТипЗнч(Маркер) = Тип("Строка") Тогда
|
||||||
Маркер = ПолучитьДвоичныеДанныеИзСтроки(Маркер);
|
Маркер = ПолучитьДвоичныеДанныеИзСтроки(Маркер);
|
||||||
Иначе
|
Иначе
|
||||||
@@ -157,6 +161,10 @@
|
|||||||
// Булево - Признак успешного выполнения
|
// Булево - Признак успешного выполнения
|
||||||
Функция ОтправитьДвоичныеДанные(Знач Соединение, Знач Данные, Знач Таймаут = 5000) Экспорт
|
Функция ОтправитьДвоичныеДанные(Знач Соединение, Знач Данные, Знач Таймаут = 5000) Экспорт
|
||||||
|
|
||||||
|
Если ТипЗнч(Соединение) = Тип("Строка") Тогда
|
||||||
|
Соединение = ОткрытьСоединение(Соединение);
|
||||||
|
КонецЕсли;
|
||||||
|
|
||||||
OPI_ПреобразованиеТипов.ПолучитьДвоичныеДанные(Данные);
|
OPI_ПреобразованиеТипов.ПолучитьДвоичныеДанные(Данные);
|
||||||
OPI_ПреобразованиеТипов.ПолучитьЧисло(Таймаут);
|
OPI_ПреобразованиеТипов.ПолучитьЧисло(Таймаут);
|
||||||
|
|
||||||
@@ -244,7 +252,7 @@
|
|||||||
OPI_ПреобразованиеТипов.ПолучитьЧисло(Порт);
|
OPI_ПреобразованиеТипов.ПолучитьЧисло(Порт);
|
||||||
OPI_ПреобразованиеТипов.ПолучитьБулево(Запустить);
|
OPI_ПреобразованиеТипов.ПолучитьБулево(Запустить);
|
||||||
|
|
||||||
TCPСервер = ПодключитьКомпонентуНаСервере("OPI_TCPServer");
|
TCPСервер = ПодключитьКомпонентуНаСервере("OPI_TCPServer", "Server");
|
||||||
TCPСервер.Port = Порт;
|
TCPСервер.Port = Порт;
|
||||||
|
|
||||||
Если Запустить Тогда
|
Если Запустить Тогда
|
||||||
@@ -310,71 +318,6 @@
|
|||||||
|
|
||||||
КонецФункции
|
КонецФункции
|
||||||
|
|
||||||
// Получить данные !NOCLI
|
|
||||||
// Получает данные из потока существующего соединения
|
|
||||||
//
|
|
||||||
// Параметры:
|
|
||||||
// TCPСервер - Произвольный - TCP сервер. См. СоздатьСервер - srv
|
|
||||||
// IDПодключения - Строка, Число - ID активного подключения. См. ОжидатьПодключение - conn
|
|
||||||
// МаксимальныйРазмер - Число - Максимальный размер данных. 0 > до конца потока - maxsize
|
|
||||||
//
|
|
||||||
// Возвращаемое значение:
|
|
||||||
// Структура Из КлючИЗначение, ДвоичныеДанные - Двоичные данные при успехе или структура с описанием ошибки
|
|
||||||
Функция ПолучитьДанные(Знач TCPСервер, Знач IDПодключения, Знач МаксимальныйРазмер = 0) Экспорт
|
|
||||||
|
|
||||||
Если Не ЭтоСервер(TCPСервер) Тогда
|
|
||||||
ВызватьИсключение "Переданное значение не является TCP-сервером!";
|
|
||||||
КонецЕсли;
|
|
||||||
|
|
||||||
OPI_ПреобразованиеТипов.ПолучитьСтроку(IDПодключения);
|
|
||||||
OPI_ПреобразованиеТипов.ПолучитьЧисло(МаксимальныйРазмер);
|
|
||||||
|
|
||||||
Данные = TCPСервер.Receive(IDПодключения, МаксимальныйРазмер);
|
|
||||||
|
|
||||||
Если ТипЗнч(Данные) = Тип("Строка") Тогда
|
|
||||||
Попытка
|
|
||||||
Результат = OPI_Инструменты.JsonВСтруктуру(Данные, Ложь);
|
|
||||||
Исключение
|
|
||||||
Результат = Новый Структура("result,error", Ложь, Данные);
|
|
||||||
КонецПопытки;
|
|
||||||
|
|
||||||
Иначе
|
|
||||||
Результат = Данные;
|
|
||||||
КонецЕсли;
|
|
||||||
|
|
||||||
//@skip-check constructor-function-return-section
|
|
||||||
Возврат Результат;
|
|
||||||
|
|
||||||
КонецФункции
|
|
||||||
|
|
||||||
// Отправить данные !NOCLI
|
|
||||||
// Отправляет данные клиенту по идентификатору подключения
|
|
||||||
//
|
|
||||||
// Параметры:
|
|
||||||
// TCPСервер - Произвольный - TCP сервер. См. СоздатьСервер - srv
|
|
||||||
// IDПодключения - Строка, Число - ID активного подключения. См. ОжидатьПодключение - conn
|
|
||||||
// Данные - ДвоичныеДанные - Данные для отправки - data
|
|
||||||
//
|
|
||||||
// Возвращаемое значение:
|
|
||||||
// Структура Из КлючИЗначение - Информация о выполнении
|
|
||||||
Функция ОтправитьДанные(Знач TCPСервер, Знач IDПодключения, Знач Данные) Экспорт
|
|
||||||
|
|
||||||
Если Не ЭтоСервер(TCPСервер) Тогда
|
|
||||||
ВызватьИсключение "Переданное значение не является TCP-сервером!";
|
|
||||||
КонецЕсли;
|
|
||||||
|
|
||||||
OPI_ПреобразованиеТипов.ПолучитьСтроку(IDПодключения);
|
|
||||||
OPI_ПреобразованиеТипов.ПолучитьДвоичныеДанные(Данные);
|
|
||||||
|
|
||||||
Результат = TCPСервер.Send(IDПодключения, Данные);
|
|
||||||
|
|
||||||
ОбработатьРезультат(Результат);
|
|
||||||
|
|
||||||
//@skip-check constructor-function-return-section
|
|
||||||
Возврат Результат;
|
|
||||||
|
|
||||||
КонецФункции
|
|
||||||
|
|
||||||
// Отключить сервер !NOCLI
|
// Отключить сервер !NOCLI
|
||||||
// Останавливает запущенный сервер
|
// Останавливает запущенный сервер
|
||||||
//
|
//
|
||||||
@@ -398,32 +341,6 @@
|
|||||||
|
|
||||||
КонецФункции
|
КонецФункции
|
||||||
|
|
||||||
// Закрыть входящее соединение !NOCLI
|
|
||||||
// Закрывает существующее соединение по идентификатору
|
|
||||||
//
|
|
||||||
// Параметры:
|
|
||||||
// TCPСервер - Произвольный - TCP сервер. См. СоздатьСервер - srv
|
|
||||||
// IDПодключения - Строка, Число - ID активного подключения. См. ОжидатьПодключение - conn
|
|
||||||
//
|
|
||||||
// Возвращаемое значение:
|
|
||||||
// Структура Из КлючИЗначение - Информация о выполнении
|
|
||||||
Функция ЗакрытьВходящееСоединение(Знач TCPСервер, Знач IDПодключения) Экспорт
|
|
||||||
|
|
||||||
Если Не ЭтоСервер(TCPСервер) Тогда
|
|
||||||
ВызватьИсключение "Переданное значение не является TCP-сервером!";
|
|
||||||
КонецЕсли;
|
|
||||||
|
|
||||||
OPI_ПреобразованиеТипов.ПолучитьСтроку(IDПодключения);
|
|
||||||
|
|
||||||
Результат = TCPСервер.Close(IDПодключения);
|
|
||||||
|
|
||||||
ОбработатьРезультат(Результат);
|
|
||||||
|
|
||||||
//@skip-check constructor-function-return-section
|
|
||||||
Возврат Результат;
|
|
||||||
|
|
||||||
КонецФункции
|
|
||||||
|
|
||||||
// Получить входящие соединения !NOCLI
|
// Получить входящие соединения !NOCLI
|
||||||
// Получает список соединений в пуле
|
// Получает список соединений в пуле
|
||||||
//
|
//
|
||||||
@@ -474,6 +391,107 @@
|
|||||||
|
|
||||||
КонецФункции
|
КонецФункции
|
||||||
|
|
||||||
|
// Получить данные !NOCLI
|
||||||
|
// Получает данные из потока существующего соединения
|
||||||
|
//
|
||||||
|
// Параметры:
|
||||||
|
// TCPОбработчик - Произвольный - TCP сервер или идентификатор подключения - hnd
|
||||||
|
// IDПодключения - Строка, Число - ID активного подключения. См. ОжидатьПодключение - conn
|
||||||
|
// МаксимальныйРазмер - Число - Максимальный размер данных. 0 > до конца потока - maxsize
|
||||||
|
//
|
||||||
|
// Возвращаемое значение:
|
||||||
|
// Структура Из КлючИЗначение, ДвоичныеДанные - Двоичные данные при успехе или структура с описанием ошибки
|
||||||
|
Функция ПолучитьДанные(Знач TCPОбработчик, Знач IDПодключения, Знач МаксимальныйРазмер = 0) Экспорт
|
||||||
|
|
||||||
|
Если Не ЭтоСервер(TCPОбработчик) Тогда
|
||||||
|
|
||||||
|
OPI_ПреобразованиеТипов.ПолучитьСтроку(TCPОбработчик);
|
||||||
|
TCPОбработчик = ПодключитьКомпонентуНаСервере("OPI_TCPServer", "Handler");
|
||||||
|
|
||||||
|
КонецЕсли;
|
||||||
|
|
||||||
|
OPI_ПреобразованиеТипов.ПолучитьСтроку(IDПодключения);
|
||||||
|
OPI_ПреобразованиеТипов.ПолучитьЧисло(МаксимальныйРазмер);
|
||||||
|
|
||||||
|
Данные = TCPОбработчик.Receive(IDПодключения, МаксимальныйРазмер);
|
||||||
|
|
||||||
|
Если ТипЗнч(Данные) = Тип("Строка") Тогда
|
||||||
|
Попытка
|
||||||
|
Результат = OPI_Инструменты.JsonВСтруктуру(Данные, Ложь);
|
||||||
|
Исключение
|
||||||
|
Результат = Новый Структура("result,error", Ложь, Данные);
|
||||||
|
КонецПопытки;
|
||||||
|
|
||||||
|
Иначе
|
||||||
|
Результат = Данные;
|
||||||
|
КонецЕсли;
|
||||||
|
|
||||||
|
//@skip-check constructor-function-return-section
|
||||||
|
Возврат Результат;
|
||||||
|
|
||||||
|
КонецФункции
|
||||||
|
|
||||||
|
// Отправить данные !NOCLI
|
||||||
|
// Отправляет данные клиенту по идентификатору подключения
|
||||||
|
//
|
||||||
|
// Параметры:
|
||||||
|
// TCPОбработчик - Произвольный - TCP сервер или идентификатор подключения - hnd
|
||||||
|
// IDПодключения - Строка, Число - ID активного подключения. См. ОжидатьПодключение - conn
|
||||||
|
// Данные - ДвоичныеДанные - Данные для отправки - data
|
||||||
|
//
|
||||||
|
// Возвращаемое значение:
|
||||||
|
// Структура Из КлючИЗначение - Информация о выполнении
|
||||||
|
Функция ОтправитьДанные(Знач TCPОбработчик, Знач IDПодключения, Знач Данные) Экспорт
|
||||||
|
|
||||||
|
Если Не ЭтоСервер(TCPОбработчик) Тогда
|
||||||
|
|
||||||
|
OPI_ПреобразованиеТипов.ПолучитьСтроку(TCPОбработчик);
|
||||||
|
TCPОбработчик = ПодключитьКомпонентуНаСервере("OPI_TCPServer", "Handler");
|
||||||
|
|
||||||
|
КонецЕсли;
|
||||||
|
|
||||||
|
OPI_ПреобразованиеТипов.ПолучитьСтроку(IDПодключения);
|
||||||
|
OPI_ПреобразованиеТипов.ПолучитьДвоичныеДанные(Данные);
|
||||||
|
|
||||||
|
Результат = TCPОбработчик.Send(IDПодключения, Данные);
|
||||||
|
|
||||||
|
ОбработатьРезультат(Результат);
|
||||||
|
|
||||||
|
//@skip-check constructor-function-return-section
|
||||||
|
Возврат Результат;
|
||||||
|
|
||||||
|
КонецФункции
|
||||||
|
|
||||||
|
// Закрыть входящее соединение !NOCLI
|
||||||
|
// Закрывает существующее соединение по идентификатору
|
||||||
|
//
|
||||||
|
// Параметры:
|
||||||
|
// TCPОбработчик - Произвольный - TCP сервер или идентификатор подключения - hnd
|
||||||
|
// IDПодключения - Строка, Число - ID активного подключения. См. ОжидатьПодключение - conn
|
||||||
|
//
|
||||||
|
// Возвращаемое значение:
|
||||||
|
// Структура Из КлючИЗначение - Информация о выполнении
|
||||||
|
Функция ЗакрытьВходящееСоединение(Знач TCPОбработчик, Знач IDПодключения) Экспорт
|
||||||
|
|
||||||
|
Если Не ЭтоСервер(TCPОбработчик) Тогда
|
||||||
|
|
||||||
|
OPI_ПреобразованиеТипов.ПолучитьСтроку(TCPОбработчик);
|
||||||
|
TCPОбработчик = ПодключитьКомпонентуНаСервере("OPI_TCPServer", "Handler");
|
||||||
|
|
||||||
|
КонецЕсли;
|
||||||
|
|
||||||
|
OPI_ПреобразованиеТипов.ПолучитьСтроку(IDПодключения);
|
||||||
|
|
||||||
|
Результат = TCPОбработчик.Close(IDПодключения);
|
||||||
|
|
||||||
|
ОбработатьРезультат(Результат);
|
||||||
|
|
||||||
|
//@skip-check constructor-function-return-section
|
||||||
|
Возврат Результат;
|
||||||
|
|
||||||
|
КонецФункции
|
||||||
|
|
||||||
|
|
||||||
// Это сервер !NOCLI
|
// Это сервер !NOCLI
|
||||||
// Определяет, является ли переданное значение объектом TCP сервера
|
// Определяет, является ли переданное значение объектом TCP сервера
|
||||||
//
|
//
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
// OneScript: ./OInt/core/Modules/OPI_Telegram.os
|
// OneScript: ./OInt/core/Modules/OPI_Telegram.os
|
||||||
// Lib: Telegram
|
// Lib: Telegram
|
||||||
// CLI: telegram
|
// CLI: telegram
|
||||||
|
|
||||||
@@ -213,6 +213,8 @@
|
|||||||
// Соответствие из Строка - Соответствие данных с результатом проверки в поле passed
|
// Соответствие из Строка - Соответствие данных с результатом проверки в поле passed
|
||||||
Функция ОбработатьДанныеTMA(Знач СтрокаДанных, Знач Токен) Экспорт
|
Функция ОбработатьДанныеTMA(Знач СтрокаДанных, Знач Токен) Экспорт
|
||||||
|
|
||||||
|
#Если Сервер Тогда
|
||||||
|
|
||||||
OPI_ПреобразованиеТипов.ПолучитьСтроку(Токен);
|
OPI_ПреобразованиеТипов.ПолучитьСтроку(Токен);
|
||||||
OPI_ПреобразованиеТипов.ПолучитьСтроку(СтрокаДанных);
|
OPI_ПреобразованиеТипов.ПолучитьСтроку(СтрокаДанных);
|
||||||
|
|
||||||
@@ -280,6 +282,10 @@
|
|||||||
СоответствиеВозврата.Вставить("passed", Ответ);
|
СоответствиеВозврата.Вставить("passed", Ответ);
|
||||||
|
|
||||||
Возврат СоответствиеВозврата;
|
Возврат СоответствиеВозврата;
|
||||||
|
|
||||||
|
#Иначе
|
||||||
|
Возврат Неопределено;
|
||||||
|
#КонецЕсли
|
||||||
|
|
||||||
КонецФункции
|
КонецФункции
|
||||||
|
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
// OneScript: ./OInt/tools/Modules/OPI_ПолучениеДанныхТестов.os
|
// OneScript: ./OInt/tools/Modules/OPI_ПолучениеДанныхТестов.os
|
||||||
|
|
||||||
// MIT License
|
// MIT License
|
||||||
|
|
||||||
@@ -2097,6 +2097,12 @@
|
|||||||
ОжидаетЧто(Результат["data"].Количество()).Равно(0);
|
ОжидаетЧто(Результат["data"].Количество()).Равно(0);
|
||||||
КонецПроцедуры
|
КонецПроцедуры
|
||||||
|
|
||||||
|
Процедура Проверка_РезультатИстина(Знач Результат) Экспорт
|
||||||
|
|
||||||
|
ОжидаетЧто(Результат["result"]).Равно(Истина);
|
||||||
|
|
||||||
|
КонецПроцедуры
|
||||||
|
|
||||||
#КонецОбласти
|
#КонецОбласти
|
||||||
|
|
||||||
#КонецОбласти
|
#КонецОбласти
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
// OneScript: ./OInt/tests/Modules/internal/OPI_Тесты.os
|
// OneScript: ./OInt/tests/Modules/internal/OPI_Тесты.os
|
||||||
|
|
||||||
// MIT License
|
// MIT License
|
||||||
|
|
||||||
@@ -16455,24 +16455,72 @@
|
|||||||
КонецПроцедуры
|
КонецПроцедуры
|
||||||
|
|
||||||
Процедура TCP_ОжидатьПодключение(ПараметрыФункции) Экспорт
|
Процедура TCP_ОжидатьПодключение(ПараметрыФункции) Экспорт
|
||||||
|
|
||||||
|
// -- Тестовый запрос для сервера
|
||||||
|
|
||||||
|
КлючКлиента = Новый УникальныйИдентификатор;
|
||||||
|
Сообщение = "Тестовая отправка данных на сервер";
|
||||||
|
|
||||||
|
ПараметрыКлиента = Новый Массив;
|
||||||
|
ПараметрыКлиента.Добавить("127.0.0.1:7788"); // Наш сервер
|
||||||
|
ПараметрыКлиента.Добавить(Сообщение); // Тестовая строка
|
||||||
|
ПараметрыКлиента.Добавить("UTF-8"); // Кодировка
|
||||||
|
ПараметрыКлиента.Добавить("20000"); // Таймаут, чтобы успеть запустить сервер
|
||||||
|
|
||||||
|
// Отправка тестового запроса через клиентские методы TCP
|
||||||
|
ФоновоеКлиента = ФоновыеЗадания.Выполнить("OPI_TCP.ОтправитьСтроку", ПараметрыКлиента, КлючКлиента);
|
||||||
|
|
||||||
TCPСервер = OPI_TCP.СоздатьСервер(7788, Истина);
|
// --
|
||||||
|
|
||||||
|
TCPСервер = OPI_TCP.СоздатьСервер(7788, Истина);
|
||||||
|
|
||||||
|
НовоеПодключение = OPI_TCP.ОжидатьПодключение(TCPСервер, 20);
|
||||||
|
|
||||||
|
Если НовоеПодключение["result"] Тогда
|
||||||
|
|
||||||
|
Подключение = НовоеПодключение["connection"]["id"];
|
||||||
|
Адрес = НовоеПодключение.connection.addr;
|
||||||
|
|
||||||
|
ЗапросДвочиные = OPI_TCP.ПолучитьДанные(TCPСервер, Подключение);
|
||||||
|
ЗапросТекст = ПолучитьСтрокуИзДвоичныхДанных(ЗапросДвочиные);
|
||||||
|
|
||||||
|
ОтправкаОтвета = OPI_TCP.ОтправитьДанные(TCPСервер, Подключение, ПолучитьДвоичныеДанныеИзСтроки("Yo"));
|
||||||
|
ЗакрытиеПодключения = OPI_TCP.ЗакрытьВходящееСоединение(TCPСервер, Подключение);
|
||||||
|
|
||||||
Для Н = 1 По 5 Цикл
|
КонецЕсли;
|
||||||
|
|
||||||
|
Отключение = OPI_TCP.ОтключитьСервер(TCPСервер);
|
||||||
|
|
||||||
|
ЗаданиеКлиента = ФоновоеКлиента.ОжидатьЗавершенияВыполнения();
|
||||||
|
|
||||||
|
// END
|
||||||
|
|
||||||
|
OPI_ПолучениеДанныхТестов.ЗаписатьЛог(TCPСервер, "ОжидатьПодключение (сервер)", "TCP");
|
||||||
|
OPI_ПолучениеДанныхТестов.Проверка_Компонента(TCPСервер, "AddIn.OPI_TCPServer.Main");
|
||||||
|
|
||||||
НовоеПодключение = OPI_TCP.ОжидатьПодключение(TCPСервер, 20);
|
OPI_ПолучениеДанныхТестов.ЗаписатьЛог(TCPСервер, "ОжидатьПодключение", "TCP");
|
||||||
|
OPI_ПолучениеДанныхТестов.Проверка_РезультатИстина(НовоеПодключение);
|
||||||
|
|
||||||
Если НовоеПодключение["result"] Тогда
|
OPI_ПолучениеДанныхТестов.ЗаписатьЛог(TCPСервер, "ОжидатьПодключение (подключение)", "TCP");
|
||||||
Подключение = НовоеПодключение["connection"];
|
OPI_ПолучениеДанныхТестов.Проверка_Строка(Подключение, "1");
|
||||||
Иначе
|
|
||||||
Продолжить;
|
OPI_ПолучениеДанныхТестов.ЗаписатьЛог(TCPСервер, "ОжидатьПодключение (сообщение)", "TCP");
|
||||||
КонецЕсли;
|
OPI_ПолучениеДанныхТестов.Проверка_Строка(ЗапросТекст, Сообщение);
|
||||||
|
|
||||||
Ответ = OPI_TCP.ОтправитьДанные(TCPСервер, Подключение, ПолучитьДвоичныеДанныеИзСтроки("Yo"));
|
OPI_ПолучениеДанныхТестов.ЗаписатьЛог(TCPСервер, "ОжидатьПодключение (ответ)", "TCP");
|
||||||
Закрытие = OPI_TCP.ЗакрытьВходящееСоединение(TCPСервер, Подключение);
|
OPI_ПолучениеДанныхТестов.Проверка_РезультатИстина(ОтправкаОтвета);
|
||||||
|
|
||||||
КонецЦикла;
|
OPI_ПолучениеДанныхТестов.ЗаписатьЛог(TCPСервер, "ОжидатьПодключение (закрытие)", "TCP");
|
||||||
|
OPI_ПолучениеДанныхТестов.Проверка_РезультатИстина(ЗакрытиеПодключения);
|
||||||
|
|
||||||
|
OPI_ПолучениеДанныхТестов.ЗаписатьЛог(TCPСервер, "ОжидатьПодключение (отвключение)", "TCP");
|
||||||
|
OPI_ПолучениеДанныхТестов.Проверка_РезультатИстина(Отключение);
|
||||||
|
|
||||||
|
Состояние = Строка(ЗаданиеКлиента.Состояние);
|
||||||
|
|
||||||
|
OPI_ПолучениеДанныхТестов.ЗаписатьЛог(TCPСервер, "ОжидатьПодключение (фоновое)", "TCP");
|
||||||
|
OPI_ПолучениеДанныхТестов.Проверка_Строка(Состояние, "Задание выполнено");
|
||||||
|
|
||||||
КонецПроцедуры
|
КонецПроцедуры
|
||||||
|
|
||||||
#КонецОбласти
|
#КонецОбласти
|
||||||
|
Binary file not shown.
Reference in New Issue
Block a user