Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C++

In-Depth Network Programming in C++

3.47/5 (9 votes)
24 May 2023CPOL3 min read 23.8K  
Network programming in C++
We provide the system primitives on the example of two networking programs, or utilities, which work on the Transfer Control Protocol (TCP) and User Datagram Protocol (UDP) for basic and system operations

Introduction

In this article, we introduce the basic primitives used to work with sockets, which are defined in winsock2.h file for the version 2.0.

We will also present the two useful utility programs which solve various networking tasks.

Of course, we could use Microsoft Foundation Classes (MFC) for asynchronous programming, however, our approach is more optimal for system network programming.

Thus, CPing program presented in this article is a utility which pings user-defined protocols and measures the speed of connection.

Another example like CSocks presents the tunneling algorithm for proxying on SOCKS-5 protocol for messengers like Internet Relay Chat (IRC).

Background

The article is to be studied according to the Request For Comment (RFC) standard for CSocks project as it uses the command order for SOCKS-4/5 internet TCP protocol and, thus, operates on user or higher level, rather, than same "ping" utility which operates on system level and protocol like Internet Control Message Protocol (ICMP).

Thus, we introduce the same ping utility in CPing project, which works as is in order to test functionality of HyperText Transfer Protocol (HTTP) and others that operate on the user-defined level.

Using the Code

Before proceeding to the description of programming techniques for network programming using sockets, we will describe several functions of Winsock API:

  • socket() - create socket entity
  • send() - send data packet to the end point of socket
  • recv() - receive data packet from the same end point
  • setsockopt() - set socket options like timeout, etc.
  • connect() - connect to the end point of socket which differs from the host machine on which the networking program works
  • select() - select the present time active socket on which the actions were made from the end point

Let's proceed to the initialization for sockets' library as follows in the CPing utility:

C++
#include <windows.h>
#include <winsock2.h> // necessary
#include "CPing.h"
#include <string.h>
#include <stdio.h>
#include <stdlib.h>

CPing::CPing()
{
    iTimeOut = 1000;
    iBytesToRecv = 0;
    iLastError = WSAStartup (MAKEWORD(2,0),&wsadata); // initialize Windows Sockets
    strcpy (szNoop, NOOP);
}

CPing::~CPing()
{
    WSACleanup(); // clean up the Windows Sockets cache
}

The class CPing is defined as follows:

C++
#ifndef __CPING_CLASS
#define __CPING_CLASS

#define MAX_SENDS 1111111
#define NOOP "NOOP"

typedef struct {
    unsigned int iTimeSend; // measured time to send TCP packet
    unsigned int iTimeRecv; // measured time to receive TCP packet
    unsigned int iTotalSent, iTotalRecvd; // total number of bytes sent and received
    unsigned int iPort; // TCP protocol port
} pingstore;

class CPing
{
public:
    CPing();
    ~CPing();
    char szNoop[256];
    int iLastError, iTotalRes, iBytesToRecv;
    unsigned int iTimeOut, iTotalSent, iTotalRecvd;
    unsigned int PingContinuous 
             (char* szHost, unsigned int iPort, unsigned int iPackets);
    unsigned int PingConnective 
             (char* szHost, unsigned int iPort, unsigned int iPackets);
    pingstore* Res;
private:
    WSADATA wsadata;
};

#endif

To be clear enough, we have to present an example of the usage of object-oriented programming with the support of windows sockets in the CPing utility as follows:

