March 1996
The Visual Programmer Fingers WinSock Functions from Visual Basic
Joshua Trupin
Joshua Trupin is a software developer specializing in C/C++ and Visual Basic apps for Windows. He can be reached at 75120.0657@compuserve.com or geeknet@ix.netcom.com.
Click to open or copy the VISPRG project files.
Everyone wants to be on the Internet nowadays. This includes grizzled veterans like me who remember when it was the ARPANet. I'm not saying this just to sound more knowledgeable about the subject. It's just thatÉwell, I guess I am just trying to sound more knowledgeable. Anyway, this tidal wave of interest has done a lot for programming. First, it's created a viable market for client programs such as mail readers and Web browsers. Second, it's made programmers like me, who have avoided network protocols and programming with all the ferocity of an Ultimate Fighting match participant, realize that we'd better figure out just what TCP/IP is. (In case you've had your head in the sand for the past year, TCP/IP stands for Transmission Control Protocol/Internet Protocol.)
TCP/IP is simply a suite of protocols. The TCP part describes how two machines set up a reliable connection to each other and transfer data chunks. IP primarily deals with how to get a message routed across the Internet. Each connected machine site has a unique IP address that allows others to figure out a path to any machine around the world. Each machine with an IP address supports numbered ports, each dedicated to a particular service. For instance, you use TCP to send mail via Simple Mail Transfer Protocol (SMTP) using port 21. You retrieve mail with Post Office Protocol (POP) on port 110. These port numbers have been chosen by convention. So a machine needs to run both TCP and IP together (a protocol stack) to communicate with the outside world.
Since the Internet is built on TCP/IP connections, Internet-enabled client programs have to use TCP/IP to connect with remote machines and transfer data in various formats. Fortunately there's a standard API used to write to TCP/IP stacks-sockets. Sockets was originally developed for Unix; Windows Sockets, or WinSock for short, was later developed for Windows®-based programs (for more information, see "Plug into Serious Network Programming with the Windows Sockets API" by J. Allard, Keith Moore, and David Treadwell, MSJ, July 1993). Here, I'll develop a small OCX that provides selected WinSock functions, then write a 32-bit Visual Basic® program that issues a "finger" command, querying a remote machine for information about a specific user.
First, a bit of background on WinSock. When a communications program is WinSock compliant, it usually means that after it dials up and connects with a remote service provider, it provides sockets for your client programs to plug into. Each communications program that implements these TCP/IP services publishes its own implementation of WINSOCK.DLL. These DLLs are almost always incompatible with one another at the server level, although they provide the same external services to clients wishing to connect with them. Therefore, if you overwrite another communication program's WINSOCK.DLL, that program won't work correctly, but a client program such as my finger program (discussed later) will work the same way with any communication package's WINSOCK.DLL. If that seems confusing to you, you've had more luck in avoiding these sorts of problems than many others have had.
The other thing to know is that a program can provide a 16-bit or a 32-bit WinSock implementation. If your communications program is 16-bit, you can't run a 32-bit WinSock client with it. However, if you have a 32-bit comm program or dialer (such as the Windows 95 Dial-Up Networking), you can use both 16- and 32-bit clients with it. While developing this month's programs, I used my account on Netcom, a popular service provider. Netcom provides only a 16-bit dialer, NetCruiser, but by setting up the Windows 95 dialer instead, I was easily able to start connecting through a more reliable 32-bit connection.
One of the biggest concerns in developing a WinSock program is that not all data requests are fulfilled immediately. If you're awaiting a return or an acknowledgment from a distant machine, it can take several seconds or even minutes before the messages make their way back across the Internet. For this reason, WinSock functions are divided into two groups, synchronous (returning data immediately) or asynchronous (you might have to wait for a return message). Synchronous messages include those that don't need to go across the network for return data, such as network-to-client byte-order conversion calls. Asynchronous calls include calls like remote connections, where you won't know whether it worked right away.
If your program sends a message to a machine halfway around the world and sits there waiting for a reply, it might appear to be hung. You should write your own handler to process other Windows messages during the wait time, or you'll risk freezing everyone's program while your selfish little app waits for incoming data (in 16-bit Windows, anyway), a problem often known as the WinCIM Syndrome.
When you have to make a WinSock call that requires asynchronous operations, the call itself handles it for you. Last month in my Visual Programmer column, I discussed that many callback functions in Windows 95 are designed to expect a window handle and message ID. Asynchronous WinSock calls use this mechanism. If you're expecting to receive some data from halfway around the world, you can call the WSAAsyncSelect function, and pass it a window handle, message ID, and a flag indicating you want to wait for data:
WSAAsyncSelect(socketID, hWnd, WM_USERDEFINED, FD_READ)
When the data actually arrives from somewhere on the network and hits your TCP/IP stack, your window's message handling procedure is notified. Meanwhile, your interface doesn't freeze and you can go do other processing.
Now suppose you want to do this all in Visual Basic. Since you can't use Visual Basic to capture user-defined messages within a Visual Basic form's code, you need to give it a bit of help. You need a WinSock OLE control that can handle these asynchronous calls and trigger events in Visual Basic when something happens.
To demonstrate how easy it can be to get started with WinSock programming, I've developed Flipper: The MSJ Finger Program. I chose to write a finger program because it's one of the simplest things you can do on the Internet. It doesn't require you to maintain a connection to a remote machine the way an FTP (file transfer protocol) session would. All you have to do is find a remote site's unique numeric IP address, send a string containing a user's name to the finger port at this IP address, and wait for return data. When your machine gets data back from that IP address, your program is notified that it has received data, and you retrieve it and display it (see Figure 1).
Figure 1 Fingering a Remote User Through WinSock
Perhaps I should define just what a finger program is. It's a simple query sent to a remote node to retrieve information about a user or users on the system. Some Internet services, like file browsing and transfer (FTP) and remote terminal access (Telnet) need a full-blown connection, with signon procedures and a persistent connection. Finger, however, doesn't need anything that fancy. When you send a CR/LF-terminated user name as a message to a site's designated finger port, it sends back a block of text about that user. This data is defined by the individual machine; it can contain connection information, real-life names, the amount of mail a user has waiting, or even display user-defined information (commonly known as a .plan file). After this information is returned, you're under no further obligation. (Just write CANCEL on the bill and you owe nothing!)
So let's go over the steps needed to implement a finger program. First, you have to connect to the TCP/IP stack and receive a socket handle to use. You then have to retrieve the address of the remote site, connect to it, send it the username you're interested in, wait for it to return finger information, and disconnect.
To start with, I created an MSJSock control with the Microsoft Developer Studio (AKA Visual C++® 4.0). To get the finger command working correctly, the control needs fivemethods:CreateSocket,gethostbyname,getservbyname, AsyncSelect, and Connect. Technically, some of these WinSock calls can be made from Visual Basic itself, but putting them in an MSJSock OCX makes some operations easier. For instance, if you don't know which standard port to use for a particular service, you can use the getservbyname socket API function. If you call
getservbyname("finger", "tcp")
you're returned information about the TCP finger protocol, but it's returned as a structure with nasty contents such as pointers to pointers. It's easier to deal with this information in C/C++, because Visual Basic doesn't handle pointer values very well. In Windows 3.1, you could potentially have called hmemcpy, a memory access API function exported by KERNEL.DLL. Sadly, this function is no longer exported by KERNEL32.DLL, so you either have to export your own equivalent method or create a call that returns a particular member of a structure. I've chosen the second path, which is less extensible but leads to easier Visual Basic code. Similarly, when you need to call an asynchronous WinSock function, the OLE control will provide the message trap for you, and will fire an event into the Visual Basic project when network data arrives. Clearly, the OCX and Visual Basic project need each other to function correctly, although the control will not be bound to one particular hunk of Visual Basic code.
A client only needs two pieces of information to perform a finger-a user ID and a site name. Therefore, the Flipper Visual Basic program can be completed with a fairly bare bones implementation. I've added two edit boxes (one for each piece of info), a text box to display results, a button that starts a finger request, and an MSJSock control. When the edit boxes are filled with a username and remote site name, the button is enabled. When it's clicked, the program swings into action. It calls the MSJSock control's CreateSocket method, which in turn calls the WinSock socket function. (MSJSock code may be found in Figure 2; the Flipper project is shown in Figure 3.) socket returns a unique socket ID immediately, so you have a handle to pass data to perform the finger task.
When you write a WinSock-compliant program, the first thing your app needs to do is initialize socket services for its instance with a WSAStartup call. WSAStartup fills in a structure with some light information like the current WinSock version and a description string such as "Running Under Windows 95." Since I'm using my MSJSock OCX to make some of the structure-passing calls easier, I've put the WSAStartup call in MSJSock's InitInstance function.
Next, the Visual Basic program calls the control's getservbyname method to get the correct port ID for the finger command. As I mentioned earlier, a site can support more than one function-for instance, one server can provide both finger and FTP services. This is done through dedicated port numbers. It is understood that when a user sends a message to the finger port (usually number 79), they're sending a finger command. However, on the off chance that a server has a nonstandard implementation, or you're using a specialized service, or you just want to make sure your local server supports a particular interface, WinSock provides getservbyname so you can be confident there's no funny business going on.
Now you have to look up the IP address for the site you're fingering. To keep track of all sites in a uniform manner, each is assigned a long integer address when it is founded. Service providers (such as MSN) know how to translate a string address such as "ftp.microsoft.com" into the matching long integer. Sometimes this number is broken up into four bytes, so that ftp.microsoft.com is reachable at 32008646, but is commonly known as 198.105.232.1 (198 + 105 x 256 + 232 x 2562 + 1 x 2563). You'll be pleased to know, by the way, that when the current stock of long integers is exhausted in a few years, IP addresses will become longer, breaking everyone's software even worse than the changeover to the 21st century that is going to nuke all those two-digit year fields so popular on mainframe programs.
Retrieving a host by name is an asynchronous function. When you call the control's gethostbyname method, it sends the request off and returns immediately. MSJSock has a separate message-handling function that will be called by WinSock when the information is ready. This function (OnGetHostCB) calls the GotHost event in Visual Basic. The Visual Basic program is free to do whatever it pleases until MSJSock_GotHost is invoked. When MSJSock_GotHost is called, the OLE control passes Visual Basic a long integer called hostent, which is really a pointer to a structure describing the host address. You don't need to dereference this; you just need to pass it back when you connect to the host site.
To get an actual connection, you need three shards of information: the socket handle retrieved earlier, the port ID for fingering, and the hostent structure containing the site's IP address. The connection to the remote host can be initiated by calling the Connect method of the MSJSock control. MSJSock.Connect does two things. First, it calls WSAAsyncSelect. WinSock uses this function to determine where to send asynchronous notification messages for a particular socket. The caller chooses what sort of data to watch for. Here, you pass the FD_CONNECT flag to WSAAsyncSelect; this tells WinSock to let the control know when a connect action has completed. The control can then check the error code-if it's 0, it fires the MSJSock1_Connect event in Visual Basic. Otherwise, it calls MSJSock1_SockError to indicate an error occurred. I've set up a Select Case block in WINSOCK.BAS that will match known WinSock error codes with their names, so the program can pop up a MsgBox.
You're more than halfway there now: determining addresses and connections is the hardest part of WinSock programming. When MSJSock_Connect is triggered inside Flipper, it means you've connected correctly to the remote site's finger port. All that's left to finger a node is to send it a user name terminated with a CR-LF pair. In the control, I've exposed an AsyncSelect method that wraps the WSAAsyncSelect function. This time you want to trap a message any time WinSock tells you there's data from the remote site to be read, so pass it FD_READ. You can now call the WinSock send function directly from Visual Basic, specifying the socket ID and string. Since the earlier Connect call linked the specific remote site and port to the socket received, you don't have to tell WinSock where to route the data again.
The send call isn't really asynchronous, because it doesn't guarantee that data will be coming back. However, the remote host usually does something in response-in the case of finger, it sends back user information and status. When the MSJSock control captures an FD_READ notification from WinSock, it generates a RecvData event in Flipper. This only tells the Visual Basic program that data is available for retrieval, so it's that program's job to call the WinSock recv function in response. recv takes three parameters: the socket ID, an empty string where the data will be stuck, and the maximum length of this string. Instead of declaring a fixed-length string in Visual Basic, I've declared a variable length string, then initialized it with blanks:
strg$ = Space(1024)
After this string is passed to recv, it can be trimmed (with the Trim function), then displayed in the text box I added to the program for just such a purpose. If all goes well, the Flipper program is complete (see Figure 4). Even if something goes wrong, it will display an error code.
Figure 4 Flipper
OK, so calling finger is sort of cinchy compared with other Internet protocols. It doesn't maintain a connection and doesn't make you learn a batch of commands to work properly. Unfortunately, describing each and every available service would make a book or two in itself-or a whole shelf of books, if you check out your local bookstore. There's no reason to let this scare you away from the job, however. There's a big need out there for commercial-quality 32-bit news readers, Telnet programs, and just about anything you might be interested in developing.
The various service protocols are described in documents called RFCs (Request For Comments) and STDs (standards). The first thing you should do is determine what type of program you're writing, then grab the documentation on it so you know what commands a server supports (see Figure 5). There's nearly 2000 Internet, TCP/IP, and sockets RFCs currently available, stretching all the way back to 1969. There are a few sources for RFCs. If you have Internet software, you can ftp anonymously to ds.internic.net, then retrieve files from the /rfc directory. An excellent book, Developing for the Internet with Winsock by Dave Roberts (Coriolis Group Books, 1995), comes with a CD-ROM with every piece of Internet and sockets documentation you'll ever find. Figure 5 shows the most common Internet services and the matching RFCs.
To assist you in debugging your socket-based program, I've developed a small program called Simple Sockets. It's an interactive dialog that lets you send commands to a server and see what gets returned from it. The entire transaction is displayed in a Windows 95 RichText edit box, tagged by color so you can easily see what's up.
As an example, I've downloaded RFC 0977 (the Usenet NNTP news protocol) and now I want to see just how it works. As you may or may not know, Usenet is the name for a network of servers that trade user-posted articles on every topic ever imagined (about 16,000 in all at press time). Using a newsreader, you can browse articles by title or number, post followups, or save and print especially useful tidbits. It's a distributed system with no single home, so you'll be sure to see many fruitless efforts by bureaucrats to legislate its content over the next few years.
But anyway. I need two things to connect: the name of my news server (nntp.ix.netcom.com) and the NNTP port (119 is standard; this is given in the RFC). I enter the two values and click connect, and Windows 95 either performs an auto-login with the Dial-Up Adapter for me or connects me to a running instance of WinSock. Non-text-returning actions like this are displayed as informational messages in blue. When the program indicates "Connected to host," I can start sending my commands, which show up in green. Whenever my program receives something from the host, it's appended to the RichText edit in red, and should a WinSock error occur (a bad server name, for instance), the message will show up in black (see Figure 6).
Figure 6 MSJ Simple Sockets
On my server, I have to sign on with the AUTHINFO command, providing my username and password. I enter it in the edit field provided, then click the Send button or hit enter. After a couple of seconds, I receive back an informational message from nntp.ix.netcom.com: "501 user Name|pass Password." Looking up a message code 501 in the RFC, I'm told that my command had a syntax error. I'm not upset, because it seems to work anyway.
Without going into too much detail, an NNTP server will provide a list of all available newsgroups with the LIST command. Be sparing with it, though-it returns the names of all 16,000 groups, plus or minus. Once you get this list downloaded once, you can use the NEWGROUPS command to see all additions from a certain date.
Figure 6 shows some other NNTP commands in action. I've decided to check out the action on alt.fan.surak. (And I don't need any editorial comment from you either.) The GROUP command returns an informational code (211, which means it's a reply to a GROUP command, as documented in the RFC). It also tells you the number of articles available, the first and last articles available, and the group name. Instead of doing this for 16,000 groups every time a user starts a program, news readers usually have a local "subscribe" option, which really only makes an internal list of which groups it should check for content automatically.
After selecting a group with GROUP, it is implicitly assumed that you're operating on it with further commands. For instance, I've issued an ARTICLE command to receive a particular numbered article (along with a couple of bad commands just to show what they might output). Article 2615 is returned to my program, headers, text and all. I'm now just a little bit more caught up with the world of Surak fandom. I know, it's too good to be true. Anyway, MSJ Simple Sockets source code is shown in Figure 7.
Naturally, there's a lot of tracking work to be done for a full-blown news reader. However, with MSJ Simple Sockets, you can see what's actually going on behind the scenes with NNTP, or any other protocol you'd like to test out.
Windows Sockets programming is currently the only pathway to every protocol and function provided on the Internet. However, up till now it's been seen as just beyond the reach of Visual Basic programmers. I've provided two components to change this-the MSJSock OLE control and the MSJ Simple Sockets Visual Basic application. With surprisingly little code, you can write a real Internet-enabled program in Visual Basic. If you get something on the market, all I ask is that you send me a couple of shares of your Internet company stock.
From the March 1996 issue of Microsoft Systems Journal.