SOCK_RAW发送tcp数据包(点对点)

本文提供了一个使用TCP/IP原始套接字进行数据发送和接收的示例程序,包括服务器端和客户端代码实现。该示例展示了如何构建IP和TCP报头,并通过原始套接字发送自定义数据。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.server

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>

/* ip首部长度 */
#define IP_HEADER_LEN sizeof(struct ip)
/* tcp首部长度 */
#define TCP_HEADER_LEN sizeof(struct tcphdr)
/* ip首部 + tcp首部长度 */
#define IP_TCP_HEADER_LEN IP_HEADER_LEN + TCP_HEADER_LEN
/* 接收数据缓冲大小 */
#define BUFFER_SIZE 1024
/* ip首部 + tcp首部 + 数据缓冲区大小 */
#define IP_TCP_BUFF_SIZE IP_TCP_HEADER_LEN + BUFFER_SIZE

void err_exit(const char *err_msg)
{
     perror(err_msg);
     exit(1);
}

/* 原始套接字接收 */
void raw_socket_recv()
{
    struct ip *ip_header;
    struct tcphdr *tcp_header;
    int sock_raw_fd, ret_len;
    char buf[IP_TCP_BUFF_SIZE];

    if ((sock_raw_fd = socket(PF_INET, SOCK_RAW, IPPROTO_TCP)) == -1)
         err_exit("socket()");

    /* 接收数据 */
    while (1)
    {
        bzero(buf, IP_TCP_BUFF_SIZE);
        ret_len = recv(sock_raw_fd, buf, IP_TCP_BUFF_SIZE, 0);
        if (ret_len > 0)
        {
             /* 取出ip首部 */
             ip_header = (struct ip *)buf;
            /* 取出tcp首部 */
            tcp_header = (struct tcphdr *)(buf + IP_HEADER_LEN);

            if (ntohs(tcp_header->th_sport) != 14692) {
                printf("=======================================\n");
                printf("from ip:%s\n", inet_ntoa(ip_header->ip_src));
                printf("from port:%d\n", ntohs(tcp_header->th_sport));
                /* 取出数据 */
                printf("get data:%s\n", buf + IP_TCP_HEADER_LEN);
            }
        }
    }

    close(sock_raw_fd);
}
int main(void)
{
     /* 原始套接字接收 */
    raw_socket_recv();

    return 0;
}

2.client(1)

#include <stdio.h>
#include <ctype.h>
#include <unistd.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include <netdb.h>
#include <errno.h>
#include <stdlib.h>
#include <time.h>
#include <arpa/inet.h>
#include <netinet/ip.h>
#include <pthread.h>

#define errorlog(msg) do{ \
                        printf(msg); \
                        exit(1);    \
}while(0)
/* 缓冲大小*/
#define BUFSIZE 1024

/*版本信息*/
#define IPVERSION 4

/* 原始套接字 */
int sockfd;

/* 程序活动标志 */


/*目的端口信息*/
#define DEST_PORT 8101

/*源端口信息*/
#define SRC_PORT  5566
/*send buffer */
char sendbuf[BUFSIZE] = {0};
char Data[] = "forever love for you!!";


struct ip{
    unsigned int        hl:4;
    unsigned int        version:4;
    unsigned char       tos;
    unsigned short      total_len;
    unsigned short      id;
    unsigned short      frag_and_flags;
    unsigned char       ttl;
    unsigned char       proto;
    unsigned short      checksum;
    unsigned int        sourceIP;
    unsigned int        destIP;
};

struct tcphdr{
    unsigned short      sport;
    unsigned short      dport;
    unsigned int        seq;
    unsigned int        ack;
    u_char              th_x2:4,                                                /* (unused)                     */
                        th_off:4;
    unsigned char       flag;
    unsigned short      win;
    unsigned short      sum;
    unsigned short      urp;
};

struct pseudohdr
{
    unsigned int        saddr;
    unsigned int        daddr;
    char                zero;
    char                protocol;
    unsigned short      length;
};

