Applying C - Sockets The Client
Written by Harry Fairhead   
Tuesday, 26 April 2022
Article Index
Applying C - Sockets The Client
Connect a socket to an address
A Web Client
Connecting Using a URL

Connecting Using a URL 

There is also a utility function that will perform a DNS lookup for you or convert an IP address specified so you don't need to specify an IP address struct. Surprisingly this is almost an easier way to do things and it has become the standard way to set up a socket. The getaddrinfo function not only looks up the URL using DNS, it also constructs all of the structs you need to open a socket and connect. It will also return as many address specifications as you request, IPv4 and IPv6 for example.

The function specification is:

int getaddrinfo(const char *node, 
                const char *service, 
                const struct addrinfo *hints, 
                struct addrinfo **res);

and you need to add:

#include <netdb.h>

If you compile with C99 or C11 selected then you will find that none of the following works. You need to add:

#define _GNU_SOURCE 

to the start of the file.

You pass getaddrinfo the IP address or the DNS name, i.e. either "93.184.216.34" or "www.example.com", as node. The service can be specified as a port address "80" or as a service name "http". The hints struct is used to specify what sort of socket and address you are going to use. The result is a linked list of structs pointed at by addrinfo. The only slightly complication in using getaddrinfo is that you might have more than one result - one for IPv4 and one for IPv6, say - and then you have to work out which one to actually use.

The result struct contains structs that you need to both open the socket and to connect. For example, setting up the hints as:

struct addrinfo hints;
memset(&hints, 0, sizeof hints); 
hints.ai_family = AF_INET ; 
hints.ai_socktype = SOCK_STREAM;

asks for structs to be made for a TCP IPv4 socket.

We can now get the address details we need:

struct addrinfo *servinfo; 
int status = getaddrinfo("www.example.com", "80",&hints,
&servinfo);

Notice that you could use the IP address as a string. As long as this works the result should be a linked list with a single entry. In this case servinfo points to the first and only addrinfo struct. If there are any additional structs they are pointed at by:

servinfo->next

which is NULL if there is no next struct. 

Using the single result is easy. To create the socket we use:

int sockfd = socket(servinfo->ai_family,
                    servinfo->ai_socktype,
                    servinfo->ai_protocol);

and to connect to the server we use:

connect(sockfd, 
        servinfo->ai_addr,
        servinfo->ai_addrlen);

This is so much simpler that whenever you need a socket connected to a given URL or IP address and port you tend to fall into the idiom of writing:

struct addrinfo hints;
memset(&hints, 0, sizeof hints); 
hints.ai_family = AF_INET ; 
hints.ai_socktype = SOCK_STREAM;
struct addrinfo *servinfo; 
int status = getaddrinfo("www.example.com", "80",
&hints, &servinfo); int sockfd = socket(servinfo->ai_family,                     servinfo->ai_socktype,                     servinfo->ai_protocol); connect(sockfd,          servinfo->ai_addr,         servinfo->ai_addrlen); 

The only minor complication is that you need to remember to free the linked list once you are finished with it using:

freeaddrinfo(servinfo);

As before this all works on any POSIX system including Linux on x86, Raspbian and so on. It doesn’t work without some modifications under Windows, even with the help of MinGW.

To make it work you need to add:

#define _WIN32_WINNT 0x501
#include <ws2tcpip.h>

to the start of the file and add the library file ws2_32.a, which you should find in mingw/lib, to the libraries.

The complete POSIX listing is:

#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
#include <string.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <netdb.h>
int main(int argc, char** argv) {
 struct addrinfo hints;
 memset(&hints, 0, sizeof hints);
 hints.ai_family = AF_INET;
 hints.ai_socktype = SOCK_STREAM;
 struct addrinfo *servinfo; 
 int status = getaddrinfo("www.example.com", "80",&hints, 
                              &servinfo);
 int sockfd = socket(servinfo->ai_family,
                     servinfo->ai_socktype,
                     servinfo->ai_protocol);    
 connect(sockfd,
        servinfo->ai_addr,
        servinfo->ai_addrlen);
 char header[] = "GET /index.html   
HTTP/1.1\r\nHost:example.org\r\n\r\n"; int n = write(sockfd, header, strlen(header)); char buffer[2048]; n = read(sockfd, buffer, 2048); printf("%s", buffer); return (EXIT_SUCCESS); }

In chapter but not in this extract

  • A Server
  • A WinSock Server

Summary

  • Sockets are a general way of making a connection between two programs, perhaps running on different machines.

  • Sockets are a POSIX standard, but not a C standard. Windows supports a modified form of sockets via WinSock.

  • To use sockets you have to create a socket, connect it to an address and then transfer data.

  • Sockets come in two general types – client sockets, which actively connect to another socket and transfer data, and server sockets, which wait for a connection and then service it.

  • You create a socket using the socket function and you have to specify the type of connection and the detailed protocol in use.

  • To connect a socket you use the connect function, which accepts a struct which specifies the address of the socket to connect to.

  • A server socket has to be bound to an address, using the bind function, which it listens on for a client trying to connect.

  • A server socket also has a listen function, which activates it ready for a client to try to connect. The server then uses the accept function to create a connection.

  • Once a socket is connected to another socket data can be transferred as if it was a file. It is very easy to create a web client or a web server.

  • The simplest way to create a server is to use a blocking call to the accept function and leave the thread idle waiting for a client to connect.

  • A more useful way to handle the connection is in non-blocking mode which allows the thread to do something else while waiting.

Related Articles

Remote C/C++ Development With NetBeans

Raspberry Pi And The IoT In C

Getting Started With C/C++ On The Micro:bit

To be informed about new articles on I Programmer, sign up for our weekly newsletter, subscribe to the RSS feed and follow us on Twitter, Facebook or Linkedin.

Banner


Wasmer 5 Adds iOS Support
12/11/2024

The Wasmer team has released Wasmer 5.0. The WebAssembly runtime adds experimental support for more back ends including V8, Wasmi and WAMR. It also now has iOS support, and upgraded compilers includin [ ... ]



Pico 2W Announced But There Is A Surprise!
25/11/2024

Raspberry Pi released the Pico 2 a few months ago and we have been waiting for the Pico 2W since then. But Pimoroni beat them to the draw with the Pico Plus 2W based on the RM2 radio module and hinted [ ... ]


More News

espbook

 

Comments




or email your comment to: comments@i-programmer.info



Last Updated ( Tuesday, 26 April 2022 )