First Internet Client

From Web (19.11.1997)

Table of Contents

1. Getting back to the Basics

All this while we've been talking about creating ActiveX Objects and Java applets and other Internet related stuff. Even though we talked long and hard about transferring files via the Internet, we haven't really tackled the basic issues of HOW the files are transferred. We've pointed out the various shortcomings of different languages and browsers and yet we've never told you how they worked. Infact, come to think of it, we've really left you in the dark about how the Internet really operates.

Well, it's confession time, we didn't know either !! Infact, if we hadn't worked for two hard weeks cracking code and reading piles of books, we still wouldn't know. So, here it is, the fruits of our labor, a complete tutorial, with source code and explanations about nearly every aspect of the 'net from TCP/IP and DNS Servers, to Traceroute Ping. So read on and broaden your horizons !!

2. A Prelude.....

I personally could never have predicted that the net would grow at such a pace that one day, commercials would give web site addresses right at the end. Even the man on the street knows that a programming by the name of Java exists. The Internet is no longer a geek hangout, it's being slowly integrated into the global and International communications mainstream. The Web has grown so fast and the software has become so complicated that in the last six months, I've had to totally revamp my entire knowledge base.

The problem with the Internet is that although everyone knows about it, it is not a technology that most programmers fully understand. Unlike the all the computer languages use , there is no central command or controlling body. Also, the net assures relative anonymity. No one can know whether your working on a UNIX workstation, a Mac, a Pentium or a 286 with 1 MB RAM and a 40 MB hard disk !! All these rambling debates about whether UNIX is good or bad or whether the Mac OS is better that Windows 95 are meaningless. This perhaps is the real reason behind the feeling of equality in cyberspace.

While learning C under DOS for the first time ( a long long time ago, in a galaxy far far away !), I realized that no matter how close the language may be to the computer, for me, it wasn't close enough. So, after my C course, I went out and learnt assembly. I personally think that the most productive time in my programming career was when I learnt to write a virus, because after that, I knew DOS inside out. I was completely comfortable with it. I could say "DOS, your mine !" with a certain amount of self confidence. I can't however, say that about Windows95, because I don't know what the hell is going on under the hood. ( In DOS, there was no hood !! ) The same goes for the 'net, I really didn't know what happened when I said http://www.neca.com/~vmis.

In India, it is below my dignity to wash, tune up or even fill up my own car ( AH ! the pleasures of a large work force !! ). It is the driver job, not mine. Yet if I took care of my machine myself, I'd know more about it, I'd know what makes it run. This is the reason perhaps that Americans are better drivers!!

What I'm trying to say is that we can't understand something as amorphous as the Internet until we get our hands dirty; unless we write our own code. Now, in India, I am quite a well known public figure. So I'm often called upon to give these great bombastic lectures about the Internet. I stand in front of a crowd full of eager listeners and I shoot my mouth off about the 'net and until a week ago, I would feel like a fake, because I really didn't know what I was talking about ! But now, that I've written my own programs, blundered through my own code, I can truthfully say, " Internet, your mine !!". Being a nice guy I like to share and share alike, so I'm going to try and teach you how to write your own Internet software suite. We'll do the basics of WinSock programming with TCP/IP and UDP.

Now there are already books available that teach you how to write an email client and other Internet software, but they usually end up confusing people because around 95 % of the code they list deals with the User Interface. Only 5 % of the code deals with the Internet. Now at the moment, I really don't want to create a marketable product, that can come later. What I want to do is understand the Internet related code and this is precisely the code we're going to teach you.

Now, if you're going to learn from us, you'll have to follow our rules. First write the code the way we want you to (No blinking, no breathing, no drooling and you have to type with the big toe on your left foot) and then add your own embellishments later. In a break from tradition we're going to provide you with finished polished code, along with the rough and ready stuff we use to help you learn ( Learning Code we call it ). This in itself should tell you how seriously we taking this and how important we think it is.

The reason we believe every programmer should learn this technology is because we think that the Internet is the future. From now on, we have to stop thinking of machines as islands, complete in themselves, but as one continuously connected nation. Even the humble wordprocessor needs to be Internet ready, able to send and receive email and faxes. Accounting packages should be able to log onto a specified server to collect the latest information about a company or product. With all these advances, EVERYONE needs to learn how to program for the 'net. After all, wouldn't you understand the intricacies of the web better if you could write a program that connects to a site and then tells you the exact route the TCP/IP packets will take to reach the said site ?

Now the code given here is in C/C++, but making the code Internet ready is a breeze. You can convert your code into an ActiveX Object ( Check out our tutorial ) or into a Java applet (we have a tutorial on this too ...) or into a Netscape plugin ( ditto). We have a Traceroute program as an ActiveX Object which is put up on our site. So when you visit our site, you can click in a certain window and type in the name of the site you want to trace and see the route the packets take from our server to the specified site.

To convert your code into an ActiveX Object, which can be used from under any language that's OLE compliant, all you have to do is add a little more code, a little more complexity. Once you've converted your program, every server on the Internet can have it. The finished programs at the end have all kinds of error checks and also a decent user interface, but if we were to try and teach using that code, you'd spend sleepless nights trying to wean the chaff from the real code. So, we be teaching using rougher and more unpolished code , which you're probably used to if you've been visiting our site regularly. Infact, along with the ActiveX Objects, we'll give you Java applets and Netscape Plugins. Maybe even some code using LiveConnect or OLE Automation !

3. A Request...

The good people from Microsoft often send us complementary email. Now that doesn't mean that Bill Gates sends us mail ( after all, I said the GOOD people from Microsoft !! ), it's the people behind Active Animation or ActiveX who write to us. And believe me, I prefer their mails to any correspondence Gates may want to send (except for large generous cheques!!). Unfortunately, their high command still hasn't noticed us, so if you like this site, do us a favor and petition Microsoft to put up the name of our site on their " To Visit " list ( Mailto : XXXXX ). Oh, and by the way, If you intend to use our programs, please place an Easter egg in them so that when the user clicks his mouse on the upper left-hand corner of the screen, jumps up and down on his chair shouting " Bonga Boola Boola !! " , kisses his toes ( in turn, from right to left ) and it's my birthday ( 29th Feb. ), you'll tell him where you got the code from.

4. Originality - The art of concealing ones sources ....

A lot of the code here has been adapted from samples we found on the Internet and in various publications. There are just too many names to be named ( and, to tell the truth, a lot of the input was anonymous ) and we can't put them all up. But here are the names of some of the people whose work helped us bring this tutorial to you :-

Sorry, but we haven't compiled the names yet ! Give us a little time.

5. Taking POTShots at the Internet...

Before we start off with the first of our programs, we'll take a short break and tell you a little bit about how the Internet works. What we'll be telling you here is full of holes, so if you know all this already, skip it.

You'll understand how the Internet works better if you have a basic understanding of how the plain old telephone system ( POTS ) functions. For example, I pick up my phone in Bombay and dial up someone in Delhi. Is my phone connected to the phone in Delhi by a physical line ? of course not. The connection can be considered physical, but only in the loosest sense. What we actually have here is a virtual connection one which only lasts for as long as the communication itself. Even the route my voice takes along the wires can vary, not only between one call and the next, but also within a call. So one moment my voice will be routed via Bombay, Madras, Delhi and ten seconds later, via Bombay, Calcutta, Delhi, depending on the line conditions and the load.

