An Introduction To Network Programming Using Winsock

Written by Stéphane Richard (MystikShadows)

INTRODUCTION:

Welcome to this introductory tutorial on network programming using winsock. The subject has been talked about and asked for both on the Official QB Express Article request thread and in a few emails I've received and although there is an example it seems that some of the things could be explained in a bit more details. As such I've decided to create this tutorial that will guide you through using winsock in order to connect to a specific website URL and retrieve information from the server. Since all you can do with winsock is far beyond the scope of an introductory tutorial, I will be providing you with links to all the information you'll need so that you'll be equipped to take on just about any network/internet related tasks.

When it comes to this tutorial, the example program supplied should also work in linux (with perhaps some minimal changes) since linux sockets work pretty much the same way as Windows sockets. Since I don't have linux installed at the moment I can't give you these changes directly or know the discrepencies of Windows and Linux when it comes to sockets programming. If someone has played with sockets programming in linux with freebasic and knows these discrepencies (if any) please let me know via email and i'll be glad to add these to this tutorial. And now, let's get right into the core of winsock programming.

FREEBASIC WINSOCK PROGRAMMING BASICS:

In most cases, when programming with winsock, there is a very simple order of things to follow. Since winsock typically talks to a server, no matter what type of interchange you'll be making with that server, you will be basically following the following order:

It doesn't matter the job to be done via winsock, there's no command that automatically opens or closes things for you so it will be a simple question of remember to open, perform the request or task and close. Just be warned that if you don't close your connections after a while of opening new connections, you (or you with other people) could overflow the server and hence freeze on your side and slow down the server dramatically for other users so just pay a little attention and show a little consideration when doing internet related network programming.

MAKING SENSE OF THE PROTOCOLS AND COMMANDS:

The commands in question, as I mentionned, differ depending on the protocol used. Each type of connection allows different sets of commands and as such, I will enumerate here the four most used protocols with links to list of commands so you get an idea of what can be done under these different protocols. The protocols are:

Of course, there are other protocols, but in all honesty, the other protocols are either used less and less or practically dissapeared from the map. When you think about it, there's not much more you'd be doing on the internet (except chatting or group gaming which is done with TCP/IP anyways) aside viewing websites, sending and receiving emails and uploading or downloading files.

FreeBASIC comes with a special library for winsock and sockets programming. In the next section we'll talk about it however, we'll limit ourselves to just what we need for this tutorial or the next section would be way passed the scope of this tutorial. So then, let's get right to it.

THE DREADED WINSOCK.BI INCLUDE FILE:

I'm not sure if any of you have looked at the "winsock.bi" file but you might notice that it's 17kb in size, has a lot of defines, user defined types, constants and sub and function declaration. There is also the winsock2.bi file which stands at a whopping 48kb, has alot more defines, constants, user defined types and subs and functions declarations. In otherwords, it has alot more definitions and declarations that we need for a first program. winsock.bi and winsock2.bi are located in your freebasic installation folder under the inc\win\ folder. If you want, you can take a look at them to get their full scope of definitions. However, for the purpose of this tutorial and your convenience, I've extracted the definitions and declarations that that we will be needing only.

Since these are extractions, I have not documented them per se, the most important thing to know here is that all these definitions are in winsock.bi and winsock2.bi. if you want a good complete reference on winsock programming, take a look at the Sockets.com which features everything you ever wanted to know about the subject and complete references to standards, protocols, function and sub references for winsock and reference lists and references for all RFC standards. sockets.com is by far the best website to use for anything winsock related, take a look and see for yourself how complete it is.

The Constant Definitions and Define Statements:

As you can see below, we don't need alot of constants here. we only have four defined here (note that winsock.bi has alot more because of the different types of internet protocols and connections that can be made) and for this tutorial it's really all we'll need. We defined a macro called MAKEWORD that essentially assures that the value return is of a valid USHORT type (0 to 65535 in range).

OPTION EXPLICIT ' -------------------- ' Library Inclusions ' -------------------- '#Include "winsock.bi" <- Commented out for tutorial purposes, ' should be used instead in projects #INCLIB "wsock32" ' ------------------------------------------------------------------ ' Constants and defines extracted from winsock.bi for this example ' ------------------------------------------------------------------ CONST AF_Inet = 2 CONST Sock_Stream = 1 CONST IPProto_TCP = 6 CONST NewLine = CHR$(13)+CHR$(10) #DEFINE MAKEWORD(a,b) ((a) shl 8 or ((b) and &h000000FF)) #DEFINE WSADESCRIPTION_LEN 256 #DEFINE WSASYS_STATUS_LEN 128

The User Defined Types:

The user defined types shown here (seven of them) are shown here because they are used either in other structures (some structures have fields that are of other structure types) or as final structures to be used by the winsock related functions. Some of the names here are quite ambiguous and in my opinion could have been given much better and clearer names than those used here.

