注册 登录
编程论坛 VB6论坛

VB winsock接收数据处理的问题

hcyang1422 发布于 2015-04-23 00:51, 6751 次点击
在接收数据时,数据长度不定,但是有一组数据比较大,是分多个包发送过来的,其中第一个包中有整个数据的长度,当遇到这个数据是,怎么样处理才能把所有的数据接收完,并放入一个数组或是字串中?
本人小白,请高手指点
41 回复
#2
wmf20142015-04-23 09:48
如果清楚数据格式,应该很容易接受并处理。其实winsocket发送接受数据更需要处理的是丢包情况,虽然tcp/ip协议说确保数据送达,但实际处理中,当不间断处理大于2k以上数据时的确会丢失数据。
#3
lianyicq2015-04-23 09:50
回复 楼主 hcyang1422
没用动态数组?
#4
hcyang14222015-04-23 12:39
我的源码如下,请大神帮忙看看中间红色部分该如何处理取完整数据
Private Sub Winsock1_DataArrival(ByVal bytesTotal As Long)
Dim S, S3, s1, flag, flag1 As String
Dim id, strlen As Integer
Dim tmp As String
Dim str() As Byte
Winsock1.getData str()
strdata = StrConv(str(), vbUnicode)                                    '转换为字符串,
flag = Left(strdata, 2)

'log.Text = log.Text & Format(Hour(Time), "00") & ":" & Format(Minute(Time), "00") & ":" & Format(Second(Time), "00") & " " & strdata   '显示日志
'log.SelStart = Len(log.Text)

Select Case flag
   
   Case "E1"                                        '登录
      Sleep 200
      Login   '登录
      log.Text = log.Text & Format(Hour(Time), "00") & ":" & Format(Minute(Time), "00") & ":" & Format(Second(Time), "00") & " " & "admin" & vbCrLf  '显示日志

  Case "E0"
       u = u + 1
       log.Text = log.Text & Format(Hour(Time), "00") & ":" & Format(Minute(Time), "00") & ":" & Format(Second(Time), "00") & " " & strdata   '显示日志
       log.SelStart = Len(log.Text)
        If u = 1 Then
    '     Sleep 500
        log.Text = log.Text & Format(Hour(Time), "00") & ":" & Format(Minute(Time), "00") & ":" & Format(Second(Time), "00") & " " & "登录成功" & vbCrLf   '显示日志
        state_lab = "TCP/IP Connect OK"                  '登录成功
        log.SelStart = Len(log.Text)
        End If
  Case "EA"                                         '温度值返回
       S = strdata
       log.Text = log.Text                          '不显示日志
       log.SelStart = Len(log.Text)
        S3 = Split(S, "^C", , vbTextCompare)
        For j = 1 To UBound(S3)
            s1 = Split(S3(j), vbCrLf)(0)
            s1 = Trim(s1)
            If Format(s1) > Val(Form4.CH(j - 1)) Then
            Form4.CH(j - 1) = Format(s1, "00.0")
            End If
        Next j
        
  Case "EB"                                         '二进制数据返回,
      tmp = StrConv(str(), vbUnicode)
      id = str(9)                                   '取ID ,测试ok
      strlen = HEX_to_DEC(Hex(str(4)) & Hex(str(5)) & Hex(str(6)) & Hex(str(7)))            '取数据长度,测试OK
     
   在此位置该如何处理才能先接收全部的数据??
  数据格式和使用Wireshark抓包的数据截图如下图


      If id = 19 Then                               '如果ID为19,则为设定信息输出   
       此处为对接收的数据进行处理,同上面温度值取出
      End If
End Select

只有本站会员才能查看附件,请 登录
只有本站会员才能查看附件,请 登录


