Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
CPlusPlusNotesForProfessionals.pdf
Скачиваний:
47
Добавлен:
20.05.2023
Размер:
5.11 Mб
Скачать

Chapter 127: Client server examples

Section 127.1: Hello TCP Client

This program is complimentary to Hello TCP Server program, you can run either of them to check the validity of each other. The program flow is quite common with Hello TCP server, so make sure to take a look at that too.

Here's the code -

#include <cstring> #include <iostream> #include <string>

#include <arpa/inet.h> #include <netdb.h> #include <sys/socket.h> #include <sys/types.h> #include <unistd.h>

int main(int argc, char *argv[])

{

// Now we're taking an ipaddress and a port number as arguments to our program

if (argc != 3)

{

std::cerr << "Run program as 'program <ipaddress> <port>'\n";

return -1;

 

}

 

auto &ipAddress

= argv[1];

auto &portNum

= argv[2];

addrinfo hints, *p;

memset(&hints,

0, sizeof(hints));

hints.ai_family

= AF_UNSPEC;

hints.ai_socktype = SOCK_STREAM;

hints.ai_flags

= AI_PASSIVE;

int gAddRes = getaddrinfo(ipAddress, portNum, &hints, &p);

if (gAddRes !=

0) {

std::cerr << gai_strerror(gAddRes) << "\n";

return -2;

 

}

 

if (p == NULL)

{

std::cerr << "No addresses found\n"; return -3;

}

//socket() call creates a new socket and returns it's descriptor int sockFD = socket(p->ai_family, p->ai_socktype, p->ai_protocol); if (sockFD == -1) {

std::cerr << "Error while creating socket\n"; return -4;

}

//Note: there is no bind() call as there was in Hello TCP Server

//why? well you could call it though it's not necessary

//because client doesn't necessarily has to have a fixed port number

//so next call will bind it to a random available port number

GoalKicker.com – C++ Notes for Professionals

611

// connect() call tries to establish a TCP connection to the specified server int connectR = connect(sockFD, p->ai_addr, p->ai_addrlen);

if (connectR == -1) { close(sockFD);

std::cerr << "Error while connecting socket\n"; return -5;

}

std::string reply(15, ' ');

//recv() call tries to get the response from server

//BUT there's a catch here, the response might take multiple calls

//to recv() before it is completely received

//will be demonstrated in another example to keep this minimal auto bytes_recv = recv(sockFD, &reply.front(), reply.size(), 0); if (bytes_recv == -1) {

std::cerr << "Error while receiving bytes\n"; return -6;

}

std::cout << "\nClient recieved: " << reply << std::endl; close(sockFD);

freeaddrinfo(p);

return 0;

}

Section 127.2: Hello TCP Server

Let me start by saying you should first visit Beej's Guide to Network Programming and give it a quick read, which explains most of this stu a bit more verbosely. We'll be creating a simple TCP server here which will say "Hello World" to all incoming connections and then close them. Another thing to note is, the server will be communicating to clients iteratively, which means one client at a time. Make sure to check out relevant man pages as they might contain valuable information about each function call and socket structures.

We'll run the server with a port, so we'll take an argument for port number as well. Let's get started with code -

#include <cstring>

// sizeof()

#include <iostream>

 

#include <string>

 

// headers for socket(), getaddrinfo() and friends #include <arpa/inet.h>

#include <netdb.h> #include <sys/socket.h> #include <sys/types.h>

#include <unistd.h> // close()

int main(int argc, char *argv[])

{

// Let's check if port number is supplied or not.. if (argc != 2) {

std::cerr << "Run program as 'program <port>'\n"; return -1;

}

auto &portNum = argv[1];

const unsigned int backLog = 8; // number of connections allowed on the incoming queue

GoalKicker.com – C++ Notes for Professionals

612

addrinfo hints, *res, *p; // we need 2 pointers, res to hold and p to iterate over memset(&hints, 0, sizeof(hints));

// for more explanation, man socket

hints.ai_family

= AF_UNSPEC;

// don't specify which IP version to use yet

hints.ai_socktype

= SOCK_STREAM;

