Обмен UDP-сообщениями в C++
Полный цикл UDP в одной программе связывает два рабочих потока.
Поток-приемник сначала вызывает bind на
18081, читает запрос и шлет ответ.
Поток-отправитель ждет сигнал о готовности, передает
датаграмму на 127.0.0.1 и читает ответ
на своем сокете. Оба потока завершают работу через
join, каждый закрывает свой дескриптор.
Синхронизацию порта удобно делать через
std::atomic<bool>: приемник
поднимает флаг после привязки, затем отправитель
работает. Ответ отправитель выведет строкой из
байтов:
#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);
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) {
const char reply[] = "pong";
sendto(
recv_fd,
reply,
std::strlen(reply),
0,
reinterpret_cast<sockaddr*>(&peer),
peer_len
);
}
close(recv_fd);
});
std::thread sender([&]() {
while (!ready.load(std::memory_order_acquire)) {
}
int send_fd = socket(AF_INET, SOCK_DGRAM, 0);
if (send_fd == -1) {
return;
}
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);
return;
}
const char msg[] = "ping";
if (sendto(
send_fd,
msg,
std::strlen(msg),
0,
reinterpret_cast<sockaddr*>(&dest),
sizeof(dest)
) == -1) {
close(send_fd);
return;
}
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();
sender.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);
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) {
const char reply[] = "done";
sendto(
recv_fd,
reply,
std::strlen(reply),
0,
reinterpret_cast<sockaddr*>(&peer),
peer_len
);
}
close(recv_fd);
});
std::thread sender([&]() {
while (!ready.load(std::memory_order_acquire)) {
}
int send_fd = socket(AF_INET, SOCK_DGRAM, 0);
if (send_fd == -1) {
return;
}
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);
return;
}
const char msg[] = "start job";
if (sendto(
send_fd,
msg,
std::strlen(msg),
0,
reinterpret_cast<sockaddr*>(&dest),
sizeof(dest)
) == -1) {
close(send_fd);
return;
}
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();
sender.join();
return 0;
}
Отправьте "time" и выведите ответ
"now" в консоли отправителя.
Обменяйтесь фразами "ask" и
"reply" в одном запуске. На экране
должна появиться только строка ответа.