Basic Socket Programming

Creating a Socket

Step Zero:  Pick Your Socket Type

There are two kinds of sockets, server side and client side.  A server socket waits for a connection and a client socket initiates a connection.  It's rather like a phone call, where one person sits by the phone waiting (server) and one person actually dials (client).  This how-to describes how to make a client socket.

Step Zero(b):  Overview

To make a client side socket, there are only a few steps.  You must

Step One:  Make a sockaddr

To make a socket, you need to fill in a sockaddr_in structure.  This structure can contain an internet address and will be used to hold the address of the server.  You can get one by just declaring a variable of type sockaddr_in.  For example,
 
struct sockaddr_in sa;

Step Two:  Fill in the sockaddr_in struct

These are the parts that need to be filled in.  All values go in NETWORK BYTE ORDER!!!
struct sockaddr_in { 
    uint8_t        sin_len;       // length of the structure? (1 byte char)
    sa_family_t    sin_family;    // what type of address is this? (1 byte char)
    in_port_t      sin_port;      // what port number to use? (2 byte short)
    struct in_addr sin_addr;      // what address (IP number) to use? (4 byte int)
    char           sin_zero[8];   // Eight empty bytes for future expansion
}
struct in_addr {
    in_addr_t      s_addr;        // An IP number (4 byte int)
}
To fill in the address, you need to get a hostname.  Servers use the local hostname (as returned by gethostname(buf, buflength).  Clients use the hostname of the server (ask the user or get it from a configuration file or something).  Then you translate the text hostname into a 4 byte IP address using code like below.   First you use gethostbyname() to translate the name into a 'hostent' which contains lots of information including the IP number and the family.  Second you copy the family and the IP number into the sockaddr.
 
// IN:  The char array 'hostname' 
// OUT: The hostent struct hp  
// OUT: A partially filled in sockadd_in struct sa 
struct sockadd_in sa; 
struct hostent *hp;
hp = gethostbyname(hostname);
sa.sin_family = hp->h_addrtype;
bcopy((char *)hp->h_addr, (char *)&sa.sin_addr, hp->h_length);
 

You also need to fill in the portnumber.  If it is a well known service (like telnet), you can search for it by name using getservbyname() or look in the file /etc/services.  If it is a service you made, you can pick your own portnumber.  Remember that ports less than 1024 are reserved for the superuser.  Also, remember that the portnumber needs to be in network byte order.  The htons() function converts a host short to a network short, and ntohs() does the opposite.  Since portnumbers are shorts, those are the functions you want.
 

// IN:  the port number 'port'. 
// OUT: The sockaddr_in struct with the portnumber set 
sa.sin_port = htons(port);

Step Three:  Making a socket

Making a socket is very simple.  Just use the socket call, telling it the type of socket you want.
 
// IN:  A host entry 'hp' for the address family 
// Out: A socket 's' 
s = socket(hp->h_addrtype, SOCK_STREAM, 0);

Step Four:  Connecting the Socket

However, if you are writing a client, then it is much easier to use a socket.  Just connect your socket from step three to the server at the address from step two.
 
// IN:      A socket 's' 
// IN:      An address to connect to 'sa' 
// OUT:   A file descriptor 'fd' ready to use. 
// NOTE: fd is less than zero on error 
int ret = connect(s, (struct sockaddr *)&sa, sizeof(sa));
if (ret < 0) {
    perror("Unable to connect the address to the socket");
}
 

Step 5:  Reading and Writing

You can read and write data using the file descriptors from step four.  What you want to read and write depends on your server and client.  However, this is how:
 
// READING 
// IN:     A length 'len' to read 
// IN:     A char array 'buf' which better be at least 'len' long 
// IN:     A file descriptor 
// OUT:  The length read 'ret' which is less than zero on error 
len = read(fd, buf, len);
/ WRITING 
// IN:     A length 'len' to write 
// IN:     A char array 'buf' which better be at least 'len' long 
// IN:     A file descriptor 
// OUT:  The length written 'ret' which is less than zero on error 
len = write(fd, buf, len);

Conclusion

Putting everything together, one can make an open server socket like this.  This code is missing all error checking!  You should add error checking for working code.
int MakeSocket(char *host, int port) {
        int s;
        int len;
        struct sockaddr_in sa;
        struct hostent *hp;
        struct servent *sp;
        int portnum;
        int ret;
 
        hp = gethostbyname(host);
        bcopy((char *)hp->h_addr, (char *)&sa.sin_addr, hp->h_length);
        sa.sin_family = hp->h_addrtype;
        sa.sin_port = htons(port);
        s = socket(hp->h_addrtype, SOCK_STREAM, 0);
        ret = connect(s, (struct sockaddr *)&sa, sizeof(sa));
        cout << "Connect to host " << host  << " port " << port << endl;
        return s;
}
main() {
   int fd = MakeSocket("euclid.nmu.edu", 25);
   write(fd, "Hello", 5);
}