原始套接字-SOCK_RAW

Linux小知识--原始套接字(raw socket)之模拟ping 利用原始套接字模拟ping 阅读详情
AI摘要

本文介绍了原始套接字(SOCK_RAW)在Windows下的使用,包括创建、设置选项、绑定、连接、发送和接收数据的过程。原始套接字允许程序员直接操作IP首部和数据,实现自定义协议或访问如ICMP和IGMP这样的低级协议。然而,使用原始套接字需要管理员权限,并受到一些系统限制,如禁止发送TCP数据等。通过示例代码展示了如何实现简单的Ping程序,利用ICMP发送和接收Echo请求及应答。

原始套接字
简介

套接口最常用的两种类型:SOCK_STREAM和SOCK_DGRAM。

  • SOCK_STREAM: 流式套接口,传输的是字节流,每次传输的数据没有边界,它是面向连接的,底层使用TCP协议。
  • SOCK_DGRAM: 数据报套接口,无连接,使用UDP协议

传送的数据格式是预先定义好的

通过原始套接字,可以了解底层协议的实现细节,自己构造协议首部和数据,发送并接受

WinSock提供了另一种类型的套接口SOCK_RAW,也被称为“原始套接口”。

当用选项IP_HDRINCL调用setsockopt时,用户可以自己构造IPv4首部。

创建套接口时设置一个系统没有处理的协议,可以在应用层实现自己的传输协议

通常网络系统只处理ICMP(1)、IGMP(2)、TCP(6)和UDP(17),使用原始套接口可以发送和接收ICMP和IGMP分组,系统在处理完后,会把数据报复制给原始套接口一份。

API
创建套接字

原始套接口也用socket函数来创建,第二个参数为SOCK_RAW,第三个参数protocol由用户设置,可以使用WinSock2.h中定义的前缀为IPPROTO_XXX的常值。另外,用户也可以选择一个头文件中没有定义的数值,直接在socket函数中传入数值即可。

设置选项

大部分选项的设置与TCP和UDP套接口是一样的

有一个选项IP_HDRINCL,只适用于原始套接口,用于控制是否由用户自己构造IP首部。如果设置为非0值,用户自己构造IP首部及后面的数据;设置为0值时,这是默认情况,用户只需要构造IP首部之后的数据部分,IP首部由系统填写。

绑定套接口

在原始套接口上调用bind函数,只是设置本地地址,系统不关心端口值,因为原始套接口没有端口的概念。

通常原始套接口不调用bind,而是在发送数据时由系统自己选择外出接口。

当bind中的地址不属于本机任何网络接口时,函数会失败。

连接套接口

函数connect设置原始套接口的目的地址,也不关心端口值,并把套接口标识为已连接,这里的已连接只表示设置了目的地址。

本地地址不受connect的影响,没有调用bind时,仍然是未设定的。

发送数据

发送数据通常都用sendto,在参数to中指定要发送的目的地址,如果已经调用了connect,也可以用send发送数据。

接收数据

接收数据通常用recvfrom,如果不关心对方的地址,也可以用recv。

调用recvfrom之前必须调用过bind、connect或者sendto中的一个函数,将本地或目的地址信息告诉系统,如果直接调用recvfrom,WinSock返回失败。

关闭

关闭原始套接口与关闭其他类型的套接口是一样的,调用closesocket函数即可。

输出处理
  1. 原始套接口发送数据通常用sendto,在第五个参数to中指定要发送的目的地址。

  2. 如果调用过connect,目的地址已经设定,发送数据时,可以直接调用send,当然也可以用sendto,并且第五个参数to设置为NULL。

    当to不为NULL时,系统会把数据发送到to所指定的目的地址,但调用connect时保存在套接口中的目的地址不会改变。

    这样产生的问题是如果to中与connect的地址不同,由于这个套接口的目的地址是由connect指定的,故从to中地址输入的数据报就不会在这个套接口上接收到。

  3. 发送的目的地址可以是任何有效的IP地址,包括广播或多播地址。为了向广播地址发送数据,程序必须用选项SO_BROADCAST调用setsockopt设置套接口才能够广播,否则send或sendto将失败,错误码为WSAEACCES。

    使用原始套接口的应用程序不需要加入一个多播组,就可以向该组播地址发送数据。

  4. 原始套接口发送的数据长度,包括IP首部在内不能大于IP协议允许的最大值65535。

  5. 如果设置了IP_HDRINCL选项,用户需要自己构造IP首部及其后面的数据,提供给系统的数据长度也包括IP首部在内。

    如果IP首部后面的数据需要校验和,则必须自己计算。

    对IP首部,校验和由系统计算,标识符(Identification)可以设置为0,系统会设置标识符字段。

    Windows中填写IP首部时,各个字段的值都要使用网络字节序。

  6. 未设置IP_HDRINCL选项时,默认值,发送数据时传给系统的缓冲区是IP首部之后的数据,系统会在用户数据前增加一个IP首部,并填写IP首部中的各个字段。

    其中协议字段设置为调用socket函数时的第三个参数,源地址是bind的本地地址,没有调用bind时,系统根据外出接口自动设置。

    目的地址是connect或sendto中指定的地址。

  7. 输出数据的长度超过外出网络接口的最大传输单元MTU时,IP协议对数据分片。

