C++
Przykłady serwerów klienta
Szukaj…
Witaj TCP Server
Zacznę od stwierdzenia, że najpierw powinieneś odwiedzić Przewodnik Beej po programowaniu sieciowym i szybko go przeczytać, który wyjaśnia większość tych rzeczy bardziej dosłownie. Stworzymy tutaj prosty serwer TCP, który powie „Hello World” dla wszystkich połączeń przychodzących, a następnie je zamknie. Inną rzeczą wartą uwagi jest to, że serwer będzie komunikował się z klientami iteracyjnie, co oznacza jednego klienta na raz. Koniecznie sprawdź odpowiednie strony podręcznika, ponieważ mogą one zawierać cenne informacje o każdym wywołaniu funkcji i strukturze gniazda.
Uruchomimy serwer z portem, więc weźmiemy również argument za numerem portu. Zacznijmy od kodu -
#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
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;
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
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;
}
Następujący program działa jako -
Detecting addresses
(1) IPv4 : 0.0.0.0
(2) IPv6 : ::
Enter the number of host address to bind with: 1
Witaj kliencie TCP
Program ten uzupełnia program Hello TCP Server, możesz uruchomić dowolny z nich, aby sprawdzić wzajemną ważność. Przebieg programu jest dość powszechny w przypadku serwera Hello TCP, więc koniecznie spójrz na to.
Oto kod -
#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
// 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;
}