.Net Core、さまざまなデバイス間でTCP / IP上の1Cと交換

「フィールドからのニュース」から始めます。 .NET Core 1.0.1のアップデートがリリースされました 。 私にとっての主なことは、Windowsでのアクセス違反でした-coreclr 6460:
Jitstartupでは、JITはstdoutのファイル記述子を作成し、失敗をチェックせずに無条件にsetmodeに渡します。 これはで起こります。 無効な記述子の確認に失敗すると、setmodeがフェイルファストをトリガーする可能性があります。

このエラーのため、64ビット1Cクライアントで静的.Netメソッドが呼び出されたときに例外がスローされました。

1cv8.exeの0x00007FFD76FB8528(ucrtbase.dll)で未処理の例外:無効なパラメーターが致命的なエラーの原因となる関数に無効なパラメーターが渡されました。

これで修復され、8.3.9の64ビットクライアントでコードが正常に実行されます。 例では、.NET Coreライブラリを1.0.1に置き換えました。 SignalRについて書きたかったのですが、今のところ.Net Coreにしかサーバーを書くことができません-Windows 10 UWPアプリ用ASP.NET Core SignalR

aspnet / SignalR-Server

まだ顧客がいません。 WCFでは、これまでのところ、Webサービスのクライアントのみ。 ServiceHost番号 サードパーティのソリューションがあります。NETコアクロスプラットフォームリモートサービス呼び出し

しかし、私は8年前の経験から、Win CE上のTSDと1Cの間でTCP / IPデータを交換して、さらに7試合の解決策を書くことにしました。 もちろん、1CはWebサービスを介して交換できますが、データの選択、クライアントで準備されたデータの取得、モバイルプリンターへの印刷のためにオペレーターとのやり取りが必要なタスクがあります。

主な問題は、倉庫内の接続不良のネットワークに関連しています。 そのため、データ圧縮によるトラフィックを削減する必要がありました。 そのため、ターミナルセッションで作業する場合、低速ネットワークでのポート転送に問題がありました。RDPを介した会計レジストラへの小切手印刷が遅くなります。

2次元バーコードの読み取りにも問題がありました。 ターミナルサーバーからの印刷が遅い。 これらの問題を解決するために、ローカルの1Cがクライアントマシンにインストールされ、クライアントとサーバーとして機能しました。 スキャナーからのデータは、ターミナルサーバーに送信され、そこで処理されました。 会計レジストラに印刷するために、サーバーからTCP / IP経由でデータが送信され、ローカル1Cからチェックが印刷されました。 サーバーからラベルを印刷するとき、データが送信され、それに基づいてローカル1Cでドキュメントが形成され、印刷に送信されました。

さらに、多くのLinuxハードウェア用のドライバーはありません。 仮想化を使用すると、LinuxとWindowsを同じマシンに保持し、Windowsでデータを読み取り、TCP / IPを介してLinuxと通信できます。

現在、多くの人がWinCe、WinMoの下にTSDを持っています(最近、彼らのために交換をセットアップする作業を提供しました)。 さらに、UWPとXamarinを使用して、他の軸でTSDを使用できます。

さらに、チャットなど、1Cクライアント間でメッセージを交換できます。

大規模な.Netでは、TCp / IP共有をよく使用します
1C 7.xb 8.xでの.NETアセンブリの使用。 外部コンポーネントの作成。

1Cからデータを受信するワイヤレススキャナーとしてWM 6のTSDを使用する

そのため、同じ交換を記述しますが、.Net Coreで新しいアプローチを追加することにしました。

純粋な1Cニックネームは、敵のコードをスキップして、このコンポーネントの使用方法の記事の最後にあるネイティブコードに移動できます。

圧縮データを使用したメッセージング用のクラスを作成する必要がありました。 データを送信するために、メソッドが使用されました:

//     //     // string        // string        // bool         public TCP (string , int , string , string , bool ) 

1C側では、このクラスが受け入れられます

  //    1    public class 1 { public bool ; public string ; public string ; TcpClient ; public 1( , TcpClient ) { this. = .; this. = .; this. = .; if () this. = ; else //       { .Dispose(); this. = null; } } //    //  ,     1    //   public void (string ) { Task.Run(() => { var strim = .GetStream(); .WriteCompressedString(strim, ); //   strim.Dispose(); .Dispose(); }); } public override string ToString() { return $"={}, ={}, ={}"; } } 