在这里插入图片描述

输入处理
  1. 原始套接口接收数据通常用recvfrom,第五个参数from可以返回对方的地址,如果应用程序不关心对方的地址,也可以用recv接收数据。

  2. 对于IPv4,应用程序接收到的是整个IP数据报,包含IP首部,即总是指向IP首部的第一个字节,不管是否设置IP_HDRINCL选项,IP首部中的所有字段都是网络字节序。

  3. 如果数据报是分片的,IP协议在收到所有分片后进行重组,并把组装完整的数据报交给原始套接口。

    在TCP/IP协议栈接收一个完整的数据报后,它检查所有的套接口,找到与数据报中信息匹配的套接口,并把该数据报复制一份,复制到匹配的套接口中。

  4. ·调用socket创建套接口时,当第三个参数protocol不为0时,则接收数据报IP首部中的协议字段必须与该值相等,不相等时,该数据报不会传送给这个套接口。如果参数protocol为0,表示套接口不关心协议字段是否匹配,只要其他条件满足,就接收该数据报。

  5. 当应用程序调用bind函数绑定了一个本地地址时,输入数据报的目的IP地址必须与绑定的地址相等,否则数据报不会交给这个套接口。如果没有指定本地地址,将不检查数据的目的地址。

  6. 如果调用connect函数规定了对方IP地址,收到数据报的源IP地址要与该地址相等,不相等时,数据报不会交给这个套接口;如果没有指定对方IP地址,协议栈不检查接收数据报的源地址。

因为满足上面条件的原始套接口会收到一份复制的数据报,所以使用原始套接口的应用程序可能会收到很多无关的数据报。

例: Ping程序会创建一个协议类型为ICMP的原始套接口,到达本机的其他ICMP分组,如目的不可达、重定向、时间戳也会交给应用程序,应用程序必须自己提供机制来识别它要处理的数据报,抛弃与它无关的数据报。Ping程序可以通过检查ICMP首部中的标识符来识别它要处理的数据报。

在这里插入图片描述

原始套接口的限制

在Windows上使用原始套接口时要求具有管理员权限,如果用户不属于管理员组成员,运行原始套接口程序时,函数调用会失败,错误码为WSAEACCES。

在Windows7、Windows Vista、Windows XP带有Service Pack 2或3上,使用原始套接口时有下面两个限制:

  1. 不能发送TCP数据,创建协议类型为IPPROTO_TCP的原始套接口,调用bind或sendto函数时会失败,错误码为WSAEINVAL;

  2. 协议类型为IPPROTO_UDP的原始套接口,输出UDP数据报的源IP地址必须是本机网络接口的地址,如果源IP地址不是本机的,调用sendto时会失败。

    在Windows Server 2008 R2、Windows Server 2008、Windows Server 2003或Windows XP(SP2)的早期版本没有上面的限制。

  3. 接收到TCP分组不会交给任何原始套接口,TCP分组由系统的协议栈处理。进程想要接收包含TCP首部的IP数据报,必须在数据链路层上读取。

  4. 对于收到的UDP分组, 如果有在数据报的目的端口侦听的UDP套接口,该分组交给UDP套接口处理,不会再交给原始套接口;如果没有在数据报的目的端口侦听的UDP套接口,系统再查找是否有协议类型为UDP的原始套接口,查到则把该分组放到原始套接口的接收缓冲区中。

  5. 接收到ICMP分组的IP数据报时,Windows在协议栈中帮助处理Echo请求(8)、时间戳请求(13)、地址掩码请求(17),不会把这三种类型的ICMP数据报交给原始套接口,其他的ICMP分组都将交给对应的原始套接口处理。

  6. 协议栈对无法识别协议字段的IP数据报,都传递给对应的原始套接口。协议栈会对IP数据报做些基本的检查,包括IP版本、长度、校验和、选项及目的地址。

  7. 当协议栈处理完IGMP后,把所有IGMP分组交给原始套接口。如果原始套接口要接收IGMP分组的话,需要创建协议类型为IPPROTO_IGMP类型的原始套接口,调用bind绑定本地地址,然后用选项IP_ADD_MEMBERSHIP把本机加入一个多播组中,才能接收到IGMP分组。发送时不用bind和加入多播组,直接构造IGMP分组发送即可。

  8. windows有很多安全限制,使用原始套接口比较麻烦,默认配置,程序接收不到ICMP分组,调用recvfrom函数总是返回WSAETIMEDOUT,需要做如下的配置才能够接收到ICMP分组:

    • 管理员身份, 否则使用原始套接口会失败。

    • 关闭UAC, User Account Control(UAC)用户账户控制, 默认是打开的,只允许写到注册表中经过认证的应用程序。

      控制面板→ 用户账户 → 更改用户账户控制设置

    • 更改/关闭防火墙, 默认不允许输入的ICMPv4分组

