-
最近はまったことですが IE(Internet Explorer) では関数のデフォルト引数が動作しないことです。
比較的規模の大きい PHP がらみのプログラムで IE では全く JavaScript の部分が動かなくなったのです。 非常にあせってしまって、何が原因かがよくわからなったのですが、どうもデフォルト引数の宣言の関数があると全ての JavaScript の関数が 動かなくなる様です。
IE 以外のブラウザでは JavaScript の関数の引数で =(イコール) を後ろに付けてデフォルト値を設定できます。 この関数を呼出す時に指定なしを記述できます。
例として以下のソースを見て下さい。 IE 以外のブラウザでは、それぞれのボタンをクリックした時にメッセージが表示されます。 しかし IE では、うんともすんとも何も実行されません。
■関数にデフォルト引数がある例
<html> <head> <meta charset="utf-8"> <title>test func</title> <script type="text/javascript"> function doTest(msg = 'test message') { alert('Test:' + msg); } </script> </head> <body> <h2>test func</h2> <br /> <button onclick="doTest();">Test</button><br /> <button onclick="doTest('aaaa');">Test</button> </body> </html>
そこで、以下の様にデフォルト引数を止めてみました。
当然のことながら、IE でも関数が動作することが分かります。
■関数にデフォルト引数を止める
<html> <head> <meta charset="utf-8"> <title>test func</title> <script type="text/javascript"> function doTest(msg) { alert('Test:' + msg); } </script> </head> <body> <h2>test func</h2> <br /> <button onclick="doTest('test message');">Test</button><br /> <button onclick="doTest('aaaa');">Test</button> </body> </html>MDN docs ではちゃんと IE はデフォルト引数は実装されていないと載っていました。
⇒https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Functions/Default_parameters
どうしてもデフォルト引数が必要な場合は以下の様にすれば動きます。
■無理やり関数にデフォルト引数
<html> <head> <meta charset="utf-8"> <title>test func</title> <script type="text/javascript"> function doTest(msg) { if(typeof msg === 'undefined') { msg = 'test message'; } alert('Test:' + msg); } </script> </head> <body> <h2>test func</h2> <br /> <button onclick="doTest();">Test</button><br /> <button onclick="doTest('aaaa');">Test</button> </body> </html>
PR -
前回の TCP の記事では、クライアント側で1回の通信毎に TCP の接続を切断していたので、 複数回の文字列送信ができませんでした。
それで今回は、クライアント側で TCP の接続のタイミングと、文字列送信のタイミングを別々で行う様にすることで 複数回の文字列送信を可能にしています。
これを行う為に、以下のクライアント側の処理手順の中で、(1)(2)を1個の処理として、また(5)の処理も別のタイミングに追い出します。 具体的にはそれぞれフォームにボタンを設置(「Start」「Stop」の名前で)し、それぞれのクリック処理内でそれらを行います。- (1)IP アドレスとポート番号で TcpClient クラスの生成によりTCPソケット作成・接続
- (2)TcpClient クラスの GetStream メソッドで NetworkStream を取得
- (3)NetworkStream の Write メソッドでテキストボックスの内容をバイト配列化して送信
- (4)NetworkStream の Read メソッドでサーバからの応答を受信しテキストボックスに表示
- (5)最後にネットワークストリーム、ソケットをクローズ
それでは以下にクライアント側のソースを示します。 TcpClient と NetworkStream クラスのオブジェクトを関数の外に出し、静的変数としています。
■TCP クライアント側のプログラム
Imports System.Net Imports System.Text Public Class frmTcpClient 'サーバーのIPアドレス(または、ホスト名)とポート番号 Dim strIpAddr As String = "localhost" Dim intPort As Integer = 60000 'ソケット Dim mTcpClient As Sockets.TcpClient 'ソケットストリーム Dim mNetStream As Sockets.NetworkStream '[Start]リック時イベント Private Sub btnStart_Click(sender As Object, e As EventArgs) Handles btnStart.Click 'ソケット生成 mTcpClient = New Sockets.TcpClient 'ソケット接続 mTcpClient.Connect(strIpAddr, intPort) 'ソケットストリーム取得 mNetStream = mTcpClient.GetStream() End Sub '[Stop]リック時イベント Private Sub btnStop_Click(sender As Object, e As EventArgs) Handles btnStop.Click 'ソケットクローズ mNetStream.Close() mTcpClient.Close() End Sub '[Send]リック時イベント Private Sub btnSend_Click(sender As Object, e As EventArgs) Handles btnSend.Click Try '送信文字列をバイト配列変換 Dim enc As Encoding = Encoding.GetEncoding("SHIFT-JIS") '最後尾にCR Dim data As Byte() = enc.GetBytes(Me.TextBox1.Text.Trim & ControlChars.Cr) Me.TextBox1.Text = "" 'ソケット送信 mNetStream.Write(data, 0, data.Count) 'サーバからの応答を受信 Dim bytRead As Byte() = New Byte(255) {} Dim intBytes As Integer = mNetStream.Read(bytRead, 0, bytRead.Length) '受信したデータを文字列に変換 Dim resMsg As String = enc.GetString(bytRead, 0, intBytes) '末尾の\rを削除し表示 Me.TextBox2.Text = resMsg.TrimEnd(ControlChars.Cr) Catch ex As Exception MsgBox(ex.Message) End Try End Sub End Classサーバ側のプログラムに変更は無いのですが、一応ソースを載せておきます。
■TCP サーバ側のプログラム
Imports System.Net Imports System.Threading Imports System.Text Public Class frmTcpServer 'リッチテキストボックスにメッセージを表示する Public Sub DispMsg(ByVal message As String) RichTextBox1.AppendText(message & vbNewLine) RichTextBox1.SelectionStart = RichTextBox1.TextLength RichTextBox1.Refresh() End Sub '[Start]ボタンクリック Private Sub btnStart_Click(sender As Object, e As EventArgs) Handles btnStart.Click 'ボタン制御 Me.btnStart.Enabled = False 'ポート番号 Dim pintPort As Integer = 60000 'IPアドレス&ポート番号設定 Dim pEndPoint As New IPEndPoint(IPAddress.Any, pintPort) 'ソケット・リスナー作成 Dim pListener As New Sockets.TcpListener(pEndPoint) 'リスナー開始 pListener.Start() Call DispMsg("リスナー開始...") 'クライアントから接続有りでこの処理を抜ける Dim pTcpClient As Sockets.TcpClient = pListener.AcceptTcpClient() Call DispMsg("クライアントから接続有り...") '送受信用ソケットストリーム取得 Dim pNetStream As Sockets.NetworkStream = pTcpClient.GetStream() 'バイト配列(取敢えず受信バッファとして256バイト) Dim bytRead As Byte() = New Byte(255) {} 'ソケット受送信ループ While True 'TCP受信(バッファ領域まで) Dim intBytes As Integer = pNetStream.Read(bytRead, 0, bytRead.Count) If intBytes = 0 Then Exit While End If '受信したデータを文字列に変換 Dim enc As Encoding = Encoding.GetEncoding("SHIFT-JIS") Dim resMsg As String = enc.GetString(bytRead, 0, intBytes) '末尾の\rを削除 resMsg = resMsg.TrimEnd(ControlChars.Cr) '表示 Call DispMsg(resMsg) '正常受信の場合、送信データ作成 Dim bytSend() As Byte = enc.GetBytes(resMsg & "...ACK" & ControlChars.Cr) 'ソケット送信 pNetStream.Write(bytSend, 0, bytSend.Length) End While 'ソケットストリームクローズ pNetStream.Close() 'クライアントクローズ pTcpClient.Close() 'リスナー停止 pListener.Stop() Call DispMsg("リスナー停止...") 'ボタン制御 Me.btnStart.Enabled = True End Sub End Class実際の実行は以下の様になります。
サーバ側の「Start」押下後、クライアント側の「Start」を押下し”abcdef”,”123456”,”ABCDEFGHI”の順に文字列を「Send」し、 更に「Stop」を押下したところまでを表しています。
ここまでのPGはサーバとクライアントが1対1のみでしか動作しません。 しかし、これでは面白くありませんので、次回は複数のクライアントからの接続を受け付けるものを紹介したいと思います。関連する記事
⇒TCP (TcpListener, TcpClient)を使ったプロセス間通信について
⇒Remoting の IPC を使ったプロセス間通信について
⇒Remoting の IPC を使ったプロセス間通信についてその2(HTTPチャネル)
⇒名前付きパイプを使ったプロセス間通信について
⇒名前付きパイプを使ったプロセス間通信についてその2(複数クライアントとの通信)
⇒名前付きパイプを使ったプロセス間通信についてその3(クライアントとの双方向通信)
-
このページでは TCP を使ったプロセス(EXE)間で通信について説明します。 (いわゆる TCP クライアント・サーバプログラム を作ることになります。)
TCP クライアントは TcpClient クラスを、 TCP サーバは TcpListener クラスを使います。
そこで先ずは TCP サーバのプログラムの例を示します。 今回の処理は TCP での受信待ち状態にし、クライアントからの1回のメッセージを受信し、 そのメッセージをクライアント側に返し、処理が終わるという簡単なものです。 以下にその手順を示します。- IP アドレスとポート番号でネットワークエンドポイントを作成し TcpListener クラスの生成
- TcpListener クラスの Start メソッドで受信接続要求の待機を開始
- TcpListener クラスの AcceptTcpClient メソッドでクライアントからの接続要求を待機
- 接続要求待機を抜けた後で送受信用ソケットストリーム取得
- ソケットストリームからデータ受信し、さらにデータを送信
- 最後にソケットストリーム等をクローズし、リスナーを停止
以下にそのソースを示します。 フォーム上にはサーバ開始用の「start」ボタンと、送受信文字列等を表示する為に RichTextBox を設置しています。
■TCP サーバープログラム
Imports System.Net Imports System.Threading Imports System.Text Public Class frmTcpServer 'リッチテキストボックスにメッセージを表示する Public Sub DispMsg(ByVal message As String) RichTextBox1.AppendText(message & vbNewLine) RichTextBox1.SelectionStart = RichTextBox1.TextLength RichTextBox1.Refresh() End Sub '[Start]ボタンクリック Private Sub btnStart_Click(sender As Object, e As EventArgs) Handles btnStart.Click 'ボタン制御 Me.btnStart.Enabled = False 'ポート番号 Dim pintPort As Integer = 60000 'IPアドレス&ポート番号設定 Dim pEndPoint As New IPEndPoint(IPAddress.Any, pintPort) 'ソケット・リスナー作成 Dim pListener As New Sockets.TcpListener(pEndPoint) 'リスナー開始 pListener.Start() Call DispMsg("リスナー開始...") 'クライアントから接続有りでこの処理を抜ける Dim pTcpClient As Sockets.TcpClient = pListener.AcceptTcpClient() Call DispMsg("クライアントから接続有り...") '送受信用ソケットストリーム取得 Dim pNetStream As Sockets.NetworkStream = pTcpClient.GetStream() 'バイト配列(取敢えず受信バッファとして256バイト) Dim bytRead As Byte() = New Byte(255) {} 'ソケット受送信ループ While True 'TCP受信(バッファ領域まで) Dim intBytes As Integer = pNetStream.Read(bytRead, 0, bytRead.Count) If intBytes = 0 Then Exit While End If '受信したデータを文字列に変換 Dim enc As Encoding = Encoding.GetEncoding("SHIFT-JIS") Dim resMsg As String = enc.GetString(bytRead, 0, intBytes) '末尾の\rを削除 resMsg = resMsg.TrimEnd(ControlChars.Cr) '表示 Call DispMsg(resMsg) '正常受信の場合、送信データ作成 Dim bytSend() As Byte = enc.GetBytes(resMsg & "...ACK" & ControlChars.Cr) 'ソケット送信 pNetStream.Write(bytSend, 0, bytSend.Length) End While 'ソケットストリームクローズ pNetStream.Close() 'クライアントクローズ pTcpClient.Close() 'リスナー停止 pListener.Stop() Call DispMsg("リスナー停止...") 'ボタン制御 Me.btnStart.Enabled = True End Sub End Class以下に TCP クライアントのプログラムの例を示します。 この処理は、TCPソケットを作成・接続した後で、NetworkStream によりサーバへデータを送信し、 その後サーバからの応答を受信します。
- IP アドレスとポート番号で TcpClient クラスの生成によりTCPソケット作成・接続
- TcpClient クラスの GetStream メソッドで NetworkStream を取得
- NetworkStream の Write メソッドでテキストボックスの内容をバイト配列化して送信
- NetworkStream の Read メソッドでサーバからの応答を受信しテキストボックスに表示
- 最後にネットワークストリーム、ソケットをクローズ
以下にそのソースを示します。 フォーム上には送信文字列用と受信文字列用のテキストボックス、送信開始用の「Send」ボタンを設置しています。
■TCP クライアントプログラム
Imports System.Net Imports System.Text Public Class frmTcpClient '[Send]リック時イベント Private Sub btnSend_Click(sender As Object, e As EventArgs) Handles btnSend.Click Me.btnSend.Enabled = False Try 'サーバーのIPアドレス(または、ホスト名)とポート番号 Dim strIpAddr As String = "localhost" Dim intPort As Integer = 60000 'ソケット生成・指定したホストの指定したポートに接続 Dim pTcpClient = New Sockets.TcpClient(strIpAddr, intPort) 'ソケットストリーム取得 Dim pNetStream As Sockets.NetworkStream = pTcpClient.GetStream() '送信文字列をバイト配列変換 Dim enc As Encoding = Encoding.GetEncoding("SHIFT-JIS") '最後尾にCR Dim data As Byte() = enc.GetBytes(Me.TextBox1.Text.Trim & ControlChars.Cr) Me.TextBox1.Text = "" 'ソケット送信 pNetStream.Write(data, 0, data.Count) 'サーバからの応答を受信 Dim bytRead As Byte() = New Byte(255) {} Dim intBytes As Integer = pNetStream.Read(bytRead, 0, bytRead.Length) '受信したデータを文字列に変換 Dim resMsg As String = enc.GetString(bytRead, 0, intBytes) '末尾の\rを削除し表示 Me.TextBox2.Text = resMsg.TrimEnd(ControlChars.Cr) 'ソケットクローズ pNetStream.Close() pTcpClient.Close() Catch ex As Exception MsgBox(ex.Message) End Try Me.btnSend.Enabled = True End Sub End Class実際の実行は以下の様になります。
今回のPGはクライアント側で文字列送信後すぐに接続を切っていますので、 1回の送受信毎にサーバ側で「start」をクリックしないといけないので実用には供しないですが、ご参考になればと思います。
次回はこれを踏まえて、サーバ側で開始したらそのまま連続でクライアントからの受信と送信を出来る様にしたいと思います。関連する記事
⇒TCP (TcpListener, TcpClient)を使ったプロセス間通信について・その2
⇒Remoting の IPC を使ったプロセス間通信について
⇒Remoting の IPC を使ったプロセス間通信についてその2(HTTPチャネル)
⇒名前付きパイプを使ったプロセス間通信について
⇒名前付きパイプを使ったプロセス間通信についてその2(複数クライアントとの通信)
⇒名前付きパイプを使ったプロセス間通信についてその3(クライアントとの双方向通信)
-
HTML で用意されているパスワード入力に input type="password" があるのですが、 この入力では通常入力内容が ●●●●… と表示されます。
これを見える様にしたいと思います。
よくあるのはパスワード入力の下にチェックボックスを設置し、チェックされていない時は通常で、 チェックされた場合に内容を表示させるやり方です。
方法はパスワード入力の type 属性を password から text に変更します。 これで input がパスワード入力から普通のテキスト入力に変わり、入力内容が見えることになります。
■チェックボックスの変化時にパスワード入力を可視化
<html> <head> <meta charset="utf-8"> <title>test canvas</title> <script src="https://code.jquery.com/jquery-3.4.1.min.js"></script> <script type="text/javascript"> $(function() { //チェックボックスの変化時関数 $("#password-check").change(function() { if ($(this).prop("checked")) { //チェックONの場合 $("#password-test").attr("type","text"); } else { //チェックOFFの場合 $("#password-test").attr("type","password"); } }); }); </script> </head> <body> <h2>test password</h2> <input type="password" id="password-test" name="Password" value="" placeholder="パスワード入力" /><br /> <label>パスワードを表示する<input type="checkbox" id="password-check" /></label> </body> </html>
以下のパスワード入力に文字を入れてみて下さい。 その後で、チェックボックスをON/OFFすることでパスワードの中身が見えると思います。
-
文字列の一種として、特殊文字の「改行」「タブ」「バックスペース」等をシステム定数として定義されています。
この定義済みの文字列を紹介します。 特殊文字として使うとすれば「復帰」「ラインフィード」「改行」「タブ」「バックスペース」ぐらいでしょうか。
以下の様にシステム定数文字列 vbCr , vbLf , vbNewLine , vbTab , vbBack を使います。 長さが1(復帰のみ2)の文字列として文字列変数にそのまま代入したり連結する形で使えます。 (これらの文字列は Microsoft.VisualBasic.Constants モジュールに定義されています。)
また Microsoft.VisualBasic.ControlChars モジュールにも同じものが定義されていますので、こちらを使うこともできます。
ところで1つはまる点があるのですが、 C# や PHP などではダブルクォテーションで文字列を定義した時に 「¥」円マーク(バックスラッシュ)でエスケープ文字として特殊文字を記述できるので VB.NET でも同じ様に記述すると 単なる「¥」と「n」等の文字そのものになります。■定義済みの文字列定数
Dim str1 As String Dim str2 As String Dim str3 As String '復帰文字 : &H0D str1 = vbCr str2 = ControlChars.Cr str3 = "\r" 'VB.NET では \ はそのままの文字列でエスケープシーケンスではない! Console.WriteLine("vbCr = ControlChars.Cr : " & (str1 = str2) & _ " ControlChars.Cr = ""\r"" : " & (str2 = str3)) 'ラインフィード文字 : &H0A str1 = vbLf str2 = ControlChars.Lf str3 = "\n" Console.WriteLine("vbLf = ControlChars.Lf : " & (str1 = str2) & _ " ControlChars.Lf = ""\n"" : " & (str2 = str3)) '復帰文字とラインフィード文字の組み合わせ : &H0D &H0A str1 = vbCrLf str2 = ControlChars.CrLf str3 = "\r\n" Console.WriteLine("vbCrLf = ControlChars.CrLf : " & (str1 = str2) & _ " ControlChars.CrLf = ""\r\n"" : " & (str2 = str3)) '改行文字(WindowsではvbCrLfと同等) str1 = vbNewLine str2 = ControlChars.NewLine str3 = "\n" Console.WriteLine("vbNewLine = ControlChars.NewLine : " & (str1 = str2) & _ " ControlChars.NewLine = ""\n"" : " & (str2 = str3)) 'タブ文字 : &H9 str1 = vbTab str2 = ControlChars.Tab str3 = "\t" Console.WriteLine("vbTab = ControlChars.Tab : " & (str1 = str2) & _ " ControlChars.Tab = ""\t"" : " & (str2 = str3)) 'バックスペース文字 : &H8 str1 = vbBack str2 = ControlChars.Back str3 = "\t" Console.WriteLine("vbBack = ControlChars.Back : " & (str1 = str2) & _ " ControlChars.Back = ""\b"" : " & (str2 = str3))上記のソースの部分のみを実行させた場合に、デバッグ出力ウインドウに以下の様に表示されました。
vbCr = ControlChars.Cr : True ControlChars.Cr = "\r" : False vbLf = ControlChars.Lf : True ControlChars.Lf = "\n" : False vbCrLf = ControlChars.CrLf : True ControlChars.CrLf = "\r\n" : False vbNewLine = ControlChars.NewLine : True ControlChars.NewLine = "\n" : False vbTab = ControlChars.Tab : True ControlChars.Tab = "\t" : False vbBack = ControlChars.Back : True ControlChars.Back = "\b" : False
当然なのですが結果から vbCr と ControlChars.Cr は同じであることがわかります。 (他の文字列も同様です)
■定義済みの文字列定数を使う場面について
上記の定義済みの文字列定数を使えば改行等がソース上に視覚的にはっきりとわかる形で記述できます。
ではどの様な時にこれを使う意味が有るのか。 私の場合、改行文字が必要な場面と言えば、 Oracle 等のデータベースへの SQL 文を作成する場合に使用します。 以下のソースでその例をしめします。生成される SQL 文は例なので意味はありません。'接続文字列(これ自身は意味がありません) Dim strConnect As String = "Data Source=//*********/test; User ID=test;Password=test;" 'オラクル接続オブジェクト Dim Conn As New OracleConnection(strConnect) 'オラクル接続オープン Conn.Open() 'SQL文 Dim strSQL As String = "" strSQL &= "SELECT " & vbCrLf strSQL &= " TT.伝票番号, TT.伝票日付" & vbCrLf strSQL &= "FROM 売上伝票 TT" & vbCrLf strSQL &= "WHERE TT.伝票日付 = TO_DATE('2019/11/12')" & vbCrLf strSQL &= "ORDER BY TT.伝票番号" & vbCrLf 'コマンドオブジェクト(SQL文と接続オブジェクト) Dim Cmd As New OracleCommand(SQL, Conn) Cmd.CommandType = CommandType.Text '読込オブジェクトに接続 Dim dr As OracleDataReader = Cmd.ExecuteReader() '*** この後はデータ処理... ***このソースでは SQL 文を SELECT FROM WHERE ORDER BY の各句で改行を入れています。
この改行ですが Oracle SQL 文の解析を行う時に空白文字の様にホワイトスペースとして扱われ、文字列の中では単語の区切りとみなされます。 そのため、この SQL 文では句の区切りとして使用しています。
改行を入れなくても、先頭に1文字空白を入れれば同じことにはなるのですが、テキストとして見ると改行が入った方が見やすい為です。 (先頭に空白を入れ忘れた時のバカ除けでもあります)
実際システムではよく実行 SQL 文のログを残したりしますが、 その時に長い文字列の1行よりも適当に改行が入っていた方が見やすいのです。
■ダブルクォテーションそのものを文字列の中に定義する方法
ダブルクォテーションそのものを文字列の中に宣言したい場合について説明します。 文字列はダブルクォテーションで囲まれていますが、その文字列の中でダブルクォテーションを2回連続して記述します。 (""として記述)
文字列の中にダブルクォテーションを記述する例として思い当たるのは、 上記のソースの SQL 文の中でカラム名とテーブル名が全角になっていますが、 Oracleではこれらをダブルクォテーションで 囲んだ方が安全です。(昔の Oracle ではある全角文字がうまく判定できないことがありました。)
結果として以下の様な記述になります。'SQL文 Dim strSQL As String = "" strSQL &= "SELECT " & vbCrLf strSQL &= " TT.""伝票番号"", TT.""伝票日付""" & vbCrLf strSQL &= "FROM ""売上伝票"" TT" & vbCrLf strSQL &= "WHERE TT.""伝票日付"" = TO_DATE('2019/11/12')" & vbCrLf strSQL &= "ORDER BY TT.""伝票番号""" & vbCrLf