Несколько UDP-пакетов в C++
Серия датаграмм на одном сокете обрабатывается в цикле с конечным счетчиком.
После каждого recvfrom тот же буфер и адрес
отправителя используют снова. На отправке цикл
вызывает sendto для каждой строки - порядок
на проводе обычно совпадает с порядком вызовов на
localhost. Приемник в потоке читает две датаграммы
и печатает каждую, отправитель шлет их на
127.0.0.1:
#include <arpa/inet.h>
#include <atomic>
#include <cstring>
#include <iostream>
#include <netinet/in.h>
#include <string>
#include <sys/socket.h>
#include <thread>
#include <unistd.h>
int main() {
std::atomic<bool> ready{false};
std::thread receiver([&]() {
int recv_fd = socket(AF_INET, SOCK_DGRAM, 0);
if (recv_fd == -1) {
return;
}
sockaddr_in local{};
local.sin_family = AF_INET;
local.sin_port = htons(18081);
if (inet_pton(AF_INET, "127.0.0.1", &local.sin_addr) != 1
|| bind(
recv_fd,
reinterpret_cast<sockaddr*>(&local),
sizeof(local)
) == -1) {
close(recv_fd);
return;
}
ready.store(true, std::memory_order_release);
for (int i = 0; i < 2; ++i) {
char buf[256];
sockaddr_in peer{};
socklen_t peer_len = sizeof(peer);
ssize_t n = recvfrom(
recv_fd,
buf,
sizeof(buf),
0,
reinterpret_cast<sockaddr*>(&peer),
&peer_len
);
if (n > 0) {
std::string text(buf, static_cast<size_t>(n));
std::cout << text << "\n";
}
}
close(recv_fd);
});
while (!ready.load(std::memory_order_acquire)) {
}
int send_fd = socket(AF_INET, SOCK_DGRAM, 0);
if (send_fd == -1) {
receiver.join();
return 1;
}
sockaddr_in dest{};
dest.sin_family = AF_INET;
dest.sin_port = htons(18081);
if (inet_pton(AF_INET, "127.0.0.1", &dest.sin_addr) != 1) {
close(send_fd);
receiver.join();
return 1;
}
const char* words[] = {"one", "two"};
for (const char* word : words) {
if (sendto(
send_fd,
word,
std::strlen(word),
0,
reinterpret_cast<sockaddr*>(&dest),
sizeof(dest)
) == -1) {
close(send_fd);
receiver.join();
return 1;
}
}
close(send_fd);
receiver.join();
return 0;
}
Ответ на каждый пакет тоже можно слать в цикле на адрес из соответствующего приема. Отправитель тогда читает несколько ответов подряд:
#include <arpa/inet.h>
#include <atomic>
#include <cstring>
#include <iostream>
#include <netinet/in.h>
#include <string>
#include <sys/socket.h>
#include <thread>
#include <unistd.h>
int main() {
std::atomic<bool> ready{false};
std::thread receiver([&]() {
int recv_fd = socket(AF_INET, SOCK_DGRAM, 0);
if (recv_fd == -1) {
return;
}
sockaddr_in local{};
local.sin_family = AF_INET;
local.sin_port = htons(18081);
if (inet_pton(AF_INET, "127.0.0.1", &local.sin_addr) != 1
|| bind(
recv_fd,
reinterpret_cast<sockaddr*>(&local),
sizeof(local)
) == -1) {
close(recv_fd);
return;
}
ready.store(true, std::memory_order_release);
for (int i = 0; i < 2; ++i) {
char buf[256];
sockaddr_in peer{};
socklen_t peer_len = sizeof(peer);
ssize_t n = recvfrom(
recv_fd,
buf,
sizeof(buf),
0,
reinterpret_cast<sockaddr*>(&peer),
&peer_len
);
if (n > 0) {
std::string tag(buf, static_cast<size_t>(n));
std::string reply = "echo " + tag;
sendto(
recv_fd,
reply.c_str(),
reply.size(),
0,
reinterpret_cast<sockaddr*>(&peer),
peer_len
);
}
}
close(recv_fd);
});
while (!ready.load(std::memory_order_acquire)) {
}
int send_fd = socket(AF_INET, SOCK_DGRAM, 0);
if (send_fd == -1) {
receiver.join();
return 1;
}
sockaddr_in dest{};
dest.sin_family = AF_INET;
dest.sin_port = htons(18081);
if (inet_pton(AF_INET, "127.0.0.1", &dest.sin_addr) != 1) {
close(send_fd);
receiver.join();
return 1;
}
const char* words[] = {"a", "b"};
for (const char* word : words) {
if (sendto(
send_fd,
word,
std::strlen(word),
0,
reinterpret_cast<sockaddr*>(&dest),
sizeof(dest)
) == -1) {
close(send_fd);
receiver.join();
return 1;
}
}
for (int i = 0; i < 2; ++i) {
char ans_buf[256];
sockaddr_in from{};
socklen_t from_len = sizeof(from);
ssize_t ans_n = recvfrom(
send_fd,
ans_buf,
sizeof(ans_buf),
0,
reinterpret_cast<sockaddr*>(&from),
&from_len
);
if (ans_n > 0) {
std::string answer(ans_buf, static_cast<size_t>(ans_n));
std::cout << answer << "\n";
}
}
close(send_fd);
receiver.join();
return 0;
}
Отправьте пять пакетов с текстами "n1" …
"n5". Приемник выведет пять строк по
одной на пакет.
Примите три запроса и на каждый ответьте
"ok". Отправитель выведет три строки
ответа.