Demo–Ping程序

ping用来检查网络的连通性、另一台主机是否可达、测量两台主机的延迟等。

原理:

  • 向目标主机发送一个ICMP类型的IP数据报,IP负载是ICMP的Echo请求,目标主机收到分组后,把ICMP的类型修改为Echo应答,并把同样的数据报返回给发送主机
  • Ping过程中,记录了包丢失的个数,往返延迟,总结了发送和接收分组的个数、丢失情况、最小和最大及平均往返延迟。

Ping向目标主机发送一个ICMP的Echo请求,Echo请求中的数据必须在应答中返回。

Echo中的标识符和序列号可以帮助发送者识别相匹配的应答消息,如:可以把标识符设置为发送进程的ID,每次发送Echo请求都把序列号加1。另外,为了计算往返时间,通常在可选数据中保存发送Echo请求的时间戳。

ICMP规定:接收者在应答中把标识符、序列号及可选数据返回给发送者。
在这里插入图片描述

Ping的负面影响

  • 暴露目标主机信息,可以确定目标主机的存在,解析IP首部字段,初步判断目标主机使用的操作系统。

  • Ping死亡攻击(Ping of Death),默认情况,Ping发送的数据大小是32字节,包含IP和ICMP首部时是60字节。

    当Ping数据报大于IPv4最大允许长度65535时,许多系统都不能处理。Windows为了解决这一漏洞,对Ping数据报的大小做了限制,最多允许发送65500字节的数据,超过这个限制时会失败。

  • 拒绝服务攻击。持续地向同一台主机发送大量的Ping数据报,制造ICMP风暴,抢占了大量网络带宽

#include <stdio.h>
#include <winsock2.h>
#pragma comment(lib, "ws2_32.lib")   /* WinSock使用的库函数 */
/* ICMP类型 */
#define ICMP_TYPE_ECHO                8
#define ICMP_TYPE_ECHO_REPLY          0
#define ICMP_MIN_LEN                  8      /* ICMP最小长度, 只有首部 */
#define ICMP_DEF_COUNT       4            /* 默认数据次数 */
#define ICMP_DEF_SIZE        32           /* 默认数据长度 */
#define ICMP_DEF_TIMEOUT     1000         /* 默认超时时间, 毫秒 */
#define ICMP_MAX_SIZE        65500        /* 最大数据长度 */
/* IP首部 -- RFC 791 */
struct ip_hdr
{
    unsigned char vers_len;                /* 版本和首部长度 */
    unsigned char tos;                     /* 服务类型 */
    unsigned short total_len;              /* 数据报的总长度 */
    unsigned short id;                     /* 标识符 */
    unsigned short frag;                   /* 标志和片偏移 */
    unsigned char ttl;                     /* 生存时间 */
    unsigned char proto;                   /* 协议 */
    unsigned short checksum;               /* 校验和 */
    unsigned int sour;                     /* 源IP地址 */
    unsigned int dest;                     /* 目的IP地址 */
};
/* ICMP首部 -- RFC 792 */
struct icmp_hdr
{
    unsigned char type;                    /* 类型 */
    unsigned char code;                    /* 代码 */
    unsigned short checksum;               /* 校验和 */
    unsigned short id;                     /* 标识符 */
    unsigned short seq;                    /* 序列号 */
    /* 这之后的不是标准ICMP首部, 用于记录时间 */
    unsigned long timestamp;
};