[ 本帖最后由 hcyang1422 于 2015-4-23 12:48 编辑 ]
#5
lianyicq2015-04-23 13:52
回复 4楼 hcyang1422
分包传送的时候,非第一个包有没有设flag标志?
收到第一个数据包就从第八位开始取值赋到目标数组中,直至本包未两位。
检测到是大数据的分包,就继续向目标数组赋值,直至达到strlen
TCP/IP不用自己考虑校验吧,以前我在vnn上分包传大文件没出过错。
#6
hcyang14222015-04-23 13:58
回复 5楼 lianyicq
其它包没有FLAG标志,
能帮忙写一下代码吗?
#7
lianyicq2015-04-23 15:06
回复 6楼 hcyang1422
分包没有标志,那就只能case else了。
写了这么多代码,已经实现想要哪个字节就取得哪个字节了。自己完成吧。
#8
hcyang14222015-04-23 17:30
回复 7楼 lianyicq
不可以使用case else的吧,因为接收到的数据文件类型还可能是不一样的,是要根据头包中的ID位来区分后再处理的。是否能在头包的case中,根据数据长度来完成接收呢?
#9
renxiaoyao362015-04-23 18:21
个人认为这和Winsock没关系
首先,Winsock传输的讯息用GetData后就相当于自己的本地变量了
你现在要做的只是把自己的本地变量想办法标示并将包含特定的字符的变量互相组合
寻找特定的字符串可以用Instr但是要求是传输的讯息丽没有标示字符
#10
hcyang14222015-04-23 20:13
回复 9楼 renxiaoyao36
第一次GETDATA,得到的只是头包,后面还有多个包呢,该怎么处理才能把后面的数据也GETDATA,并和前面的包合在一起?
#11
lianyicq2015-04-23 20:35
回复 8楼 hcyang1422
我以前传大文件设缓冲区8192
客户端设一布尔变量receivedata,预设为false
receivedata为false时要分析包头.
首先由服务器端发"LEN"和 文件长度
客户端收到后,创建文件,置receivedata为True,发"PS"
服务端收到"PS",开始传文件
客户端收到数据后写文件,判断是否达到文件长度,如果没达到,发"CO"。如果达到了发"OV"并关闭文件,清零receivedata
服务器端收到"CO",则继续传,若收到"OV"则关闭文件。

现在你的情况是服务器端不能和客户端“握手”,由客户端自身判断是否是数据接收状态。但是如果有分包,总是数据连续发送吧。就直接由客户端接收到的字节数判断。
这和什么文件没什么关系吧,难道不同文件会混着传?把我上次说的第8字节开始取改为12字节开始.
#12
hcyang14222015-04-23 22:36
回复 11楼 lianyicq
我再说明一下情况,我是通过winsock向仪器发出一条查询指令,然后仪器就开始通过异步方式返回数据,即仪器通过本身的协仪设定,开始不停的发送数据,直到数据发送完成。
返回的数据根据查询指令的不同,数据长度也不同,大部分数据一次GETDATA可以接收完成,但是当出现"EB"开头的数据时,返回数据的格式如之前提供的图片(12byte包头+DATA包+……+DATA包+校验和包),
不能一次GETDATA就接收完成,所以就只能考虑通过包头中的数据长度来进行特别处理,以完成接收全部数据,这也正是我搞不定,来求助的内容。
#13
renxiaoyao362015-04-24 07:39
你把头包设定一下特定语句,到时候GETDATA后先对得到的数据进行检查是否是头包,如果是,对之后接受到的数据进行检查,用Left函数检查开头的字符是否是标志,如果是,就将其和原来的存储组合包的变量合并,用&进行合并,千万不要用+号啊。
#14
hcyang14222015-04-24 08:36
回复 13楼 renxiaoyao36
我就是GETDATA后,先检查是否是头包标志,当出现较长的数据时,先取出数据长度的,后面的接收合并过程不太会处理。
请问,用下面红色代码进行接收后面的数据并合并,可以吗?不行的话请帮忙修改一下。
Case "EB"                                         '二进制数据返回,
      tmp = StrConv(str(), vbUnicode)
      id = str(9)                                   '取ID ,测试ok
      strlen = HEX_to_DEC(Hex(str(4)) & Hex(str(5)) & Hex(str(6)) & Hex(str(7)))            '取数据长度,测试OK
    if len(tmp)<strlen +8 then                '如果当前数据长度小于取得的当前数据量长度
     winsock1.getdata str()                    '再次取出数据
     tmp=tmp & strconv(str(),vbunicode)        '数据合并
     end if

         If id = 19 Then                               '如果ID为19,则为设定信息输出   
       此处为对接收的数据进行处理,同上面温度值取出
      End If
End Select
#15
风吹过b2015-04-24 08:50
如果后面的数据包也有 EB 标志,那么就使用你这种 select case  来

12byte包头+DATA包+……+DATA包+校验和包
就是第二个 DATA包也是否包含 12字节的包头。


如果后面的数据包没有了 EB 了,那就必须使用状态来辅助。

if 状态=5  then
   取数据
判断是否结束
没有结束,连接起来,退出过程
  否则 状态=6 ,继续后面的包头分析