/* CRC16校验 */
unsigned short inline
checksum (unsigned short *buffer, unsigned short size)
{

    unsigned long cksum = 0;

    while(size>1){
        cksum += *buffer++;
        size  -= sizeof(unsigned short);
    }

    if(size){
        cksum += *(unsigned char *)buffer;
    }

    cksum = (cksum >> 16) + (cksum & 0xffff);
    cksum += (cksum >> 16);

    return((unsigned short )(~cksum));
}

/* 发送SYN包函数
 * 填写IP头部,TCP头部
 * TCP伪头部仅用于校验和的计算
 */
void
init_header(struct ip *ip, struct tcphdr *tcp, struct pseudohdr *pseudoheader,struct sockaddr_in *dest, char *src)
{
    int len = sizeof(struct ip) + sizeof(struct tcphdr) + strlen(Data);
    // IP头部数据初始化
    ip->hl = 5;
    ip->version = IPVERSION;
    ip->tos = 0;
    ip->total_len = htons(len);
    ip->id = 0;
    ip->frag_and_flags = 0;
    ip->ttl = 255;
    ip->proto = IPPROTO_TCP;
    ip->checksum = 0;
    ip->sourceIP = inet_addr(src);
    ip->destIP = dest->sin_addr.s_addr;

    // TCP头部数据初始化
    tcp->sport = htons( SRC_PORT );
    tcp->dport = htons(DEST_PORT);
//    tcp->seq = htonl( rand()%90000000 + 2345 );
//    tcp->ack = 0;
    tcp->th_off = sizeof(struct tcphdr) / 4;
    tcp->flag = 0x02;
    tcp->win = 4096;
    tcp->sum = 0;
//    tcp->urp = 0;

//    //TCP伪头部
//    pseudoheader->zero = 0;
//    pseudoheader->protocol = IPPROTO_TCP;
//    pseudoheader->length = htons(sizeof(struct tcphdr));
//    pseudoheader->daddr = dest->sin_addr.s_addr;
//    pseudoheader->saddr = inet_addr(src);
////    srand((unsigned) time(NULL));
}

/*********************************************************************************************************
** 函数名称: send_data
** 功能描述:
** 输 入  : NONE
** 输 出  : NONE
** 全局变量:
** 调用模块:
                                           API 函数
*********************************************************************************************************/
void send_data(int fd,  char *client, struct sockaddr_in *server)
{

//    int i = 0;
    int bufsize;
    int send_len = 0;
    bufsize = sizeof(struct ip) + sizeof(struct tcphdr) + strlen(Data) + 1;
//    for(i = 0; i < 50; i++){
    memset(sendbuf, 0, sizeof(sendbuf));

    struct ip *iphdr;
//    iphdr = (struct ip *)malloc(sizeof(struct ip ));
    struct tcphdr *tcph;
//    tcph = (struct tcphdr *)malloc(sizeof(struct tcphdr));
    struct pseudohdr tcpv;


    iphdr = (struct ip *)sendbuf;
    tcph = (struct tcphdr *)(sendbuf + 20);

    init_header(iphdr, tcph, &tcpv, server, client);
//    memset(sendbuf,0,BUFSIZE);
//    memcpy(sendbuf,iphdr,sizeof(struct ip ));
//    memcpy(sendbuf + sizeof(struct ip ), tcph, sizeof(struct tcphdr));
    memcpy(sendbuf + sizeof(struct ip ) + sizeof(struct tcphdr), Data, strlen(Data) + 1);


    /*伪首部校验和*/
//        memcpy(sendbuf, &tcpv, sizeof(tcpv));
//        memcpy(sendbuf + sizeof(tcpv), tcph, sizeof(struct tcphdr));
//        memcpy(sendbuf + sizeof(tcpv) + sizeof(tcph), Data, sizeof(Data));
//        tcph->sum = checksum((unsigned short *)sendbuf, sizeof(sendbuf));


    send_len = sendto(fd, sendbuf, bufsize, 0, (struct sockaddr *)server, sizeof(struct sockaddr_in));
    if(send_len < 0){
        printf("sendto error\n");
        exit(1);
    }
//    free(iphdr);
//    free(tcph);
//    sleep(1);

//    }


}
/*********************************************************************************************************
** 函数名称: main
** 功能描述: 主函数
** 输 入  : NONE
** 输 出  : NONE
** 全局变量:
** 调用模块:
*********************************************************************************************************/
int main (int argc, char **argv)
{
    int sockfd;
    int flag = 1;
//    int timeout=1000;
    struct sockaddr_in serveraddr;
    char  *srcaddr;


    if(argc < 3){
        errorlog("argument too less\n");
    }

    sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_TCP);
    if(sockfd <0){
        errorlog("fail to socket\n");
    }
    bzero(&serveraddr, sizeof(serveraddr));         /*clear serveraddr*/
    /*server   fill*/
    serveraddr.sin_family = AF_INET;
    serveraddr.sin_port = htons(DEST_PORT);
    serveraddr.sin_addr.s_addr = inet_addr(argv[2]);

    setsockopt(sockfd, IPPROTO_IP, IP_HDRINCL, (void *)&flag, sizeof(flag)); /*ipheader change*/