' --------------------------------------------------------------- ' User Defined Types extracted from winsock.bi for this example ' --------------------------------------------------------------- TYPE WSAData wVersion AS SHORT wHighVersion AS SHORT szDescription AS STRING * WSADESCRIPTION_LEN+1-1 szSystemStatus AS STRING * WSASYS_STATUS_LEN+1-1 wMaxSockets AS USHORT wMaxUDPDG AS USHORT dwVendorInfo AS BYTE PTR END TYPE TYPE HostEnt H_Name AS BYTE PTR H_Aliases AS BYTE PTR PTR H_AddrType AS SHORT H_Length AS SHORT H_Addr_List AS UINTEGER PTR PTR END TYPE TYPE S_Un_B_ S_B1 AS UBYTE S_B2 AS UBYTE S_B3 AS UBYTE S_B4 AS UBYTE END TYPE TYPE S_Un_W_ S_W1 AS USHORT S_W2 AS USHORT END TYPE UNION In_Addr S_Un_B AS S_Un_B_ S_Un_W AS S_Un_W_ S_Addr AS UINTEGER END UNION Type SockAddr_In Sin_Family AS SHORT Sin_Port AS USHORT Sin_Addr As In_Addr Sin_Zero(0 TO 7) AS BYTE End Type Type SockAddr SA_Family AS SHORT SA_Zero(0 TO 13) AS BYTE END TYPE

The A.P.I. Function Declarations:

Again, winsock.bi and winsock2.bi have a whole lot more API function declarations than those shown here. However, for the sake of this tutorial, once again, I merely included the functions that we will need. the WS prefix you can see in most of these functions are to indentify that the functions are WinSock related.

' ---------------------------------------------------------------------- ' API Function Declarations extracted from winsock.bi for this example ' ---------------------------------------------------------------------- DECLARE FUNCTION WSAStartup _ LIB "WSock32" _ ALIAS "WSAStartup" (BYVAL wVersionRequired AS SHORT, _ BYVAL lpWSAData AS WSAData PTR) AS LONG DECLARE FUNCTION WSAGetLastError _ LIB "WSock32" _ ALIAS "WSAGetLastError" () AS LONG DECLARE FUNCTION WSACleanup _ LIB "WSock32" _ ALIAS "WSACleanup" () AS LONG DECLARE FUNCTION Socket _ LIB "WSock32" _ ALIAS "socket" (BYVAL AF AS LONG, _ BYVAL S_Type AS LONG, _ BYVAL Protocol AS LONG) AS LONG DECLARE FUNCTION CloseSocket _ LIB "WSock32" _ ALIAS "closesocket" (BYVAL S AS LONG) AS LONG DECLARE FUNCTION Connect _ LIB "WSock32" _ ALIAS "connect" (BYVAL AS LONG, _ BYVAL AS SockAddr PTR, _ BYVAL AS LONG) AS LONG DECLARE FUNCTION HTons _ LIB "WSock32" _ ALIAS "htons" (BYVAL HostShort AS USHORT) AS USHORT DECLARE FUNCTION GetHostByName _ LIB "WSock32" _ ALIAS "gethostbyname" (BYVAL szHost AS STRING) AS HostEnt PTR DECLARE FUNCTION Send _ LIB "WSock32" _ ALIAS "send" (BYVAL SocketID AS LONG, _ BYVAL BufferPtr AS LONG, _ BYVAL BufLen AS LONG, _ BYVAL Flags AS LONG) AS LONG DECLARE FUNCTION Recv _ LIB "WSock32" _ ALIAS "recv" (BYVAL SocketID AS LONG, _ BYVAL BufferPtr AS LONG, _ BYVAL BufLen AS LONG, _ BYVAL Flags AS LONG) AS LONG

Now, take a look at the following 2 functions. You'll notice they are quite empty (1 line each) and well I thought I'd explain why I didn't just use the WS related functions themselves. Usually, a winsock "session" needs to have an initialization/creation section prior to the work to be done, and a closing section after, one never knows what might be needed when creating or closing an internet connection. Therefore, I've created these two functions as a skeleton app. In your projects, you might need to create variables, assign some values, to different things before you execute your task and keeping this initialization material in one function is a great way to keep the code organized and clear. Same goes for closing or terminating a winsock session. So think of these two functions as a template to follow. But again, my style is my style and you're more than welcome to use it, or not, depends on what you need initialized and prepared before you perform your work as well as what needs to be closed and released from resources when the work is done.

'==================================================== ' StartSockets gets a WSAData structure ready to be ' used in the rest of the program. '==================================================== FUNCTION StartSockets() AS LONG DIM WSAInfo AS WSAData WSAStartup(MakeWord(1,1), @WSAInfo) END FUNCTION ' ==================================================== ' EndSockets clears resources used by StartSockets ' ==================================================== FUNCTION EndSockets() As Long WSACleanup() END FUNCTION