struct icmp_user_opt
{
    unsigned int  persist;                 /* 一直Ping            */
    unsigned int  count;                   /* 发送Echo请求的数量 */
    unsigned int  size;                    /* 发送数据的大小       */
    unsigned int  timeout;                 /* 等待答复的超时时间   */
    char          *host;                   /* 主机地址     */
    unsigned int  send;                    /* 发送数量     */
    unsigned int  recv;                    /* 接收数量     */
    unsigned int  min_t;                   /* 最短时间     */
    unsigned int  max_t;                   /* 最长时间     */
    unsigned int  total_t;                 /* 总的累计时间 */
};
/* 随机数据 */
const char icmp_rand_data[] = "abcdefghigklmnopqrstuvwxyz0123456789"
                                   "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
struct icmp_user_opt user_opt_g = {
    0, ICMP_DEF_COUNT, ICMP_DEF_SIZE, ICMP_DEF_TIMEOUT, NULL,
    0, 0, 0xFFFF,0, 0
};

/*
* 计算校验和
 http://t.zoukankan.com/Evil-Rebe-p-5043765.html
*/
unsigned short ip_checksum(unsigned short *buf, int buf_len){
	unsigned long cksum = 0;
    while(buf_len>1)
    {
        cksum += *buf++;
        buf_len -= sizeof(unsigned short);
    }
    if(buf_len)
    {
        cksum += *(unsigned char*)buf;
    }
    cksum = (cksum>>16) + (cksum&0xffff); 
    cksum += (cksum>>16); 
    return (unsigned short)(~cksum);
}

/**
* 构造ICMP数据
* @param icmp_data  数据缓冲区
* @param data_size  缓冲区长度
* @param sequence   ICMP序列号 
*/
void icmp_make_data(char *icmp_data, int data_size, int sequence)
{
    struct icmp_hdr *icmp_hdr;
    char *data_buf;
    int data_len;
    int fill_count = sizeof(icmp_rand_data) / sizeof(icmp_rand_data[0]);
    /* 填写ICMP数据 */
    data_buf = icmp_data + sizeof(struct icmp_hdr);
    data_len = data_size - sizeof(struct icmp_hdr);
    while (data_len > fill_count)
    {
        memcpy(data_buf, icmp_rand_data, fill_count);
        data_len -= fill_count;
    }
    if (data_len > 0)
        memcpy(data_buf, icmp_rand_data, data_len);
    /* 填写ICMP首部 */
    icmp_hdr = (struct icmp_hdr *)icmp_data;
    icmp_hdr->type = ICMP_TYPE_ECHO;
    icmp_hdr->code = 0;
    icmp_hdr->id = (unsigned short)GetCurrentProcessId();
    icmp_hdr->checksum = 0;
    icmp_hdr->seq = sequence;
    icmp_hdr->timestamp = GetTickCount();
    icmp_hdr->checksum = ip_checksum((unsigned short*)icmp_data, data_size);
}

/**
* 解析接收到的ICMP响应
* @param buf 接收到的缓冲区
* @param buf_len  数据的长度
* @param from 对方的ip 
*/ 
int icmp_parse_reply(char *buf, int buf_len,struct sockaddr_in *from)
{
	/*
	hdr_len : ip首部长度
	icmp_len: icmp头部+ 数据的长度
	buf_len: icmp_数据的长度 ( 最后会赋为这个值 ) 
	*/
    struct ip_hdr *ip_hdr;
    struct icmp_hdr *icmp_hdr;
    unsigned short hdr_len;
    int icmp_len;
    unsigned long trip_t;
    ip_hdr = (struct ip_hdr *)buf;
    hdr_len = (ip_hdr->vers_len & 0xf) << 2 ; /* IP首部长度 */
    if (buf_len < hdr_len + ICMP_MIN_LEN)
    {
        printf("[Ping] Too few bytes from %s\n", inet_ntoa(from->sin_addr));
        return -1;
    }
    icmp_hdr = (struct icmp_hdr *)(buf + hdr_len);
    icmp_len = ntohs(ip_hdr->total_len) - hdr_len;
    /* 检查校验和 */
    if (ip_checksum((unsigned short *)icmp_hdr, icmp_len))
    {
        printf("[Ping] icmp checksum error!\n");
        return -1;
    }
    /* 检查ICMP类型 */
    if (icmp_hdr->type != ICMP_TYPE_ECHO_REPLY)
    {
        printf("[Ping] not echo reply : %d\n", icmp_hdr->type);
        return -1;
    }
    /* 检查ICMP的ID */
    if (icmp_hdr->id != (unsigned short)GetCurrentProcessId())
    {
        printf("[Ping] someone else's message!\n");
        return -1;
    }
    /* 输出响应信息 */
    trip_t = GetTickCount() - icmp_hdr->timestamp;
    buf_len = ntohs(ip_hdr->total_len) - hdr_len - ICMP_MIN_LEN;
    printf("%d bytes from %s:", buf_len, inet_ntoa(from->sin_addr));
    printf(" icmp_seq = %d  time: %d ms\n",icmp_hdr->seq, trip_t);
    user_opt_g.recv++;
    user_opt_g.total_t += trip_t;
    /* 记录返回时间 */
    if (user_opt_g.min_t > trip_t)
         user_opt_g.min_t = trip_t;
    if (user_opt_g.max_t < trip_t)
        user_opt_g.max_t = trip_t;
    return 0;
}

