ホーム 主筆 その他ソフト その他情報 Syuhitu.org English

Windows関連

スクリーンセーバー作成法

半透明ウインドウの性能

bootfont.bin

キャビネット形式

ウインドウスタイルをいじる

Java製ソフトをServiceに登録する

イベントログにメッセージを出力する

コントロールパネルにアイコンを追加する

スクリプトによる拡張1

スクリプトによる拡張2

ガジェットの作成

大容量メモリ

メモリ搭載量の下限に挑む

スパースファイルにする

表示されるアイコンの種類を調べてみた

メモリマップIOとエラー処理

ファイルを作る順番と速度の関係

Cryptography API: Next Generationを使う

Windows 10のアクセントカラー

iSCSIディスクにバックアップを取る

サーバプロセスを分離して実装する

サーバプロセスを分離して実装する - F#

レジストリに大量に書き込む

Solaris関連

OpenGL

Solaris設定

ディレクトリの読み込み

主筆プラグイン開発

マルチスレッドでの開発

door

音を出す

Blade100の正しい虐め方

パッケージの作成

画像入出力

BMPファイル

ICOファイル

ANIファイル

JPEGファイル

減色アルゴリズム

減色アルゴリズムの並列化

その他アルゴリズムなど

自由軸回転

Base64

文字列操作

CPU利用率の取得

正規表現ライブラリ

メタボールを作る

メタボールを作る2

正規表現とNFA・DFA

C言語の構文解析

液晶ディスプレイを解体してみた

iSCSIの理論と実装

単一フォルダにファイルを沢山作る

USB-HUBのカスケード接続

SafeIntの性能

VHDファイルのフォーマット

USBメモリに書き込み続けてみた

サーバプロセスを分離して実装する - F#

2016年7月4日公開

TCPのサーバプロセスをC言語で実装する話を書いたが、それのF#版である。

やりたい事としてはCの場合と変わらない。TCPのサーバプロセスをスレッドで実装する場合と、プロセスを分離して実装する場合について考えてみる。

プロセスで作る場合

スレッドで作る場合

スレッドでの実装

抽象的な前置きはC言語版のページに書いたからそれは飛ばすものとして、さっそくスレッドで実装する場合の例を示す。

実装例

こうなる。

open System.Net
open System.Text
open System.Threading
 
let lsn = Sockets.TcpListener ( ( Dns.GetHostEntry ( "localhost" ) ).AddressList.[0], 12345 )
lsn.Start()
 
let parent_loop n =
 
    // クライアントからの接続を待ち受ける
    let s = lsn.AcceptSocket ()
    printf "Parent accept %d\n" n
 
    // クライアントの要求を処理する(スレッドを生成する)
    Async.Start (
        async {
            printf "Child start\n"
            Thread.Sleep 10000
            let result = s.Send ( Encoding.ASCII.GetBytes ( "abcdefg" ) )
            s.Close ()
            printf "Child end\n"
        }
    )
 
// 最大10回の着信まで繰り返す
seq { 1 .. 10 } |> Seq.iter parent_loop
 

概説

C言語での実装に比べるとずいぶん短い。

簡単に流れだけ述べる。

  1. ポート番号12345でTcpListenerのオブジェクトを作り、クライアントからの接続を待ち合わせる。
  2. クライアントからの接続を受け入れると、Async.Startおよびasyncによりクライアントの相手をする処理を別スレッドとして実行する。

実行例

実行例はCの場合と何も変わりはしないので割愛する。

子プロセスを事前に作っておく方法

プロセスの生成はコストが高い処理である。

C言語であればまだしも、F#は.NET Frameworkで動作する以上、その分プロセス起動の負担が大きい。クライアントに対応する処理を別プロセスで実装しようと思うのなら、それこそ事前にプロセスを生成しておきたくなるものだ。

実装例(親プロセス側)

open System.Net
open System.Text
open System.Diagnostics
open System.IO.Pipes
 
let lsn = Sockets.TcpListener ( Dns.GetHostEntry( "localhost" ).AddressList.[0], 12345 )
lsn.Start ()
 
let parent_loop n =
    // 無名のパイプを作る
    let pipe =
        new AnonymousPipeServerStream(
            PipeDirection.Out,
            System.IO.HandleInheritability.Inheritable
        )
 
    // 子プロセスを起動する
    let psi = ProcessStartInfo( @"D:\……\SvcTest_child.exe" )
    psi.Arguments <- pipe.GetClientHandleAsString ()
    psi.UseShellExecute <- false
    let proc = Process.Start psi
    printf "Parent : child proc id = %d\n" proc.Id
 
    // クライアントからの着信を待ち受ける
    let s = lsn.AcceptSocket ()
    printf "parent accept %d\n" n
 
    // 子プロセスに引き渡すソケットの情報を取得する
    let si = s.DuplicateAndClose proc.Id
 
    // ソケットの情報のバイト数をパイプに書き込む
    pipe.Write( System.BitConverter.GetBytes( si.ProtocolInformation.Length ), 0, 4 )
 
    // ソケットの情報をパイプに書き込む
    pipe.Write( si.ProtocolInformation, 0, si.ProtocolInformation.Length )
    pipe.Close()
 
 
seq[ 1 .. 10 ] |> Seq.iter parent_loop

実装例(子プロセス側)

open System.Net
open System.Text
open System.IO.Pipes
 
[<EntryPoint>]
let main ( args : string[] ) =
 
    // 起動時のメッセージを表示する
    printf "Child proc id = %d, PipeHandle = %s\n"
        ( ( System.Diagnostics.Process.GetCurrentProcess () ).Id )
        args.[0]
 
    // 親プロセスを通信するためのパイプを取得する
    let pipe = new AnonymousPipeClientStream( PipeDirection.In, args.[0] )
 
    // バイト数(4バイト)
    let ByteCountData : byte[] = Array.zeroCreate 4
 
    // バイト数を取得する
    let ReadResult = pipe.Read( ByteCountData, 0, 4 )
    let ByteCount = System.BitConverter.ToInt32( ByteCountData, 0 )
 
    // 親プロセスから受信するデータのバイト数を表示する
    printf "Child : byte count = %d\n" ByteCount
 
    // 親プロセスからポートの情報を取得する
    let pi : byte[] = Array.zeroCreate ByteCount
    let ReadResult = pipe.Read( pi, 0, ByteCount )
    let s =
        new System.Net.Sockets.Socket (
            new System.Net.Sockets.SocketInformation ( ProtocolInformation = pi )
        )
 
    // クライアントにデータを送信する
    let SendResult = s.Send( Encoding.ASCII.GetBytes( "abcdefg" ) )
    s.Close()
    0

概説

やっていることはC言語の場合と全く変わりはない。いくらかコードが短いだけである。

結局、親プロセスを子プロセスが通信するための匿名パイプを作って、ソケットの情報を子プロセスに渡しているだけだ。

実行例

ロジックが複雑化しているが、結局のところC言語版と動きは変わらないので、これについても実行結果は割愛する。

 


連絡先 - サイトマップ - 更新履歴
Copyright (C) 2000 - 2016 nabiki_t All Rights Reserved.