0% found this document useful (0 votes)
25 views25 pages

BTN 415-Data Communications Programming: Week 5 - Socket Programming For UDP and TCP

This document covers socket programming for UDP and TCP, detailing the differences between the two protocols, their use cases, and client/server interactions. It includes example implementations for both UDP and TCP servers and clients, as well as information on broadcasting with UDP. The document emphasizes the importance of choosing the appropriate transport layer protocol based on application requirements and the trade-offs between reliability and network overhead.

Uploaded by

leomoca19
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PPTX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
25 views25 pages

BTN 415-Data Communications Programming: Week 5 - Socket Programming For UDP and TCP

This document covers socket programming for UDP and TCP, detailing the differences between the two protocols, their use cases, and client/server interactions. It includes example implementations for both UDP and TCP servers and clients, as well as information on broadcasting with UDP. The document emphasizes the importance of choosing the appropriate transport layer protocol based on application requirements and the trade-offs between reliability and network overhead.

Uploaded by

leomoca19
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PPTX, PDF, TXT or read online on Scribd
You are on page 1/ 25

BTN 415- Data

Communications Programming
Week 5- Socket Programming for UDP and TCP
Outline

 Difference between UDP and TCP


 When UDP will be used and when TCP will be used
 UDP Client/server socket interaction
 Example UDP Server Implementation
 Example UDP Client Implementation
 UDP Broadcast Implementation
 TCP Client/server socket interaction
 Example TCP Server Implementation
 Example TCP Client Implementation
2
Socket Programing

Two socket types for two transport services:


 UDP: Unreliable datagram
 TCP: Reliable, byte stream-oriented

• The IP protocol is responsible only for attempting to deliver each packet to the
correct machine.
• Two additional features are usually necessary if separate applications are to
maintain conversations, and it is the job of the protocols built on IP to provide
the following two features.
• If many packets traveling between two hosts, need to be labeled so that they can be separated from one
another. This is called multiplexing.
 If any damage occurred to a stream of packets traveling separately from one host to another needs to be
repaired. This is known as providing a reliable transport.
 Missing packets need to be retransmitted.
3
 Packets that arrive out of order need to be reassembled into the correct order.
 Finally, duplicate packets need to be discarded so that no information in the data stream gets repeated.
Socket Programing
Two socket types for two transport services:
 UDP: unreliable datagram
 TCP: reliable, byte stream-oriented

• The IP protocol is responsible only for attempting to deliver each packet to the correct machine.
• Two additional features are usually necessary if separate applications are to maintain
conversations, and it is the job of the protocols built on IP to provide the following two features.
• Multiplexing.
• Reliable transport.

• The User Datagram Protocol (UDP) solves only the first problems. It provides port numbers, so
that the packets destined for different services on a single machine can be properly
demultiplexed.
• The Transmission Control Protocol (TCP), solves both problems. It both incorporates port
numbers using the same rules as UDP and offers ordered and reliable data streams that hide
from applications the fact that the continuous stream of data has in fact been chopped into
packets and then reassembled at the other end. 4
The Right Transport Layer Protocol
TCP or UDP
 There is a trade-off between the value of
reliability and the burden it places on the
network.
 Application developers choose the transport
protocol based on the requirements of their
applications.
 In applications where either one or both of
the requirements of having the segments
received in specific sequence and being fully
received, the TCP transmission protocol is
used. Examples: data bases, web browsers
and email applications.
 In applications where some transmission
errors is tolerated for the benefit of putting
low overhead in the network which also
means reducing the delay in delivering UDP
is used. Example: streaming audio, video and 5

Voice-over-IP (VoIP) UDP is used.


Socket Programming with UDP

UDP: “connectionless” between client & server


 No handshaking before sending data
 Sender explicitly attaches IP destination address and port # to each
packet
 Receiver extracts sender IP address and port# from received packet

UDP: Transmitted data may be lost or received out-of-order


Application viewpoint:
 UDP provides unreliable transfer of groups of bytes (“datagrams”)
between client and server
6
Client/server Socket Interaction: UDP

server (running on serverIP) client