/**
* 接收数据并处理ICMP响应
* @param icmp_soc 原始套接字描述符 
*/
int icmp_process_reply(SOCKET icmp_soc)
{
	/*
	从原始套接字中接收到的数据包含了IP首部 
	*/ 
    struct sockaddr_in from_addr;
    int result, data_size = user_opt_g.size;
    int from_len = sizeof(from_addr);
    char *recv_buf;
    data_size += sizeof(struct ip_hdr) + sizeof(struct icmp_hdr);
    recv_buf = (char *)malloc(data_size);
    /* 接收数据 */
    result = recvfrom(icmp_soc, recv_buf, data_size, 0,
            (struct sockaddr*)&from_addr, &from_len);
    if (result == SOCKET_ERROR)
    {
        if (WSAGetLastError() == WSAETIMEDOUT)
            printf("timed out\n");
        else
            printf("[PING] recvfrom_ failed: %d\n", WSAGetLastError());
        return -1;
    }
    result = icmp_parse_reply(recv_buf, result, &from_addr);
    free(recv_buf);
    return result;
}

/**
* 打印帮助信息
* @param prog_name 文件名(xxx.c 或者 xxx\\yyy.c) 
*/ 
void icmp_help(char *prog_name)
{
    char *file_name;
    file_name = strrchr(prog_name, '\\');
    if (file_name != NULL)
        file_name++;
    else
        file_name = prog_name;
    /* 显示帮助信息 */
    printf(" usage:     %s host_address [-t] [-n count] [-l size] "
        "[-w timeout]\n", file_name);
    printf(" -t         Ping the host until stopped.\n");
    printf(" -n count   the count to send ECHO\n");
    printf(" -l size    the size to send data\n");
    printf(" -w timeout timeout to wait the reply\n");
    exit(1); // 直接退出 
}

/**
* 解析用户的输入命令行参数 
* t           一直ping
* n  count    ping的次数
* l  size    发送数据的大小
* w  timeout 等待响应的时间 
*/
void icmp_parse_param(int argc, char **argv)
{
    int i;
    for(i = 1; i < argc; i++)
    {
        if ((argv[i][0] != '-') && (argv[i][0] != '/'))
        {
            /* 处理主机名 */
            if (user_opt_g.host)
                icmp_help(argv[0]);
            else
            {
                user_opt_g.host = argv[i];
                continue;
            }
        }
        switch (tolower(argv[i][1]))
        {
          	case 't':   /* 持续Ping */
               	user_opt_g.persist = 1;
               	break;
          	case 'n':   /* 发送请求的数量 */
              	i++;
               	user_opt_g.count = atoi(argv[i]);
               	break;
          	case 'l':   /* 发送数据的大小 */
               	i++;
               	user_opt_g.size = atoi(argv[i]);
              	if (user_opt_g.size > ICMP_MAX_SIZE)
                   	user_opt_g.size = ICMP_MAX_SIZE;
               	break;
        	case 'w':   /* 等待接收的超时时间 */
               	i++;
               	user_opt_g.timeout = atoi(argv[i]);
               	break;
           default:
               	icmp_help(argv[0]);
              	break;
        }
    }
}

