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 = [
|
||||
"addin1c",
|
||||
"dashmap",
|
||||
"once_cell",
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
|
@@ -15,4 +15,5 @@ strip = true # Automatically strip symbols from the binary.
|
||||
[dependencies]
|
||||
addin1c = "0.5.0"
|
||||
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;
|
||||
mod core;
|
||||
pub mod server;
|
||||
pub mod handler;
|
||||
pub mod commons;
|
||||
|
||||
|
||||
use std::{
|
||||
@@ -7,18 +8,25 @@ use std::{
|
||||
sync::atomic::{AtomicI32, Ordering},
|
||||
};
|
||||
|
||||
use component::AddIn;
|
||||
use addin1c::{create_component, destroy_component, name, AttachType};
|
||||
|
||||
pub static mut PLATFORM_CAPABILITIES: AtomicI32 = AtomicI32::new(-1);
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn GetClassObject(_name: *const u16, component: *mut *mut c_void) -> c_long {
|
||||
|
||||
let addin = AddIn::new();
|
||||
create_component(component, addin)
|
||||
pub unsafe extern "C" fn GetClassObject(name: *const u16, component: *mut *mut c_void) -> c_long {
|
||||
|
||||
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)]
|
||||
@@ -30,7 +38,7 @@ pub unsafe extern "C" fn DestroyObject(component: *mut *mut c_void) -> c_long {
|
||||
#[allow(non_snake_case)]
|
||||
#[no_mangle]
|
||||
pub extern "C" fn GetClassNames() -> *const u16 {
|
||||
name!("Main").as_ptr()
|
||||
name!("Server|Handler").as_ptr()
|
||||
}
|
||||
|
||||
#[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;
|
||||
|
||||
use addin1c::{name, Variant};
|
||||
use crate::core::getset;
|
||||
use std::net::{TcpListener, TcpStream};
|
||||
use std::sync::{Arc, Mutex};
|
||||
use dashmap::DashMap;
|
||||
use std::net::{TcpListener};
|
||||
|
||||
pub use crate::commons::{getset, CONNECTIONS};
|
||||
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 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 => {
|
||||
|
||||
let connection_id = params[0].get_string().unwrap_or("".to_string());
|
||||
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 => {
|
||||
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)),
|
||||
6 => Box::new(methods::remove_inactive_connections(obj)),
|
||||
5 => Box::new(methods::list_connections()),
|
||||
6 => Box::new(methods::remove_inactive_connections()),
|
||||
7 => Box::new(methods::stop_server(obj)),
|
||||
_ => Box::new(false), // Неверный номер команды
|
||||
}
|
||||
@@ -87,7 +87,6 @@ pub const PROPS: &[&[u16]] = &[
|
||||
|
||||
pub struct AddIn {
|
||||
port: i32,
|
||||
connections: Arc<DashMap<String, Arc<Mutex<TcpStream>>>>,
|
||||
next_id: i32,
|
||||
listener: Option<TcpListener>,
|
||||
}
|
||||
@@ -97,7 +96,6 @@ impl AddIn {
|
||||
pub fn new() -> Self {
|
||||
AddIn {
|
||||
port: 0,
|
||||
connections: Arc::new(DashMap::new()),
|
||||
next_id: 1,
|
||||
listener: None,
|
||||
}
|
@@ -1,19 +1,21 @@
|
||||
pub mod getset;
|
||||
pub mod component;
|
||||
|
||||
use addin1c::{name, RawAddin, Variant};
|
||||
|
||||
use crate::component::METHODS;
|
||||
use crate::component::PROPS;
|
||||
use crate::component::get_params_amount;
|
||||
use crate::component::cal_func;
|
||||
use crate::component::AddIn;
|
||||
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!("Main")
|
||||
name!("Server")
|
||||
}
|
||||
fn get_n_props(&mut self) -> usize {
|
||||
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
|
||||
// CLI: tcp
|
||||
|
||||
@@ -105,6 +105,10 @@
|
||||
OPI_ПреобразованиеТипов.ПолучитьЧисло(Таймаут);
|
||||
OPI_ПреобразованиеТипов.ПолучитьЧисло(МаксимальныйРазмер);
|
||||
|
||||
Если ТипЗнч(Соединение) = Тип("Строка") Тогда
|
||||
Соединение = ОткрытьСоединение(Соединение);
|
||||
КонецЕсли;
|
||||
|
||||
Если ТипЗнч(Маркер) = Тип("Строка") Тогда
|
||||
Маркер = ПолучитьДвоичныеДанныеИзСтроки(Маркер);
|
||||
Иначе
|
||||
@@ -157,6 +161,10 @@
|
||||
// Булево - Признак успешного выполнения
|
||||
Функция ОтправитьДвоичныеДанные(Знач Соединение, Знач Данные, Знач Таймаут = 5000) Экспорт
|
||||
|
||||
Если ТипЗнч(Соединение) = Тип("Строка") Тогда
|
||||
Соединение = ОткрытьСоединение(Соединение);
|
||||
КонецЕсли;
|
||||
|
||||
OPI_ПреобразованиеТипов.ПолучитьДвоичныеДанные(Данные);
|
||||
OPI_ПреобразованиеТипов.ПолучитьЧисло(Таймаут);
|
||||
|
||||
@@ -244,7 +252,7 @@
|
||||
OPI_ПреобразованиеТипов.ПолучитьЧисло(Порт);
|
||||
OPI_ПреобразованиеТипов.ПолучитьБулево(Запустить);
|
||||
|
||||
TCPСервер = ПодключитьКомпонентуНаСервере("OPI_TCPServer");
|
||||
TCPСервер = ПодключитьКомпонентуНаСервере("OPI_TCPServer", "Server");
|
||||
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
|
||||
// Останавливает запущенный сервер
|
||||
//
|
||||
@@ -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
|
||||
// Получает список соединений в пуле
|
||||
//
|
||||
@@ -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
|
||||
// Определяет, является ли переданное значение объектом TCP сервера
|
||||
//
|
||||
|
@@ -1,4 +1,4 @@
|
||||
// OneScript: ./OInt/core/Modules/OPI_Telegram.os
|
||||
// OneScript: ./OInt/core/Modules/OPI_Telegram.os
|
||||
// Lib: Telegram
|
||||
// CLI: telegram
|
||||
|
||||
@@ -213,6 +213,8 @@
|
||||
// Соответствие из Строка - Соответствие данных с результатом проверки в поле passed
|
||||
Функция ОбработатьДанныеTMA(Знач СтрокаДанных, Знач Токен) Экспорт
|
||||
|
||||
#Если Сервер Тогда
|
||||
|
||||
OPI_ПреобразованиеТипов.ПолучитьСтроку(Токен);
|
||||
OPI_ПреобразованиеТипов.ПолучитьСтроку(СтрокаДанных);
|
||||
|
||||
@@ -280,6 +282,10 @@
|
||||
СоответствиеВозврата.Вставить("passed", Ответ);
|
||||
|
||||
Возврат СоответствиеВозврата;
|
||||
|
||||
#Иначе
|
||||
Возврат Неопределено;
|
||||
#КонецЕсли
|
||||
|
||||
КонецФункции
|
||||
|
||||
|
@@ -1,4 +1,4 @@
|
||||
// OneScript: ./OInt/tools/Modules/OPI_ПолучениеДанныхТестов.os
|
||||
// OneScript: ./OInt/tools/Modules/OPI_ПолучениеДанныхТестов.os
|
||||
|
||||
// MIT License
|
||||
|
||||
@@ -2097,6 +2097,12 @@
|
||||
ОжидаетЧто(Результат["data"].Количество()).Равно(0);
|
||||
КонецПроцедуры
|
||||
|
||||
Процедура Проверка_РезультатИстина(Знач Результат) Экспорт
|
||||
|
||||
ОжидаетЧто(Результат["result"]).Равно(Истина);
|
||||
|
||||
КонецПроцедуры
|
||||
|
||||
#КонецОбласти
|
||||
|
||||
#КонецОбласти
|
||||
|
@@ -1,4 +1,4 @@
|
||||
// OneScript: ./OInt/tests/Modules/internal/OPI_Тесты.os
|
||||
// OneScript: ./OInt/tests/Modules/internal/OPI_Тесты.os
|
||||
|
||||
// MIT License
|
||||
|
||||
@@ -16455,24 +16455,72 @@
|
||||
КонецПроцедуры
|
||||
|
||||
Процедура 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"] Тогда
|
||||
Подключение = НовоеПодключение["connection"];
|
||||
Иначе
|
||||
Продолжить;
|
||||
КонецЕсли;
|
||||
OPI_ПолучениеДанныхТестов.ЗаписатьЛог(TCPСервер, "ОжидатьПодключение (подключение)", "TCP");
|
||||
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