8年前に小さな変更を加えて作成されたメッセージを生成するためのモジュール。

それでも、私はRuslishを力強くメインで使用しました。

たくさんのコード
ExchangePTSPのパブリッククラス
{
public static readonly Encoding CurrentEncoder; // = Encoding.GetEncoding(1251);

static for ExchangePTSP()
{

//ここに.Net Coreの機能があります
//プロバイダーを登録する必要があります
//そして、依存関係「System.Text.Encoding.CodePages」に書き込みます
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
// CurrentEncoder = Encoding.GetEncoding( "windows-1251");
// Ruslishを使用するため、エンコーディング1251を使用します
CurrentEncoder = Encoding.GetEncoding(1251);

}

public static byte [] Unload Data(byte [] Commandのデータ配列)
{
var memStream = new MemoryStream(コマンドのデータ配列);
var DecompressStream = new MemoryStream();
使用(GZipStream gzipStream = new GZipStream(memStream、CompressionMode.Decompress、false))
{

バイト[]バッファー=新しいバイト[1 << 16];
int h;
while((h = gzipStream.Read(buffer、0、buffer.Length))> 0)
{
DecompressStream.Write(buffer、0、h);
}
}
return DecompressStream.ToArray();
}

//
public static byte [] CompressData(バイト[]値)
{
var memStream = new MemoryStream();
memStream.Position = 0;
使用(GZipStream gzipStream = new GZipStream(memStream、CompressionMode.Compress))
{
gzipStream.Write(Value、0、Value.Length);
gzipStream.Flush();
}
return memStream.ToArray();

}

//受信したデータのサイズを知っているNetworkStreamからの古典的な読み取り
private static byte [] Byte Array FromStream(NetworkStreamストリーム、int配列サイズ)
{
バイト[]結果=新しいバイト[配列サイズ];
カウントされた文字のint数= 0;
while(配列サイズ>カウントされる文字の数)
{
カウントされたシンボルの数+ = stream.Read(結果、カウントされたシンボルの数、配列のサイズ-カウントされたシンボルの数);
}

結果を返す;
}

public static voidバイトの配列をストリームに書き込みます(NetworkStreamストリーム、バイト[]配列)
{

stream.Write(Array、0、Array.Length);
}

//ストリームから1バイトを読み取り、boolに変換します
public static bool ReadBool(NetworkStreamストリーム)
{
return BitConverter.ToBoolean(ストリームからのバイトの配列(ストリーム、1)、0);
}

// boolを1バイトに変換し、ストリームに書き込みます
public static void Write(NetworkStreamストリーム、bool値)
{
バイトの配列をストリームに書き込む(ストリーム、BitConverter.GetBytes(値));

}

//ストリームから4バイトを読み取り、intに変換します
public static Int32 ReadInt32(NetworkStreamストリーム)
{
return BitConverter.ToInt32(ストリームからのバイトの配列(ストリーム、4)、0);
}

// intを4バイトに変換してストリームに書き込みます
public static void Write(NetworkStreamストリーム、Int32値)
{
バイトの配列をストリームに書き込む(ストリーム、BitConverter.GetBytes(値));

}

//行を読み取ります。 まず、intデータのサイズがあります
//次に、データを読み取り、エンコーディング1251を使用して文字列を取得します
public static string ReadString(NetworkStreamストリーム)
{
intデータサイズ= ReadInt32(ストリーム);
if(Data Size == 0)return "";

return CurrentEncoder.GetString(ストリームからのバイト配列(ストリーム、データサイズ));
}

//行を書きます。 最初に、文字列のサイズを書き込み、次にエンコーディング1251を使用してバイト[]に変換します
public static void Write(NetworkStreamストリーム、文字列値)
{
if(Value.Length == 0)
{
書き込み(ストリーム、0);
帰る
}
バイト[]結果= CurrentEncoder.GetBytes(値);
Write(stream、result.Length);
ストリーム(ストリーム、結果)にバイトの配列を書き込みます。

}

// WriteCompressedStringは逆の操作である
public static string ReadCompressedString(NetworkStreamストリーム)
{
// intData Size = ReadInt32(ストリーム);
// CurrentEncoder.GetString(ストリームからのバイト配列(ストリーム、データサイズ))を返します;
boolこれは、圧縮文字列= ReadBool(ストリーム)です。

if(!これは圧縮された文字列です)return ReadString(stream);

コマンドのintデータサイズ= BitConverter.ToInt32(ストリームからのバイト配列(ストリーム、4)、0);
byte []コマンドのデータ配列=ストリームのバイト配列(ストリーム、コマンドのデータサイズ)。
コマンドのデータ配列=データのアンロード(コマンドのデータ配列);
return CurrentEncoder.GetString(チームのデータ配列);

}

// GZIP文字列を圧縮しようとしています。 圧縮データのサイズが元のサイズよりも小さい場合は、圧縮データを書き込みます
//それ以外の場合は元の
//次の順序でデータを書き込みます
// boolデータ圧縮フラグ
// intデータサイズ
//バイト[]データ
public static void WriteCompressedString(NetworkStreamストリーム、文字列値)
{
if(Value.Length == 0)
{
書き込み(ストリーム、false);
書き込み(ストリーム、0);
帰る
}

バイト[]結果= CurrentEncoder.GetBytes(値);
var CompressedData = CompressData(結果);
if(result.Length> Compressed Data.Length)
{
書き込み(ストリーム、true);
書き込み(ストリーム、圧縮されたData.Length);
バイト配列をストリームに書き込む(ストリーム、圧縮データ);
}
他に
{
書き込み(ストリーム、false);
Write(stream、result.Length);
ストリーム(ストリーム、結果)にバイトの配列を書き込みます。
}


}

//サーバーにデータを送信します
//文字列コマンドは、データを処理するメソッドの名前です
//文字列DataFor a Command、これは文字列としてシリアル化されたデータです
// bool Is応答は、データを処理するメソッドの関数またはプロシージャのサインです
public static void Send to Team(NetworkStream strim、string Command、string Data For Team、boolはい
{
書きます(ストリム、答えがあります)。
書き込み(ストリム、コマンド);
WriteCompressedString(ストリーム、コマンドのデータ);
}

//クライアントからデータを読み取ります
パブリックスタティック構造メッセージ受け入れチーム(NetworkStream strim)
{
bool IsAnswer = ReadBool(ストリム);
string Command = ReadString(ストリム);
string DataFor Command = ReadCompressedString(strim);
新しいメッセージ構造(コマンド、コマンドのデータ、返信があります)を返します。
}
}



サーバー上でリスニング用のクラスが作成されます。

 //       public class TCPConnector { TcpListener Server; //      //     "System.Diagnostics.TextWriterTraceListener" //      DLL TextWriterTraceListener myTextListener; //     bool  = false; //       Socket ; //       1 //        1 public Action<string, string, object> 1; //       public Action<string> 1; //       string AssemblyDirectory { get { string codeBase = typeof(TCPConnector).GetTypeInfo().Assembly.Location; UriBuilder uri = new UriBuilder(codeBase); string path = Uri.UnescapeDataString(uri.Path); return Path.GetDirectoryName(path) + @"\"; } } public TCPConnector() { myTextListener = null; } //   a       1 void (string ) { if (myTextListener == null) { try { FileStream fs = new FileStream(AssemblyDirectory + @"", FileMode.OpenOrCreate, FileAccess.Write, FileShare.ReadWrite); StreamWriter myOutputWriter = new StreamWriter(fs, Encoding.GetEncoding(1251)); myTextListener = new TextWriterTraceListener(myOutputWriter); Trace.Listeners.Add(myTextListener); } catch (Exception) { //       } } Trace.WriteLine(); Trace.Flush(); 1?.DynamicInvoke(); } //            //  ,  1         public void (int  = 6891, int  = 1) {  = false; IPEndPoint ipEndpoint = new IPEndPoint(IPAddress.Any, ); Server = new TcpListener(ipEndpoint); Server.Start(); //      //      //   https://github.com/imatitya/netcorersi/blob/master/src/NETCoreRemoveServices.Core/Hosting/TcpServerListener.cs for (int i = 0; i < ; i++) Server.AcceptTcpClientAsync().ContinueWith(); } //       private void (Task<TcpClient> task) { if (task.IsFaulted || task.IsCanceled) { //    Server.Stop(); return; } //   TcpClient client = task.Result; //       // (client); //  Server       if (!) Server.AcceptTcpClientAsync().ContinueWith(); } private void (TcpClient client) { NetworkStream  = client.GetStream(); try { //          // 1     //TcpClient    var  = new 1(.(), client); //   1      //      1?.DynamicInvoke("TCPConnector", ., ); } catch (Exception e) { (DateTime.Now.ToString() + e.ToString()); } } //   public void () { if (Server != null) {  = true; Server.Stop(); Server = null; } if (myTextListener != null) { Trace.Listeners.Remove(myTextListener); myTextListener.Dispose(); } } 

すべてが非常に簡単です。 接続するとき、データを読み取り、1Cで送信するためのオブジェクトを作成します。 新しいリスナーを起動します。

ベアソケットで行われた送信は、ソースにあります。

簡略化すると、次のようになります。

 IPEndPoint ipEndpoint = new IPEndPoint(IPAddress.Parse(), ); //6891    = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); .Connect(ipEndpoint); var = new NetworkStream(); .(, , , ); //      if () result = .ReadCompressedString(strim); .Dispose(); .Dispose(); 

1Cでの処理方法は次のとおりです。

 //  Net core  NetStandard System.Threading.Tasks   Task=("System.Threading.Tasks.Task","System.Threading.Tasks");  TCP()  TCP<>   ; TCPConnector=("TCPConnectTo1C.TCPConnector","TCPIPCore.dll"); TCP=(TCPConnector.()); =TCP.(); .(,"1"); .(,"1"); // TCP()  TCPConnect() //        //  //   ,        1 //     //    //         (.(3*2)); (.(3*2)); TCP(); TCP.(6891,3); ..=; ..=; ..=;   ( ) //        (Task.Delay(1000)).Wait(); ="   "+.+" | "+.+" |="+Xml(()); .();   ( ) //        (Task.Delay(1000)).Wait();  //       TCP( ) ("="+.); ("="+.); ("="+.); (Task.Delay(1000)).Wait();  .  ="   "+.+" | "+.+" |="+Xml(()); .(); ;   (, , )  ="TCPConnector"  //     =(); ("="+.(.())); //    NetObjectToIDispatch  ="  "  TCP()  //      (+"()"); ; ;   ( TCP,ServerAdress,,,,) =(TCP.(ServerAdress,,,,)); (.(.()));  .  =" |"+.; (); ;   () TCP(); TCP=TCP; ServerAdress="127.0.0.1"; =6891; ="  "; =Xml(()); =; =; =false;  =1  3  (TCP,ServerAdress,,,,); (TCP,ServerAdress,,"",,); (TCP,ServerAdress,,"","12345678901",); ;   () //   .  TCP<>   TCP.(); TCP=; ; GC=("System.GC"); GC.Collect(); GC.WaitForPendingFinalizers(); =;  

応答は、受信したオブジェクトを介して送信されます。

  .(); 

デフォルトでは、1Cのイベントのキューは1です。したがって、1つのタスクを実行し、別のタスクをキューで待機できます。

複数のデバイスで作業できるため、次の方法で目的のキューサイズを設定する必要があります。

 .( )); 

現在のキューサイズを返します。

もちろん、複数の1Cアプリケーションを実行し、異なるポートでTCP / IPサーバーを実行できます。 しかし実際には、オペレーターは混乱しています。 それらが単純であればあるほど良い。

メソッドを使用して、必要なデリゲートを設定します。

 .(,"1"); .(,"1"); 

デリゲートのタイプに応じて、必要なデリゲートが設定されます。

  if (ReturnType == typeof(Action<string, string, object>)) return new Action<string, string, object>(); if (ReturnType == typeof(Action<string, string, string>)) return new Action<string, string, string>(AutoWrap.1); 


もちろん、イベントと動的コンパイルを使用できます出版物「開発→1C、.Net Core」を参照してください 1Cの.Netオブジェクトのイベントを受信するためのラッパークラスの動的コンパイル

ただし、1Cで記述しているため、目的のタイプのデリゲートを宣言し、1Cからインストールする方が簡単です。

テストでは、3つの1Cクライアントを使用し、TCPIP.epfでTestExchangeを呼び出して、1Cのイベントのキューを確認する必要があります。

ソースはここからダウンロードできます

Source: https://habr.com/ru/post/J311128/


All Articles