int main(int argc, char **argv)
{
    WSADATA wsaData;
    SOCKET icmp_soc;
    struct sockaddr_in dest_addr;
    struct hostent *host_ent = NULL;
    int result, data_size, send_len;
    unsigned int i, timeout, lost;
    char *icmp_data;
    unsigned int ip_addr = 0;
    unsigned short seq_no = 0;
    if (argc < 2)
        icmp_help(argv[0]);
    icmp_parse_param(argc, argv);
    WSAStartup(MAKEWORD(2,0),&wsaData);
    /* 解析主机地址 */
    ip_addr = inet_addr(user_opt_g.host);
    if (ip_addr == INADDR_NONE)
    {
        host_ent = gethostbyname(user_opt_g.host);
        if (!host_ent)
        {
            printf("[PING] Fail to resolve %s\n", user_opt_g.host);
            return -1;
        }
           memcpy(&ip_addr, host_ent->h_addr_list[0], host_ent->h_length);
    }
    icmp_soc = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
    if (icmp_soc == INVALID_SOCKET)
    {
        printf("[PING] socket() failed: %d\n", WSAGetLastError());
        return -1;
    }
    /* 设置选项, 接收和发送的超时时间  */
    timeout = user_opt_g.timeout;
    result = setsockopt(icmp_soc, SOL_SOCKET, SO_RCVTIMEO,
                        (char*)&timeout, sizeof(timeout));
    timeout = 1000;
    result = setsockopt(icmp_soc, SOL_SOCKET, SO_SNDTIMEO,
                        (char*)&timeout, sizeof(timeout));
    memset(&dest_addr,0,sizeof(dest_addr));
    dest_addr.sin_family = AF_INET;
    dest_addr.sin_addr.s_addr = ip_addr;
    data_size = user_opt_g.size + sizeof(struct icmp_hdr) - sizeof(long);
    icmp_data = (char *)malloc(data_size);
    if (host_ent)
        printf("Ping %s [%s] with %d bytes data\n", user_opt_g.host,
            inet_ntoa(dest_addr.sin_addr), user_opt_g.size);
    else
        printf("Ping [%s] with %d bytes data\n", inet_ntoa(dest_addr.sin_addr),
            user_opt_g.size);
    /* 发送请求并接收响应 */
    for (i = 0; i < user_opt_g.count; i++)
    {
        icmp_make_data(icmp_data, data_size, seq_no++);
        send_len = sendto(icmp_soc, icmp_data, data_size, 0,
                            (struct sockaddr*)&dest_addr, sizeof(dest_addr));
        if (send_len == SOCKET_ERROR)
        {
            if (WSAGetLastError() == WSAETIMEDOUT)
            {
                printf("[PING] sendto is timeout\n");
                continue;
            }
           printf("[PING] sendto failed: %d\n", WSAGetLastError());
           break;
        }
        user_opt_g.send++;
        result = icmp_process_reply(icmp_soc);
        user_opt_g.persist ? i-- : i; /* 持续Ping */
        Sleep(1000); /* 延迟 1 秒 */
    }
    lost = user_opt_g.send - user_opt_g.recv;
    /* 打印统计数据 */
    printf("\nStatistic :\n");
    printf("    Packet : sent = %d, recv = %d, lost = %d (%3.f%% lost)\n",
    user_opt_g.send, user_opt_g.recv, lost, (float)lost*100/user_opt_g.send);
    if (user_opt_g.recv > 0)
    {
        printf("Roundtrip time (ms)\n");
        printf("    min = %d ms, max = %d ms, avg = %d ms\n", user_opt_g.min_t,
            user_opt_g.max_t, user_opt_g.total_t / user_opt_g.recv);
    }
    free(icmp_data);
    closesocket(icmp_soc);
    WSACleanup();
    return 0;
}

在这里插入图片描述

linux sock_raw原始套接字编程 来自:http://blog.youkuaiyun.com/baixue6269/article/details/6863408 sock_raw原始套接字编程可以接收到本机网卡上的数据帧或者数据包,对与监听网络的流量和分析是很有作用的.一共可以有3种方式创建这种socket   1.socket(AF_INET, SOCK_RAW, IPPROTO_TCP|IPPROTO_UDP|IPPR 阅读详情

相关推荐

使用SOCK_DGRAM类型的socket实现的ping程序

SOCK_DGRAM类型的socket常用于UDP通信,本文将尝试把这种socket用于ICMP协议,并完成一个简单的ping程序。使用ping去测试某个主机是否可用可能是一件很平常的事,尽管ping非常普通,但是编写一个实现ping功能的程序却并不是那么简单,因为ping使用的ICMP协议并不是一个应用层协议,本文简要介绍ICMP协议,并给出一个使用普通的常用于UDP通信的socket实现ping的实例,本文将提供完整的源程序;阅读本文需要熟悉socket编程,对初学者而言,本文有一定的难度。

whowin 1700

sock_raw和sock_packet

套接字SOCK_RAWSOCK_PACKET 实际上,我们常用的网络编程都是在应用层的报文的收发操作,也就是大多数程序员接触到的流式套接字(SOCK_STREAM)和数据包式套接字(SOCK_DGRAM)。而这些数据包都是由系统提供的协议栈实现,用户只需要填充应用层报文即可,由系统完成底层报文头的填充并发送。然而在某些情况下需要执行更底层的操作,比如修改报文头、避开系统协议栈等。这个时候就需要使用其他的方式来实现。 一原始套接字 原始套接字(SOCK_RAW)是一种不同于SOCK_STREA...

