Ответ
Да, приходилось. В основном для двух целей: отладки сетевых протоколов в разрабатываемом ПО и создания специализированных анализаторов производительности.
Пример: простой сниффер TCP-заголовков с использованием RAW-сокетов в Linux.
#include <iostream>
#include <cstring>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
int main() {
// Создаём RAW-сокет для получения IP-пакетов
int sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_TCP);
if (sockfd < 0) {
perror("socket");
return 1;
}
char buffer[65536]; // Максимальный размер IP-пакета
while (true) {
struct sockaddr_in sender;
socklen_t sender_len = sizeof(sender);
// Читаем пакет
ssize_t packet_size = recvfrom(sockfd, buffer, sizeof(buffer), 0,
(struct sockaddr*)&sender, &sender_len);
if (packet_size < 0) {
perror("recvfrom");
continue;
}
// Парсим IP-заголовок
struct iphdr* ip_header = (struct iphdr*)buffer;
if (ip_header->protocol != IPPROTO_TCP) continue; // Нас интересует только TCP
// Парсим TCP-заголовок (после IP-заголовка)
unsigned int ip_header_len = ip_header->ihl * 4;
struct tcphdr* tcp_header = (struct tcphdr*)(buffer + ip_header_len);
// Выводим информацию о соединении
char src_ip[INET_ADDRSTRLEN], dst_ip[INET_ADDRSTRLEN];
inet_ntop(AF_INET, &(ip_header->saddr), src_ip, INET_ADDRSTRLEN);
inet_ntop(AF_INET, &(ip_header->daddr), dst_ip, INET_ADDRSTRLEN);
std::cout << "TCP Packet: " << src_ip << ":" << ntohs(tcp_header->source)
<< " -> " << dst_ip << ":" << ntohs(tcp_header->dest)
<< " [Flags: ";
if (tcp_header->syn) std::cout << "SYN ";
if (tcp_header->ack) std::cout << "ACK ";
if (tcp_header->fin) std::cout << "FIN ";
std::cout << "]n";
}
close(sockfd);
return 0;
}
Для чего это применялось на практике:
- Отладка своего TCP-сервера: Анализировали последовательность SYN/ACK/FIN пакетов, чтобы найти причину "висящих" соединений.
- Мониторинг трафика приложения: Писали легковесный инструмент для подсчёта количества и размера пакетов, отправляемых конкретным процессом.
- Анализ протоколов: Изучали структуру пакетов сторонних протоколов для написания совместимых клиентов.
Важные нюансы:
- Для работы с RAW-сокетами обычно требуются права суперпользователя (
root). - В продакшене для сложного анализа часто используют готовые библиотеки, такие как libpcap (лежит в основе
tcpdump) или PcapPlusPlus, которые абстрагируют низкоуровневые детали и работают кроссплатформенно.
Ответ 18+ 🔞
А, ну это ж классика, ебать мои старые костыли! Да, конечно, приходилось ковыряться в этих raw-сокетах. Не то чтобы каждый день, но когда обычные инструменты вроде tcpdump или Wireshark — это как вилкой в глаз или в жопу раз, а тебе нужен свой, кастомный костыль, вот тут и лезешь в это болото.
Пример: простой сниффер TCP-заголовков с использованием RAW-сокетов в Linux.
#include <iostream>
#include <cstring>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
int main() {
// Создаём RAW-сокет для получения IP-пакетов
int sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_TCP);
if (sockfd < 0) {
perror("socket");
return 1;
}
char buffer[65536]; // Максимальный размер IP-пакета
while (true) {
struct sockaddr_in sender;
socklen_t sender_len = sizeof(sender);
// Читаем пакет
ssize_t packet_size = recvfrom(sockfd, buffer, sizeof(buffer), 0,
(struct sockaddr*)&sender, &sender_len);
if (packet_size < 0) {
perror("recvfrom");
continue;
}
// Парсим IP-заголовок
struct iphdr* ip_header = (struct iphdr*)buffer;
if (ip_header->protocol != IPPROTO_TCP) continue; // Нас интересует только TCP
// Парсим TCP-заголовок (после IP-заголовка)
unsigned int ip_header_len = ip_header->ihl * 4;
struct tcphdr* tcp_header = (struct tcphdr*)(buffer + ip_header_len);
// Выводим информацию о соединении
char src_ip[INET_ADDRSTRLEN], dst_ip[INET_ADDRSTRLEN];
inet_ntop(AF_INET, &(ip_header->saddr), src_ip, INET_ADDRSTRLEN);
inet_ntop(AF_INET, &(ip_header->daddr), dst_ip, INET_ADDRSTRLEN);
std::cout << "TCP Packet: " << src_ip << ":" << ntohs(tcp_header->source)
<< " -> " << dst_ip << ":" << ntohs(tcp_header->dest)
<< " [Flags: ";
if (tcp_header->syn) std::cout << "SYN ";
if (tcp_header->ack) std::cout << "ACK ";
if (tcp_header->fin) std::cout << "FIN ";
std::cout << "]n";
}
close(sockfd);
return 0;
}
Для чего это применялось на практике:
- Отладка своего TCP-сервера: Бывало такое, что соединения висли, как будто на дворе 2002-й год. Анализировали, как летят эти SYN/ACK/FIN пакеты, чтобы понять, кто кого послал нахуй и где потерялся последний FIN. Иногда находили такие косяки, что сам от себя охуевал.
- Мониторинг трафика приложения: Писали легковесный инструмент, который считал, сколько пакетов и какого размера хавает конкретный процесс. Когда продакшн-админы смотрят на тебя, как на пидараса шерстяного, и требуют цифры, а не "ой, вроде много" — тут без своего сниффера никуда.
- Анализ протоколов: Бывало, нужно было понять, как работает какая-нибудь хитрая жопа — сторонний протокол. Чтоб написать к нему совместимого клиента. Распарсишь его пакеты, поймёшь структуру — и вперёд.
Важные нюансы, ёпта:
- Чтобы запустить эту красоту, тебе обычно нужны права root. Без них система тебе скажет что-то вроде "пошёл ты на хуй, распиздяй", и всё. Доверия ебать ноль у ядра к таким запросам от простых смертных.
- В нормальном, серьёзном продакшене для сложного анализа обычно не изобретают велосипед, а берут готовые, отполированные библиотеки. Например, libpcap (она же основа
tcpdump) или PcapPlusPlus. Они всю эту низкоуровневую муть прячут под капот, да ещё и на разных системах работать заставляют. Писать всё с нуля — это терпения ноль ебать, честно говоря. Только если очень хочется пострадать или понять, как оно устроено внутри.