Reputation: 7192
What I need to happen with a VB6 application I maintain is the following.
Send a request
Wait for a response.
I tried using WinSock and Winsock replacement but they all rely in one form or another the messaging loop inherent in various Windows application. I don't know enough about the Winsock API how to implement the above algorithm in VB6 (or in any other language.
My application is a VB6 CAD/CAM software that controls metal cutting machines over a dedicated Ethernet network. The software has been maintain for 20 years and we developed several driver for different types of motion controllers. To date the API for these motion controllers consist of
Some of these controller work over a Ethernet Network but until now I never had to directly interact with the ports. I used the company supplied libraries to handle things. And they work in the way I mentioned above and throw a timeout error if a response doesn't occur in some defined time.
The problem with Winsock is that I have to insert DoEvents to get it a respond. This causes havoc with how we handle multi-tasking in our VB6 application. The replacement like CSocketMaster use subclassing which also cause havoc with our multi-tasking.
So any help on how to use the Winsock API or a third party dll that can do what I need to do as outlined above. I wouldn't be asking if I haven't seen other motion control do what I want to do.
Upvotes: 1
Views: 4584
Reputation: 7192
As it turns out the answer involved implementing Allen's Suggestion.
The specific problem was communicating between two device involved in the control of a piece of machinery. The device that did the motion control is acting as a server, while the PC providing the motion data was a client.
The Ethernet network was being used in lieu of a proprietary bus interface or a RS-232/422 serial interface. So many of the considerations involved in serving up data over a widespread internet were not a factor. The network consisted of known devices residing at fixed IPs listening to specific ports.
After talking to people who make other motion control. The logic for the client turned out to be surprisingly simple.
On the server side, we were lucky in that we had control over the software running on the motion controller. So there the communication loop was designed to be as fast as possible. One key point was to keep all data below 512 bytes so it is all contained in a single packet. They also took great care in priortizing the communication handler and the data structure so it can send out a response in tens of microseconds.
Another point was that in this application of dedicated clients and server that UDP is preferred over TCP as Operation Systems particularly Windows were in the habit of shutting down idle TCP Connections unexpectedly.
Because the client software is slowly transition over to the .NET framework that was another factor for implementing Allen's idea. The library discussed by @wqw worked as well.
Upvotes: 1
Reputation: 11991
Check out VbAsyncSocket repo on github for pure VB6 asynchronous sockets implementation (using WSAAsyncSelect
API for sockets to post event notifications).
Contrary to it's name the class does support SyncSendArray
and SyncReceiveArray
methods for synchronous operations -- without DoEvents
but with Timeout
s.
In the same repo there is a handy cWinSockRequest
contributed class, that is very similar to WinHttpRequest
object as baked into the OS. This helper class will be very familliar to you if you have experience with JSON/XML (generally RESTful services over http/https) for accessing services/devices over plain tcp/udp sockets.
Another option would be to use cTlsClient
contributed class, that can connect to host/device over tcp (no udp here) and provides ReadText
/WriteText
and ReadArray
/WriteArray
(synchronous) methods. The added benefit here is that the class supports both plain unencrypted sockets and SSL encrypted channels if need be.
We are using these classes to (synchronously) access ESP/POS printers from our LOB applications. Most POS printers also provide serial (USB-to-COM) links too, so we are abstracting our access w/ connector classes -- SyncWaitForEvent
over async sockets and WaitForMultipleObjects
on overlapped ReadFile
/WriteFile
APIs (oh, the irony)
Upvotes: 4
Reputation: 567
I think it is rare for it to be appropriate to do networking synchronously, however this isn't networking in the traditional sense. This is a wire from a PC to a controller. This is like a string between two cans. In this case with a large old program, the most appropriate approach is the one that works the best and is the easiest to maintenance. < /end2cents >
If VB6 + Winsock isn't working out for you, writing this in .NET and building it into a COM visible DLL for your VB6 program will fit the bill.
The example below will get you started. If you do more than the occasional call to this, it will be slow as it opens and closes the connection on each call. It should be easy to expand it to allow for reusing an open connection for back and forth communication between the PC and controller. Just be careful that you don't create a memory leak!
/// <summary>
/// Sends a message to the specified host:port, and waits for a response
/// </summary>
public string SendAndReceive(string host, int port, string messageToSend, int millisecondTimeout)
{
try
{
using (var client = new TcpClient())
{
client.SendTimeout = client.ReceiveTimeout = millisecondTimeout;
// Perform connection
client.Connect(host, port);
if (client.Connected)
{
using (var stream = client.GetStream())
{
// Convert the message to a byte array
var toSend = Encoding.ASCII.GetBytes(messageToSend);
// Send the message
stream.Write(toSend, 0, toSend.Length);
// Get a response
var response = new byte[client.ReceiveBufferSize];
stream.Read(response, 0, client.ReceiveBufferSize);
return Encoding.ASCII.GetString(retVal);
}
}
else
{
return null;
}
}
}
catch
{
return null;
}
}
Upvotes: 2
Reputation: 13267
Your problem is that you are using the Winsock control incorrectly. That probably stems from a flaw in your interaction model.
Don't "send and wait" because blocking like that is your big mistake. There is no "waiting" anyway unless you think sitting in a buzz loop is waiting.
Instead send your request and exit from that event handler. All of your code is contained in event handlers, that's how it works. Then as DataArrival events are raised you append the new arrived fragment to a stream buffer then scan the assembled stream for a complete response. Then go ahead and process the response.
Handle timeouts using a Timer control that you enable after sending. When you have assembled a completed response disable the Timer. If the interval elapses and Timer event is raised do your error processing there.
You seem to have a particularly chatty protocol so you shouldn't have to do anything else. For example you can just clear your stream buffer after processing a complete response, since there can't be anything else left in there anyway.
Forget "multitasking" and avoid DoEvents() calls like the plague they are.
This is very simple stuff.
Upvotes: 0