end if

分解包头
select case ...
case XX
状态=1
   ...

case "EB"
  状态=5                '设置状态,后面的数据直接连接,不分析包头的。  
  处理
case else
  状态=7               

end select  

-----------
随笔写的,希望你能看懂理解。
#16
lianyicq2015-04-24 09:37
回复 12楼 hcyang1422
是在人家代码上作修改?在Private Sub Winsock1_DataArrival(ByVal bytesTotal As Long)中执行两次getdata?
仔细看看11楼的回复。
Private Sub Winsock1_DataArrival(ByVal bytesTotal As Long)事件发生后,执行getdata。
根据receivedata的值判断是接收数据还是分析包头。

[ 本帖最后由 lianyicq 于 2015-4-24 09:39 编辑 ]
#17
lianyicq2015-04-28 15:15
有点时间,写了一个非应答方式的文件传送。以ReceiveStatus标志两种状态,以接收文件长度为结束标志。
程序代码:
Option Explicit
Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (Destination As Any, Source As Any, ByVal Length As Long)

Const SendBSize = 200
Dim ReceiveStatus As Boolean
Dim ReFiLen As Long

Private Sub Command1_Click()
Dim temp() As Byte
Dim i As Integer
Dim FileLenth As Long
Dim FileLenByte(3) As Byte
Dim FileLenLong(3) As Long


CommonDialog1.ShowOpen
Open CommonDialog1.FileName For Binary As #1
FileLenth = LOF(1)
FileLenLong(0) = FileLenth And &HFF& 'The data not belong to the long integer style must mark a & at the end.Important
FileLenLong(1) = (FileLenth And &HFF00&) / 256
FileLenLong(2) = (FileLenth And &HFF0000) / 65536
FileLenLong(3) = (FileLenth And &HFF000000) / 16777216

ReDim temp(2) As Byte
temp(0) = Asc("L")
temp(1) = Asc("E")
temp(2) = Asc("N")
SendSock.SendData temp

For i = 0 To 3
  FileLenByte(i) = FileLenLong(i)
Next

SendSock.SendData FileLenByte

ReDim temp(SendBSize) As Byte
While (Loc(1) < LOF(1))
  If (LOF(1) - Loc(1) < SendBSize) Then ReDim temp(LOF(1) - Loc(1) - 1)
  Get #1, , temp
  SendSock.SendData temp
Wend
Close #1
End Sub

Private Sub Form_Load()
ReceiveSOCK.Listen
SendSock.Connect
End Sub

Private Sub Form_Unload(Cancel As Integer)
ReceiveSOCK.Close
SendSock.Close
End Sub

Private Sub ReceiveSOCK_ConnectionRequest(ByVal requestID As Long)
If ReceiveSOCK.State <> sckClosed Then ReceiveSOCK.Close
ReceiveSOCK.Accept requestID
Form1.Caption = "Connect"

End Sub

Private Sub ReceiveSOCK_DataArrival(ByVal bytesTotal As Long)
Dim temp() As Byte
Dim FileByte() As Byte
Dim i As Integer
ReceiveSOCK.GetData temp, vbByte
Select Case ReceiveStatus
  Case Is = False
    ReceiveStatus = True
    ReFiLen = CLng(temp(3)) + CLng(temp(4)) * 256 + CLng(temp(5)) * 65536 + CLng(temp(6)) * 16777216 'temp(0-2)="LEN"
    ReceiveInfo.Text = ReceiveInfo.Text & ReFiLen & vbCrLf
    Open "c:\t.dat" For Binary As #2
    ReDim FileByte(UBound(temp) - 7)
    CopyMemory FileByte(0), temp(7), UBound(temp) - 6
    Put #2, , FileByte
  Case Is = True
    Put #2, , temp
End Select
If Loc(2) = ReFiLen Then
   ReceiveInfo.Text = ReceiveInfo.Text & "Send Complete." & vbCrLf
   ReceiveStatus = False
   Close #2
End If

End Sub