create socket:
create socket, port= x:
clientSocket =
serverSocket = socket(AF_INET, SOCK_DGRAM)
socket(AF_INET, SOCK_DGRAM)
Create datagram with server IP and
port=x; send datagram via
read datagram from clientSocket
serverSocket

write reply to
serverSocket read datagram from
specifying clientSocket
client address,
port number close
clientSocket
7
Example app: UDP client
#include <arpa/inet.h>
#include <unistd.h>

int main() {
const char* serverName = "hostname";
int serverPort = 12000;
char message[1024];
char receivedMessage[2048];

std::cout << "Input lowercase sentence: ";


std::cin.getline(message, sizeof(message));

int clientSocket = socket(AF_INET, SOCK_DGRAM, 0);


sockaddr_in serverAddr{};
serverAddr.sin_family = AF_INET;
serverAddr.sin_port = htons(serverPort);
inet_pton(AF_INET, serverName, &serverAddr.sin_addr);

sendto(clientSocket, message, strlen(message), 0,


(sockaddr*)&serverAddr, sizeof(serverAddr));

socklen_t addrLen = sizeof(serverAddr);


int receivedLen = recvfrom(clientSocket, receivedMessage, sizeof(receivedMessage), 0,
(sockaddr*)&serverAddr, &addrLen);
receivedMessage[receivedLen] = '\0';

std::cout << receivedMessage << std::endl;


close(clientSocket);

return 0; 8
}
Example app: UDP server
#include <iostream>
#include <cstring>
#include <iostream>
#include <cstring>
#include <arpa/inet.h>
#include <unistd.h>

int main() {
int serverPort = 12000;
char buffer[2048];

int serverSocket = socket(AF_INET, SOCK_DGRAM, 0);


sockaddr_in serverAddr{}, clientAddr{};
serverAddr.sin_family = AF_INET;
serverAddr.sin_port = htons(serverPort);
serverAddr.sin_addr.s_addr = INADDR_ANY;

bind(serverSocket, (sockaddr*)&serverAddr, sizeof(serverAddr));


std::cout << "The server is ready to receive" << std::endl;

while (true) {
socklen_t clientLen = sizeof(clientAddr);
int receivedLen = recvfrom(serverSocket, buffer, sizeof(buffer), 0,
(sockaddr*)&clientAddr, &clientLen);
buffer[receivedLen] = '\0';

for (int i = 0; buffer[i]; ++i)


buffer[i] = toupper(buffer[i]);

sendto(serverSocket, buffer, strlen(buffer), 0,


(sockaddr*)&clientAddr, clientLen);
}

close(serverSocket);
return 0;
} 9
Creating an UDP socket Object
#include <sys/socket.h>

int sock = socket(AF_INET, SOCK_DGRAM, 0);


if (sock < 0) {
// Handle error
}

•AF_INET: Specifies the address family (IPv4).


•SOCK_DGRAM: Indicates a datagram (UDP) socket.
•0: Protocol is automatically selected (UDP for SOCK_DGRAM).

10
UDP – Server Example
#include <iostream>
#include <cstring>
#include <arpa/inet.h>
#include <unistd.h>

const char* host = "127.0.0.1"; // localhost


const int payload_size = 2048;

