原始套接字提供如下功能:

1、读写ICMPv4,IGMPv4及ICMPv6分组。如ping程序,就是使用原始套接口中发送ICMP回显请求,并接受ICMP回显应答

2、读写特殊的IPv4数据报。大多数内核处理值为1(ICMP),2(IGMP)、6(TCP)和17(UDP)的数据报。协议字段还可能为其他值

3、使用IP_HDRINCL套接口选项可以构造自己的IPv4头部。

原始套接口的创建

一般分为以下几步

1、第二个参数为SOCK_RAW,调用 socket函数创建一个原始套接口。第三个参数一般不为0。protocol可以为IPPROTO_ICMP或者 IPPROTO_IGMP。只有超级用户才有权创建原始套接字

2、可以设置 IP_HDRINCL套接口选项,如

const int on = 1;

setsockopt(sockfd, IPPROTO_IP, IP_HDRINCL, &on, sizeof(on));

3、可以对原始套接品调用 bind函数,但是不常用 。这个仅用来设置 本地地址,对于端口号没有意义

4、可以在原始套接口上调用 connect函数,也不常用 ,只是设置 目地地址,对于端口也没有意义。调用 connect后,因为指定了目的地址,可以调用 write 或send,而不是sendto

原始套接口输出

输出有以下规则

1、普通输出调用 sendto或sendmsg并指定目的IP地址来完成。如果套接口已连接,可以用write,writev或send

2、如果IP_HDRINCL没有设置 ,写的数据起始地址为IP头部后的第一个字节,其中头部协议字段填写为socket调用时的第三个参数

3、如果IP_HDRINCL已经设置,写的数据起始地址为IP头部的第一个字节,用户提供的数据大小值必须包括头部的字节数。此进进程除了标识字段和检验和字段外,其它可能由进程来设置 。检验和是由内核计算填充的

4、对于 超出外出接口MTU的分组,内核将其分片。

原始套接口输入

接收到的以下分组哪些会及不会传递给原始套接口,有以下规则

1、TCP和UDP分组不会传递给原始套接口

2、当内核处理完ICMP消息后,绝大部分 ICMP分组会传递给原始套接口

3、内核处理完IGMP消息后,所有IGMP分组都将传递给原始套接口

4、内核不能识别的协议字段的IP数据报都将传递给原始套接口。内核对这些分组唯一做的就是检验IP头部的某些字段:IP版本,IPv4头部检验和,头部长度及目的IP地址

5、如果数据以片段形式到达,则该分组将原所有片段到达重组后传给原始套接口