#18
hcyang14222015-04-30 16:25
回复 17楼 lianyicq
非常感谢您能抽空写代码帮我解决问题,可惜本人太小白了,依然没有领悟出解决方法。
现在针对我的问题,我有以下方案,
我只有对仪器发出两组命令的时候才会出现数据量大于8192字节的情况
所以我在发出命令后设置一个状态符dataflag=true
接收端处理时,当状态符为dataflag=true时以byte型接收,否则以string型接收
但是在出现byte型数据时,我的代码仍然不能按预期接收数据,
经实测,数据长度每次会递增12字节,但是全部为0.请帮忙看看此处如何修改才能完整的接收数据。
为便于分析问题,再次添加数据格式以及仪器输出数据时使用Wireshark抓取的数据包
接收代码如下
Private Sub Winsock1_DataArrival(ByVal bytesTotal As Long)
Dim S, S3, s1, flag, flag1 As String
Dim id, strlen As Integer
Dim tmp As String
Dim str(), tmp() As Byte
Dim lngDataSize As Long

lngDataSize = 0                                                                            '初始化接收到的数据量
ReDim str(bytesTotal - 1)                                                                  '初始化接收缓冲区
'=======================================
'数据接收过程
'=======================================
If dataflag Then                                                                        '判断接收类型,为真时,接收类型为Byte
    '循环接收数据,直到接收数据长度等于应收数据长度
    Do
    Winsock1.getData str, vbByte + vbArray
    id = str(9)                                                                            '取ID ,测试ok
    strlen = HEX_to_DEC(Hex(str(4)) & Hex(str(5)) & Hex(str(6)) & Hex(str(7)))             '取数据长度,完整数据长度为此长度+8
    ReDim tmp(lngDataSize + bytesTotal - 1) As Byte
    copymemory tmp(), str(0), bytesTotal
    lngDataSize = lngDataSize + bytesTotal
    loop until  lngDataSize=stren+8               
                                       
   
    '数据接收完成后的预处理
    strdata = StrConv(str(), vbUnicode)                                                    '转换为字符串,
    flag = Left(strdata, 2)                                                                '取数据类型标志
   
    Else
    Winsock1.getData strdata                                                               '接收类型为string
    flag = Left(strdata, 2)
End If
'===================================
'     接收数据处理过程
'===================================
Select Case flag                                   '数据处理类型标志   
  Case "E1"                                        '登录     
  Case "E0"      
  Case "EA"                                         '温度值返回
  Case "EB"                                         '二进制数据返回,
只有本站会员才能查看附件,请 登录


[ 本帖最后由 hcyang1422 于 2015-4-30 16:27 编辑 ]
#19
lianyicq2015-05-04 11:33
回复 18楼 hcyang1422
试试这个,我没有测试条件。有问题再说
程序代码:
Dim ReceiveState As Boolean

Private Sub Winsock1_DataArrival(ByVal bytesTotal As Long)
Dim S, S3, s1, flag, flag1 As String
Dim id, strlen As Integer
Dim tmp As String
Dim str() As Byte
Dim FileByte() As Byte

Winsock1.GetData str()

flag = Chr(str(8)) & Chr(str(9))

Select Case flag
  
'...
    Case "EB"     '二进制数据返回,
          If ReceiveState = False Then
             Open "c:\temp.dat" For Binary As #1
             ReceiveState = True
             id = str(9)
          End If
          strlen = HEX_to_DEC(Hex(str(4)) & Hex(str(5)) & Hex(str(6)) & Hex(str(7)))   '取数据长度,测试OK
          ReDim FileByte(UBound(temp) - 14)
          CopyMemory FileByte(0), temp(12), UBound(temp) - 13
          Put #1, , FileByte
          If Loc(1) = strlen Then
             ReceiveState = False
             Select Case id
               Case Is = 19
                 '显示信息
             End Select
                        
             Close #1
         
          End If
End Select
事实上ID号用来决定接收文件的类型(扩展名)。
在接收到EB的第一包时,就可以新建该类型文件。显示信息是在二进制文件传送完成之后,你也可以加一个开关来决定是否显示。
接收数据建议用二进制方式,避免转码,除非主要是传送文本。
#20
hcyang14222015-05-04 16:34
回复 19楼 lianyicq
非常感谢抽空帮我解决问题
按照你给的方法我进行了测试,在下面这句代码处出现下标越界错误
ReDim FileByte(UBound(temp) - 14)
我将代码作如下变更后
ReDim FileByte(UBound(temp) - 1)
          CopyMemory FileByte(0), temp(11), UBound(temp) - 1
未再出现错误提示,
但是If Loc(1) = strlen Then这句运行结果一直为false
我查看临时文件temp.bat,里面只有8k的数据,后面的数据未接收到。