//    setsockopt(sockfd,SOL_SOCKET,SO_SNDTIMEO,(char*)&timeout,sizeof(timeout));/*timeout manage*/

    /*client fill*/
    srcaddr = argv[1];
    send_data(sockfd, srcaddr, &serveraddr);
    close(sockfd);

    return  (0);
}

3.client(2)(转)

/**
  2  * @file ip_tcp_send.c
  3  */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>

 /* ip首部长度 */
 #define IP_HEADER_LEN sizeof(struct ip)
 /* tcp首部长度 */
 #define TCP_HEADER_LEN sizeof(struct tcphdr)
 /* ip首部 + tcp首部长度 */
 #define IP_TCP_HEADER_LEN IP_HEADER_LEN + TCP_HEADER_LEN

 void err_exit(const char *err_msg)
 {
    perror(err_msg);
     exit(1);
 }
 /* 填充ip首部 */
 struct ip *fill_ip_header(const char *src_ip, const char *dst_ip, int ip_packet_len)
 {
     struct ip *ip_header;

     ip_header = (struct ip *)malloc(IP_HEADER_LEN);
     ip_header->ip_v = IPVERSION;
     ip_header->ip_hl = sizeof(struct ip) / 4;        /* 这里注意,ip首部长度是指占多个32位的数量,4字节=32位,所以除以4 */
     ip_header->ip_tos = 0;
     ip_header->ip_len = htons(ip_packet_len);        /* 整个IP数据报长度,包括普通数据 */
     ip_header->ip_id = 0;                            /* 让内核自己填充标识位 */
     ip_header->ip_off = 0;
     ip_header->ip_ttl = MAXTTL;
     ip_header->ip_p = IPPROTO_TCP;                   /* ip包封装的协议类型 */
     ip_header->ip_sum = 0;                           /* 让内核自己计算校验和 */
     ip_header->ip_src.s_addr = inet_addr(src_ip);    /* 源IP地址 */
     ip_header->ip_dst.s_addr = inet_addr(dst_ip);    /* 目标IP地址 */

     return ip_header;
 }

 /* 填充tcp首部 */
 struct tcphdr *fill_tcp_header(int src_port, int dst_port)
 {
     struct tcphdr *tcp_header;

     tcp_header = (struct tcphdr *)malloc(TCP_HEADER_LEN);
     tcp_header->th_sport = htons(src_port);
     tcp_header->th_dport = htons(dst_port);
     /* 同IP首部一样,这里是占32位的字节多少个 */
     tcp_header->th_off = sizeof(struct tcphdr) / 4;
     /* 发起连接 */
     tcp_header->th_flags = TH_SYN;
     tcp_header->th_win = 4096;
     tcp_header->th_sum = 0;

     return tcp_header;
 }

 /* 发送ip_tcp报文 */
 void ip_tcp_send(const char *src_ip, int src_port, const char *dst_ip, int dst_port, const char *data)
 {
     struct ip *ip_header;
     struct tcphdr *tcp_header;
     struct sockaddr_in dst_addr;
     socklen_t sock_addrlen = sizeof(struct sockaddr_in);

     int data_len = strlen(data);
     int ip_packet_len = IP_TCP_HEADER_LEN + data_len;
     char buf[ip_packet_len];
     int sockfd, ret_len, on = 1;

     bzero(&dst_addr, sock_addrlen);
     dst_addr.sin_family = PF_INET;
     dst_addr.sin_addr.s_addr = inet_addr(dst_ip);
     dst_addr.sin_port = htons(dst_port);

     /* 创建tcp原始套接字 */
     if ((sockfd = socket(PF_INET, SOCK_RAW, IPPROTO_TCP)) == -1)
         err_exit("socket()");

     if (setsockopt(sockfd, IPPROTO_IP, IP_HDRINCL, &on, sizeof(on)) == -1)
         err_exit("setsockopt()");


     ip_header = fill_ip_header(src_ip, dst_ip, ip_packet_len);

     tcp_header = fill_tcp_header(src_port, dst_port);

     bzero(buf, ip_packet_len);
     memcpy(buf, ip_header, IP_HEADER_LEN);
     memcpy(buf + IP_HEADER_LEN, tcp_header, TCP_HEADER_LEN);
     memcpy(buf + IP_TCP_HEADER_LEN, data, data_len);

     /* 发送报文 */
     ret_len = sendto(sockfd, buf, ip_packet_len, 0, (struct sockaddr *)&dst_addr, sock_addrlen);
     if (ret_len > 0)
         printf("sendto() ok!!!\n");
     else printf("sendto() failed\n");

     close(sockfd);
     free(ip_header);
     free(tcp_header);
 }

 int main(int argc, const char *argv[])
 {
     if (argc != 6)
     {
         printf("usage:%s src_ip src_port dst_ip dst_port data\n", argv[0]);
        exit(1);
     }

     /* 发送ip_tcp报文 */
     ip_tcp_send(argv[1], atoi(argv[2]), argv[3], atoi(argv[4]), argv[5]);

     return 0;
 }

 