下面是用原始套接口中写的类似Ping程序

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <arpa/inet.h>
#include <netinet/in_systm.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#include <netdb.h>
#include <signal.h>
#include <sys/time.h>
#include <stdio.h>#define BUFSIZE 1500char recvbuf[BUFSIZE];
char sendbuf[BUFSIZE];
//int datalen;
char *host;
int nsent;
pid_t pid;
int sockfd;
int verbose;void proc_v4(char *, ssize_t, struct timeval *);
//void proc_v6(char *, ssize_t, struct timeval *);
void send_v4();
//void send_v6();
void readloop();
void sig_alrm(int);
void tv_sub(struct timeval*, struct timeval*);struct proto
{void (*fproc)(char *, ssize_t, struct timeval*);void (*fsend)(void);struct sockaddr *sasend;struct sockaddr *sarecv;socklen_t salen;int icmpproto;
}*pr;char *sock_ntop(struct sockaddr *sa, socklen_t len)
{char portstr[7];   static char str[128];switch (sa->sa_family) {case AF_INET:{struct sockaddr_in *sin = (struct sockaddr_in*)sa;if (inet_ntop(AF_INET, &sin->sin_addr, str, sizeof(str)) == NULL) return NULL;if (ntohs(sin->sin_port) != 0) {snprintf(portstr, sizeof(portstr), "port=%d", ntohs(sin->sin_port));  strcat(str, portstr);}return str;}}
}struct addrinfo *host_serv(const char *host, const char *serv, int family, int socktype)
{struct addrinfo hints, *res;int n;bzero(&hints, sizeof(hints));hints.ai_flags = AI_CANONNAME;hints.ai_family = family;hints.ai_socktype = socktype;if ((n = getaddrinfo(host, serv, &hints, &res)) != 0) return NULL;return res;
}struct proto proto_v4 = {proc_v4, send_v4, NULL, NULL, 0, IPPROTO_ICMP};
int datalen = 56;int main(int argc, char **argv)
{int c;struct addrinfo *ai;pid = getpid();signal(SIGALRM, sig_alrm);ai = host_serv(argv[1], NULL, 0, 0);pr = &proto_v4;printf("ICMP_ECHO=%d\n", ICMP_ECHO);pr->sasend = ai->ai_addr;pr->sarecv = calloc(1, ai->ai_addrlen);pr->salen = ai->ai_addrlen;readloop();    exit(0);
}void readloop(void)
{int size;char recvbuf[BUFSIZE];socklen_t len;ssize_t n;struct timeval tval;sockfd = socket(pr->sasend->sa_family, SOCK_RAW, pr->icmpproto);setuid(getuid());size = 60 * 1024;setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, &size, sizeof(size));sig_alrm(SIGALRM);for (;;){len = pr->salen; n = recvfrom(sockfd, recvbuf, sizeof(recvbuf), 0, pr->sarecv, &len);if (n < 0) {if (errno == EINTR) continue;else {printf("recvfrom error:%s\n", strerror(errno));return;}}gettimeofday(&tval, NULL);(*pr->fproc)(recvbuf, n, &tval);}
}void tv_sub(struct timeval* out, struct timeval *in)
{if ((out->tv_usec -= in->tv_usec) < 0) {--out->tv_sec;out->tv_usec += 1000000;}out->tv_sec -= in->tv_sec;
}void proc_v4(char *ptr, ssize_t len, struct timeval *tvrecv)
{int hlen1, icmplen;double rtt;struct ip *ip;struct icmp *icmp;struct timeval *tvsend;ip = (struct ip*)ptr;hlen1 = ip->ip_hl << 2;icmp = (struct icmp*)(ptr + hlen1);if ((icmplen = len - hlen1) < 8) {fprintf(stderr, "icmp len error\n");return;}if (icmp->icmp_type == ICMP_ECHOREPLY) {if (icmp->icmp_id != pid) return;if (icmplen < 16) {fprintf(stderr, "icmplen (%d) < 16\n", icmplen);return;}tvsend = (struct timeval *)icmp->icmp_data;tv_sub(tvrecv, tvsend);rtt = tvrecv->tv_sec * 1000 + tvrecv->tv_usec / 1000;printf("%d bytes from %s:seq=%u, ttl=%d, rtt=%.3f ms\n", icmplen, sock_ntop(pr->sarecv, pr->salen), icmp->icmp_seq, ip->ip_ttl, rtt); } else if (verbose) {printf("%d bytes from %s:type=%d, code=%d\n", icmplen,sock_ntop(pr->sarecv, pr->salen), icmp->icmp_type, icmp->icmp_code);}
}void sig_alrm(int signo)
{(*pr->fsend)();alarm(1);return;
}unsigned short in_cksum(unsigned short *addr, int len)
{int nleft = len;int sum = 0;unsigned short *w = addr;unsigned short answer = 0;while (nleft > 1){sum += *w++;nleft -= 2;}if (nleft == 1) {*(unsigned char *)(&answer) = *(unsigned char *)w;sum += answer;}sum = (sum >> 16) + (sum & 0xffff);sum += (sum >> 16);answer = ~sum;return answer;
}void send_v4()
{int len;struct icmp *icmp;icmp = (struct icmp *)sendbuf;icmp->icmp_type = ICMP_ECHO;icmp->icmp_code = 0;icmp->icmp_id = pid;icmp->icmp_seq = nsent++;gettimeofday((struct timeval*)icmp->icmp_data, NULL);len = 8 + datalen;icmp->icmp_cksum = 0;icmp->icmp_cksum = in_cksum((unsigned short*)icmp, len);sendto(sockfd, sendbuf, len, 0, pr->sasend, pr->salen);
}