Next on the list, take a look at the ConnectToURL function below. This is where "the work" actually happens. I have commented the code here so you see what each part does but the basic idea is to perform the following (once the socket is opened):

   1. Retrieve the URL's host
   2. Create the URL's socket
   3. Connect to the URL
   4. Prepare HTTP GET request and send it to throught the socket
   5. Retrieve the contents
   6. Display the retrieved contents on the screen

' ==================================================== ' NAME: ConnectToURL() ' PARAMETERS: BYVAL URL AS STRING ' RETURN VALUE: A LONG typed value ' ASSUMES: URL to be a valid website URL ' ---------------------------------------------------- ' DESCRIPTION: This function performs the actual ' work of this demonstration. it will ' connect to the URL passed as its ' parameter and retrieve and display ' some information about the URL. ' ==================================================== FUNCTION ConnectToURL(BYVAL URL AS STRING) AS LONG ' ---------------- ' Work Variables ' ---------------- DIM URLHost AS HostEnt PTR DIM URLSocket AS SockAddr_In DIM URLSocketID AS UINTEGER DIM Connection AS LONG DIM Message AS STRING DIM Buffer AS STRING DIM BufferReceive AS ZSTRING *1001 DIM BytesReceived AS LONG ' --------------------- ' Retreive URL's host ' --------------------- URLHost = GetHostByName(URL) ' --------------------- ' Create URL's Socket ' --------------------- WITH URLSocket .Sin_Family = AF_Inet .Sin_Port = HTons(80) .Sin_Addr.S_Addr = **URLHost->H_Addr_List END WITH ' ------------------- ' Create The Socket ' ------------------- URLSocketID = Socket(AF_Inet, Sock_Stream, IPProto_TCP) ' -------------------- ' Connect To The URL ' -------------------- Connection = Connect(URLSocketID, @URLSocket, LEN(URLSocket)) ConnectURL = URLSocketID ' ------------------------------------------------------------- ' Prepare HTTP GET request and send it to throught the socket ' ------------------------------------------------------------- Message = "GET / HTTP/1.0" + NewLine _ + "Host: " + URL + NewLine _ + "User-Agent: MystikShadows/1.0" + NewLine + NewLine Send(URLSocketID, STRPTR(Message), LEN(Message), 0) ' --------------- ' Retrieve File ' --------------- BytesReceived = Recv(URLSocketID, STRPTR(BufferReceive), 1000, 0) WHILE BytesReceived > 0 Buffer = Buffer + LEFT$(BufferReceive, BytesReceived) BytesReceived = Recv(URLSocketID, STRPTR(BufferReceive), 1000, 0) WEND ' -------------------------------------------- ' Display received file and info to the user ' -------------------------------------------- PRINT Buffer END FUNCTION

With all of these created, it makes the main part of the program quite short. All it does now is declare a variable called Connection of type LONG, calls the StartSockets() function (with whatever initialization code you may need), calls the ConnectToURL() function which performs the actual task and finally calls the EndSockets() function to close what's been opened (and release resources as needed by your projects).

'========================== ' MAIN PART OF THE PROGRAM '========================== DIM Connection AS LONG StartSockets() Connection = ConnectToURL("www.ascii-world.com") EndSockets() SLEEP

And that just about covers it really. This program will perform the intended work which is to connect to http://www.ascii-world.com and display information and contents about the URL. Note that you can change the call to ConnectToURL to supply other URL addresses to see what they will show you. Don't worry, you don't have to type (or cut and paste) all that in, here's a link to the sockets.bas so you can just download the file and compile it for yourself. Here is what the program produced with the ascii-world URL.


AND TO CONCLUDE THIS TUTORIAL:

There you have it, we are at the end of this winsock programming tutorial in FreeBASIC. I think that as an introduction, it covered alot of grounds and gave you a good start on your own winsock projects. As far as winsock itself is concerned, the functions needed are all present in winsock.bi and winsock2.bi. Don't forget to consult the links I provide in this tutorial to learn everything that you can do with winsock and how to do them as well. The references to the protocols will tell you which you should use on a given wonsick project and also have valid protocol commands for you so you can learn HTTP, FTP, SMTP programming in all it's details. Winsock programming is by no means a simple subject. When looking at the example shown here we can see that it still takes quite a few steps to perform a very basic winsock related task. However, there's no denying the necessity of winsock programming whetehr you are creating an online game, or an application that can really benefit from a connection to some online resource. So I believe this should be enough to get you curious to see just what else can be done with winsock.

Remember, as always, that i'm always opened to comments, suggestions, ideas, constructive criticism and questions from you, just email me with anything you want to share with me about winsock programming. I'll be happy to answer questions, and bring suggestions and help to your winsock projects as well. If you'd like me to write on other winsock related subjects just let me know either by emailing me (see the email address below) or on the Official QB Express Article request thread and I'll see what I can do. Until next time, happy reading, and coding.

MystikShadows
Stéphane Richard
mystikshadows@gmail.com