void UDPserver(int port) {


int sock = socket(AF_INET, SOCK_DGRAM, 0);
if (sock < 0) {
perror("Failed to create socket");
return;
}

sockaddr_in serverAddr{}, clientAddr{};


serverAddr.sin_family = AF_INET;
serverAddr.sin_port = htons(port);
inet_pton(AF_INET, host, &serverAddr.sin_addr);

if (bind(sock, (sockaddr*)&serverAddr, sizeof(serverAddr)) < 0) {


perror("Bind failed");
close(sock);
return;
}

std::cout << "Starting up echo server on " << host << " port " << port << std::endl;

while (true) {
char buffer[payload_size];
socklen_t clientLen = sizeof(clientAddr);

std::cout << "Waiting to receive message from client ... ... ..." << std::endl;
int receivedBytes = recvfrom(sock, buffer, payload_size, 0,
(sockaddr*)&clientAddr, &clientLen);

if (receivedBytes < 0) {
perror("recvfrom failed");
break;
}

buffer[receivedBytes] = '\0';
std::string clientIP = inet_ntoa(clientAddr.sin_addr);
int clientPort = ntohs(clientAddr.sin_port);

std::cout << "Received " << receivedBytes << " bytes from "
<< clientIP << ":" << clientPort << std::endl;
std::cout << "Received Message from Client: " << buffer << std::endl;

int sentBytes = sendto(sock, buffer, receivedBytes, 0,


(sockaddr*)&clientAddr, clientLen);
if (sentBytes < 0) {
perror("sendto failed");
break; 11
}

std::cout << "Sent " << sentBytes << " bytes back to "
<< clientIP << ":" << clientPort << std::endl;
}
UDP – Client Example
#include <iostream>
#include <cstring>
#include <arpa/inet.h>
#include <unistd.h>

const char* host = "127.0.0.1"; // localhost


const int payload_size = 2048;

void UDPclient(int port) {


int sock = socket(AF_INET, SOCK_DGRAM, 0);
if (sock < 0) {
perror("Failed to create socket");
return;
}

sockaddr_in serverAddr{};
serverAddr.sin_family = AF_INET;
serverAddr.sin_port = htons(port);
inet_pton(AF_INET, host, &serverAddr.sin_addr);

std::cout << "Connecting to " << host << " port " << port << std::endl;

try {
const char* message = "Test message... This will be echoed";
std::cout << "Sending to server: " << message << std::endl;

int sentBytes = sendto(sock, message, strlen(message), 0,


(sockaddr*)&serverAddr, sizeof(serverAddr));
if (sentBytes < 0) {
perror("sendto failed");
return;
}

sockaddr_in localAddr{};
socklen_t localAddrLen = sizeof(localAddr);
if (getsockname(sock, (sockaddr*)&localAddr, &localAddrLen) == 0) {
char localIP[INET_ADDRSTRLEN];
inet_ntop(AF_INET, &localAddr.sin_addr, localIP, INET_ADDRSTRLEN);
std::cout << "The OS assigned me the address "
<< localIP << ":" << ntohs(localAddr.sin_port) << std::endl;
} else {
perror("getsockname failed");
}

char buffer[payload_size];
sockaddr_in serverResponseAddr{};
socklen_t serverAddrLen = sizeof(serverResponseAddr);

int receivedBytes = recvfrom(sock, buffer, payload_size, 0,


(sockaddr*)&serverResponseAddr, &serverAddrLen);
if (receivedBytes < 0) {
perror("recvfrom failed");
return; 12
}

buffer[receivedBytes] = '\0';
char serverIP[INET_ADDRSTRLEN];
inet_ntop(AF_INET, &serverResponseAddr.sin_addr, serverIP, INET_ADDRSTRLEN);
UDP – Broadcast

• Instead of sending a datagram to some other specific host, you can address it to
an entire subnet to which your machine is attached and have the physical network
card broadcast the datagram so that all attached hosts see it without its having to
be copied separately to each one of them.

13
UDP – Broadcast –
Example #include <iostream>
#include <cstring>
#include <arpa/inet.h>
#include <unistd.h>

#include <iostream> int main() {


#include <cstring> int clientSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
#include <arpa/inet.h> if (clientSocket < 0) {
#include <unistd.h> perror("Failed to create socket");
#include <chrono> return 1;
#include <thread> }

int main() { int broadcastEnable = 1;


int serverSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); if (setsockopt(clientSocket, SOL_SOCKET, SO_BROADCAST, &broadcastEnable, sizeof(broadcastEnable)) < 0) {
if (serverSocket < 0) { perror("Failed to enable broadcast");
perror("Failed to create socket"); close(clientSocket);
return 1; return 1;
} }

int broadcastEnable = 1; sockaddr_in clientAddr{};


if (setsockopt(serverSocket, SOL_SOCKET, SO_BROADCAST, &broadcastEnable, sizeof(broadcastEnable)) < 0) { clientAddr.sin_family = AF_INET;
perror("Failed to enable broadcast"); clientAddr.sin_port = htons(37020); // Bind to port 37020
close(serverSocket); clientAddr.sin_addr.s_addr = INADDR_ANY; // Bind to all interfaces
return 1;
} if (bind(clientSocket, (sockaddr*)&clientAddr, sizeof(clientAddr)) < 0) {
perror("Failed to bind socket");
sockaddr_in broadcastAddr{}; close(clientSocket);
broadcastAddr.sin_family = AF_INET; return 1;
broadcastAddr.sin_port = htons(37020); }
broadcastAddr.sin_addr.s_addr = INADDR_BROADCAST; // Broadcast address
timeval timeout{};
const char* message = "your very important message"; timeout.tv_sec = 0;
timeout.tv_usec = 200000; // 0.2 seconds
while (true) { if (setsockopt(clientSocket, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)) < 0) {
int sentBytes = sendto(serverSocket, message, strlen(message), 0, perror("Failed to set socket timeout");
(sockaddr*)&broadcastAddr, sizeof(broadcastAddr)); close(clientSocket);
if (sentBytes < 0) { return 1;
perror("Failed to send broadcast message"); }
} else {
std::cout << "Message sent!" << std::endl; char buffer[1024];
} sockaddr_in senderAddr{};
socklen_t senderAddrLen = sizeof(senderAddr);
std::this_thread::sleep_for(std::chrono::seconds(1));
} while (true) {
int receivedBytes = recvfrom(clientSocket, buffer, sizeof(buffer) - 1, 0,
close(serverSocket); (sockaddr*)&senderAddr, &senderAddrLen);
return 0;
} if (receivedBytes < 0) {
if (errno == EAGAIN || errno == EWOULDBLOCK) {
// Timeout occurred, continue the loop
continue;
}
perror("recvfrom failed");
break;
}

buffer[receivedBytes] = '\0'; // Null-terminate the received message


char senderIP[INET_ADDRSTRLEN];
inet_ntop(AF_INET, &senderAddr.sin_addr, senderIP, INET_ADDRSTRLEN);

std::cout << "Received message: " << buffer


<< " from " << senderIP
<< ":" << ntohs(senderAddr.sin_port) << std::endl; 14
}

close(clientSocket);
return 0;
}
When to USE UDP
 UDP is efficient only if your host sends only one message at a time
