.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のイベントのキューを確認する必要があります。



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



All Articles