HideInTime的博客 3181

MLX90640 热成像 STM32

MLX90640 热成像

zhaocundang's blog 5005

socket 中 SOCK_STREAM 和 SOCK_DGRAM的区别?

sock_stream 是有保障的(即能保证数据正确传送到对方)面向连接的SOCKET,多用于资料(如文件)传送。 sock_dgram 是无保障的面向消息的socket , 主要用于在网络上发广播信息。 SOCK_STREAM是基于TCP的,数据传输比较有保障。SOCK_DGRAM是基于UDP的,专门用于局域网,基于广播SOCK_STREAM 是数据流,一般是tcp/ip协议的编程,SOCK_DGRAM分是数据报,是udp协议网络编程 参考文章:SOCK_STREAM和SOCK_DGRAM两种类型的区别

Dontla的博客 7889

WorkBuddy结合DeepSeek与Skill:文献检索自动化与Token成本控制实战

Agent工具与大模型API的结合正重塑知识工作者的日常流程。在实际工程中,调用大模型处理批量任务常面临Token消耗过快、成本不可控的痛点,而国产大模型DeepSeek以极低的API定价和兼容OpenAI接口的特性,为规模化文本处理提供了“Token自由”的可能。同时,可复用的Skill机制能够将特定领域的工作流固化为标准化步骤,让Agent自动执行检索式生成、文献筛选与摘要归纳等复杂操作。本文从这一技术背景出发,以科研文献检索为应用场景,演示如何利用WorkBuddy、DeepSeek API与自建文献

weixin_29061509的博客 485

Unity安卓打包实战:SDK与JDK配置全攻略

本文详细介绍了Unity安卓打包过程中SDK与JDK的配置方法,包括工具下载、安装步骤、环境变量设置及常见错误排查。通过实战指南帮助开发者快速完成Unity安卓打包环境搭建,提升开发效率。

weixin_30338481的博客 415

原始套接字(SOCK_RAW)概述[转载]

大多数程序员所接触到的套接字(Socket)为两类:   (1)流式套接字(SOCK_STREAM):一种面向连接的Socket,针对于面向连接的TCP服务应用;   (2)数据报式套接字(SOCK_DGRAM):一种无连接的Socket,对应于无连接的UDP服务应用。   从用户的角度来看,SOCK_STREAM、SOCK_DGRAM这两类套接字似乎的确涵盖了TCP/IP应用的

木牛的博客 5540

UE4 Instanced Mesh 半透明渲染顺序不对解决

以绿色方块为Instanced Mesh为例。 正确的半透应该是这样的: 这是一般蓝图Actor方块,其半透是正常的。 然而Instanced Mesh因为过多,UE4不给你缓存正确的渲染顺序。所以,在不改代码的情况下,想解决Instanced Mesh半透问题,是不可能的。不正确的半透如下: 注意到错误发生在Instanced Mesh的方块与方块之间。 但我就是要解决,...

qq_41524721的博客 5569

原始套接字SOCK_RAW

实际上,我们常用的网络编程都是在应用层的报文的收发操作,也就是大多数程序员接触到的流式套接字(SOCK_STREAM)和数据包式套接字(SOCK_DGRAM)。而这些数据包都是由系统提供的协议栈实现,用户只需要填充应用层报文即可,由系统完成底层报文头的填充并发送。然而在某些情况下需要执行更底层的操作,比如修改报文头、避开系统协议栈等。这个时候就需要使用其他的方式来实现。 一原始套接字 原始套接字(SOCK_RAW)是一种不同于SOCK_STREAM、SOCK_DGRAM的套接字,它实现于系统核心..

qq_18287147的博客 1214

高效利用ChEMBL数据库:从靶点查询到活性数据处理的完整指南

本文提供了高效利用ChEMBL数据库的完整指南,从靶点查询到活性数据处理。详细介绍了靶点精准定位、高级过滤技巧、数据导出优化以及活性数据清洗、特征工程和可视化等实战技巧,帮助药物研发人员快速获取和处理生物活性数据,提升工作效率。

weixin_29278805的博客 792

RimWorld Mod开发避坑指南:从Defs命名规范到XML结构详解(附实战代码)

本文深入解析RimWorld Mod开发中Defs文件的编写规范与高级技巧,涵盖命名冲突预防、XML结构优化及高级功能实现。通过实战代码示例,帮助开发者避开常见陷阱,提升Mod兼容性与稳定性,特别适合RimWorld Mod开发者参考。

weixin_30790841的博客 450

UNIX网络编程——原始套接字SOCK_RAW

