,使用原始套接字需要具有root权限或者特殊的权限(混杂模式要打开),因为它可以绕过操作系统的网络栈。此外,原始套接字通信在不同的操作系统上可能会有一些差异,上述代码在Linux上编译运行正常。
使用原始套接字进行客户端和服务端通信是比较复杂的,因为它需要手动构建和解析网络数据包。以下是一个简单的示例,展示了如何使用原始套接字进行客户端和服务端通信。
服务端代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#define PACKET_SIZE 4096
// 计算校验和
unsigned short checksum(unsigned short *buf, int size) {
unsigned long sum = 0;
while (size > 1) {
sum += *buf++;
size -= 2;
}
if (size == 1) {
sum += *(unsigned char *)buf;
}
sum = (sum >> 16) + (sum & 0xffff);
sum += (sum >> 16);
return (unsigned short)(~sum);
}
int main() {
int sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
if (sockfd < 0) {
perror("socket");
exit(1);
}
char packet[PACKET_SIZE];
struct sockaddr_in client;
socklen_t client_len = sizeof(client);
while (1) {
memset(packet, 0, PACKET_SIZE);
if (recvfrom(sockfd, packet, PACKET_SIZE, 0, (struct sockaddr *)&client, &client_len) < 0) {
perror("recvfrom");
exit(1);
}
struct iphdr *ip = (struct iphdr *)packet;
struct icmphdr *icmp = (struct icmphdr *)(packet + sizeof(struct iphdr));
if (icmp->type == ICMP_ECHO) {
printf("Received ICMP Echo Request from %s\n", inet_ntoa(client.sin_addr));
// 构造ICMP Echo Reply
icmp->type = ICMP_ECHOREPLY;
icmp->checksum = 0;
icmp->checksum = checksum((unsigned short *)icmp, sizeof(struct icmphdr));
// 修改源和目的IP地址
ip->saddr = client.sin_addr.s_addr;
ip->daddr = inet_addr("192.168.1.100");
// 发送数据包
if (sendto(sockfd, packet, sizeof(struct iphdr) + sizeof(struct icmphdr), 0, (struct sockaddr *)&client, client_len) < 0) {
perror("sendto");
exit(1);
}
printf("Sent ICMP Echo Reply to %s\n", inet_ntoa(client.sin_addr));
}
}
close(sockfd);
return 0;
}
客户端代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#define PACKET_SIZE 4096
// 计算校验和
unsigned short checksum(unsigned short *buf, int size) {
unsigned long sum = 0;
while (size > 1) {
sum += *buf++;
size -= 2;
}
if (size == 1) {
sum += *(unsigned char *)buf;
}
sum = (sum >> 16) + (sum & 0xffff);
sum += (sum >> 16);
return (unsigned short)(~sum);
}
int main() {
int sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
if (sockfd < 0) {
perror("socket");
exit(1);
}
char packet[PACKET_SIZE];
struct sockaddr_in dest;
dest.sin_family = AF_INET;
dest.sin_addr.s_addr = inet_addr("192.168.1.100"); // 服务端IP地址
struct iphdr *ip = (struct iphdr *)packet;
struct icmphdr *icmp = (struct icmphdr *)(packet + sizeof(struct iphdr));
// 构造IP头部
ip->ihl = 5;
ip->version = 4;
ip->ttl = 64;
ip->protocol = IPPROTO_ICMP;
ip->saddr = inet_addr("192.168.1.200"); // 源IP地址
ip->daddr = dest.sin_addr.s_addr;
// 构造ICMP头部
icmp->type = ICMP_ECHO;
icmp->code = 0;
icmp->un.echo.id = getpid();
icmp->un.echo.sequence = 1;
icmp->checksum = 0;
icmp->checksum = checksum((unsigned short *)icmp, sizeof(struct icmphdr));
// 发送数据包
if (sendto(sockfd, packet, sizeof(struct iphdr) + sizeof(struct icmphdr), 0, (struct sockaddr *)&dest, sizeof(dest)) < 0) {
perror("sendto");
exit(1);
}
// 接收数据包
while (1) {
char buffer[PACKET_SIZE];
struct sockaddr_in src;
socklen_t src_len = sizeof(src);
if (recvfrom(sockfd, buffer, PACKET_SIZE, 0, (struct sockaddr *)&src, &src_len) < 0) {
perror("recvfrom");
exit(1);
}
struct iphdr *recv_ip = (struct iphdr *)buffer;
struct icmphdr *recv_icmp = (struct icmphdr *)(buffer + sizeof(struct iphdr));
if (recv_icmp->type == ICMP_ECHOREPLY) {
printf("Received ICMP Echo Reply from %s\n", inet_ntoa(src.sin_addr));
break;
}
}
close(sockfd);
return 0;
}