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:
#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); strcpy (szNoop, NOOP);
}
CPing::~CPing()
{
WSACleanup(); }
The class CPing
is defined as follows:
#ifndef __CPING_CLASS
#define __CPING_CLASS
#define MAX_SENDS 1111111
#define NOOP "NOOP"
typedef struct {
unsigned int iTimeSend; unsigned int iTimeRecv; unsigned int iTotalSent, iTotalRecvd; unsigned int iPort; } 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:
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:
#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):
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