// SOCK_STREAM refers to TCP, SOCK_DGRAM will be?

hints.ai_flags

= AI_PASSIVE;

 

// man getaddrinfo

int gAddRes = getaddrinfo(NULL, portNum, &hints, &res); if (gAddRes != 0) {

std::cerr << gai_strerror(gAddRes) << "\n"; return -2;

}

std::cout << "Detecting addresses" << std::endl;

unsigned int numOfAddr = 0;

char ipStr[INET6_ADDRSTRLEN]; // ipv6 length makes sure both ipv4/6 addresses can be stored in this variable

//Now since getaddrinfo() has given us a list of addresses

//we're going to iterate over them and ask user to choose one

//address for program to bind to

for (p = res; p != NULL; p = p->ai_next) { void *addr;

std::string ipVer;

// if address is ipv4 address if (p->ai_family == AF_INET) {

ipVer

= "IPv4";

sockaddr_in *ipv4 = reinterpret_cast<sockaddr_in *>(p->ai_addr);

addr

= &(ipv4->sin_addr);

++numOfAddr;

 

}

 

// if address is ipv6 address

else {

 

ipVer

= "IPv6";

sockaddr_in6 *ipv6 = reinterpret_cast<sockaddr_in6 *>(p->ai_addr);

addr

= &(ipv6->sin6_addr);

++numOfAddr;

 

}

 

// convert IPv4 and IPv6 addresses from binary to text form inet_ntop(p->ai_family, addr, ipStr, sizeof(ipStr));

std::cout << "(" << numOfAddr << ") " << ipVer << " : " << ipStr << std::endl;

}

//if no addresses found :( if (!numOfAddr) {

std::cerr << "Found no host address to use\n"; return -3;

}

//ask user to choose an address

std::cout <<

"Enter

the number of host address to bind with: ";

unsigned int

choice

=

0;

bool madeChoice

=

false;

GoalKicker.com – C++ Notes for Professionals

613

do {

std::cin >> choice;

if (choice > (numOfAddr + 1) || choice < 1) { madeChoice = false;

std::cout << "Wrong choice, try again!" << std::endl; } else

madeChoice = true; } while (!madeChoice);

p = res;

//let's create a new socket, socketFD is returned as descriptor

//man socket for more information

//these calls usually return -1 as result of some error

int sockFD = socket(p->ai_family, p->ai_socktype, p->ai_protocol); if (sockFD == -1) {

std::cerr << "Error while creating socket\n"; freeaddrinfo(res);

return -4;

}

// Let's bind address to our socket we've just created int bindR = bind(sockFD, p->ai_addr, p->ai_addrlen); if (bindR == -1) {

std::cerr << "Error while binding socket\n";

// if some error occurs, make sure to close socket and free resources close(sockFD);

freeaddrinfo(res); return -5;

}

// finally start listening for connections on our socket int listenR = listen(sockFD, backLog);

if (listenR == -1) {

std::cerr << "Error while Listening on socket\n";

// if some error occurs, make sure to close socket and free resources close(sockFD);

freeaddrinfo(res); return -6;

}

// structure large enough to hold client's address sockaddr_storage client_addr;

socklen_t client_addr_size = sizeof(client_addr);

const std::string response = "Hello World";

//a fresh infinite loop to communicate with incoming connections

//this will take client connections one at a time

//in further examples, we're going to use fork() call for each client connection while (1) {

//accept call will give us a new socket descriptor

GoalKicker.com – C++ Notes for Professionals

614

int newFD

= accept(sockFD, (sockaddr *) &client_addr, &client_addr_size); if (newFD == -1) {

std::cerr << "Error while Accepting on socket\n"; continue;

}

// send call sends the data you specify as second param and it's length as 3rd param, also returns how many bytes were actually sent

auto bytes_sent = send(newFD, response.data(), response.length(), 0); close(newFD);

}

close(sockFD); freeaddrinfo(res);

return 0;

}

The following program runs as -

Detecting addresses (1) IPv4 : 0.0.0.0 (2) IPv6 : ::

Enter the number of host address to bind with: 1

GoalKicker.com – C++ Notes for Professionals

615