クライアント・サーバー型システムの試作(クライアント)

クライアント・サーバー型システムの試作(サーバー)の続きでクライアント側のプログラムです。プロトコルや画面表示内容はサーバー側の投稿にあるので、さっそくコードを。ちなみにコンソール・アプリケーションです。

Main メソッドは、getData メソッドの呼び出しとサーバーから取得したデータの表示を行なっているだけです。

//
        static void Main(string[] args)
        {
            var receiveData = getData();
            Console.WriteLine("取得したデータ:");
            Console.WriteLine(receiveData);

            Console.ReadLine();
        }

getData メソッドでは、サーバーへの接続、HELO コマンドによるサーバーとのコマンド処理状態への遷移、GET コマンドを実装する get メソッドの呼び出し、QUIT コマンドによるコマンド処理状態の終了そしてサーバーとの接続の切断を行います。

//
        private static string getData()
        {
            // 文字コードを UTF-8 とする
            var enc = System.Text.Encoding.UTF8;

            // サーバーのホスト名とポート番号
            var host = "localhost";
            int port = 2001;

            // TcpClient を作成し、サーバーと接続する
            var tcp = new System.Net.Sockets.TcpClient(host, port);
            Console.WriteLine("サーバーと接続しました。");
            var rData = "";

            try
            {
                var rStr = "";
                // NetworkStream を取得する
                using (var stream = tcp.GetStream())
                {
                    // サーバーから受信
                    rStr = receiveData(stream, enc);
                    if (!rStr.StartsWith("100 "))
                    {
                        Console.WriteLine("サーバーの準備ができていません。");
                        return rStr;
                    }

                    sendData(stream, "HELO\r\n");
                    rStr = receiveData(stream, enc);
                    if (!rStr.StartsWith("110 "))
                    {
                        Console.WriteLine("サーバーの準備ができていません。");
                        return rStr;
                    }

                    rData = get(stream, enc);

                    sendData(stream, "QUIT\r\n");
                    rStr = receiveData(stream, enc);

                    // 閉じる
                    stream.Close();
                }
            }
            catch (System.IO.IOException e)
            {
                if (e.InnerException.GetType() == typeof(System.Net.Sockets.SocketException) &&
                    (e.InnerException as System.Net.Sockets.SocketException).ErrorCode == 10053)
                {
                    Console.WriteLine("サーバーとの接続がサーバー側のタイムアウトにより切断されました");
                }
                else
                {
                    throw;
                }
            }
            finally
            {
                tcp.Close();
                Console.WriteLine("切断しました。");
            }

            return rData;
        }

get メソッドでは、GET コマンドの処理を実装しています。
GET コマンドでは、サーバーからコマンドの受信と実行 OK を伝える [120 Send data\r\n] が送られ、続けてデータが送られてきます(終端は [\x1a\r\n])。
そのため、受信したデータの最初のコードが [120 ] であることを確認したのち、最初の [\r\n] までの文字列を飛ばして、[\x1a\r\n] が送られてくるまで蓄積しています。そして得られた文字列から [\x1a\r\n] を削除してデータを得ています。

//
        private static string get(System.IO.Stream stream, System.Text.Encoding enc)
        {
            var result = "";

            sendData(stream, "GET\r\n");

            var rData = receiveData(stream, enc);
            if (rData.StartsWith("120 "))
            {
                var workArea = new StringBuilder(rData.Substring(rData.IndexOf("\r\n") + 2));
                if (!rData.EndsWith("\x1a\r\n"))
                {
                    do
                    {
                        rData = receiveData(stream, enc);
                        workArea.Append(rData);
                    }
                    while (!rData.EndsWith("\x1a\r\n"));
                }
                result = workArea.Replace("\x1a\r\n", "").ToString();
            }
            else
            {
                Console.WriteLine("サーバーからエラーが通知されました。");
            }

            return result;
        }

そして、最後は receiveData メソッドと sendData メソッドです。
こちらの receiveData メソッドは、コマンド及びデータの終端である [\r\n] が受信したデータの終わりにあったら、受信したデータを文字列に変換して返すようにしてみました。

//
        private static string receiveData(System.IO.Stream stream, System.Text.Encoding enc)
        {
            // サーバーから送られてきたデータを受信する
            var recieveMessage = "";
            using (var memStream = new System.IO.MemoryStream())
            {
                var rBuff = new byte[50];
                int rSize;
                do
                {
                    // データの一部を受信する
                    rSize = stream.Read(rBuff, 0, rBuff.Length);
                    // rSize が 0 のときにはサーバーが切断したと判断
                    if (rSize == 0)
                    {
                        Console.WriteLine("サーバーが切断しました。");
                        break;
                    }
                    // 受信したデータを蓄積する
                    memStream.Write(rBuff, 0, rSize);
                } while (rBuff[rSize - 2] != 0x0D || rBuff[rSize - 1] != 0x0A);  // 0x0D:\r, 0x0A:\n

                recieveMessage = enc.GetString(memStream.ToArray());
                memStream.Close();
            }

            Console.WriteLine("[S]" + recieveMessage);
            return recieveMessage;
        }

        private static void sendData(System.IO.Stream stream, string str)
        {
            // 文字列をバイト配列へ
            var data = System.Text.Encoding.UTF8.GetBytes(str);
            stream.Write(data, 0, data.Length);
            Console.WriteLine("[C]" + str);
        }

クライアント・サーバー型システムの試作(クライアント)」への1件のフィードバック

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です