<think>好的,我现在需要解答用户关于socket函数参数的问题,特别是AF_INET、SOCK_RAW和htons(0x9001)的含义及用法。首先,我得回忆一下这些参数在socket编程中的作用。 AF_INET是地址族,表示使用IPv4协议,这应该是基础。用户可能已经知道这点,但需要确认。接下来是SOCK_RAW,这个参数说明用户想要创建原始套接字,允许直接处理网络层的数据包,比如IP头。这点需要详细解释,可能涉及内核不自动处理包头的情况。 然后,第三个参数是htons(0x9001),这里比较特殊。通常第三个参数在socket函数中是协议类型,比如IPPROTO_TCP或IPPROTO_UDP。但0x9001可能是一个自定义的协议号。需要确认协议号的分配,IANA的保留范围是0x9000到0xFFFF,所以0x9001属于这个范围,属于实验性或私有用途。htons函数是将主机字节序转换为网络字节序,这里可能需要解释字节序转换的重要性。 接下来,用户可能需要知道这些参数组合在一起的作用。AF_INET和SOCK_RAW的组合允许用户处理IP层的数据包,而第三个参数指定了协议类型。但根据引用中的资料,比如引用[3]提到AF_INET+SOCK_RAW只能到IP层,而AF_PACKET+SOCK_RAW能到数据链路层。所以用户可能是在尝试接收或发送特定协议的数据包,比如自定义协议。 需要注意权限问题,创建原始套接字通常需要root权限,这点需要提醒用户。此外,不同操作系统对原始套接字的支持可能不同,特别是第三个参数的处理,比如Linux可能允许自定义协议号,而其他系统可能有限制。 可能还需要解释数据包的结构,当使用SOCK_RAW时,发送的数据需要包含IP头,而接收的数据可能包含或不包含IP头,这取决于是否设置了IP_HDRINCL选项。这部分需要结合代码示例,但用户没有提供代码,可能需要简要说明。 另外,htons(0x9001)中的0x9001作为协议号,用户可能在尝试处理自定义协议的数据包,或者特定的协议类型。需要说明IANA的协议号分配情况,确认0x9001是否被注册,或者是否属于实验性用途。 最后,可能需要给出一个使用示例,比如创建socket的代码片段,并解释各参数的作用。同时,提醒用户注意错误处理和权限问题。 在回答过程中,需要参考用户提供的引用,比如引用[3]提到AF_INET+SOCK_RAW与AF_PACKET+SOCK_RAW的区别,这可能帮助用户理解他们能够访问的网络层数。此外,引用[1]中提到了使用不同的协议类型(如IPPROTO_UDP或IPPROTO_ICMP)来过滤接收的数据包,这可能与第三个参数相关,用户可能想通过0x9001来过滤特定的协议类型。 总结来说,回答需要分点解释每个参数的含义,组合使用的效果,权限要求,协议号的作用,以及可能的用例和注意事项。同时,结合引用中的信息来增强回答的可信度。</think>### Socket参数`AF_INET`、`SOCK_RAW`和`htons(0x9001)`的功能解析 #### 1. **`AF_INET`的作用** - **定义**:表示使用IPv4协议族的地址格式,用于指定网络层通信的协议类型。 - **功能**: 在创建Socket时,`AF_INET`声明了数据包的封装和解析遵循IPv4标准。例如,IP地址格式为`127.0.0.1`,端口号为16位整数。 - **典型场景**:适用于需要IPv4通信的应用程序,如TCP/UDP服务。 #### 2. **`SOCK_RAW`的作用** - **定义**:表示创建原始套接字,允许用户直接操作网络层(IP层)或传输层协议头。 - **功能**: - **数据包构造**:应用层需自行构建完整的IP头部(需设置`IP_HDRINCL`选项)或传输层头部(如TCP/UDP)。 - **数据包接收**:内核不会自动剥离IP头部,需手动解析。例如,接收ICMP包时需处理IP头后的ICMP报文[^3]。 - **权限要求**:通常需要`root`权限(Linux)或管理员权限(Windows)。 - **典型场景**:实现自定义协议、网络嗅探或发送特定类型的包(如PING工具[^3])。 #### 3. **`htons(0x9001)`的作用** - **定义**:`htons`将16位整数从主机字节序转为网络字节序,`0x9001`是自定义的协议类型。 - **协议号的意义**: - IANA保留协议号范围`0x9000-0xFFFF`供私有或实验用途[^1]。此处`0x9001`属于实验性协议。 - 内核会根据协议号过滤接收的数据包。例如,`socket(AF_INET, SOCK_RAW, IPPROTO_ICMP)`仅接收ICMP包[^1]。 - **特殊用法**: 若协议号为`ETH_P_ALL`(如`htons(0x0003)`),套接字会接收所有类型的包(需使用`AF_PACKET`地址族[^3])。 #### 4. **参数组合的完整解释 ```c int sockfd = socket(AF_INET, SOCK_RAW, htons(0x9001)); ``` - **功能**: 创建一个IPv4原始套接字,用于收发协议号为`0x9001`的自定义数据包。 - **限制**: - **操作系统支持**:Linux允许自定义协议号,但需内核未占用该值。 - **数据构造**:若未设置`IP_HDRINCL`,内核会自动添加IP头,此时应用层需提供传输层数据(如TCP负载)。 - **代码示例**(接收自定义协议包): ```c // 需root权限 int sock = socket(AF_INET, SOCK_RAW, htons(0x9001)); if (sock == -1) { perror("socket creation failed"); exit(EXIT_FAILURE); } // 绑定到特定IP(可选) struct sockaddr_in addr; memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_addr.s_addr = inet_addr("192.168.1.100"); bind(sock, (struct sockaddr*)&addr, sizeof(addr)); // 接收数据包 char buffer[1024]; recvfrom(sock, buffer, sizeof(buffer), 0, NULL, NULL); ``` #### 5. **注意事项** - **权限问题**:Linux中需`CAP_NET_RAW`能力或root权限。 - **协议冲突**:避免与系统已用协议号冲突(如ICMP为`1`,TCP为`6`)。 - **跨平台差异**:Windows可能限制`SOCK_RAW`的使用范围。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值