and then waits for a response.
 Because you are implementing a protocol that already exists and it
uses UDP.
 Because you are designing a time-critical media stream whose
redundancy allows for occasional packet loss and you never want this
second’s data getting hung up waiting for old data from several
seconds ago that has not yet been delivered (as happens with TCP).
 Because unreliable LAN subnet multicast is a great pattern for your
application and UDP supports it perfectly.
15
 Broadcast transmission
Socket Programming with TCP
 when contacted by client, server TCP
Client must contact server
creates new socket for server
 server process must first be process to communicate with that
running particular client
 server must have created  allows server to talk with multiple
socket (door) that welcomes clients
client’s contact
 source port numbers used to
Client contacts server by: distinguish clients (more in Chap
3)
 Creating TCP socket,
specifying IP address, port
Application viewpoint:
number of server process TCP provides reliable, in-order
byte-stream transfer (“pipe”)
 when client creates socket: between client and server
client TCP establishes 16
connection to server TCP
TCP Sockets Mean
 TCP uses port numbers to distinguish different applications running at the same IP address
 In UDP a server can open a UDP port and then receive datagrams from thousands of different clients. But in TCP, the connect() call
becomes the opening step. It is the moment when your operating system’s network stack kicks off the handshake protocol described
in the previous section that, if successful, will make both ends of the TCP stream ready for use.
 TCP actually involves two completely different kinds of sockets: “passive” listening sockets and active “connected” ones.

 The passive socket or listening socket maintains the “socket name”—the address and
port number—at which the server is ready to receive connections. No data can ever be
received or sent by this kind of socket. It does not represent any actual network
conversation. Instead, it is how the server alerts the operating system to its willingness to
receive incoming connections at a given TCP port number in the first place.
 An active, connected socket is bound to one particular remote conversation partner with
a particular IP address and port number. It can be used only for talking back and forth
with that one partner, and it can be read and written to without worrying about how the
resulting data will be split up into packets. The stream looks so much like a pipe or file
that, on Unix systems, a connected TCP socket can be passed to 17another program that
expects to read from a normal file, and that program will never even know that it is
Client/Server socket Interaction: TCP
server (running on hostid) client
create socket,
port=x, for incoming
request:
serverSocket = socket()