网络编程(原始套接字)相关推荐

  1. Linux原始网络编程,Linux操作系统网络编程 原始套接字 (1)

    Linux操作系统网络编程--原始套接字 (1) http://soft.zdnet.com.cn/software_zone/2007/1020/568223.shtml 我们在前面已经学习过了网络 ...

  2. Linux网络编程——原始套接字编程

    Linux网络编程--原始套接字编程 转自:http://blog.csdn.net/tennysonsky/article/details/44676377 原始套接字编程和之前的 UDP 编程差不 ...

  3. 网络编程——原始套接字实现原理

    目录 1. 基础知识 1.1.概述 1.2.链路层原始套接字 1.3.网络层原始套接字 2.原始套接字的实现 2.1  原始套接字报文收发流程 2.2链路层原始套接字的实现 2.2.1  套接字创建 ...

  4. Linux网络编程——原始套接字能干什么?

    一.知识回顾: 通常情况下程序员接所接触到的套接字(Socket)为两类: (1)流式套接字(SOCK_STREAM):一种面向连接的 Socket,针对于面向连接的TCP 服务应用: (2)数据报式 ...

  5. linux串口编程实例_Linux 网络编程——原始套接字实例:发送 UDP 数据包

    以太网报文格式: IP 报文格式: UDP 报文格式: 校验和函数: /*******************************************************功能:校验和函数参 ...

  6. Linux 网络编程——原始套接字实例:MAC 地址扫描器

    如果 A (192.168.1.1 )向 B (192.168.1.2 )发送一个数据包,那么需要的条件有 ip.port.使用的协议(TCP/UDP)之外还需要 MAC 地址,因为在以太网数据包中 ...

  7. python socket清空接收缓冲区_Python网络编程——修改套接字发送和接收的缓冲区大小...

    很多情况下,默认的套接字缓冲区大小可能不够用.此时,可以将默认的套接字缓冲区大小改成一个更合适的值. 1. 代码 # ! /usr/bin/env python # -*- coding: utf-8 ...

  8. [python学习] 专题七.网络编程之套接字Socket、TCP和UDP通信实例

    很早以前研究过C#和C++的网络通信,参考我的文章:                  C#网络编程之Tcp实现客户端和服务器聊天                 C#网络编程之套接字编程基础知识   ...

  9. TCP/IP网络编程:P1->理解网络编程和套接字

    本系列文章为<TCP/IP网络编程----尹圣雨>学习笔记 文章目录 一.理解网络编程和套接字 1.1 构建接电话套接字 1.2 编写"Hello world!"服务器 ...

  10. C# 网络编程之套接字编程基础知识

    最近阅读了周存杰编写的<C#网络编程实例教程>并阅读了很多相关方面的资料,同时自己也做了一些套接字编程方面的C#程序,所以根据它的知识总结了最近的套接字编程的一些知识点,方便自己的理解与他 ...

最新文章

  1. HDU2196[树形dp+二次扫描]java和c++版本题解
  2. 菱形开合的实现 IOS
  3. Mysql创建修改删除-表
  4. python爬虫常见的那点问题!
  5. 特意向大家推荐.NET技术圈一些优秀开发者的公众号
  6. pycharm python部署_使用PyCharm配合部署Python的Django框架的配置纪实
  7. 主成分分析碎石图_ISLR读书笔记十九:主成分分析(PCA)
  8. JUnit5 @BeforeAll注解示例
  9. 为什么有些人看了别人的总结、经验、教训,依然没有用。
  10. 数据结构与算法之-----图(拓扑排序)
  11. 手把手写一个vue3的组件
  12. 【电脑讲解】压缩包的使用技巧
  13. word公式编辑器复制粘贴未响应_Word公式编辑器使用中的常见问题的解决办法(图文教程)...
  14. Python爬虫实战(6)-爬取QQ空间好友说说并生成词云(超详细)
  15. ps添加的阴影怎么去除_ps怎么可以把阴影去除
  16. 【测开方法论】测开平台pk心得-抉择
  17. 考 PMP 证书真有用吗?
  18. 数据库实验三 嵌套查询和视图操作
  19. 嵌入式系统基本概念(硬件篇)
  20. 2019 年会抽奖项目总结

热门文章

  1. 值转换器IValueConverter
  2. POJ1573-Robot Motion
  3. 为什么说python是世界上最好的语言-Python才是世界上最好的语言
  4. python语音播报-用Python写一个语音播放软件
  5. python2.7爬虫实例-Python2.7爬虫-爬取简书文章-入门
  6. python.freelycode.com-Python日期时间处理: datestuff
  7. python xpath语法-Python爬虫之XPath语法和lxml库的用法
  8. python入门基础代码图-Python入门基础学习一
  9. python语言入门详解-Python unittest详解一(基础入门)
  10. 自学python找到工作-学完python能找到工作么