另外Loc(1) = strlen这句代码中的1代表的就是上面产生的临时数组#1吗?
在后面的处理中,如果我要把全部的数据转换为字符串,
是否可以用tmp=StrConv(1, vbUnicode)得到全部20多K数据?

[ 本帖最后由 hcyang1422 于 2015-5-4 17:00 编辑 ]
#21
lianyicq2015-05-04 16:57
回复 20楼 hcyang1422
我从17楼截的代码,遗漏了.
不能调试是容易出问题
          ReDim FileByte(UBound(temp) - 14)
          CopyMemory FileByte(0), temp(12), UBound(temp) - 13
这两句中的"temp"换为"str"

loc(1)是返回找开的1#文件当前的读写位置.
每一包到后数据都getdata到了str中,下一包到前都可以使用.
tmp=StrConv(1, vbUnicode)中的"1"是?

#22
hcyang14222015-05-04 17:19
回复 21楼 lianyicq
我将代码作如下变更后
temp=str()
ReDim FileByte(UBound(temp) - 1)
          CopyMemory FileByte(0), temp(11), UBound(temp) - 1
未再出现错误提示,
但是If Loc(1) = strlen Then这句运行结果一直为false
我查看临时文件temp.bat,里面只有8k的数据,后面的数据未接收到。

现在这部分的接收和处理代码如下
请再帮忙看下8K之后数据未收到的原因,以及后面信息处理时全部数据转换字符串的过程是否正确。

  Case "EB"                                         '二进制数据返回,
       If ReceiveState = False Then
             Open "c:\temp.dat" For Binary As #1
             ReceiveState = True
             id = str(9)
          End If
          strlen = HEX_to_DEC(Hex(str(4)) & Hex(str(5)) & Hex(str(6)) & Hex(str(7))) + 8 '取数据长度,测试OK
          temp = str()
          ReDim FileByte(UBound(temp) - 14)
          copymemory FileByte(0), temp(12), UBound(temp) - 13
          Put #1, , FileByte
          If Loc(1) = strlen Then
             ReceiveState = False
             Select Case id
               Case id = 19                                                            'id为19时,取通道标记
               tmp = Mid(StrConv(1, vbUnicode), 6000, 10000)                           '将全部数据转换为字符串
               S3 = Split(tmp, "ST", , vbBinaryCompare)   '字符串分解,区分大小写
                'Text4.Text = Trim(s3)
                For i = 1 To UBound(S3)
                    s1 = Split(Split(S3(i), vbCrLf)(0), ",")(1)
                    s1 = Trim(s1)
                    Text2 = Text2 + s1 + vbCrLf
                    Form4.CHNAME(j - 1) = Trim(s1)
                Next i
                Case id=13
              End Select

'命令发送完成后,添加有如下状态设置,未全部接收数据是否与此有关?
Private Sub Winsock1_SendComplete()
If sdata = "FE4" Or sdata = "FC GET" Then
ReceiveState = 1
Else
ReceiveState = 0

End If
End Sub

[ 本帖最后由 hcyang1422 于 2015-5-4 17:26 编辑 ]
#23
lianyicq2015-05-05 08:45
回复 22楼 hcyang1422
有两个问题没清楚:
1、每个包的第5~8字节是本包的数据长度,不是本次查询回传数据的总数;
2、验证第5~8字节,是否是高位在前,低位在后。一般是低位在前,高位在后。
如果第一个EB包不是本次查询回传二进制数据的总长度,以前代码就不对。只能靠检测是否是连续的EB包,即每次查询,设备第一个返回包总是EB包,若第N个包不是EB包,就说明本次查询的返回数据传送完成。
#24
hcyang14222015-05-05 09:55
回复 23楼 lianyicq
对于你提出的问题,回答如下:
1;只有在12byte头包中的5-8位是本次查询回传数据总长度,后面的包中只包含数据,除最后的包以EN&VBCRLF&2byte校验值结尾外,没有任何标志。
2;5-8位是低位在前,高位在后的,而且这个顺序也有命令可以设置的。

如果你电脑上面装有Wireshark这个软件的话,可以下载一下18楼的附件,里面有整个数据返回时的抓包数据。
为了方便分析问题,我将抓包的数据转换成了TXT文档,并在相关内容后面加了注释。请参考附件。
只有本站会员才能查看附件,请 登录