wait for incoming create socket,


connection request TCP connect to hostid, port=x
connectionSocket = connection setup clientSocket = socket()
serverSocket.accept()

send request using


read request from clientSocket
connectionSocket

write reply to
connectionSocket read reply from
clientSocket

close
connectionSocket close 18
clientSocket
Example app: TCP client
#include <iostream>
#include <cstring>
#include <arpa/inet.h>
#include <unistd.h>

int main() {
const char* serverName = "servername";
const int serverPort = 12000;

// Create a socket
int clientSocket = socket(AF_INET, SOCK_STREAM, 0);
if (clientSocket < 0) {
perror("Failed to create socket");
return 1;
}

// Server address setup


sockaddr_in serverAddr{};
serverAddr.sin_family = AF_INET;
serverAddr.sin_port = htons(serverPort);

// Convert server name to binary form


if (inet_pton(AF_INET, serverName, &serverAddr.sin_addr) <= 0) {
perror("Invalid address/Address not supported");
close(clientSocket);
return 1;
}

// Connect to the server


if (connect(clientSocket, (sockaddr*)&serverAddr, sizeof(serverAddr)) < 0) {
perror("Connection failed");
close(clientSocket);
return 1;
}

// Get input from the user


std::string sentence;
std::cout << "Input lowercase sentence: ";
std::getline(std::cin, sentence);

// Send the input to the server


if (send(clientSocket, sentence.c_str(), sentence.size(), 0) < 0) {
perror("Send failed");
close(clientSocket);
return 1;
}

// Receive response from the server


char buffer[2048];
int receivedBytes = recv(clientSocket, buffer, sizeof(buffer) - 1, 0);
if (receivedBytes < 0) {
perror("Receive failed");
} else {
buffer[receivedBytes] = '\0'; // Null-terminate the received message
std::cout << "From Server: " << buffer << std::endl;
}
19
// Close the socket
close(clientSocket);
return 0;
}
Example app: TCP server
#include <iostream>

#include <cstring>

#include <arpa/inet.h>

#include <unistd.h>

