I have found it very easy and it works well to use the LookAhead function of the socket until the data in the socket has what I am looking for.
So in some code I just spent quite a bit of time working on, I do this and it works well. What I am doing is logging into a device and then sending that device commands and reading back what the response is. I have quite a number of different commands to send to get all the device parameters that I need. So I have a select case statement and an enumeration to keep track of where I am at in my process. Things like GetFIrmwareVersion, GetSerialPortSettings, GetIFConfigData, etc. are all parts of my enumeration. As I step through each step, I set the enumeration to the next step in the process. Then the select case statement in the DatAvailable event directs me to the proper branch for what I want to process.
Once in the branch I want to process, I use a RegEx to verify if the receive buffer has the data I want. Here’s an example of one of those. I’ve sent the command ifconfig to the device I am reading. This is the code to process that…
Case HardwareParameterStep.GetIFConfigData
s = Lookahead(Encodings.ASCII) ' s is a string variable defined earlier in the DataAvailable Event.
dim rx as new RegEx
rx.SearchPattern = _
"(?mi-Us)HWaddr (\\b[[:xdigit:]]{1,2}(:|-)(?:[[:xdigit:]]{1,2}\\g2){4}[[:xdigit:]]{1,2}\\b)\\s*(?(DEFINE)(?'octet'25[0-5]|2[0" + _
"-4]\\d|[01]?\\d{1,2}))(?(DEFINE)(?'IPAddy'\\b(?'octet1'(?&octet))\\.(?'octet2'(?&octet))\\.(?'octet3'(?&octet))\\.(?'octet4'(?" + _
"&octet))\\b))inet addr:(?'IPAddy1'(?&IPAddy))\\s*Bcast:(?'IPAddy2'(?&IPAddy))\\s*Mask:(?'IPAddy3'(?&IPAddy))"
dim rxOptions as RegExOptions = rx.Options
rxOptions.LineEndType = 4
dim match as RegExMatch = rx.Search( s )
If match <> Nil Then
s = ReadAll(Encodings.ASCII) ' I have received all the data I want. So now, read everything in the buffer.
If mMacAddress <> match.SubExpressionString(1) Then ' Set my MacAddress property to the first sub-expression
mMacAddress = match.SubExpressionString(1) ' If it doesn't match what I already have as the MAC address
End If
If IPAddress <> match.SubExpressionString(9) Then ' Set my IP Address property
IPAddress = match.SubExpressionString(9)
End If
If SubnetMask <> match.SubExpressionString(11) Then ' Set my SubnetMask property
SubnetMask = match.SubExpressionString(11)
End If
Else
Return ' The match is nil - so I have not found the data I am looking for in the buffer. Return and wait for next fire of event.
End If
So I know the format that the IFConfig data comes back to me. I look for that format. It’s quick and I’ve not had any issues with losing data.
In my development, I tried using a timer to do all the processing. I fired the timer from the DataAvailable event. My results were not as fast or as good. I don’t understand why people want to use a property to hold the value of the receive buffer and effectively buffer it again. The buffer is there and the lookahead function gives you that ability to look at it. I have one case where I do cache the data in a separate property and look at it later, but that is only because I need to do other stuff with the data that comes in.
Personally, I have found that letting the framework fire the DataAvailable event and checking for your data there tends to work really, really well. Yes, I agree if you end up doing some long computation or something that is CPU intensive, then you may miss data. But once you have all your data, it is all good anyhow.
One thing I have found that you do NOT want to do when using sockets and the data available event is to sleep the main thread. I used to sleep the main thread sometimes for some various reasons and found that sleeping the main thread causes data to get missed in the DataAvailable event. That caused problems. As soon as I stopped sleeping the main thread during any i/o events, it went much better.
Anyhow, YMMV, but this has worked out really well for me like this. It’s fast and works.