C++
unsigned int CPing::PingContinuous 
         (char* szHost, unsigned int iPort, unsigned int iPackets)
{
    struct hostent* host = NULL;
    struct sockaddr_in saddr;
    unsigned int s = 0;
    unsigned int dw1, dw2, dw3;
    char szBuffer[256];

    if (!iBytesToRecv) iBytesToRecv = strlen(szNoop);

    if (iPackets>MAX_SENDS) return (0);
    free (Res);
    Res = (pingstore*)malloc (sizeof(pingstore)*iPackets);
    memset (Res, 0, sizeof(pingstore)*iPackets);

    s = socket(AF_INET, SOCK_STREAM, 0);
    if (!s) return (0);
    setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, (char*)&iTimeOut, sizeof(iTimeOut));
    setsockopt(s, SOL_SOCKET, SO_SNDTIMEO, (char*)&iTimeOut, sizeof(iTimeOut));

    host = gethostbyname (szHost);
    if (host==NULL) return (0);
    saddr.sin_family = AF_INET;
    saddr.sin_port = htons(iPort);
    saddr.sin_addr = *((struct in_addr*)host->h_addr);

    if (connect (s,(struct sockaddr*)&saddr, sizeof(saddr)) == -1) return (0);    
    for (int i=0;i<iPackets;i++)
    {
        iTotalRes++;
        sprintf (szBuffer, "%s\r\n", szNoop);
        dw1 = GetTickCount();
        int iSent = send (s, szBuffer, strlen(szBuffer), 0);
        dw2 = GetTickCount();
        int iRecv = recv (s, szBuffer, iBytesToRecv, 0);
        dw3 = GetTickCount();
        Res[i].iPort = iPort;
        Res[i].iTimeSend = dw2-dw1;
        Res[i].iTimeRecv = dw3-dw2;
        Res[i].iTotalSent = ((iSent==SOCKET_ERROR)?0:iSent);
        Res[i].iTotalRecvd = ((iRecv==SOCKET_ERROR)?0:iRecv);
        if (iRecv<=0)
        {
            closesocket (s);
            return ((iTotalRes)?1:0);
        }
    }
    closesocket (s);
    return (1);
}

Another example like CSocks network utility for proxying on SOCKS-5 protocol is defined as follows:

C++
#ifndef __CSOCKS_HEADER
#define __CSOCKS_HEADER

#include "common.h"
#include "cauth.h"

class CSocks {
private:
    
public:
    PSOCKSNOTIFYPROC pNotifyProc;
    u_short uPort, mode;
    u_int uAccept;
    u_long LastError;
    CSocksBasicAuth* basicauth = NULL;

    CSocks() {
        uAccept = 0;
        mode = SOCKS_MODE_TCP;
        uPort = SOCKS_DEF_PORT;
        LastError = SOCKS_ERROR_NONE;
        pNotifyProc = NULL;
    };

    ~CSocks() {
        if(uAccept) closesock(uAccept);
    };

    bool DNSAddrLookup(char* sHost, struct in_addr* addr);
    bool StartChaining();
    bool SocksTCPTunneling(u_int sres, u_int sdest);
    bool SocksUDPTunneling(void* sadest, char* sData, u_int len);
    bool PrepareListening();
    char* GetLastError();
    virtual bool ForceConnection(u_int sock) = 0;
};

#endif

We present this class for the example realization of the network operation like tunneling when the network data are passed through the mediate socket - this concept primarily is used in proxy software, this operation is defined as follows (sres and sdest are sockets between which the tunneling operation performs):

C++
bool CSocks::SocksTCPTunneling(u_int sres, u_int sdest)
{
    register u_int sockr, sockw, ret;
    register u_int uread, uwrote;
    char szBuffer[1024];
    struct fd_set fd;
    struct timeval tv = {0,0};
    
    do
    {
        FD_ZERO(&fd);
        FD_SET(sres,&fd);
        FD_SET(sdest,&fd);
        
        if((ret = select(0,&fd,NULL,NULL,&tv))>0 && VALID_SOCKET(ret))
        {
            if(FD_ISSET(sres,&fd))
            {
                sockr = sres;
                sockw = sdest;
            }
            else
            {
                sockr = sdest;
                sockw = sres;
            }

            uread = recv(sockr,szBuffer,1023,0);
            
            if (uread >= 1023) break;
            
            szBuffer[uread] = 0;
            uwrote = 0;
            if(!VALID_SOCKET(uread) || uread==0) break;

            while(uwrote<uread)
            {
                ret = send(sockw,szBuffer+uwrote,uread-uwrote,0);
                if(!VALID_SOCKET(ret)) goto __quit;
                uwrote += ret;
            }
        }

        FD_ZERO(&fd);
        FD_SET(sres,&fd);
        FD_SET(sdest,&fd);
        if(select(0,NULL,NULL,&fd,&tv)>0) break;
    } while(1);
    
__quit:
    return(true);
}

That's all for now, other minor procedures and functions can be studied from our repositories, however, all the above represent the necessary knowledge in order to work with sockets on system level in C++ programming language.

Points of Interest

Thus, we have learned how to adopt sockets layer model for particular network tasks for better performance and more deep functionality and assessment.

History

  • 27th October, 2022 - Initial release
  • 2nd November, 2022 - Removed links
  • 3rd November, 2022 - Added thumbnails
  • 4th November, 2022 - License changed

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)