实际上,我们常用的网络编程都是在应用层的报文的收发操作,也就是大多数程序员接触到的流式套接字(SOCK_STREAM)和数据包式套接字(SOCK_DGRAM)。而这些数据包都是由系统提供的协议栈实现,用户只需要填充应用层报文即可,由系统完成底层报文头的填充并发送。然而在某些情况下需要执行更底层的操作,比如修改报文头、避开系统协议栈等。这个时候就需要使用其他的方式来实现。一 原始套接字

ctthuangcheng 6500

银行一表通监管报送系统详解

国家金融监管总局对于“一表通”的总体描述为:通过建立一套明细级、标准化的数据体系和汇总计算规则,将银行机构传送至监管可信区的业务明细数据汇总生成财务指标、监管指标和监管报表,大幅提升监管数据时效性,统一数据标准,实现数据间的穿透和整合,同时改变机构多头报送数据模式,减轻机构报送负担。

银行信息系统应用架构详解 2516

go原始套接字镜像_浅谈linux下原始套接字 SOCK_RAW 的内幕及其应用

一、SOCK_RAW 内幕首先在讲SOCK_RAW 之前,先来看创建socket 的函数:int socket(int domain, int type, int protocol);domain :指定通信协议族(protocol family/address)/usr/include/i386-linux-gnu/bits/socket.h/*Supportedaddressfamili...

weixin_36459367的博客 695

华东理工大学计算机预推免面试

华东理工大学预推免面试经验分享

D1379951481的博客 1402

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

浅谈linux下原始套接字 SOCK_RAW 的内幕及其应用

真正从网卡进来的数据是完整的以太网帧,底层用sk_buff 数据结构描述,最终进入接收缓冲区recv buffer,而我们应用层调用read / recv /recvfrom 从接收缓冲区拷贝数据到应用层提供的buffer,对一般的套接字,如SOCK_STREAM, SOCK_DGRAM 来说,此时缓冲区只有user data,其他各层的头部已经被去除,而对于SOCK_RAW 来说是IP head + IP payload,当然也可以是arp/rarp 包,甚至是完整的帧(加上MAC头)。

木牛的博客 201

STM32F407中文手册

STM32f407中文手册 本参考手册面向应用开发人员,提供有关使用 STM32F405xx/07xx、STM32F415xx/17xx、 STM32F42xxx 和 STM32F43xxx 微控制器存储器与外设的完整信息。STM32F405xx/07xx、STM32F415xx/17xx、STM32F42xxx 和 STM32F43xxx 构成一个微 控制器系列,各产品具有不同的存储器大小、封装和外设。有关订购信息以及器件的机械与电气特性,请参见数据手册。有关 ARM Cortex™-M4F 内核的信息,请参见《Cortex™-M4F 技术参考手册》。

linux原始套接字raw获取ip包,Linux网络编程:原始套接字 SOCK_RAW, IPV6 rawsocket

比较新的内核才支持socket(fd, SOCK_RAW, IPPROTO_UDP); // 指定 TCP/UDP/ICMPint val = 1;ret = setsockopt(test->state.sockfd, IPPROTO_IPV6, IPV6_HDRINCL, &val, sizeof(val)); // 比较新的内核才支持remote.sin6_port = 0; ...

weixin_39576127的博客 850

SAP B1中文培訓教材_SDK

公司内部培训资料,对开发工具 ,DIAPI, UIAPI,Add-ON有详细解说

【LWIP】原始套接字(SOCK_RAW)

通常情况下我们接触到的套接字为两类:   (1)字节流套接字(SOCK_STREAM):面向连接的socket套接字,用于TCP服务应用。   (2)数据包套接字(SOCK_DGRAM):无连接、不可靠的socket套接字,用于UDP服务应用。 从用户的角度来看,SOCK_STREAM、SOCK_DGRAM 这两类套接字似乎的确涵盖了 TCP/IP 应用的全部,因为基于 TCP/IP 的应用,从协议栈的层次上讲,在传输层的确只可能建立于 TCP 或 UDP 协议之上,而 SOCK_STREAM、SOCK_D

诸葛一帆丶的博客 5726

原始套接字编程(AF_PACKET+SOCK_RAW)模拟一个PING

最近看一个客户的代码片段,发现他在用原始套接字编程,一般学习套接字都是流式套接字和数据报套接字,本来也不是搞网络的,原始套接字了解得很少,借着这次机会,自己来学习一下原始套接字编程。

liaojunwu的博客 1883
上一篇: I/O多路复用-select函数
下一篇: CGI通用网关接口
暴风雨中的白杨
博客等级 码龄6年 1070粉丝 162原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

暴风雨中的白杨

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值