プロジェクトの要件:
2台のコンピュータ、コンピュータA(Windowsシステム)がLANが(など、リモートデスクトップペースト、FTPアップロード、共有フォルダ、)ファイルを受信するための主に担当してありますが、希望が自動的にファイルAを受信した後、コンピュータB(Windowsシステム)に転送することができますこのディレクトリにペーストファイルは、と自動的に保存するAに転送する場合は、バックアップを行うには、コンピュータB上のディレクトリがあります。
だから、百度で、System.IO.FileSystemWatcherこのカテゴリを見つけました(、ファイルの作成、ファイルの変更のファイルを削除し、ファイルの名前を変更)指定したフォルダを監視するためにそれを通していくつかのメッセージは、対応するアクションを行い、現在はファイルの作成を監視する必要がありますに対処するためではない他のイベント。
ファイル転送には、ソケットサーバーとクライアント上で独自に書きますが、スティックパッケージの問題に注意を払うことができます。私は、ここにいる、問題を解決するために、クライアントとサーバは、それを使用している場合、パッケージ構築されたソリューションを固執オープンソースNewLife.Net(https://github.com/NewLifeX/NewLife.Net)を使用していますが、管理が非常に簡単ですそれは、強力なログ出力が付属しています。ここだけでなく、いくつかの問題を達成するために、コードを共有します。
1、Winフォームプロジェクト、.Netframework4.6の運用フレームワークを作成します
NewLife.Net Nugetに関する参考文献
次のようにインターフェイスの構造は次のとおりです。
ローカルポートは、自動的にサーバに送信されるユニットは、ファイルの監視が作成されているサーバ(サーバ)リスニングポート、リモートサーバーのIPとポート、などの機械に代わって(受信サーバ、ソフトウェアを実行する必要があります)。
ローカル監視自動的に送信フォルダ:新しい(通常ペースト)ファイルで指定したフォルダに行く自動的に送信された者です。
自動的に受信フォルダを保存します。リモートから送信されたすべてのファイルが、自動的にこのフォルダに保存されています。
図2に示すように、実装コード
Program.csのは、フォルダ情報を保存するために使用される二つのグローバル変数を定義しました
/// <要約> ///保存フォルダ受信監視するために /// </要約> 公共の静的な文字列SAVEDIR =「」; /// <概要> 送信を監視するため///フォルダ </// /概要> 公共の静的な文字列SendDir = "";
使用した一部のクラス
システムを使用しました。 System.Dataを使用しました。 System.IOを使用しました。 System.Textのを使用しました。 System.Threading.Tasksを使用しました。 System.Windows.Formsを使用しました。 DirectoryWatch.Classを使用しました。 NewLife.Dataを使用しました。 NewLife.Logを使用しました。 NewLife.Netを使用しました。 NewLife.Net.Handlersを使用しました。
フォームは、共通の変数をロードし、ログ出力ベクトルテキストボックスについては、以下の指定いくつかのフォームを定義したとき
プライベート静的int型次に、remoteport = 0; //リモートポート プライベート静的int型localPortでは= 0; //ローカルポート プライベート静的な文字列のremoteIP = ""; // リモートIP プライベートFileSystemWatcherウォッチャー; //フォルダ監視 プライベートネットサーバーのサーバーを; //ローカルサービス 専用空隙MainFrm_Load(SENDERオブジェクト、のEventArgs E) { textBox1.UseWinFormControl(); }
ローカル監視フォルダ選択ボタンのコード
空Btn_dirbd_Clickプライベート(SENDERオブジェクト、EventArgsのE) { 使用して(FolderBrowserDialog新しい新=()folderBrowser VAR) { IFリターン(folderBrowser.ShowDialog()= DialogResult.OK!); Program.SendDir = folderBrowser.SelectedPath; IF(ディレクトリ! (Program.SendDir))が存在する { ; MessageBox.Show( "選択されたパスは、存在しないか、アクセス権を持っていない" @ "エラー"、MessageBoxButtons.OK、MessageBoxIcon.Error) ;リターン } IF(String.equals(プログラム。 SaveDir.ToLower()、Program.SendDir.ToLower())) { MessageBox.Showは(@ MessageBoxButtons.OK、MessageBoxIcon、@ "エラー" "自動受信フォルダとフォルダと同じフォルダを自動的に送信することができません"。エラー); リターン; } txt_localPath.Text = folderBrowser.SelectedPath。 Program.SendDir = folderBrowser.SelectedPath。 } }
ローカルに自動的に保存されたフォルダ選択ボタンのコード
空Btn_saveDic_Clickプライベート(SENDERオブジェクト、EventArgsのE) { 使用して(FolderBrowserDialog新しい新=()folderBrowser VAR) { IFリターン(folderBrowser.ShowDialog()= DialogResult.OK!); Program.SaveDir = folderBrowser.SelectedPath; IF(ディレクトリ! (Program.SendDir))が存在する { ; MessageBox.Show( "選択されたパスは、存在しないか、アクセス権を持っていない" @ "エラー"、MessageBoxButtons.OK、MessageBoxIcon.Error) ;リターン } IF(String.equals(プログラム。 SaveDir.ToLower()、Program.SendDir.ToLower())) { MessageBox.Showは(@ MessageBoxButtons.OK、MessageBoxIcon、@ "エラー" "自動受信フォルダとフォルダと同じフォルダを自動的に送信することができません"。エラー); リターン; } txt_remoteDir.Text = folderBrowser.SelectedPath。 Program.SaveDir = folderBrowser.SelectedPath。 } }
スタートアップコード(ローカルSocketServerサービスを有効に、ローカル監視を開始)
ボイドBtn_Start_Clickプライベート(SENDERオブジェクト、のEventArgs E) { int.TryParse(txt_remotePort.Text、次に、remoteport OUT); int.TryParse(txt_localPort.Text、localPortではがOUTである); IF(String.IsNullOrEmpty(txt_remoteIP.Text.Trim())) { ; MessageBox.Show( "エラー" @、 "リモートサーバのIPを入力してください" MessageBoxButtons.OK、MessageBoxIcon.Error @) ;戻り } remoteip txt_remoteIP.Text.Trim =(); IF(次に、remoteport == 0) { メッセージボックス.SHOW( "エラー" @、MessageBoxButtons.OK、MessageBoxIcon.Errorを"リモートサーバーのポートをご記入ください" @); を返す; } (次に、localport == 0)のIF { MessageBox.Show(@ @ "エラー" "開くために、ローカルサーバー上のポートを記入してください"、MessageBoxButtons.OK、MessageBoxIcon.Error); リターン; } IF(String.IsNullOrEmpty(Program.SendDir)) { MessageBox.Show(@ 、 "エラー" @、 "自動変速機のローカルフォルダパスを選択してください" MessageBoxButtons.OK、MessageBoxIcon.Error); 返す; } (String.IsNullOrEmpty(Program.SaveDir)IF) { MessageBox.Show(自動ローカルを選択してください」@ファイルフォルダ"@"エラーオーバーパス送受信"MessageBoxButtons.OK、MessageBoxIcon.Error); リターン; } IF(Btn_Start.Text.Equals("停止「)) { falseにwatcher.EnableRaisingEventsの=; server.Stop( 「手動停止」); Btn_Start.Text = @ "開始"; foreachの(コントロールでコントロール制御) { 続行((コントロールはボタンです)&&(コントロールがテキストボックスです)!)。 もし(control.Name = "Btn_Start"!) { control.Enabled =はtrue。 } } を返します。 } ウォッチャ=新しいFileSystemWatcher { PATH = Program.SendDir、 フィルタ= "*" //监控所有文件 }。 watcher.Created + = OnProcess; //只监控新增文件 //watcher.Changed + = OnProcess。 //watcher.Deleted + =新しいFileSystemEventHandler(OnProcess)。 //watcher.Renamed + =新しいRenamedEventHandler(OnRenamed)。 trueにwatcher.EnableRaisingEventsの=; //監視イベントを有効にすることを許可するかどうか //watcher.NotifyFilter = NotifyFilters.Attributes | NotifyFilters.CreationTime | NotifyFilters.DirectoryName | NotifyFilters.FileName | NotifyFilters.LastAccess | NotifyFilters.LastWrite | NotifyFilters.Security | NotifyFilters.Size ; watcher.NotifyFilter = NotifyFilters.FileName; //これは、現在持っていない、いくつかの通知のプロパティである //サブフォルダが含まれ; =真watcher.IncludeSubdirectories サーバーネットサーバーを新しい新しい= { ログイン= XTrace.Log、 SessionLog = XTrace.Log、 SocketLog = XTrace.Logは、 ポート= localPortではは、ある ProtocolType = NetType.Tcp }; //使用NewLife.Net Serverサービスを作成し、唯一のTCPプロトコルは、 非同期+ =(X、Y)=>をserver.Received { //ファイルが受信 = X NetSessionとしてVARセッションを、 リターンIF((ISパケットy.Message PK)!); Int.TryParse(Encoding.UTF8.GetString(pk.ReadBytes(0、1))、VAR fileState OUT); //ファイルのステータスバイト1 int.TryParse(Encoding.UTF8.GetString(pk.ReadBytes(1、アウト10))、VaRのheadinfo); //は10バイトの長さの合計ファイル int.TryParse(Encoding.UTF8.GetString(pk.ReadBytes(11、 8 ))、アウトするvarのfileNameLength); // 8バイトファイル名の長 VARファイル名= Encoding.UTF8.GetString(pk.ReadBytes(19 、のfileNameLength)); // ファイル名 int.TryParse(Encoding.UTF8.GetString(PK .ReadBytes(19 +のfileNameLength、10) )、VAR外オフセット); // 位置が10のバイトオフセット VARデータ=のpk.ReadBytes(29 +のfileNameLength 、pk.Count - (29 +のfileNameLengthを)); // データ概要 ;(data.length == 0)を返すIF > =ザはTask.Runを待つ(非同期() { server.Add <StandardCodec>(); //スティックパッケージ溶液、導入StandardCodec VARの書き込みデータ=データ。 使用(VAR =新しい新規のFileStreamのFileStream($ "} {Program.SaveDir \\ {fileNameを}"、FileMode.OpenOrCreate、 FileAccess.Write、FileShare.ReadWrite)) { filestream.Seekは(SeekOrigin.Beginオフセット); ザがFileStreamをを待ちます。 WriteAsync(書き込みデータ、0、writeData.Length); インクルードはfilestream.FlushAsyncを(待つ); } //データを書き込むファイルへ }); XTrace.WriteLine($ステータス」@:{} fileState、ID:{} session.ID、ファイル全長:{headinfo}、ファイル名の長さ:{}のfileNameLength、ファイル名:{fileNameを}、オフセット:{}オフセット、コンテンツ長:data.length {}「); //XTrace.Log.Debug(Encoding .UTF8.GetString(pk.Data)); //出力ログ }; server.Start(); Btn_Start.Text = string.Equals( "启动"、Btn_Start.Text)?"停止": "启动"。 foreachの(コントロールでコントロール制御) { 続行((コントロールはボタンです)&&(コントロールがテキストボックスです)!)。 もし(control.Name = "Btn_Start"!) { control.Enabled =偽; } } }
トリガ監視イベントを実行するコード
プライベート静的ボイドOnProcess(オブジェクトソース、FileSystemEventArgs E) { IF(e.ChangeType == WatcherChangeTypes.Created) { OnCreated(ソース、E)。 } //そうであれば(e.ChangeType == WatcherChangeTypes.Changed) // { // onChangedイベント(ソース、E)。 //} //そうであれば(e.ChangeType == WatcherChangeTypes.Deleted) // { // OnDeleted(ソース、E)。 //} }
ファイルを作成するためのコードの実行を監視する場合
/// <要約> ///は、10秒の遅延が(同じ問題の10秒以内にコピー仕上げ)を送るの実装に作成されていないファイルを防止し、キューを送信した後、ファイルを書き込み、イベント・モニター・ファイルを作成し、 最初/// 0は、新しいファイルが最後の再開が表す表し /// 2-11ビットはファイルの全体の長さを表す 18ビットが表す-は、12のファイル名の長さ/// ファイル名を表すNビットの情報- /// 19 /// 19 - (N + 1) -オフセット位置は、送信されたファイルのオフセット位置を示し /// 29+(N + 1) -送信されたコンテンツファイルの終わりを示す /// < /要約> /// <PARAM NAME = "ソース"> </ PARAM> /// <PARAM NAME = "E"> </ PARAM> プライベート静的ボイドOnCreated(ソースオブジェクト、FileSystemEventArgs E) { Task.Run(非同期()=> { のawait Task.Delay(10000); VAR =新しい新NetUriれるtcpClient($ "TCP:// {} remoteip:次に、remoteport {}"); //リモートサーバに送信する必要が Netclient.LogSend =はtrue。 VAR Netclient = TcpClient.CreateRemote( ); Netclient.Log = XTrace.Log。 IF(streamReader.CanRead) { データ=新しいバイト[streamReader.Length]。 Netclient.LogReceive =はtrue。 Netclient.Add <StandardCodec>(); Netclient.Received + =(S、EE)=> { IF(!(ee.MessageがパケットPK1である))のリターン; XTrace.WriteLine( "收到服务器:{0}"、pk1.ToStr())。 }。 もし(!File.Exists(e.FullPath))を返します。 []データバイト。 (VAR用のStreamReader =新規のFileStream(e.FullPath、FileMode.Open、FileAccess.Read、FileShare.ReadWrite))を使用して、 { streamReader.ReadAsync(データ、0、(INT)streamReader.Length)を待ちます。 } 他 { XTrace.Log.Error($ "{} e.FullPathアクセスできない"); リターン; } } VAR fileState Encoding.UTF8.GetBytes =( "0"); //新しいファイル送信 するvar headinfo =新しいバイトを[10] 。 //全長 headinfo = Encoding.UTF8.GetBytes(data.Length.ToString()); VAR =のfileNameLength新しい新しいバイト[8]; //ファイル名の長さ のfileNameLength = Encoding.UTF8.GetBytes(Encoding.UTF8.GetBytes(E .nameの).Length.ToString()); VAR = fileNameByte新しい新しいバイト[e.Name.Length]; //ファイル名 fileNameByte = Encoding.UTF8.GetBytes(e.name); VARオフセット= 0; //オフセット するvar sendLength = 409600; //単一の伝送サイズ Netclient.Open() ; 一方(data.length>オフセット) { (> 0をオフセット)IF { fileState = Encoding.UTF8.GetBytes(」1" ); //ファイルを追加 } IF(sendLength> data.length -オフセット) { sendLength = data.length -オフセット; fileState = Encoding.UTF8。 GetBytesメソッド( "2"); //最後の送信 } VAR = offsetByte新しい新しいバイト[10]; //オフセットバイト offsetByte = Encoding.UTF8.GetBytes(offset.ToString()); //ファイル状態0 1は、第1の追加のファイル2の最後の送信 Array.Copy(fileState、0、のsendData、 0、fileState.Length)を、 //バイト総数を送信します sendData新しい新しいバイト= VARの[1。8 + 10 + 10 + + + fileNameByte.Length sendLength。]; //合計ファイル長 Array.Copy(headinfo、0、のsendData ,. 1、headinfo.Length); //ファイル名の長さの 配列。コピー(のfileNameLength、0、のsendData、11、fileNameLength.Length); //ファイル名情報 Array.Copy(fileNameByte、0、のsendData、19、fileNameByte.Length); //コンテンツオフセット Array.Copy(offsetByte 、0、のsendData、19 + fileNameByte.Length 、offsetByte.Length); 10バイトの概要offsetByte //初期伝送 Array.Copy(データ、オフセット、のsendData、29 + fileNameByte.Length、sendLength); + = sendLengthオフセット。 新しい新しいパケットPK = VAR(のsendData); Netclient.SendMessage(PK); } //Netclient.Close( "送信完了、接続を閉じます。"); }); }
レンダリング:
未実装の機能:
HTTP
理由:
1、当然に、NewLife.Netが追従動作フロー(TCPプロトコル)を受信するために受信されている可能性があり、通常のソケットプログラミング、フローに達するが、彼らはそれぞれの時間を送っていたイベントをトリガする各時間をサポートしていません発見潜在的に流量を増加させる、いくつかのヘッダ情報(ファイル名、オフセット、サイズなど)をもたらします。
図2に示すように、パケットの順序がクライアントの送信と一致しない場合に効果サーバによって受信NewLife.Net、とは(例えば、クライアントは、12,345送信)、サーバ21によって受信される瞬間この順序で354は、この問題は私と一緒たぶんTCPパケットの順序が保証された非同期の関係であってもよい、私はGitHubの上の質問があり、作者(大きな石を待っています。https:// WWW。 cnblogs.com/nnhy/)ソリューション。