[ 本帖最后由 hcyang1422 于 2015-5-5 10:25 编辑 ]
#25
lianyicq2015-05-05 10:39
看了文本文件,是单包能完成的数据回传。
如果单包不能完成,是不是这样?
只有本站会员才能查看附件,请 登录
#26
hcyang14222015-05-05 10:58
回复 25楼 lianyicq
不是单包可以发送完的,
你写的非单包数据格式基本正确,
不对的地方有以下3点
1:头包只有12byte,不包含数据
2:倒数第二包是以EN&VBCRLF结尾
3:最终的尾包只有2byte,即数据校验和
#27
lianyicq2015-05-05 11:44
回复 26楼 hcyang1422
既然数据有结尾标志,就用结尾标志来决定后续事件。
试试
程序代码:
Dim ReceiveState As Boolean

Private Sub Winsock1_DataArrival(ByVal bytesTotal As Long)
Dim S, S3, s1, flag, flag1 As String
Dim id, strlen As Integer
Dim tmp As String
Dim str() As Byte
Dim FileByte() As Byte

Winsock1.GetData str()

Select Case ReceiveState
  Case Is = True
    If str(UBound(str)) = &HD And str(UBound(str) - 1) = &HA And str(UBound(str) - 3) = Asc("E") And str(UBound(str) - 2) = Asc("N") Then
         ReDim FileByte(UBound(str) - 4)
         copymemory FileByte(0), str(0), UBound(str) - 3
         Put #1, , FileByte
         rectivestate = False
         Select Case id
           Case Is = 19
             '显示信息
         End Select
         Close #1
       Else
         Put #1, , str
    End If


 Case Is = False
   flag = Chr(str(8)) & Chr(str(9))
     Select Case flag

 
       '...
       Case "EB"     '二进制数据返回,
          If ReceiveState = False Then
             Open "c:\temp.dat" For Binary As #1
             ReceiveState = True
             id = str(9)
             'strlen = HEX_to_DEC(Hex(str(4)) & Hex(str(5)) & Hex(str(6)) & Hex(str(7)))
             Exit Sub
          End If
         
End Select

 
#28
hcyang14222015-05-05 18:55
回复 27楼 lianyicq
已按给出的方案修正了代码并测试了,有以下问题
1:当程序初始化过程中发送查询命令时,只能接收到19K的数据,前面的8K丢失了。
2:程序运行过程中再次发送查询命令时,数据可以全部接收,但是会累计在之前的数据后面。
   即重新接收数据时,在新建临时文件前的Kill "c:\temp.dat"命令没有运行。
3:Select Case id 没有运行,而且id的值也随着新数据包在变化,不能固定取值为头包中的值。
现贴出全部接收代码,请再帮忙看看
Private Sub Winsock1_DataArrival(ByVal bytesTotal As Long)
Dim S, S3, s1, flag, flag1 As String
Dim id, strlen As Integer
Dim tmp As String
Dim str() As Byte
Dim temp() As Byte
Dim FileByte() As Byte

ReDim str(bytesTotal - 1)                                                                  '初始化接收缓冲区

