Winsockを使って,サーバーからクライアント側にデータを送る
プログラムはサンプルなどで作り動作を確認しました.
WindowsAPIのTimesetEventを使って,一定周期ごとにデータを
生成し,それをクライアント側に送ることを考えており,
サーバーのフォーム内で,以下を,
tmevent = timeSetEvent(10, 1, AddressOf TimeProc, 0, 1)
モジュール内で,以下を実行させるようにしています.
Public Function TimeProc(ByVal IDEve 省略 ong) As Long
T = T + 1
Form1.Winsock1.SendData T
End Function
サーバー・クライアントが接続した状態で,上記を実行すると,
実行時エラー'-2147417848(80010108)':
'SendData'メソッドは失敗しました:'IMSWinsockControl'オブジェクト
となります.
以上,Winsockを使って一定間隔でデータを送る方法として本手法はマ
ズいのでしょうか?他にいい方法がありますでしょうか?
なお,VBのタイマーイベントは正確ではないとのことなので,
APIを利用して行いたいのです.
よろしくお願いします.
そういう状況にはないけど、下の人が似た問題を解決したようです。:http://www.vbcodelibrary.co.uk/modules.php?op=modload&name=Forums&file=reply&topic=1068&forum=3&post=5617"e=1&mod=0
http://madia.world.coocan.jp/vb/vb_bbs/200408_04080125.html
残念ながら、マルチメディアタイマーはVB6では使えない様です。
自作でゴリゴリ。
マルチメディアタイマーで自作コールバック関数を使いたいのであれば、
http://www.koalanet.ne.jp/~akiya/vbtaste/vbp/StpWch20.lzh
のようにしれば、VB6 Native Code Compileでも使うことはできます。
ですが、まぁ、業務ではやるべきではないでしょうね。
# そもそも、いろんなところで相変わらずアプローチを間違えている
# としか言いようがないような。
## 次はどのような名前を使うのだろう?
## とりあえず、投稿者名は統一しておいた方が。
ありがとうございます>各位 当方,WinXP,VB6でやっています.
まず,マルチメディアタイマでWinsockは不可能と言うことは理解できました.
K.J.KさんのStpWchを見させていただきましたが,よく分かりませんでした.
自作コールバック関数を用いれば,一定周期でWinsockを実行することが
出来るという理解は正しいでしょうか?聞き聞きマシンですいませんが,よろしくお願いします.
生兵法は.....と言うこともあるので。
タイマーの基本は、
1、CPUにあまり負担をかけない、出来れば通常多くとも1%以下。
2、正確である。
この2点かな。
たとえば、目標時間に近づくとSleepを短くする方法が思いつくが。
Option Explicit
Private Declare Function timeGetTime Lib "winmm.dll" () As Long
Private Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)
Private Sub Command1_Click()
timeWaiteTime Val(Text1.Text)
End Sub
Private Sub timeWaiteTime(ByVal timeWaite)
Dim timeBack As Long, timeSleep As Long, timeRest As Long
timeSleep = 10
timeBack = timeGetTime '押された時間
timeRest = 1 '残された時間 とりあえず1
Do While (timeRest > 0)
timeRest = timeWaite - (timeGetTime - timeBack) '残された時間
timeSleep = Abs(timeRest \ 10) 'Sleep時間の設定
If timeSleep > 1000 Then timeSleep = 1000 '最低でも1秒に一回割り込みを許可
Label1.Caption = CStr(timeGetTime - timeBack) 'とりあえず表示
Sleep (timeSleep) '居眠り
DoEvents '割り込み許可
Loop
End Sub
*テキストには待ち時間がmmSecondで設定されている。
まず、元質問者に対して。
論理展開が無茶苦茶です。マルチメディアタイマーが使えない
というのならば、私のサンプルが成り立ちません。
で、あのサンプルが理解できないようであれば、VB6でマルチ
メディアタイマーを使うことはできない、と考えてください。
それに、そもそもsocketはそういう使い方をするものでは
ないでしょう。Windowsとsocketの係わり合い方を考慮すれば、
マルチメディアタイマーをそのまま使おうとは考えないはず。
> 我龍院忠太 さん。
Sleepを使うのではなく、API関数のMsgWaitForMultipleObjectsと
DoEventsを併用するべきです。Windowを持ったスレッドで
メッセージ処理を止めるべきではないからです。
>K.J.K.さん
>Sleepを使うのではなく、API関数のMsgWaitForMultipleObjectsと
>DoEventsを併用するべきです。Windowを持ったスレッドで
>メッセージ処理を止めるべきではないからです。
ケースバイケースかと、CPUの負荷を減らすためにLoopにSleepを入れるのは一般的な手法です。
ただし、Loopの中で中止などの操作が出来る様にSleepの最長時間を制限します。
上の例では1秒に制限していますが、0.1秒でもCPUの負荷は1%以下で、この場合は最もメッセージが
流れるWindowを掴んで引きずり回す様なことをしても、それほど違和感は無いと思われます。
まあそのような要望があればですが。
メッセージキューを持つスレッドでは待機にはSleepではなく
MsgWaitForMultipleObjectsを使うようにしてください。
Private Declare Function MsgWaitForMultipleObjects Lib "user32.dll" _
(Optional ByVal Count As Long = 0&, _
Optional ByVal HandlesPointer As Long = 0&, _
Optional ByVal WaitAll As Long = 0&, _
Optional ByVal Milliseconds As Long = 0&, _
Optional ByVal WakeMask As Long = &HFF&) As Long
Private Declare Function GetTickCount Lib "kernel32.dll" () As Long
Dim iInterval As Long
Dim iStart As Long
iStart = GetTickCount()
iInterval = 1000&
Do
If 0& = MsgWaitForMultipleObjects(, , , iInterval) _
Then DoEvents
iInterval = 1000& - GetTickCount() + iStart
Loop While (iInterval >= 0&)
メッセージキューを持たない、もしくは、メッセージを考慮しなくて
いい場合であればSleepでも構いませんが、VB6はCOMベースなので、
知らないメッセージが沢山飛び交うものだと考えておくのが無難では。
それに、WinSockコントロールもまた特殊なメッセージを利用しますし。
# マルチスレッド云々の段階でWinSockコントロールを使うことは
# ありえないとは思いますが。
K.J.Kさん,理論展開無茶苦茶ですいません.
小生がよく理解してない+すぐに早合点しているのが原因です.
マルチメディアタイマを使って(TimeSetEventなどを使って)ストップウォッチは作りました.
だからVB6でマルチメディアタイマが使えないという認識はありません.ただ,KJKさんの
コードは小生のVB理解度を越えていました.
結局,VBやAPIについて深く理解せずにあれこれ適当にサンプルを組み合わせて作ってみた結果
がこれで,途方にくれていたワケです.
初心者掲示板ということで投稿させていただきましたが,初心者の域を越えているのか,
と勝手に思うようになってしまいました.Windowsとsocketの係わり合い方について
何も理解していないので.
で,我龍院忠太さんのは,DoWhile内でDoEventsを呼んでいるので,Winsockを別の所で呼べば良いのかな?
と勝手に解釈してしまったのですが,いかがでしょうか?Winsockじゃないものでデータをインターネット
に送れば,小生の要望は実現可能なのでしょうか?
また聞き聞きマシンですいませんが,よろしくお願いします.
ちょっと確認なのですが、
1、サーバーからデーターを送るクライアントの数は幾つですか。
2、タイマーの時間的精度はどの位を考えていますか。
3、タイマーの時間的間隔は。
4、送るデーターの量はおよそどの位ですか。
5、あるデーターを送っている間に次のデーターを送ることは考えられますか。
6、このサーバーのプログラムは、タイマーが働いていてタイムアップするまでの
間に何か他のタスクをしていますか。
我龍院忠太さん,すいません,仕様については
1.クライアント数=1つ
2.±1msecの精度
3.タイマー間隔:10msec
4.実数型のデータを4つほど
5.10msec毎にデータが上記データが送れればOKです.
但しクライアント側からサーバーへデータ送信の開始,停止のシグナルは
送る(不定期に)
6.他のタスクは極力しないようにする
以上です.よろしくお願いします.
>2.±1msecの精度
今までのレスは全て無効です。
TimeGetTimeの理論上の精度が1mSecなので、±1msecの精度は理論上でも限界に近い、
C++のDLLでLoopを回す方法位しか思いつかない。さらにWindowは非表示とする。
(Windowを引きずり回されたらおしまい。)
ただしサーバー側がいくら精度良く送っても、回線の速度を含めたクライアントの
レスポンスが追いつかない可能性が大、仕様を見直す必要があるかと。
10ms周期で送信しなければならない理由は何でしょうか?
TCP/IPの場合、TCPならエラー発生時の再送に起因する遅延、UDPならエラー時のパケット消失やパケット到着順の逆転のリスクが常に存在することを前提とした上位プロトコル設計が必要ですが、そこまで考えた仕様になっていますか?
その様な本質的に回避不可能なリアルタイム性を損ねる要因についてまできちんと理解しておられて、その上でどうしてもその様な精度を得たいのなら頑張ってみる価値はありますが、そうでない場合は、まずリアルタイム性を追求する際のネットワークの問題についてきちんと勉強されることをお勧めします。
IP電話、VoIPの分野は「リアルタイムでないと音声が途切れて使い物にならない」ということで、遅延対策の技術が色々出てきていますので参考になるかと思います。
10msでデータをサンプリングしているので,仕様送信周期を10msとしました.
しかし,データを間引きできるのであれば,10msにこだわりません.
間引き可能かどうかはこちらの仕様によるので,もし間引き可能
TimeGetTimeでも行けるかもしれません.
TimeGetTimeを使ったデータサンプルは別途作成していて,コレはとりあえずうまく動いていますから,これを流用すると良いかなと思い始めました.
また,ひろ さんご指摘のように,データ遅延やエラー時のリカバリーなどについても無視しています.今のインターネット環境であれば,10ms以内に4つの実数型データなど,楽勝で送受信できるだろうと考えていました(不確実さは残るが).少なくとも家庭内LAN内ぐらいであれば… といった感じです.
>10msでデータをサンプリングしているので,仕様送信周期を10msとしました.
データーをサンプルし終わったらすぐデーター送信を開始する仕様にすれば
送る方のタイマーは不要の気がするのですが。
我龍院忠太さん>おっしゃる通りです.要は,
1.データサンプリング
2.データ処理
3.処理済データをモニタに出力
4.クライアントにデータ+処理済データを送信
といったことを10ms以内で行いたかったのです.
1〜3までは,10msの間隔で行いたかったので(要はサンプリングデータの差分がほしい),
TimeSetEventで行っていて,それがうまく動いていると考えていました.それに4を追加
したところNGになった,というワケです.最初にこう質問してればよかったことでした.
言葉足らずでスイマセン.
で,TimeGetTimeを使って自分なりにプログラムを組んで見たところ,誤差はあるかも
しれませんが,ある程度の目処が立ちました.
ただ,他に方法・ご意見があるかもしれませんので,ここで解決とせず模索していきたいと思
います.自分の方で何か出来たら再度投稿いたします.
マルチメディアタイマーの詳細やWinsockなどは初心者にはちょっと難しいという事が収穫でし
た.KJKさんには不快な思いをさせて申し訳なく思っております.
そう言うことなら単に
Do
If timeGetTime Mod 10 = 0 Then
'サンプリング、送信、その他の処理
DoEvents
End If
Loop
これだけでいいのでは。
ts = timeGetTime
Do
t0 = timeGetTime
Do
t1=timeGetTime
Loop While (t1-t0<9)
Loop
投稿失敗しました.すいません.結局
--
ts = timeGetTime
Do
t0 = timeGetTime
DoEvents
if flag = 1 then 'クライアント側からの開始指令
new_data = 〜〜 'データ取得
Do
t1=timeGetTime ' 待ち時間 DoEventsも入れている
Loop While (t1-t0<9) '10ms以下の待ち時間
d_data = (new_data-old_data)/(t1-t0) 'データの時間差分計算
t = t1 mod ts '経過時間の計算
送信
t0 = t1
old_data=new_data
End If
Loop
のようにして,10ms間隔ではないのですが,大体所望のものが出来ました.
クライアント側がストップウォッチ風に動くのは結構感動ものでした.
おっしゃっているように,サーバー側のウインドウを動かすと,timeGetTimeが出来ないので
時間が止まります.その点JKJさんのは動き続けているわけで,理解の無さを痛感した次第です.
一応の解決としたいと思います.ありがとうございました.
ツイート | ![]() |