Socket Coding, Part One

Introduction

  Okie doke, ds9 just finished, the bastards decided to play a double bill with a three part series, it's the one where the cardassians are... oh, right.. time for a new article. As you've probably seen, CoRNSouP isn't exactly what you might call a frequently updated hive of information, but we'll get there once an anti-lazy-ass-whore drug is developed. Thankfully I'm working with enough of a lapse in doing-fuck-all to start this article.

Today class, we'll be looking at sockets.

  Nowadays most of stuff that people use their 'puter for revolves quite heavily around comms. "man I'm bored/horny, I'll just download that new tutorial/game/patch/pron vcd". The majority (if not all) of the communication that takes place on the net is via two protocols, TCP/IP and UDP.

  Think of the net as a big fat cable, if we wanna use a web/ftp/telnet/etc client we need somewhere to plug it in. We need a socket.

To begin with...

First we need to create that socket we mentioned.

  Before we can do anything under windows, we need some startup code (don't we always?) this is completed with a call to WSAStartup(). This function takes two parameters, the first is a word which describes the winsock version that the user must have installed for the function to succeed (to allow for different socket implementations/old versions that dain't werk)
  Secondly we pass a structure of the type WSADATA which contains various crap about the sockets implementation when the function returns. Lets take a look:

WSADATA wsInfo;
WSAStartup( MAKEWORK(2,0),&wsInfo );

As you've prolly guessed, this call asks for winsock 2.0. Additionally, winsock requires that when you've finished with it (end of program) that you inform it. We do this with a call to WSACleanup().

Where do j00 want to go today?

Creation of a socket is simplicity in itself, we simply create an object of type SOCKET which is simply a typedef of an unsigned int. Ok, so we have our socket what can we do with it? Pretty much anything we damn well want to. Once we've got our socket object we need to specify how we want to use it, done with a call to socket()

SOCKET s;                                 
s = socket( AF_INET, SOCK_STREAM, 0 );

Let's look at these parameters, AF_INET (PF_INET is the same thing, windows gunf) is an address format specification, this is almost always AF_INET. Next we have a type specification, which can be either:

SOCK_STREAM     Uses TCP. Reliable, data is held at the socket until it has been read by the handling program. This is typically used for the majority of the major protocols, http/ftp/telnet/etc

SOCK_DGRAM      Uses UDP. Connectionless, no data blocking, packets can be lost etc.. typically used for multiplayer games since while waiting for data the prog would freeze.

Rather than just go through all the api calls which, lets face it, is dull, we're gonna write a simple lil prog that will encompass a fair few of the useful functions.. This lil prog downloads an html file from the address specified.

socksrc.zip

As you can see, although creating a socket is piss-easy, before we can use it to do anything we have to create a socket descriptor. This tells the socket layer where we want to connect to, which port, and other associated information. Socket descriptors are created as such:

struct sockaddr_in variable_name

Now that we have our socket descriptor we need to fill in the fields sin_family, sin_port, sin_addr.

sin_family is the address format specification used, again this is simply AF_INET.

sin_port is the port that we want to use, to convert the port number to network byte order (which is the required format) we use the htons() function.

sin_addr is the address that we want to connect to. First we need to convert the address from string form into a long int using the function inet_addr(), then we copy this information into the sin_addr member using a simple memcpy()

Okie, now we've created a socket, and created a socket descriptor that contains the details of where we want to connect to, for an outgoing connection we use connect() to erm.. connect.

Connecting your call.

The connect function is the aforementioned magical api call that'll do all the hard work for us. Let's look at some code to connect with a socket s and socket descriptor sa.

connect( s,(struct sockaddr*)&sa,sizeof(sa) );

As you can see, the parameters are the socket that has been created using socket() and a pointer to a socket descriptor which we typecast to the correct format (sockaddr)

Note that it is only possible to use connect() to connect outgoing sockets, more on this later.

It's better to send than recv.

Now that we've connected (hopefully there haven't been any errors, yet) we need to either send or receive data, which we do *gasp* with send() and recv(). I won't insult you by explaining which is which.

send( s,buf,sizeof(buf),0 );   Sends the contents of buf to socket s

recv( s,buf,sizeof(buf),0 );
   Receives data from socket s into buf

The fourth parameter in both cases is a flag which may influence the behavoir of the function call. For recv() the following flags should be considered:

MSG_PEEK   The data is copied into the buffer but is not removed from the input queue.
MSG_OOB     Specifies to process out-of-band data (remember the old oob attack?)

With send() we shoudl consider the following flags:

MSG_DONTROUTE   Specifies that the data should not be subject to routing.
MSG_OOB               Sends out-of-band-data

Call Terminated.

To close an open socket, we call closesocket() passing the socket as the only parameter, for example:

closesocket( s );

Betcha really needed that one explained. So now we've looked through the functions used in http.c. Now let's look at the pitfalls of the methods used thus far.

Thankyou for your call

In an effort to break this subject down into easily digestible (and very tasty) chunks of goodness I've decided to split it down to two or three parts. This part should allow you to write simple progs to automate a few annoying tasks around the house, mebbe get it to do your ironing? Next time we'll be looking at converting addresses between hostname and dotted string notation, handling incoming connections and making an ident daemon along the way. If there's space we'll also look at switching the whole thing over to a message based system (ala win, great stuff).

Welp, till next time, laters. oh aye, here's the winsock 1.1 api ref.

--NRoC [19/07/1999 21:24]