Winsock1.getData str, vbByte + vbArray
Select Case ReceiveState
  Case Is = True
    If str(UBound(str)) = &HA And str(UBound(str) - 1) = &HD And str(UBound(str) - 3) = Asc("E") And str(UBound(str) - 2) = Asc("N") Then
         ReDim FileByte(UBound(str) - 4)
         CopyMemory FileByte(0), str(0), UBound(str) - 3
         Put #1, , FileByte
         ReceiveState = False
         Select Case id
           Case Is = 19                                                   '当id为19时,表示接收的为设定信息
             '显示信息
             tmp = Mid(StrConv(1, vbUnicode), 6000, 10000)                '将全部查询数据转换为字符串,并截取中间部分
               S3 = Split(tmp, "ST", , vbBinaryCompare)                   '字符串分解,区分大小写
                'Text4.Text = Trim(s3)
                For i = 1 To UBound(S3)
                    s1 = Split(Split(S3(i), vbCrLf)(0), ",")(1)           '取通道标记
                    s1 = Trim(s1)
                    Text2 = Text2 + s1 + vbCrLf
                    Form4.CHNAME(j - 1) = Trim(s1)                        
                Next i
         End Select
         Close #1
       Else
         Put #1, , str
    End If
    Case Is = False
    flag = Chr(str(0)) & Chr(str(1))
   
        Select Case flag
           
           Case "E1"                                        '登录
              Sleep 200
              Login   '登录
              log.Text = log.Text & Format(Hour(Time), "00") & ":" & Format(Minute(Time), "00") & ":" & Format(Second(Time), "00") & " " & "admin" & vbCrLf  '显示日志
        
          Case "E0"
               u = u + 1
               log.Text = log.Text & Format(Hour(Time), "00") & ":" & Format(Minute(Time), "00") & ":" & Format(Second(Time), "00") & " " & strdata   '显示日志
               log.SelStart = Len(log.Text)
                If u = 1 Then
            '     Sleep 500
                log.Text = log.Text & Format(Hour(Time), "00") & ":" & Format(Minute(Time), "00") & ":" & Format(Second(Time), "00") & " " & "登录成功" & vbCrLf   '显示日志
                state_lab = "TCP/IP Connect OK"                  '登录成功
                log.SelStart = Len(log.Text)
                End If
          Case "EA"                                         '温度值返回
               S = strdata
               log.Text = log.Text                          '不显示日志
               log.SelStart = Len(log.Text)
                S3 = Split(S, "^C", , vbTextCompare)
                For j = 1 To UBound(S3)
                    s1 = Split(S3(j), vbCrLf)(0)
                    s1 = Trim(s1)
                    If Format(s1) > Val(Form4.CH(j - 1)) Then
                    Form4.CH(j - 1) = Format(s1, "00.0")
                    End If
                Next j
           Case "EB"                                         '二进制数据返回,
               If ReceiveState = False Then
                     Kill "c:\temp.dat"
                     Sleep 100
                     Open "c:\temp.dat" For Binary As #1
                     ReceiveState = True
                     id = str(9)
                     Exit Sub
                  End If
          End Select
End Select
End Sub

[ 本帖最后由 hcyang1422 于 2015-5-5 18:59 编辑 ]
#29
lianyicq2015-05-06 09:18
回复 28楼 hcyang1422
1:当程序初始化过程中发送查询命令时,只能接收到19K的数据,前面的8K丢失了。
2:程序运行过程中再次发送查询命令时,数据可以全部接收,但是会累计在之前的数据后面。
   即重新接收数据时,在新建临时文件前的Kill "c:\temp.dat"命令没有运行。
3:Select Case id 没有运行,而且id的值也随着新数据包在变化,不能固定取值为头包中的值。

1、2问题可能的解释就是第一个EB包除了12字节头以外,还包含数据。从你最早发的包格式图片看得出来。
临时文件建议在显示之后删除,也可以加代码另存。
第3问题的ID变化是否发生在一次查询返回的多个数据包时,难道一次查询会返回多个不同类型的数据文件?
写了这几次代码,分次接受的思路还是比较清晰了。
#30
hcyang14222015-05-06 10:40
回复 29楼 lianyicq
现在我在代码判断包头Case "EB"部分加了写入数据后,现在已可以接收全部数据了,
但是数据接收完成后的Select Case id部分仍然没有运行。
为判断原因,我在取id后加入了debug语句查看了取值的结果,发现结果是正确的,
请再帮忙看看是什么原因造成的。
#31
lianyicq2015-05-06 10:50
把id定义为全局变量,不要定义在Private Sub Winsock1_DataArrival(ByVal bytesTotal As Long)
之内
#32
hcyang14222015-05-06 11:20
回复 31楼 lianyicq
把id定义为全局变量后依然不行。是否应该把这部分的判断处理放在if语句外面,即先接收数据,当数据全部接收完成后,再进行处理。
#33
lianyicq2015-05-06 11:34
...
CopyMemory FileByte(0), str(0), UBound(str) - 3
Put #1, , FileByte
ReceiveState = False
Select Case id
Case Is = 19
...
如果能执行红色这条,那就在下一条设个断点。看看id究竟是多少。最早粘的图片中ID列表不会是16进制数吧
#34
hcyang14222015-05-06 14:32
回复 33楼 lianyicq
现在我把代码又修正了一下,红色字体的代码也可以执行了,而且id值也是正确的,即id=19
但是后面的处理程序没有正常工作,即下面红色字体处,该怎样才能把刚收到的全部数据转换成字符串进行处理?
现在的代码输出的tmp值为空。
Select Case id
           Case Is = 19                                                   '当id为19时,表示接收的为设定信息
             '显示信息
             tmp = Mid(StrConv(1, vbUnicode), 6000, 10000)                '将全部查询数据转换为字符串,并截取中间部分
             text7.text = tmp

               S3 = Split(tmp, "ST", , vbBinaryCompare)                   '字符串分解,区分大小写
                'Text4.Text = Trim(s3)
                For i = 1 To UBound(S3)
                    s1 = Split(Split(S3(i), vbCrLf)(0), ",")(1)           '取通道标记
                    s1 = Trim(s1)
                    Text2 = Text2 + s1 + vbCrLf
                    Form4.CHNAME(j - 1) = Trim(s1)                        
                Next i
         End Select

