你好 TCP 伺服器
首先讓我說你應該首先訪問 Beej 的網路程式設計指南並快速閱讀,這大部分內容都會更加詳細地解釋。我們將在這裡建立一個簡單的 TCP 伺服器,它將對所有傳入連線說 Hello World
,然後關閉它們。另外需要注意的是,伺服器將迭代地與客戶端通訊,這意味著一次只能有一個客戶端。請務必檢視相關的手冊頁,因為它們可能包含有關每個函式呼叫和套接字結構的有價值資訊。
我們將使用埠執行伺服器,因此我們也將獲取埠號的引數。讓我們開始使用程式碼 -
#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;
}
以下程式執行為 -
Detecting addresses
(1) IPv4 : 0.0.0.0
(2) IPv6 : ::
Enter the number of host address to bind with: 1