This concept of a virtual connection is at the very heart of the Internet. The routes my TCP/IP packets take to and from a certain site can vary. One moment they'll come down one pathway and a few seconds later, by another. This routing of information packets is what information transfer on the 'net is based on. The router is what telephone companies spend the most amount of time and money on, because it is the router that calculates the load factors on each line and decides which route a packet should take, optimizing the limited number of telephone cables and speeding up information exchange. Infact, routers often send the packets along convoluted and extremely complex routes in order to bypass busy exchanges and land lines. In fact, most telephone exchanges in the world today use the Packet Switching Networks (PSN's) explain above. This means that every word you say is converted into a packet and send winging it's way to it's destination.

Since the routers ( in their never ending quest for the fastest route ) force the information to take such convoluted paths, these packets sometimes wander off and go AWOL. This is OK if you're having a conversation with someone and you miss a vowel or two, but if, for instance, you're transporting binary files the results can be disastrous. It was to combat this problem of missing packets that the TCP/IP protocol was established. A protocol, by the way, is any form of communication which follows certain predefined rules.

The TCP/IP protocol is actually two protocols sandwiched one above the other. The IP (Internet Protocol) protocol deals exclusively with the routers. It's IP's job is to make sure your packet goes from one end to the other in the shortest possible time. IP is the one who informs the router about the location of it's destination, it's source and other such details. The IP protocols primary concern is speed. It has to try and get to the destination as fast as possible and it cares about nothing else. The IP protocol has sacrificed reliability for speed and it shows. If the Internet we're to rely exclusively on IP, the result would be absolutely chaotic !

It was to combat this problem of unreliability that the TCP ( Transmission Control Protocol ) protocol was established. The TCP protocol is the exact opposite of the IP protocol. It's primary concern is reliability and it's makers forfeited speed to get it. It is the TCP protocol that takes care of checksums and sequence, to make sure that every packet reaches and is used in the correct order.

When TCP/IP was invented, the architects made it quite clear that TCP/IP would be entirely machine and operating system independent. So these furious debates about the supremacy of the Macintosh over Wintel machines or vice versa, no longer makes any sense. On the Internet, it's impossible to distinguish between the two.

A misconception prevalent in the programming world is that something called "TCP/IP programming" exists. TCP/IP is a very low level protocol and practically no one writes TCP/IP code. It's like trying to write a word processor for Windows95, in Assembler ! It's possible, but the people who try and do it often end up in large comfortable farmhouse surrounded with electrified barbed wire, wearing strange white jackets and learning to weave straw baskets !!

Of course, we were crazy enough to take the plunge ! Check out our tutorial's on the various Internet Protocols, but read this tutorial first.

The only sane way to write Internet related code is to use Sockets Programming or as it is called under Windows, WinSock Programming. The WinSock internally calls TCP/IP, but it's easier to use because it equates Internet information transfers to file transfers to and from your hard disk. So we'll be teaching you WinSock programming in this tutorial and not direct TCP/IP.

6. Getting our feet wet...

Let's not waste anymore time over theoretical discussions on the workings of the Internet, instead, let's jump right in and I promise I'll explain all the important concepts as we proceed. After all, it makes more sense to explain the theory after you've seen the source code and tried it out.

Here's a program that leads up to our first true Internet client. If you're already familiar with C-SDK Windows programing, then jump ahead to the Time Client.

Note:-To run this program, create a project in Visual C++ ( We're using Visual C++ 4.0 ) and insert the file a1.cpp into the project a1.

Now build the project.

a1.cpp

     #include <windows.h>
     #include <stdio.h>
     void abc(char *p)
     {
             FILE *fp=fopen("c:\\z.txt","a+");
             fprintf(fp,"%s\n",p);
             fclose(fp);
     }
     WNDCLASS a;HWND b;MSG c;
     long _stdcall zzz (HWND,UINT,WPARAM,LPARAM);
     int _stdcall WinMain(HINSTANCE i,HINSTANCE j,char *k,int l)
     {
             a.lpszClassName="a1";
             a.hInstance=i;
             a.lpfnWndProc=zzz;
             a.hbrBackground=GetStockObject(WHITE_BRUSH);
             RegisterClass(&a);
             b=CreateWindow("a1","aaa",WS_OVERLAPPEDWINDOW,1,1,10,20,0,0,i,0);
             ShowWindow(b,3);
             while ( GetMessage(&c,0,0,0) )
                     DispatchMessage(&c);
             return 1;
     }
     long _stdcall zzz (HWND w,UINT x,WPARAM y,LPARAM z)
     {
             if ( x == WM_LBUTTONDOWN)
             {
                   MessageBox(0,"end","end",0);
             }
             if ( x == WM_DESTROY)
                     PostQuitMessage(0);
             return DefWindowProc(w,x,y,z);
     }

This is a basic Windows program. We've initialized lpfnWndProc, a member of the structure a which looks like WNDCLASS to the function zzz. This function will be our callback function.So, whenever anything of importance happens to the window (the window is minimized, maximized etc.), our call back function is called.

We'll be putting all our WinSock code in the callback function.

The Time Client

#include <windows.h>
#include  <stdio.h>
void abc(char *p)
{
      FILE *fp=fopen("z.txt","a+");
      fprintf(fp,"%s\n",p);
      fclose(fp);
}
WNDCLASS a; HWND b; MSG c; char aa[200]; SOCKET s; struct hostent h;
WSADATA ws; DWORD e; int ii,dw; char bb[100]; struct sockaddr_in Sa;
long _stdcall zzz (HWND,UINT,WPARAM,LPARAM);
int _stdcall WinMain(HINSTANCE i,HINSTANCE j,char *k,int l)
{
      a.lpszClassName="a1";
      a.hInstance=i;
      a.lpfnWndProc=zzz;
      a.hbrBackground=GetStockObject(WHITE_BRUSH);
      RegisterClass(&a);
      b=CreateWindow("a1","time client",WS_OVERLAPPEDWINDOW,1,1,10,20,0,0,i,0);
      ShowWindow(b,3);
      while ( GetMessage(&c,0,0,0) )
            DispatchMessage(&c);
      return 1;
}
long _stdcall zzz (HWND w,UINT x,WPARAM y,LPARAM z)
{
if ( x == WM_LBUTTONDOWN)
{
      e=WSAStartup(0x0101,&ws);
      sprintf(aa,"e = %ld",e);
      abc(aa);
      s = socket(PF_INET,SOCK_DGRAM,0);
      sprintf(aa,"s = %ld",s);
      abc(aa);
      Sa.sin_family=AF_INET;
      Sa.sin_addr.s_addr = inet_addr("140.252.1.32");
      Sa.sin_port=htons(13);
      strcpy (bb,"hello how are you");
      e=sendto(s,bb,100,0,(struct sockaddr *)&Sa,sizeof(Sa));
      sprintf(aa,"SendTo %ld",e);
      int dw = sizeof(Sa);
      recvfrom(s,bb,100,0,(sockaddr *)&Sa,&dw);
      MessageBox(0,bb,"data from server",0);
      MessageBox(0,"end","end",0);
}
if ( x == WM_DESTROY)
      PostQuitMessage(0);
return DefWindowProc(w,x,y,z);
}

This is it ! your very first Internet Client ! It doesn't seem like much, but it doesn't do much either !! It's just the bare code and the user interface is absolutely pukey. But it works and that's what counts.

To run this program, first create a project in Visual C++ ( We're using Visual C++ 4.0 ) and insert these files into the project :-

  • A1.cpp - This is the file displayed above.
  • Wsock32.lib - This is a lib file not displayed above.

Build the project. Before you run the .exe, be sure to log into your local Internet Service Provider. Now run the file. You will be confronted with a beautifully rendered and artistically designed blank window. Click anywhere in it and the Internet code in the Callback function is called.

The code itself is rather simple. WinMain() creates and initializes a window and we've associated a callback function with it. The window is assigned certain properties and then maximized. The callback, zzz() contains the real juicy bits. When you click in the window (WM_LBUTTONDOWN ), the function zzz() is called and it inturn kicks off the code for the Time Client. The header file, windows.h internally calls the file winsock.h.

In the first line, the function WSAStartup() is absolutely necessary. This is the function which initializes the whole code. It's first parameter is 0x0101 which symbolizes the Winsock's version number . 0x0101 stands for WinSock version 1.01 (you have to read it backwards and consider the zero to be a decimal point). &ws is the location in memory of the structure tag WSAData. The uses of &ws will be discussed later. If all went well, WSAStartup() will return a zero , a non-zero value implies that an error occurred.

      #include <windows.h>
     #include <stdio.h>
     void abc(char *p)
     {
             FILE *fp=fopen("c:\\z.txt","a+");
             fprintf(fp,"%s\n",p);
             fclose(fp);
     }
     WNDCLASS a;HWND b;MSG c;char aa[200];char bb[20000];
     WSADATA ws;DWORD e;
     long _stdcall zzz (HWND,UINT,WPARAM,LPARAM);
     int _stdcall WinMain(HINSTANCE i,HINSTANCE j,char *k,int l)
     {
             a.lpszClassName="a1";
             a.hInstance=i;
             a.lpfnWndProc=zzz;
             a.hbrBackground=GetStockObject(WHITE_BRUSH);
             RegisterClass(&a);
             b=CreateWindow("a1","aaa",WS_OVERLAPPEDWINDOW,1,1,10,20,0,0,i,0);
             ShowWindow(b,3);
             while ( GetMessage(&c,0,0,0) )
                     DispatchMessage(&c);
             return 1;
     }
     long _stdcall zzz (HWND w,UINT x,WPARAM y,LPARAM z)
     {
             if ( x == WM_LBUTTONDOWN)
             {   abc("Before WSAStartup");
                 sprintf(aa,"wVersion....%d",ws.wVersion);
                 MessageBox(0,aa,aa,0);
                 abc(aa);
                 sprintf(aa,"wHighVersion....%d",ws.wHighVersion);
                 MessageBox(0,aa,aa,0);
                 abc(aa);
                 sprintf(aa,"szDescription....%s",&ws.szDescription);
                 MessageBox(0,aa,aa,0);
                 abc(aa);
                 sprintf(aa,"szSystemStatus....%s",&ws.szSystemStatus);
                 MessageBox(0,aa,aa,0);
                 abc(aa);
                 sprintf(aa,"iMaxSockets....%u",ws.iMaxSockets);
                 MessageBox(0,aa,aa,0);
                 abc(aa);
                 sprintf(aa,"iMaxUdpDg....%u",ws.iMaxUdpDg);
                 MessageBox(0,aa,aa,0);
                 abc(aa);
                 sprintf(aa,"lpVendorInfo....%s",ws.lpVendorInfo);
                 MessageBox(0,aa,aa,0);
                 abc(aa);
                 e=WSAStartup(0x0101,&ws);
                 sprintf(aa,"e..%ld",e);
                 MessageBox(0,aa,aa,0);
                 abc("After WSAStartup");
                 sprintf(aa,"wVersion....%d",ws.wVersion);
                 MessageBox(0,aa,aa,0);
                 abc(aa);
                 sprintf(aa,"wHighVersion....%d",ws.wHighVersion);
                 MessageBox(0,aa,aa,0);
                 abc(aa);
                 sprintf(aa,"szDescription....%s",&ws.szDescription);
                 MessageBox(0,aa,aa,0);
                 abc(aa);
                 sprintf(aa,"szSystemStatus....%s",&ws.szSystemStatus);
                 MessageBox(0,aa,aa,0);
                 abc(aa);
                 sprintf(aa,"iMaxSockets....%u",ws.iMaxSockets);
                 MessageBox(0,aa,aa,0);
                 abc(aa);
                 sprintf(aa,"iMaxUdpDg....%u",ws.iMaxUdpDg);
                 MessageBox(0,aa,aa,0);
                 abc(aa);
                 sprintf(aa,"lpVendorInfo....%s",ws.lpVendorInfo);
                 MessageBox(0,aa,aa,0);
                 abc(aa);
                 MessageBox(0,"end","end",0);
             }
             if ( x == WM_DESTROY)
                     PostQuitMessage(0);
             return DefWindowProc(w,x,y,z);
     }

In this program, we've listed out the members of the structure with their respective values before and after WSAStartup function is called. After running the program, check the file z.txt in the root directory.

The next important function is socket(), which initializes the WinSock socket to be used for communication. A socket is a hypothetical entity, created solely to help us understand Internet programming better. It's first parameter, PF_INET is a number that informs the socket that it is to be used for the Internet. WinSock sockets can also be used over Novel and other networks and therefore the intended use has to be specified. SOCK_DGRAM tells the socket that the protocol to be used for communication is UDP/IP, or as it is commonly known, UDP (User Datagram Protocol). UDP, like TCP is an Internet protocol which rides piggy back on IP. Unlike TCP however, UDP is not a very stable or reliable protocol, but it is much faster than the former. UDP is used in cases where reliability is not an issue, like with Real Audio and Internet Phone, where a few lost packets don't really affect sound quality, but where speed is of the essence. Since sending a simple request for the current date and time is not really that demanding a job, UDP works fine. If the socket is created without problems, then the socket function returns a zero, which is caught and displayed by s. ( SOCKET is a macro which stands for 'unsigned int' ).

After creating a socket, we have to initialize the members of a structure called Sa. Sa.sin_family=AF_INET means that the packets will travel on the Internet. Sa.sin_addr.s_addr = inet_addr ("140.252.1.32") fills up this member of the structure with the IP address of the destination server. An IP address is a bit like a phone number. For me to talk to you, I must have a phone ( with its own number ) and I must know your phone number. To be on the Internet, every computer must have an IP address, otherwise the packets don't know where to go. A unique IP address, which is a long number ( 232 bits ), is given to each and every computer logged onto the Internet. If the IP number is permanent, it means that it is the number of a server, which is always on-line. If the number is random, it means it belongs to a dial-up subscriber, who is given an IP number, valid for only for the length of the session.

When packet is sent, the IP header holds the IP address from which the packet has come. That is, it holds the IP address of your computer, e.g. 10. The IP header also holds the IP address of the destination computer, which is, for example, 22. Your data packet is first sent to you ISP's router, who checks the IP header for the destination address. Since all routers know the IP addresses of the other routers and also the range of IP addresses they handle, your router, which handles IP address from 0 to 10 for example, sends the packet to a router in Singapore, which handles IP address from 15 to 25. That router then sends the data to the target computer, whose IP address of 22 falls within it's range. Of course, this is a highly simplified example, involving only two routers. In actuality, your packet may pass through about six to seven routers, going one way and about the same number coming back.

IP addresses are usually given in the Dotted Decimal Notation, where every char of the long is given individually and separated with a dot. There is no compulsion to do so, it just increases the readability of the IP address. However, to use the address, it has to be converted into a long first and the function inet_addr does just that.

      #include <windows.h>
     #include <stdio.h>
     void abc(char *p)
     {
             FILE *fp=fopen("c:\\z.txt","a+");
             fprintf(fp,"%s\n",p);
             fclose(fp);
     }
     WNDCLASS a;HWND b;MSG c;char aa[200];
     WSADATA ws;DWORD e;long dd;
     long _stdcall zzz (HWND,UINT,WPARAM,LPARAM);
     int _stdcall WinMain(HINSTANCE i,HINSTANCE j,char *k,int l)
     {
             a.lpszClassName="a1";
             a.hInstance=i;
             a.lpfnWndProc=zzz;
             a.hbrBackground=GetStockObject(WHITE_BRUSH);
             RegisterClass(&a);
             b=CreateWindow("a1","aaa",WS_OVERLAPPEDWINDOW,1,1,10,20,0,0,i,0);
             ShowWindow(b,3);
             while ( GetMessage(&c,0,0,0) )
                     DispatchMessage(&c);
             return 1;
     }
     long _stdcall zzz (HWND w,UINT x,WPARAM y,LPARAM z)
     {
             if ( x == WM_LBUTTONDOWN)
             {
                     dd = inet_addr("140.252.1.32");
                    sprintf(aa,"dd=%ld",dd);
                    abc(aa);
                    MessageBox(0,aa,aa,0);
                    MessageBox(0,"end","end",0);
             }
             if ( x == WM_DESTROY)
                     PostQuitMessage(0);
             return DefWindowProc(w,x,y,z);
     }

Here's a small little program that demonstrates the effect inet_addr has on a number in the Dotted Decimal Format.

Next we have Sa.sin_port = htons( 13 ). This member of the structure holds the port number the packets must go to. The ports referred to here, are not the serial or parallel ports. A port here is simply a number which resides in the TCP header. For example, the Time server uses port 13. This means, whenever a packet appears at the server end's front door, bearing the port number 13, it is passed onto the time server code, which does with it what it may. If there was no such thing as a port number, every packet that arrived would have to be sent to every server on that machine, leading to a massive and unavoidable overhead. So when we say that the Time server " is on port 13 ", we mean that the Time server is listening to packets that hold the number 13 in the TCP header.

The function htons has been created to combat a problem that arose when the creators of these protocols attempted to make them machine independent. The Intel chip stores long numbers as four chars stacked one above the other, smaller numbers first. So a number like 515 will be stored as 3 and 2, i.e. 3x1 plus 2x256, which equals to 515. However, the Motorola chip, used in the Macintosh stores number the other way round, so 515 is stored as 2 and then 3. If a number stored in this format is read by a machine with the Intel chip, instead of 515, the answer will be 2x1 plus 3x256, equals 770 !! So another protocol was established, the Network Byte Order Protocol. From now on, all numbers will be stored the Motorola way. So people like you and me, who use Intel machines, require a function like htons to convert the number 13 into the format that the Macintosh likes. If you don't want to use htons, you can always write, 13*256 and it will still work.

      #include <windows.h>
     #include <stdio.h>
     void abc(char *p)
     {
             FILE *fp=fopen("c:\\z.txt","a+");
             fprintf(fp,"%s\n",p);
             fclose(fp);
     }
     WNDCLASS a;HWND b;MSG c;char aa[200];SOCKET s;
     WSADATA ws;DWORD e;long ii;
     long _stdcall zzz (HWND,UINT,WPARAM,LPARAM);
     int _stdcall WinMain(HINSTANCE i,HINSTANCE j,char *k,int l)
     {
             a.lpszClassName="a1";
             a.hInstance=i;
             a.lpfnWndProc=zzz;
             a.hbrBackground=GetStockObject(WHITE_BRUSH);
             RegisterClass(&a);
             b=CreateWindow("a1","aaa",WS_OVERLAPPEDWINDOW,1,1,10,20,0,0,i,0);
             ShowWindow(b,3);
             while ( GetMessage(&c,0,0,0) )
                     DispatchMessage(&c);
             return 1;
     }
     long _stdcall zzz (HWND w,UINT x,WPARAM y,LPARAM z)
     {
             if ( x == WM_LBUTTONDOWN)
             {
                     ii=htons(2);
                     sprintf(aa,"ii.htons(2)=..%ld",ii);
                     abc(aa);
                     MessageBox(0,aa,aa,0);
                     ii=htons(512);
                     sprintf(aa,"ii.htons(512)=..%ld",ii);
                     abc(aa);
                     MessageBox(0,aa,aa,0);
                     MessageBox(0,"end","end",0);
             }
             if ( x == WM_DESTROY)
                     PostQuitMessage(0);
             return DefWindowProc(w,x,y,z);
     }

This example should clear up any lingering doubts you might still harbor.

After filling up the structure Sa, we send a request to the Time server, asking for the date and Time. We copy the string " hello how are you " into the array bb. bb can hold any string, the time server doesn't pay attention to it. All it wants is some characters and the senders IP address, which it extracts from the IP header.

We then use sendto() to send the contents of the array over to the Time server. We give the function sendto(), in order, the address of the socket, the array bb, the size of the array, a null, the typecasted address of the structure Sa and finally, the size of the structure Sa. Using the information contained in the structure Sa and in the socket, the function sends the contents of the array over to the Time server on port 13, at IP address 140.252.1.32.

After sending the request, we wait at the function recvfrom() for the answer. Recvfrom() is a blocking function, i.e. program execution doesn't proceed until recvfrom()receives an answer and returns control. Before it does, recvfrom() fills up the array bb with the reply. We've gone the extra mile and made the variable dw equal to the size of Sa, you cannot put sizeof(Sa) as a parameter directly.

We can then access bb and display it's contents using a message box.

So, there you have it. A time client which accesses a time server and asks it for the current time of the area in which the server resides.

The Time Server

#include <windows.h>
#include <winsock.h>
#include <stdio.h>
void abc(char *p)
{       FILE *fp=fopen("z.txt","a+");
         fprintf(fp,"%s\n",p);
         fclose(fp);
}
WNDCLASS a;HWND b;MSG c;char aa[200];char bb[20000];char cc[2000];int xx;
long _stdcall zzz (HWND,UINT,WPARAM,LPARAM);
int _stdcall WinMain(HINSTANCE i,HINSTANCE j,char *k,int l)
{
      a.lpszClassName="a1";
      a.hInstance=i;
      a.lpfnWndProc=zzz;
      a.hbrBackground=GetStockObject(WHITE_BRUSH);
      RegisterClass(&a);
      b=CreateWindow("a1","aaa",WS_OVERLAPPEDWINDOW,1,1,10,20,0,0,i,0);
      ShowWindow(b,3);
      while ( GetMessage(&c,0,0,0) )
            DispatchMessage(&c);
      return 1;
}
WSADATA ws;SOCKET s,s1,s2;sockaddr_in A,A1;int d,d1,d2,dd;
struct sockaddr_in A;
long _stdcall zzz (HWND w,UINT x,WPARAM y,LPARAM z)
{
      if ( x == WM_LBUTTONDOWN)
      {
            d=WSAStartup(0x0101,&ws);
            sprintf(aa,"d = %ld",d);
            abc(aa);
            s=socket(PF_INET, SOCK_DGRAM,0);
            sprintf(aa,"socket %ld",s);
            abc(aa);
            A.sin_family=AF_INET;
            A.sin_port = htons(13);
            A.sin_addr.s_addr =INADDR_ANY;
            d=bind(s,(struct sockaddr *) &A,sizeof(A));
            sprintf(aa,"bind = %ld",d);
            abc(aa);
            MessageBox(0,aa,aa,0);
            int dw=sizeof(A);
            d=recvfrom(s,bb,100,0,(sockaddr *)&A,&dw);
            sprintf(aa,"recvfrom =%ld..bb=%s",d,bb);
            abc(aa);
            d=sendto(s,"hello",5,0,(sockaddr *)&A,sizeof(A));
            sprintf(aa,"sendto=%ld.. ,d);
            abc(aa);
            MessageBox(0,aa,aa,0);
            MessageBox(0,"end","end",0);
      }
      if ( x == WM_DESTROY)
            PostQuitMessage(0);
      return DefWindowProc(w,x,y,z);
}

Now that you've written your first client, it's time for you to write you're very first Internet server. As before, it's not much, but it's substantially more than what you could write before, so I see absolutely no reason to complain !

To run this program, first create a project in Visual C++ ( We're using Visual C++ 4.0, as mentioned before ) and insert these files into the project :-

  • A2.cpp - That's the file given above
  • Wsock32.lib -

Build the project. As before, be sure to log onto your local Internet Service Provider before you run the .exe. Now run the file. Clicking in the window starts up the server. Now go back to windows ( Alt + Tab) and click on the start button, click on run and type in winipcfg. This is one of the many nifty little utilities which come with Windows95. Winipcfg is stuffed full of useful information and you'll understand all of those weird words as we proceed. What we're presently interested in is the IP address. Since your IP address changes every time you log on, winipcfg is one of the simpler methods to find out the new IP address. Now open your time client workspace and edit the code, changing the line Sa.sin_addr.s_addr = inet_addr("140.252.1.32"); to Sa.sin_addr.s_addr = inet_addr("666.666.666.665");, 666.666.666.665 being you're current IP address (make sure you don't add one to that number, or you'll end up connected to Gate's server, a definite no no !). Compile and run both the server and client programs, in that order. The words Hello appear in the message box on you screen. Yes ! those words have been sent to you by your server !

N.B - By the way, did you notice a mistake I've made with the IP address? Each number in the Dotted Decimal Notation represents a char and the highest value possible with a char is 256, yet I've miraculously managed to stuff 666.666.666.665 in as the IP address ! If you try this, you will get weird errors and you'll deserve them too !

Instead of obtaining your IP address through winipcfg, change the IP address from 140.252.1.32 to 127.0.0.1. This is a reserved address and it loops the IP packets back to your machine. This process is known as a Local LoopBack. This enables you to run both the client and server on the same machine, without being live on the Internet. This is a common technique used by many software firms to test their software and it's very convenient.

The server code is similar to the client and I see no need to wear my fingers away typing the same thing again as I've already explained the client in detail. There are a few changes though and I'll be explaining those.

The first thing you'll notice is that the third member of the structure A, A.sin_addr.s_addr, is equal to INADDR_ANY. It is a #define, which means that the server will accept requests from any IP address. If we were to replace INADDR_ANY with the IP address of Microsoft's server, then your server will only accept requests that originate from there. The server knows about the source from the IP header.

The function bind(), "binds" a server to a certain port. The image that comes to mind is that of a funnel, with the port number 13 engraved on it, sticking out of the wall. This particular funnel is dripping IP packets in a steady stream because there is no one around to claim them. By binding my server to a port, I attach a pipe to the funnel channeling all the port 13 packets to my server, where they can be properly dealt with. The bind function accepts certain parameters, such as the socket address, the typecasted address of the structure A and it's size.

After the bind statement, control passes to recvfrom(), which, since it is blocking, waits for a connection to be established. The moment an UDP/IP packet appears, recvfrom() is activated and it fills up the array bb with the incoming characters. It also stuffs the structure A with details like the IP address of the computer that sent the request.

After recvfrom() is sendto(), which sends the string "hello" to the remote client. In real time, the current time is calculated, put into an array and sent to the client, but you can do that for yourself. The structure A is necessary because that's where the IP address of the remote client is stored. Infact, here's an exercise to the user. Open the header file and find out the other members of the structure sockaddr_in and display them using message boxes.

This server can only handle one user request at one time, unlike the other servers on the Internet who can chew through a hundred requests every second. But we'll get to that later.

And that just about wraps it up for now, on to the Web browser !!

7. A Web Browser !! Well, almost...

#include <windows.h>
#include <stdio.h>
void abc (char *p)
{
        FILE *fp=fopen("z.txt","a+");
        fprintf(fp,"%s\n",p);
        fclose(fp);
}
WNDCLASS a;HWND b;MSG c;int d;char aa[100];char bb[100];
WSADATA ws; SOCKET s;struct sockaddr_in A;int ii;long gg;
char cc[100];
long _stdcall zzz (HWND,UINT,WPARAM,LPARAM);
int _stdcall WinMain(HINSTANCE i,HINSTANCE j,char *k,int l)
{
        a.lpszClassName="a1";
        a.hInstance=i;
        a.lpfnWndProc=zzz;
        a.hbrBackground=GetStockObject(WHITE_BRUSH);
        RegisterClass(&a);
        b=CreateWindow("a1","aaa",WS_OVERLAPPEDWINDOW,1,1,10,20,0,0,i,0);
        ShowWindow(b,3);
        while ( GetMessage(&c,0,0,0) )
                DispatchMessage(&c);
        return 1;
}
long _stdcall zzz (HWND w,UINT x,WPARAM y,LPARAM z)
{
if ( x == WM_LBUTTONDOWN)
{
        gg=WSAStartup(0x0101,&ws);
        sprintf(aa,"WSAStartup ..%ld",gg);
        MessageBox(0,aa,aa,0);
        abc(aa);
        s = socket(AF_INET,SOCK_STREAM,0);
        sprintf(aa,"socket s = %ld",s);
        abc(aa);
        MessageBox(0,aa,aa,0);
        A.sin_family = AF_INET;
        A.sin_port = htons(80);
        A.sin_addr.s_addr = inet_addr("198.105.232.5");
        d=connect(s,(struct sockaddr *)&A,sizeof(A));
        sprintf(aa,"d = %ld",d);
        abc(aa);
        MessageBox(0,aa,aa,0);
        strcpy(bb,"GET / \r\n");
        d=send(s,bb,strlen(bb),0);
        sprintf(aa,"d=%ld",d);
        abc(aa);
        ii=1;
        while(ii !=0)
        {
                strset(cc,' ');
                ii=recv(s,cc,sizeof(cc),0);
                abc(cc);
        }
        MessageBox(0,"all","over",0);
}
if ( x == WM_DESTROY)
      PostQuitMessage(0);
return DefWindowProc(w,x,y,z);
}

All right, so this isn't much of a browser !! All it does is connects to the Microsoft's site and downloads an HTML file. Though simplistic, this is what really happens under the fancy hoods of Netscape Navigator or Internet Explorer. Ofcourse, they have a lot more code and they do a lot more, ( like display the HTML file on the screen, for starters !! ).

HTTP is the real protocol behind the popularity of the Web. You can read this document because it exists. HTTP, like most other high level protocols, uses TCP/IP to communicate between the client ( any browser ) and the server. The reason most top-level protocols use TCP/IP instead of UDP is because it is reliable and reliability is often more important than speed.

Anyway, lets understand the code.

We first have our usual WSAStartup() with the version number and the address of WSADATA. After that we define a socket. As before, AF_INET ( same as PF_INET ) means that this socket will be used for the Internet and not for any other protocol like Novel etc. However, in this program, instead of SOCK_DGRAM, we have a SOCK_STREAM. SOCK_DGRAM stood for UDP while the SOCK_STREAM stands for the TCP, which is a 'streaming protocol' (whatever that means !)

After the socket(), we have to fill up the members of A. Sin_family is equal to AF_INET and sin_port is equal to 80, the port number of HTTP. As before, the address is that of Microsoft's site on the Internet.

But now, instead of copying the string and sending it immediately, as we did in UDP, we first connect to the other server. For that we have the connect(). To connect all the necessary information like the socket, the address of structure A and it's size is supplied. Now we use send() instead of sendto(). Check the value of d. If it is 0, then all is well, it the value is non-zero, then an error has occurred.

Connect() is not used with UDP because UDP is a connectionless protocol, one which needs no connection to be made. All you do is,go directly to the server and clobber it with something or the other. This approach can and does lead to problems. What if the server is down? Or if it's too busy to respond? You'll never know the answers to these. Your recvfrom() will just keep waiting indefinitely for a response. Since UDP refuses to be polite and make connections before it opens it's big mouth, it's termed a Datagram Oriented Protocol.

Right after the connect(), we copy the command GET / \r\n into the array bb. The \r\n is an absolute must and cannot be omitted. We then have a send() statement, which uses s to extract all the relevant information about the connection. The GET command, a part of the HTTP syntax, does exactly what you think it does, it gets a file specified by you. The / is to pick up the default .htm or .html file from the server. Some sites have index.htm or index.html or default.htm or default.html specified as the default file. To get the default file no matter what, send a GET / \r\n ). Netscape and IExplorer both get this file as soon as they connect to a site. Since the receive() returns the number of bytes it receives, we get a 0 when it's all over. We can, therefore, set up a loop as shown and copy all the data into a file on disk. If you want, you can write some more code to strip off everything in angle brackets (<>) and display what remains on the screen. That's all there is to it !!

Just what exactly is a stream oriented protocol? This is a quoestion you may ask. Well, in a stream oriented protocol, the server you're connected to sends the data as large a chunk as it deems fit and you receive it in as many chunks as you want. Here for instance, the server could send us the data in chunks of 20 bytes each, yet, since we have an array 100 bytes large, we grab them all in one fell swoop. In a Datagram oriented protocol, if the chunks are 20 bytes large, then we need to grab them in 20 byte chunks, no alternatives here.

8. The TCP/IP Stack

TCP and IP both cocoon your data in their headers used to protect it during transit and to hold valuable information about it. However, once the data packet reaches your computer, something has to be done about these headers or they may be considered part of your data and displayed on screen !! Some way has to be found to get rid of them. That's where the TCP/IP stack comes in.

The TCP/IP stack is part of the Windows 95 operating system and it is the one who strips away the TCP/IP headers. The stack also matches the header against the data and makes sure that the packet hasn't been altered or damaged during transit. If all is well, the 20 bytes of the IP header and the 20 bytes of the TCP header are removed ( Remember, the IP header is on the outside, the TCP on the inside ) and the data sent off to the waiting receive statement. The TCP/IP stack is the file Wsock32.dll in C:\Window\System. Not only does the stack strip off headers, it also adds them when data is sent across the Internet from your machine.

In actuality, 'TCP/IP stack' is just another name for the WinSock.

9. An HTTP Server

#include <windows.h>
#include <winsock.h>
#include <stdio.h>
void abc(char *p)
{       FILE *fp=fopen("z.txt","a+");
        fprintf(fp,"%s\n",p);
        fclose(fp);
}
WNDCLASS a;HWND b;MSG c;char aa[200];char bb[20000];
long _stdcall zzz (HWND,UINT,WPARAM,LPARAM);
int _stdcall WinMain(HINSTANCE i,HINSTANCE j,char *k,int l)
{
        a.lpszClassName="a1";
        a.hInstance=i;
        a.lpfnWndProc=zzz;
        a.hbrBackground=GetStockObject(WHITE_BRUSH);
        RegisterClass(&a);
               b=CreateWindow("a1","aaa",WS_OVERLAPPEDWINDOW,1,1,10,20,0,0,i,0);
        ShowWindow(b,3);
        while ( GetMessage(&c,0,0,0) )
                DispatchMessage(&c);
        return 1;
}
WSADATA ws;SOCKET s,s1,s2;sockaddr_in A,A1;int d,d1=200;
long _stdcall zzz (HWND w,UINT x,WPARAM y,LPARAM z)
{
        if ( x == WM_USER+1)
        {
        if ( LOWORD(z)&FD_ACCEPT == FD_ACCEPT)
        {
            d=sizeof(A1);
            s1=accept(s,(struct sockaddr *) &A1,&d);
            sprintf(aa,"accept s1 %ld",s1);
            abc(aa);
        }
        if(LOWORD(z)&FD_READ == FD_READ)
        {
            d=recv(y,bb,2000,0);
            sprintf(aa,"recv %ld",d);
            abc(bb);
            strcpy(aa,"HTTP/1.0 200 OK\r\n" );
            strcat(aa,"Date: Sunday Sun, 21 Oct 1995 19:38:46 GMT\r\n");
            strcat(aa,"Server: Webster/1.0\r\n");
            strcat(aa,"MIME-version: 1.0\r\n");
            strcat(aa,"Content-type: text/plain\r\n");
            strcat(aa,"Last-modified: Sunday Sun, 21 Oct 1995 19:38:46 GMT\r\n");
            strcat(aa,"Content-length: 1000\r\n\r\n");
            strcat(aa,"<html>Hello<hr><hr><hi>");
            d=send(y,bb,strlen(bb),0);
            sprintf(aa,"send %ld",d);
            abc(bb);
        }
        MessageBox(0,"WM_USER","WM_USER",0);
        }
        if ( x == WM_LBUTTONDOWN)
        {
                d=WSAStartup(0x0101,&ws);
                sprintf(aa,"WSAStartup %ld",d);
                abc(aa);
                s=socket(AF_INET, SOCK_STREAM,0);
                sprintf(aa,"socket %ld",s);
                abc(aa);
                A.sin_family=AF_INET;
                A.sin_port = htons(80);
                A.sin_addr.s_addr =INADDR_ANY;
                d=bind(s,(struct sockaddr *) &A,sizeof(A));
                sprintf(aa,"bind %ld",d);
                abc(aa);
                WSAAsyncSelect(s,b,WM_USER+1,FD_ACCEPT|FD_READ);
                d=listen(s,100);
                sprintf(aa,"listen %ld",d);
                abc(aa);
                MessageBox(0,"hi","hi",0);
        }
        if ( x == WM_DESTROY)
                PostQuitMessage(0);
        return DefWindowProc(w,x,y,z);
}

An HTTP server is a little different from a Time Server because now we have to handle TCP/IP rather than UDP/IP. The key difference between TCP/IP and UDP is that UDP is not a connection oriented protocol. In other words, it doesn't form a permanent (and virtual) link with the computer at the other end. TCP does, so you can treat a TCP connection in the same way you'd treat reading data from a file on the Hard Disk Drive.

It's because you, as the server, have to form a connection with the client that this server's a little more complex than the Time Server.

Read through the code and then jump directly to the if (x == WM_LBUTTONDOWN) part of it. That's the portion that is called when you click in the window. Most of the code is the same as always, but you'll start seeing the changes by the time you reach the ninth line. The line A.sin_addr.s_addr=INADDR_ANY; should be familiar to you from the Time Server and it doesn't need much explaining anyway. The Bind() function too has been explained before. It's the WSAAsyncSelect() that we have to tackle here.

WSAAsyncSelect() is a function which helps implement some sort of multitasking under Windows95. It's first parameter is s, the handle to the socket. B is another handle, this time a handle to the window, WM_USER+1 will take some explaining. WM_USER+1 is the message WSAAsyncSelect is supposed to call whenever anything happens to the socket. It's the same thing as the function zzz being called everytime something significant happens to the window. WM_USER is a macro which represents the number 1024. All message calls from 0 to 1023 have been reservered by Microsoft for itself.

The fourth parameter holds the number of the socket attributes to be made non-blocking i.e. the message WM_USER+1 should be called for the specified events. Here we've specified that WM_USER+1 should be called for every read and accept. When we make a function non-blocking, it means that the program will no longer wait for the function to end before it proceeds. If we hadn't used WSAAsyncSelect() then we'd have to place a recv() right here, an act which would halt program execution till someone connected to our site. By making the function non-blocking we make sure that recv() is only called when there's data ready for it in the buffer.

The next function is listen() which is passed the handle to the socket and the maximum length of the queue.

When someone connects to the server and his connection is accepted, WM_USER+1 is called. In WM_USER+1 we examine the LOWORD of z to discover the reason we've been called. If we've been called because of an Accept then one IF statement is executed and if we've been called for a Read, then another IF statement is called.

Since we're first called for an Accept, the function accept(), which is passed the handle to the socket, the address of the structure &A and the address of the variable d (which is a int), is called. The structure A will hold information about the person attempting to connect to us.

If on the other hand, we've been called because someone has sent us data, the code in the other IF statement is executed. There we first use recv() to collect all we've been sent and then we send our caller bogus information about ourselves. What we should have done was check exactly what he'd sent us and then react accordingly, but we don't care enough!!

That's about all there is to it.

10. The SMTP Server...

E-mail has changed the way we work, communicate and interact with people around us. It's cheaper than snail mail, more reliable and infinitely faster. E-mail is just another one of those Internet protocols that's made our lives so much easier and I guess it's only fitting that we tackle this protocol next.

E-mail is actually handled using two protocols; SMTP and POP3. SMTP or the Simple Mail Transfer Protocol is used to shoot the message across the vast expanse of the Internet. POP3 or the Post Office Protocol -3 is used by our E-mail clients to retrieve the mail from our service provider's mail server.

SMTP is indeed very simple. It's just a bunch of words we keep shooting at the SMTP server which tells it what we expect of it and where we want this mail to go. It is a far-cry from other protocols you might find as you stumble you're way through the hundreds of RFC's littering the 'net.

E-Mail (or SMTP - Simple Mail Transfer Protocol)

smtp.cpp

#include <windows.h>
#include <stdio.h>
void abc(char *p)
{       FILE *fp=fopen("z.txt","a+");
        fprintf(fp,"%s\n",p);
        fclose(fp);
}
struct sockaddr_in A;WSADATA W;SOCKET S;struct hostent *H;
char aa[100];int i;char R[10000];
int _stdcall WinMain(HINSTANCE ii, HINSTANCE j, char *k, int l)
{
  WSAStartup (0x101, &W);

  S=socket(AF_INET, SOCK_STREAM,0);
  A.sin_family=AF_INET;
  A.sin_port = htons(25);
  H=gethostbyname("giasbm01.vsnl.net.in");
  A.sin_addr.s_addr=*((unsigned long *) H->h_addr);
  i=connect(S,(struct sockaddr *) &A,sizeof(A));
  i=recv(S,R,10000,0);
  sprintf(aa,"recv %d R %s",i,R);
  abc(aa);
  strset(aa,' ');
  strcpy(R,"HELO vijay.com\r\n");

  i=send(S,R,strlen(R),0);

  i=recv(S,R,10000,0);
  sprintf(aa,"recv %d R %s",i,R);
  abc(aa);
  strset(aa,' ');

  strcpy(R,"MAIL FROM:<vijay1@giasbm01.vsnl.net.in>\r\n");
  i=send(S,R,strlen(R),0);

  i=recv(S,R,10000,0);
  sprintf(aa,"recv %d R %s",i,R);
  abc(aa);
  strset(aa,' ');

  strcpy(R,"RCPT  TO:<ravi@giasbm01.vsnl.net.in>\r\n");
  i=send(S,R,strlen(R),0);

  i=recv(S,R,10000,0);
  sprintf(aa,"recv %d R %s",i,R);
  abc(aa);
  strset(aa,' ');

  strcpy(R,"DATA\r\n");
  i=send(S,R,strlen(R),0);

  i=recv(S,R,10000,0);
  sprintf(aa,"recv %d R %s",i,R);
  abc(aa);
  strset(aa,' ');

  strcpy(R,"TO: aaa.com\r\n");
  i=send(S,R,strlen(R),0);

  strcpy(R,"FROM: vijay1@giasbm01.vsnl.net.in\r\n");
  i=send(S,R,strlen(R),0);

  strcpy(R,"DATE: 10 Jan 95 13:24 PST\r\n");
  i=send(S,R,strlen(R),0);

  strcpy(R,"MESSAGE_ID: <123@e.com>\r\n");
  i=send(S,R,strlen(R),0);

  strcpy(R,"Hello\r\n");
  i=send(S,R,strlen(R),0);

  strcpy(R,"How are you\r\n");
  i=send(S,R,strlen(R),0);

  strcpy(R,".\r\n");
  i=send(S,R,strlen(R),0);

  i=recv(S,R,10000,0);
  sprintf(aa,"recv %d R %s",i,R);
  abc(aa);
  strset(aa,' ');

  strcpy(R,"QUIT\r\n");
  i=send(S,R,strlen(R),0);

  i=recv(S,R,10000,0);
  sprintf(aa,"recv %d R %s",i,R);
  abc(aa);
  return 0;
}

Most the of the program should be quite familiar by now. We've been a little lazy and we have omitted the callback function. Now the program will start working as soon as you execute it.

The function WSAStartup() is the same as before and so it most of the other initialisation code. The function socket()is exactly the same and so is the first member of the structure A. It's from the second member onwards that we start seeing some changes.

The SMTP protocol is on Port 25 and that's why A.sin_port is set to 25.

When you're surfing on the Internet, you don't usually need to know the IP address of a site to visit it. All you do is type in it's domain name and the browser does the rest. The code that follows htons shows you how it all really works.

H is a pointer to a structure which looks like hostent. Gethostbyname returns a pointer to a structure which looks like hostent and it fills up that structure with valuable information about the site within it's parenthesis.

This is what the structure looks like...

struct hostent
{
    char FAR *h_name;                   //official name of host
    char FAR * FAR *h_aliasses;         // alias list
    short h_addrtype;                   //host address type
    short h_length;                     //length of address
    char FAR * FAR * h_addr_list;       //list of addresses
//first member of the list of address .i.e. server address
#define h_addr *h_addr_list[0];
};

The only member we're interested in right now is h_addr which is the site's IP address. This IP address is stored in A.sin_addr.s_addr. That's how we convert a URL into an IP address

The name we've given gethostbyname is the name of the mail server. It's the name after the @ sign in the email address and in our case it's giasbm01.vsnl.net.in.

Now comes the actual SMTP protocol commands. SMTP and POP3 both work in lockstep with the server at the other end. This means that the client sends one command and then it waits for a response from the server and vice-versa. The conversation does not proceed until an appropriate answer is received.

The first command we have to send the server is HELO (in CAPS and yes, it is HELO, not HELLO). The command is followed with the name of the computer we're coming from. The domain name doesn't have to exist because it's rarely checked and we've sent vijay.com as our domain.

After receiving our HELO, the server sends us some bytes too. These are caught by the receive and stored in the file z.txt. Check them out too.

After receiving the server's response, we send it the words MAIL FROM: < Vijay1@giasbm01.vsnl.net.in>\r\n. Vijay1@giasbm01.vsnl.net.in is our email address and this string appears in the Mail From field in your mail clients window. This too is not checked by the server and thus can be anything at all. We've sent mails to people with our return address as bgates@microsoft.com!!!

After capturing the servers response we send another bit of information to the server; RCPT TO: <e-mail address>\r\n where <e-mail address > is the email address of the person you want this message to go to. This is one data field which must be correct or the email will never reach it's destination.

It's only after RCPT TO: that our actual message starts. We send the server the word DATA and then wait for the servers acknowledgment. Once we have it, we can start the actual message information. The message ends when the server encounters a '.' (Period) all by itself on a fresh line.

The TO:, FROM:, DATE and MESSAGE ID: fields can also be filled up and they usually are, thought it's not compulsory. The Message ID is any old random number.

The actual email is then sent, line by line and the message ends with a period on a line all by itself.

Once we're through sending our mail we send a QUIT, terminating the session. If we had more than one mail to send, we'd again start with the MAIL FROM field.

Now lets tackle the other side of e-mails, POP3. POP3 is the protocol used to retrieve a mail from the POP3 server, the server where your mail eventually lands up. Lets check the code out first.

pop3.cpp

#include <windows.h>
     #include <stdio.h>
     void abc(char *p)
     {       FILE *fp=fopen("z.txt","a+");
             fprintf(fp,"%s\n",p);
             fclose(fp);
     }
     struct sockaddr_in A;
     WSADATA W;
     SOCKET S;
     char aa[60000];
     int i;
     struct hostent *H;
     char R[60000];
     int _stdcall WinMain(HINSTANCE ii, HINSTANCE j, char * k, int l)
     {
       WSAStartup (0x101, &W);
       S = socket(AF_INET, SOCK_STREAM,0);
       A.sin_family=AF_INET;
       A.sin_port = htons(110);
       H=gethostbyname("giasbm01.vsnl.net.in");
       A.sin_addr.s_addr=*((unsigned long *) H->h_addr);
       i=connect(S,(struct sockaddr *) &A,sizeof(A));
       sprintf(aa,"connect %d",i);
       abc(aa);
       i=recv(S,R,10000,0);
       sprintf(aa,"recv %d R %s",i,R);
       abc(aa);
       strcpy(R,"USER userid\r\n");              // enter your user id here
       i=send(S,R,strlen(R),0);
       sprintf(aa,"send %d ",i);
       abc(aa);
       i=recv(S,R,10000,0);
       sprintf(aa,"recv %d R %s",i,R);
       abc(aa);
       strcpy(R,"PASS password\r\n");      //enter your pass word here
       i=send(S,R,strlen(R),0);
       sprintf(aa,"send %d ",i);
       abc(aa);
       i=recv(S,R,10000,0);
       sprintf(aa,"recv %d R %s",i,R);
       abc(aa);
       strcpy(R,"STAT\r\n");
       i=send(S,R,strlen(R),0);
       sprintf(aa,"send %d ",i);
       abc(aa);
       i=recv(S,R,10000,0);
       sprintf(aa,"recv %d R %s",i,R);
       abc(aa);
       strcpy(R,"RETR 1\r\n");
       i=send(S,R,strlen(R),0);
       sprintf(aa,"send %d ",i);
       abc(aa);
       i=recv(S,R,60000,0);
       sprintf(aa,"recv %d ",i);
       abc(aa);
       sprintf(aa,"R...%s",R);
       abc(aa);
       i=recv(S,R,60000,0);
       sprintf(aa,"recv %d ",i);
       abc(aa);
       sprintf(aa,"R...... %s",R);
       abc(aa);
       MessageBox(0,"Hi","Over",0);
       return 0;
     }

If you quickly run through the program, you'll see that it's really quite similar to the SMTP program. The only difference is that here we have to identify ourselves as authorized users. This is necessary because you wouldn't want someone else reading your mail, now would you? The program has no call back and it runs sequentially.

After receiving our user ID and password, the server will authenticate us. If we pass the test then the server will wait patiently for input from us.

We immediately send the server a STAT command. This command tells the server to send us the STATus of our mailbox. We totally ignore the response we receive and ask for the first mail in the mailbox by sending RETR 1 which means RETRieve mail one. The message is stored in an array and displayed.

11. PING and TRACEROUTE

In order to understand these programs a little better, let's first try and understand how our packet travels over the wire. When a data packet, all wrapped up in it's TCP/IP cocoon, zooms across the Internet, it passes through many routers. Each router checks the information in the IP header and then sends the packet to the next router on the way to it's final destination. Each time the packet encounters a router in it's path, the value in it's Time To Live field (a one byte field in the IP header) is decrement by one. The TTL field contains a number from 0 - 255 and this is the maximum number of routers the packet can pass through before being destroyed. So when the TTL is 1, the router decrements it by one to make it zero and then it drops it. The TTL field was created to make sure that a packet that got lost wouldn't end up wandering around on the Internet till eternity rolled by. If the number in the TTL is 10, then the packet must reach it's destination within 10 router hops, or else it'll die. When ever a router kills a packet because it's TTL has expired, it sends us an ICMP message informing us of the loss.

ICMP is another protocol we should know about before we begin. ICMP or the Internet Control Message Protocol rides piggy back on IP and is used to deal with routers and to check whether a certain server is up or not. It's not used very often and in fact, in WinSock version 1.01 under in Windows 95 it's a little complicated to implement. That's set to change when WinSock version 2.0 is released.

Now imagine a situation when we want to download a 5MB binary file. Sometimes it takes quite a while to download such a big file over a 14.4 kbps link. At other times the file may come at a fair clip. This happens because the number of routers between different sites and at different times varies. The more the router, the slower the download. It would therefor be wise to check the condition of the line before we start and that's where Ping comes in.

Ping, the program given below, simply sends one packet to the destination site and times it. This tells us how fast or slow a link is and whether the site is up. Ping sends an ICMP (Internet Control Management Protocol) packet, a protocol understood by all machines on the Internet.

Ping

P.C

     #include <windows.h>
     struct o
     {
             unsigned char Ttl,Tos,Flags,OptionsSize,*OptionsData;
     };
     struct
     {
             DWORD Address;
             unsigned long  Status,RoundTripTime;
             unsigned short DataSize,Reserved;
             void *Data;
             struct o  Options;
     } E;
     HANDLE hIP;WSADATA wsa;
     HANDLE hIcmp;
     DWORD *dwIPAddr;
     struct hostent *phostent;
     DWORD d;char aa[100];struct o I;
     HANDLE ( WINAPI *pIcmpCreateFile )( VOID );
     BOOL ( WINAPI *pIcmpCloseHandle )( HANDLE );
     DWORD (WINAPI *pIcmpSendEcho)(HANDLE,DWORD,LPVOID,WORD,LPVOID,
                                             LPVOID,DWORD,DWORD);
     int APIENTRY WinMain( HINSTANCE hInstance, HINSTANCE hPrev,
                                     LPSTR lpCmd,int nShow )
     {
             hIcmp = LoadLibrary( "ICMP.DLL" );
             WSAStartup( 0x0101, &wsa );
             phostent = gethostbyname( "www.microsoft.com");
             dwIPAddr = (DWORD *)( *phostent->h_addr_list );
             pIcmpCreateFile=GetProcAddress( hIcmp,"IcmpCreateFile");
             pIcmpCloseHandle=GetProcAddress( hIcmp,"IcmpCloseHandle");
             pIcmpSendEcho =GetProcAddress( hIcmp,"IcmpSendEcho" );
             hIP = pIcmpCreateFile();
             I.Ttl=6;
             pIcmpSendEcho(hIP,*dwIPAddr,0,0,&I,&E,sizeof(E),8000 );
             d=E.Address;
             phostent = gethostbyaddr((char *)&d,4,PF_INET);
             sprintf(aa,"gethostbyaddr %p",phostent );
             MessageBox(0,aa,aa,0);
             if ( phostent  != 0 )
                     MessageBox(0,phostent->h_name,"hi",0);
             wsprintf(aa,"RTT: %dms,TTL:%d", E.RoundTripTime,E.Options.Ttl);
             MessageBox(0,aa,"hi",0);
             pIcmpCloseHandle( hIP );
             FreeLibrary( hIcmp );
             WSACleanup();
     }

This looks a little complicated and it is! This isn't code we've written, rather we've downloaded it off the Web. The variable names aren't three alphabets long and that kind'a makes it stand out but after these two programs, you should appreciate our coding style a little more.

The first function we look at is WinMain(). Here we load the DLL icmp.dll into memory and pass it's handle to hIcmp. The reason we do this rather than simply including the .lib file into the project and calling code directly is because Microsoft hasn't released a .lib file for this DLL yet! So we have to LoadLibrary it to use it. We then call WSAStartup() as usual. Right after that we initialize phostent, which is a pointer to the structure hostent, to the address returned by gethostbyname(). The next line assigns the IP address of www.microsoft.com to dwIPAddr.

The next three lines are a bunch of initialzations where we initialse three pointers to functions; pIcmpCreateFile, pIcmpCreateHandle and pIcmpSendEcho to the addresses of related functions.

We're getting to the real heart of the matter now...

After initializing the three pointers, we pass the handle returned by pIcmpCreateFile to hIP and then set the TTL to 60, a comfortably large number. The structure I looks like o which is the first structure to be defined in our source file. The fields in it correspond to fields in the IP header which can be changed by us.

pIcmpSendEcho is the main function of the code around which the entire program revolves. In pIcmpSendEcho, the first parameter is hIP, the handle to the ICMP connection. The next parameter is dwIPAddr which is a pointer to the IP address of www.microsoft.com. The next two parameters are zeros and these parameters are used when we're sending some data along with the ICMP packet. The reason we occasionally ping a computer with data is to check line reliability. When the recipient receives the bytes, it's supposed to respond with the same bytes. At our end, we can check the bytes to see it they've been corrupted or not.

After the zero's the next parameter is &I which is the address of the structure. The parameter after that is the address of the structure E. It too is defined before the WinMain(). E will hold data about the ping target and other information. The next parameter is the size of the structure and the final one is the total size.

The ICMP Ping packet is sent now and the structure E is filled up.

In the next couple of lines, we display data about the target, like it's IP address, the Round Trip Time of the packet and the TTL of the return trip packet.

Before we end, we close the ICMP handle, unload the DLL from memory and WSACleanup after ourselves.

Traceroute

trace.c

#include <windows.h>
     #include <stdio.h>
     void abc(char *p)
     {
            FILE *fp=fopen("z.txt","a+");
            fprintf(fp,"%s\n",p);
                 fclose(fp);
     }
     struct o
     {
     unsigned char Ttl;unsigned char a[7];
     };
     struct
     {
     DWORD Address;
     unsigned long  Status,RoundTripTime;
     unsigned char a[8];
     struct o  Options;
     } E;
     HANDLE hIP;
     WSADATA wsa;
     HANDLE hIcmp;
     DWORD *dwIPAddr;
     struct hostent *phostent;
     DWORD d;char aa[100];struct o I;char bb[100];int z;
     HANDLE ( WINAPI *pIcmpCreateFile )( VOID );
     BOOL ( WINAPI *pIcmpCloseHandle )( HANDLE );
     DWORD (WINAPI *pIcmpSendEcho) (HANDLE,DWORD,LPVOID,
                                   WORD,LPVOID,LPVOID,DWORD,DWORD);
     int APIENTRY WinMain( HINSTANCE hInstance, HINSTANCE hPrev,
                                             LPSTR lpCmd,int nShow )
     {
     hIcmp = LoadLibrary( "ICMP.DLL" );
     WSAStartup( 0x0101, &wsa );
     pIcmpCreateFile=GetProcAddress( hIcmp,"IcmpCreateFile");
     pIcmpCloseHandle=GetProcAddress( hIcmp,"IcmpCloseHandle");
     pIcmpSendEcho =GetProcAddress( hIcmp,"IcmpSendEcho" );
     hIP = pIcmpCreateFile();
     for ( z = 1; z<= 20 ; z++)
     {
            I.Ttl=(unsigned char)z;
                 phostent = gethostbyname( "www.neca.com");
            dwIPAddr = (DWORD *)( *phostent->h_addr_list );
            pIcmpSendEcho(hIP,*dwIPAddr,0,0,&I,&E,sizeof(E),8000 );
            d=E.Address;
                 phostent = gethostbyaddr((char *)&d,4,PF_INET);
            if ( phostent != 0)
                   strcpy(aa,phostent->h_name)  ;
            else
                   strcpy(aa,"no host name");
           wsprintf(bb," RTT: %dms,  TTL:      %d", E.RoundTripTime,E.Options.Ttl);
           strcat(aa,bb);
           abc(aa);
           if ( E.Options.Ttl )
             break;
     }
     MessageBox(0,"over","hi",0);
     pIcmpCloseHandle( hIP );
     FreeLibrary( hIcmp );
     WSACleanup();
     }

OUTPUT-(z.txt)

     no host name RTT: 241ms,  TTL: 0
     no host name RTT: 224ms,  TTL: 0
     border7-serial3-7.WestOrange.mci.net RTT: 798ms,  TTL: 0
     core2-fddi-0.WestOrange.mci.net RTT: 856ms,  TTL: 0
     core3.WestOrange.mci.net RTT: 841ms,  TTL: 0
     sprint-nap.WestOrange.mci.net RTT: 990ms,  TTL: 0
     sl-pen-2-F4/0.sprintlink.net RTT: 804ms,  TTL: 0
     sl-pen-7-F0/0.sprintlink.net RTT: 796ms,  TTL: 0
     sl-new2-1-S0-1544k.sprintlink.net RTT: 850ms,  TTL: 0
     cygnus.neca.com RTT: 803ms,  TTL: 246

The traceroute program given above is just an extension of the previous program. Through this program we try and discover the route our packet takes to and from it's final destination. If the route is overly long and complicated, then the data flow will be slow and unreliable, but if the server is just a few hops away, then the connection should be firm and stable. A tracer also helps us pinpoint the source of a network problem. There could be two reasons behind a failed ping; a dead server or a malfunctioning router on the way. If it's the latter, then a tracer helps us map out the route till the dead router and if it's part of an Intranet (a corporate Internet) we administer, we can call the service personnel and have it repaired.

The traceroute is an extension of the ping idea. What we do here is send a large number of packets with different TTL's. The first packet we send will have a TTL of 1. So when the packet reaches the very first router on the way, it'll expire. When the router drops the packet, it will send us an ICMP error message which we trap and decipher. That error message contains data about the router like it's IP address and Hostname. We capture that data and store it to a file on disk.

The next packet we send has a TTL of 2. So it's be dropped by the second router. That router too will send us an ICMP error message and we'll decipher that message too to extract the IP address and the Hostname. The next packet will have a TTL of 3 and it'll die at the third router and so on.

In this way we can map out the route our packets would take if sent down the Internet at that time. The very last message should be from the destination server. If it isn't there, then that means there's a broken link along the way somewhere, try another IP address or try again later.

12. Wrapping it all up

That just about wraps it up for now. If you want to dig deeper into the intricacies of WinSock programming, check out our tutorials on various Socket level protocols like LDAP, SNMP, IMAP and others. If you feel the urge to discover more about the protocols that actually constitute the Internet, like TCP, IP, UDP etc. Read our Core Internet Concepts tutorial.


The above tutorial is a joint effort of

Mr. Vijay Mukhi
Mr. Arsalan Zaidi
Ms. Sonal Kotecha
Mr. R. D. Parab


Back to the main page


Vijay Mukhi's Computer Institute
VMCI, B-13, Everest Building, Tardeo, Mumbai 400 034, India
Tel : 91-22-496 4335 /6/7/8/9     Fax : 91-22-307 28 59
e-mail : vmukhi@giasbm01.vsnl.net.in
http://www.neca.com/~vmis