[ 本帖最后由 hcyang1422 于 2015-5-6 14:56 编辑 ]
#35
lianyicq2015-05-06 15:11
2:倒数第二包是以EN&VBCRLF结尾
3:最终的尾包只有2byte,即数据校验和

以上两条可能有误导致以下条件语句不满足
    If str(UBound(str)) = &HD And str(UBound(str) - 1) = &HA And str(UBound(str) - 3) = Asc("E") And str(UBound(str) - 2) = Asc("N") Then
改为
    If str(UBound(str-5)) = &H45 And str(UBound(str) - 4) = &H4E And str(UBound(str) - 3) = &HD And str(UBound(str) - 2) = &HA Then
......
#36
hcyang14222015-05-06 15:16
回复 35楼 lianyicq
这个问题我已修正了,现在的问题是后面的处理程序没有正常工作,即下面红色字体处,该怎样才能把刚收到的全部数据转换成字符串进行处理?
现在的代码输出的tmp值为空。
Select Case id
           Case Is = 19                                                   '当id为19时,表示接收的为设定信息
             '显示信息
             tmp = Mid(StrConv(1, vbUnicode), 6000, 10000)                '将全部查询数据转换为字符串,并截取中间部分
             text7.text = tmp

               S3 = Split(tmp, "ST", , vbBinaryCompare)                   '字符串分解,区分大小写
                'Text4.Text = Trim(s3)
                For i = 1 To UBound(S3)
                    s1 = Split(Split(S3(i), vbCrLf)(0), ",")(1)           '取通道标记
                    s1 = Trim(s1)
                    Text2 = Text2 + s1 + vbCrLf
                    Form4.CHNAME(j - 1) = Trim(s1)                        
                Next i
         End Select
#37
lianyicq2015-05-06 15:36
以前就说过tmp = Mid(StrConv(1, vbUnicode), 6000, 10000) 中"1"是什么意思
可以把字节数组转为字符串,比如字节数组a,字符串tmp
tmp=strconv(a,vbunicode)
#38
hcyang14222015-05-06 15:47
回复 37楼 lianyicq
现在的程序是把temp.bat做为动态数组保存接收数据的吧!并没有哪一个临时数组里面是包含全部数据的。现在我要调用已接收的全部数据,应怎么把temp.bat中的数据调入一个临时数组里面?

可以在接收完数据后,关闭temp.bat,然后再打开,用Get #2, , tmp语句进行全部数据调用吗?
#39
lianyicq2015-05-06 15:59
程序代码:
ReDim str(Loc(1))
Loc(1) = 0
Get #1, , str
Text1.Text = StrConv(str, vbUnicode)
Close #1
显示信息的时候,temp.dat并没有关闭。可以
...
loc(1)=0改为
seek #1,1
...
迷糊了
其实只需要
ReDim str(Loc(1))
Get #1,1 , str
Text1.Text = StrConv(str, vbUnicode)
Close #1


[ 本帖最后由 lianyicq 于 2015-5-6 16:14 编辑 ]
#40
hcyang14222015-05-06 16:10
回复 39楼 lianyicq
非常感谢你一直以来的帮助,刚才这一问题我用先关闭再打开的方法也搞定了,你的这种方法等下我再去测试一下。
另外,再向你请教一个比较小白的问题
我在这个处理过程中,得出的结果是传送给另外一个窗口的LABEL的Caption,我第一次点击显示子窗口时,可以看到得到的值,但是关掉后再打开,这个属性值又返回为默认值了。
请问如何才可以在得到数据后,将值锁定给子窗口的LABEL。
#41
lianyicq2015-05-06 16:24
回复 40楼 hcyang1422
可能你关闭的时候使用了unload,再次显示的时候就恢复初使值了。用HIDE试试
#42
hcyang14222015-05-06 16:44
回复 41楼 lianyicq
用HIDE的方法把这个问题搞定了,非常感谢!!
后面我还要尝试读出图片数据,如果遇到问题还要继续向你请教,
还望能继续帮助我。
谢谢!!!
1