只今、クライアント・サーバシステムを用いた
チャットソフトを作っておりまして、
winsockを使用して、TCP/IPでデータの送受信をしたいのですが、
送信したいデータは以下の文字列だけのユーザー定義型になっています。
Public Type DATAFILE
DF_Command As String * 3 '送受信用コマンド
DF_Name As String * 50 '送受信用ユーザー名
DF_Address As String * 16 '送受信用アドレス(IPアドレス)
DF_Message As String * 255 '送受信用データ内容
End Type
過去ログを探したところ、
ユーザー定義型をバイト配列に置き換えて送ると良いとの事でした。
そこで、その方法を試してみたのですが、
送信時にはバイト配列から復元も出来るのですが、
受信になるとバイト配列から復元出来ません。
Public Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" _
(destination As Any, source As Any, ByVal length As Long)
Dim Byte_text() As Byte
Dim SEND_DATA As DATAFILE
Dim GET_DATA As DATAFILE
'-- 送信
SEND_DATA.DF_Command = "MES"
SEND_DATA.DF_Message = "test"
SEND_DATA.DF_Name = "User"
SEND_DATA.DF_Address = winsock(0).LocalIP
ReDim Byte_text(LenB(SEND_DATA))
CopyMemory Byte_text(0), SEND_DATA, LenB(SEND_DATA)
'この時に復元は可能
winsock(0).SendData Byte_text
'--受信
Private Sub winsock_DataArrival(Index As Integer, ByVal bytesTotal As Long)
tcpSock(Index).GetData Byte_text, vbArray + vbByte, bytesTotal
CopyMemory GET_DATA, Byte_text(0), bytesTotal
Debug.Print GET_DATA.DF_Command '復元出来ず
End Sub
復元する方法等、ご指摘がありましたら、
何方か教えて頂けないでしょうか?宜しくお願い致します。
なお、環境はVB6.0のSP5です。
編集 削除まず通信の問題か手法の問題化を切り分けるために以下のことを
試してください。
Dim SEND_DATA As DATAFILE
Dim GET_DATA As DATAFILE
SEND_DATA.DF_Command = "MES"
SEND_DATA.DF_Message = "test"
SEND_DATA.DF_Name = "User"
SEND_DATA.DF_Address = winsock(0).LocalIP
ReDim Byte_text(LenB(SEND_DATA))
CopyMemory Byte_text(0), SEND_DATA, LenB(SEND_DATA)
CopyMemory GET_DATA, Byte_text(0), LenB(SEND_DATA)
これでGET_DATAにちゃんとコピーできていれば手法的に問題ありません。
通信でちゃんと全バイトを取得できていない可能性があります。
そうでなければ手法がまずいということですね。
その方法で確かめたところ、
GET_DATAの方にて値が取得出来ていたので、ちゃんとコピーは出来ている様です。
となると、やはり全バイト取得出来てないということですか・・・。
そこで、受信側のbytesTotalをデバッグしてみたのですが、
送信時のバイト数よりも1バイト多いことが解りました。
送信側 : LenB(SEND_DATA) = 324
受信側 : bytesTotal = 325
これはwinsockの方で何か処理をしているためでしょうか?
また、このデバッグ結果は全バイト取得出来ているということですかね?
私の予想では、これが原因では無いかと思うのですが・・・。
どうなんでしょうか?
送信時と受信時のバイト配列の中身を比較してみればどうでしょう?
編集 削除> 送信時と受信時のバイト配列の中身を比較してみればどうでしょう?
比較してみましたらほぼ同じでした。
(頭の部分等の一部は確認しましたが、最後のバイトは確認していません。
それとデバッグをしていて、ある事に気が付きました。
このプログラムは制作の途中でユーザー定義型を固定長に変えたのですが、
固定長にしてから受信時の
CopyMemory GET_DATA, Byte_text(0), bytesTotal
が実行出来なくなってしまいました。
その箇所に行くとVB諸共アラートも表示されずに落とされてしまいます。
確かkarnel32の共有違反だったと思いますが、回避方法を何方かご存じありませんか?
アラートなどの情報源が何も無いので手がつけられない状態です。
<修正:バイト数が間違ってました。>
送信側 : LenB(SEND_DATA) = 648
受信側 : bytesTotal = 649
> 送信側 : LenB(SEND_DATA) = 648
> 受信側 : bytesTotal = 649
送受信で1バイト違うのはこれが原因では?
ReDim Byte_text(LenB(SEND_DATA))
Option Baseが0ならByte_textはLenB(SEND_DATA)+1の配列となります。
>その箇所に行くとVB諸共アラートも表示されずに落とされてしまいます。
受信側で648バイトしかメモリを確保していないのに649バイト書き込んでいるからでは?
> 受信側で648バイトしかメモリを確保していないのに649バイト書き込んでいるからでは?
1レコードのサイズが648バイトという事は解っているので、試しに
tcpSock(Index).GetData Byte_text, vbArray + vbByte, 648
CopyMemory GET_DATA, Byte_text(0), 648
と受信時を定数で行ってみましたが、やはりVB諸共落とされます・・・。
ついでに、送信側を定数にして検証してみましたが結果は同じでした。
ちなみに、可変長型で行うとCopyMemoryの際に落とされる事は無くなるのですが、
バイト配列の値が受信時と送信時で変わってしまい値を取得する事が出来ませんでした。
>ReDim Byte_text(LenB(SEND_DATA))
>CopyMemory Byte_text(0), SEND_DATA, LenB(SEND_DATA)
ReDim Byte_text(Len(SEND_DATA)-1)
CopyMemory Byte_text(0), SEND_DATA, Len(SEND_DATA)
>1レコードのサイズが648バイトという事は解っているので
-->324
送信データーが全て文字列なら、適当なデリミタを決めるか、
プロトコールを決めて、文字列で送信した方が楽では。
デリミタを用いる方法は私も考えたのですが、
ユーザーにデリミタとして用いている文字列を
入力される可能性が出てくるので諦めていました。
やはりデリミタに頼る他無いのでしょうか?
> >1レコードのサイズが648バイトという事は解っているので
> -->324
Win98SEで検証した時は324バイトでしたが・・・。
OSに依存する物なんでしょうかね?
>ユーザーにデリミタとして用いている文字列を
>入力される可能性が出てくるので諦めていました。
エスケープキャラクターを使います。
http://www.atmarkit.co.jp/fdotnet/basics/regex01/regex01_03.html
私は最近はタグでやっています。
<body>
<command>aaaa</command>
<name>nero</name>
<add>172.xx.x.xxx</add>
<data>Hello World</data>
</body>
こんな具合に送ります。
正規表現によるエスケープキャラクターは
CやPerlをやっていたので知ってはいるのですが、
デリミタとして使うのにはちょっと戸惑いますね。
一部の人だけだとは思いますが、
入力箇所があるとエスケープキャラクターを使う衝動に狩られまして
"\t"や"\n"等が使えないかを試したくなるのです。
タグを用いる方法は良いですね。
応用としてログの保存をXMLで保存していけば、
テキストで保存するよりは管理が楽になる様な気が・・・。
(当方がXMLに関する知識が乏しいのは御察し下さい。
>"\t"や"\n"等が使えないかを試したくなるのです。
"\n"をデリミタとして使用した場合は、データーとして、
"\n"を送るには、"\\n"に変換して送る事になりますよね。
タグの方法を取るとしても、エスケープキャラクターとは
言わないでしょうが、データーの中に使用する、
"<" から "<"、">" から ">"、"&" から "&"の変換は
必要になります。
固定長のByte型配列の先頭をAPIに渡したいなら、
Type DataBlockType
data(0 To 323) As Byte
End Type
Dim dataBolck As DataBlockType
みたいに構造体で宣言してAPIに渡すようにすれば先頭アドレスが渡ります。
とりあえずタグを使ってString型にて転送する事にしました。
どうもありがとうございました。