int main() {

const int serverPort = 12000;

// Create the socket

int serverSocket = socket(AF_INET, SOCK_STREAM, 0);

if (serverSocket < 0) {

perror("Failed to create socket");

return 1;

// Server address setup

sockaddr_in serverAddr{};

serverAddr.sin_family = AF_INET;

serverAddr.sin_port = htons(serverPort);

serverAddr.sin_addr.s_addr = INADDR_ANY; // Listen on all interfaces

// Bind the socket

if (bind(serverSocket, (sockaddr*)&serverAddr, sizeof(serverAddr)) < 0) {

perror("Bind failed");

close(serverSocket);

return 1;

// Listen for incoming connections

if (listen(serverSocket, 1) < 0) {
20
perror("Listen failed");

close(serverSocket);

return 1;
TCP Provide a Reliable Connection:
 Every TCP packet is given a sequence number so that the system on the receiving end
can put them back together in the right order and can also notice missing packets in the
sequence and ask that they be retransmitted.
 Instead of using sequential integers (1, 2, 3...) to sequence packets, TCP uses a counter
that counts the number of bytes transmitted. A 1,024-byte packet with a sequence
number of 7,200, for example, would be followed by a packet with a sequence number of
8,224.
 The initial sequence number, in good TCP implementations, is chosen randomly so that
villains cannot assume that every connection starts at byte zero.
 Rather than running very slowly in lock step by needing every packet to be
acknowledged before it sends the next one, TCP sends whole bursts of packets at a time
before expecting a response. The amount of data that a sender is willing to have on the
wire at any given moment is called the size of the TCP window.
 The TCP implementation on the receiving end can regulate the window size of the
transmitting end and thus slow or pause the connection. This is called
21 flow control.
 If TCP believes that packets are being missed the receiver asked for retransmission.
When to Use TCP
 Hosts needs to send multiple packets.
 Tow hosts need to set up a TCP connection
 SYN: “I want to talk to you; here is the packet sequence number I will be starting
with.”
 SYN-ACK: “Okay, here is the initial sequence number I will be using in my direction.”
 ACK: “Okay!”
 Another three or four packets are necessary to shut the connection down
when it is finished—either a quick FIN, FIN-ACK, and ACK, or else a slightly
longer pair of separate FIN and ACK packets in each direction.
 Flow control and retransmission is important
 Not a good idea to use TCP in Realtime communication like live voice 22

communication or video live streaming.


Creating an TCP socket Object
#include <iostream>
#include <arpa/inet.h>
#include <sys/socket.h>

int main() {
// Create the socket
int sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock < 0) {
perror("Failed to create socket");
return 1;
}

std::cout << "Socket created successfully!" << std::endl;

// You can now use the `sock` variable to bind, listen, connect, etc.

// Close the socket


close(sock);
return 0;
} 23
A Simple TCP Server - Example
#include <iostream>
#include <cstring>
#include <arpa/inet.h>
#include <unistd.h>
#include <sys/socket.h>

const std::string host = "localhost";


const int data_buff = 2048;
const int backlog = 5;

void TCPserver1(int port) {


// Create the socket
int serverSocket = socket(AF_INET, SOCK_STREAM, 0);
if (serverSocket < 0) {
perror("Failed to create socket");
return;
}

// Set socket options


int reuseAddr = 1;
if (setsockopt(serverSocket, SOL_SOCKET, SO_REUSEADDR, &reuseAddr, sizeof(reuseAddr)) < 0) {
perror("Failed to set socket option");
close(serverSocket);
return;
}

// Set up the server address


sockaddr_in serverAddr{};
serverAddr.sin_family = AF_INET;
serverAddr.sin_port = htons(port);
serverAddr.sin_addr.s_addr = inet_addr(host.c_str());

// Bind the socket


if (bind(serverSocket, (sockaddr*)&serverAddr, sizeof(serverAddr)) < 0) {
perror("Bind failed");
close(serverSocket);
return;
}

// Start listening for incoming connections


if (listen(serverSocket, backlog) < 0) {
perror("Listen failed");
close(serverSocket);
return;
}

std::cout << "Starting up echo server on " << host << " port " << port << std::endl;

while (true) {
std::cout << "Waiting to receive message from client" << std::endl;

// Accept a new client connection


sockaddr_in clientAddr{};
socklen_t clientAddrLen = sizeof(clientAddr);
int clientSocket = accept(serverSocket, (sockaddr*)&clientAddr, &clientAddrLen);
if (clientSocket < 0) { 24
perror("Accept failed");
continue;
}

// Receive data from the client


char buffer[data_buff];
A Simple TCP Client - Example
#include <iostream>
#include <cstring>
#include <arpa/inet.h>
#include <unistd.h>
#include <sys/socket.h>

const std::string host = "localhost";

void TCPclient1(int port) {


// Create the socket
int sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock < 0) {
perror("Failed to create socket");
return;
}

// Set up the server address


sockaddr_in serverAddr{};
serverAddr.sin_family = AF_INET;
serverAddr.sin_port = htons(port);
serverAddr.sin_addr.s_addr = inet_addr(host.c_str());

std::cout << "Connecting to " << host << " port " << port << std::endl;

// Connect to the server


if (connect(sock, (sockaddr*)&serverAddr, sizeof(serverAddr)) < 0) {
perror("Connection failed");
close(sock);
return;
}

try {
// Send message to the server
std::string message = "Test message. This will be echoed";
std::cout << "Sending: " << message << std::endl;

ssize_t bytesSent = send(sock, message.c_str(), message.size(), 0);


if (bytesSent < 0) {
perror("Send failed");
close(sock);
return;
}

// Receive the echoed message from the server


char buffer[16];
ssize_t amountReceived = 0;
ssize_t amountExpected = message.size();

while (amountReceived < amountExpected) {


ssize_t bytesReceived = recv(sock, buffer, sizeof(buffer), 0);
if (bytesReceived < 0) {
perror("Receive failed");
close(sock);
return;
}
amountReceived += bytesReceived; 25
std::cout << "Received: " << std::string(buffer, bytesReceived) << std::endl;
}
}
catch (const std::exception& e) {
std::cerr << "Other exception: " << e.what() << std::endl;
}

You might also like

pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy