socket编程

OSI模型如下:

socket编程参数区别

网卡对数据帧进行硬过滤,根据网卡的模式不同采取不同的操作:

  • 如果设置了混杂模式,则不做任何过滤直接交给下一层;

  • 否则非本机mac或者广播mac的会被直接丢弃。

套接字创建方式

  1. 发送接收IP层数据包

    socket(AF_INET, SOCK_RAW, IPPROTO_TCP|IPPROTO_UDP|IPPROTO_ICMP) 
    
    • 该套接字可以接收协议类型为tcp、udp、icmp等发往本机的ip数据包

    • 不能接收非发往本机ip的数据包,因为IP地址会过滤掉丢弃非本机IP的数据包

    • 不能接收从本机发出去的数据

    • 发送:需要自己组UDP/TCP头,IP和以太网头部由内核完成

      即用户需要组装 TCP/UDP + data

    • 注意: 想自己也想亲自处理IP头,则需要IP_HDRINCL的socket选项。如下 :

      int flag = 1;
      setsockopt(sockfd, IPPROTO_IP, IP_HDRINCL, &flag, sizeof(int));
      

      即用户需要组装IP头 + TCP/UDP + data

      注意: 即使是这种情况,在我们发送IP包的时候,也不是填充ip头的所有字段,而是应该将ip头的id(identification)字段设置为0。表示让内核来处理这个字段。同时,内核还帮你完成ip头的校验和的计算,并随后填充check字段。

    • 第三个参数如果是 IPPROTO_TCP等非0非255 的数据,内核查看是否是自己填的IPPROTO_TCP等协议,如果是交给这个socket处理。

    • 第三个参数如果是 IPPROTO_RAW(255),这个 socket只能用来发送IP包,而不能接收任何数据。发送的数据需要自己填充IP包头,并且自己计算校验和。谨慎使用。

  2. 发送接收以太网数据帧

    socket(PF_PACKET, SOCK_RAW, htons(ETH_P_IP|ETH_P_ARP|ETH_P_ALL)) 
    
    • 能接收发往本机mac的数据帧

    • 能接收从本机发出去的数据帧,需要第三个参数为ETH_P_ALL

    • 能接收非发往本地mac的数据帧:网卡需要设置为混杂模式 promisc

    协议类型就四种

    • ETH_P_IP 0x800 :只接收发往本机mac的ip类型的数据帧

    • ETH_P_ARP 0x806 :只接受发往本机mac的arp类型的数据帧

    • ETH_P_RARP 0x8035 :只接受发往本机mac的rarp类型的数据帧

    • ETH_P_ALL 0x3 :接收发往本机mac的所有类型的数据帧, 接收从本机发出的所有类型的数据帧。(混杂模式打开的情况下,会接收到非发往本地mac的数据帧)

总结

类型

SOCK_RAW

SOCK_DGRAM

SOCK_STREAM

AF_INET

接收:IP报文头 发送:UDP/TCP头开始(**注意:**使用IP_HDRINCL选项可以从IP报文头开始)

接收:UDP应用层数据 发送:UDP应用层数据

接收:TCP应用层数据 发送:TCP应用层数据

PF_PACKET

接收:以太网报文头 发送:以太网头开始

接收:IP报文头 发送:IP报文头开始

使用技巧

调整socket接收缓存大小

通过getsockopt可以获得socket的当前接收和发送缓存的大小,我的程序中获取出来的发送和接收缓存都是262144字节(256KB)。采用默认的接收缓存大小的测试结果中可能会出现文件大小偏小。

使用setsockopt设置接收缓存的大小为2x1024x1024字节(2M,已经很大了),然后再多次测试,基本上没有结果偏小的情况,但是有时会出现结果偏大(重传以及选择性重传过滤不完全,内容有交叠导致的)

设置混杂模式

发向所有机器的广播数据帧。如果网卡要接收所有通过它的数据,而不管是不是发给它的,就必须把网卡置于混杂模式。

struct ifreq ethreq;

strncpy(ethreq.ifr_name, "eth0", IFNAMSIZ);

ethreq.ifr_flags |= IFF_PROMISC;

ioctl(sock, SIOCGIFFLAGS, &ethreq);

协议族和地址族区别

协议族

协议族就是不同协议的集合,在Linux中,用宏来表示不同的协议族,这个宏的形式是PF开头,比如IPv4协议族为PF_INET,PF的意思是PROTOCOL FAMILY,这些宏定义在/usr/include/bits/socket.h

/* Protocol families.  */
#define PF_UNSPEC	0	/* Unspecified.  */
#define PF_LOCAL	1	/* Local to host (pipes and file-domain).  */
#define PF_UNIX		PF_LOCAL /* POSIX name for PF_LOCAL.  */
#define PF_FILE		PF_LOCAL /* Another non-standard name for PF_LOCAL.  */
#define PF_INET		2	/* IP protocol family.  */
#define PF_AX25		3	/* Amateur Radio AX.25.  */
#define PF_IPX		4	/* Novell Internet Protocol.  */
#define PF_APPLETALK	5	/* Appletalk DDP.  */
#define PF_NETROM	6	/* Amateur radio NetROM.  */
#define PF_BRIDGE	7	/* Multiprotocol bridge.  */
#define PF_ATMPVC	8	/* ATM PVCs.  */
#define PF_X25		9	/* Reserved for X.25 project.  */
#define PF_INET6	10	/* IP version 6.  */
#define PF_ROSE		11	/* Amateur Radio X.25 PLP.  */
#define PF_DECnet	12	/* Reserved for DECnet project.  */
#define PF_NETBEUI	13	/* Reserved for 802.2LLC project.  */
#define PF_SECURITY	14	/* Security callback pseudo AF.  */
#define PF_KEY		15	/* PF_KEY key management API.  */
#define PF_NETLINK	16
#define PF_ROUTE	PF_NETLINK /* Alias to emulate 4.4BSD.  */
#define PF_PACKET	17	/* Packet family.  */
#define PF_ASH		18	/* Ash.  */
#define PF_ECONET	19	/* Acorn Econet.  */
#define PF_ATMSVC	20	/* ATM SVCs.  */
#define PF_RDS		21	/* RDS sockets.  */
#define PF_SNA		22	/* Linux SNA Project */
#define PF_IRDA		23	/* IRDA sockets.  */
#define PF_PPPOX	24	/* PPPoX sockets.  */
#define PF_WANPIPE	25	/* Wanpipe API sockets.  */
#define PF_LLC		26	/* Linux LLC.  */
#define PF_IB		27	/* Native InfiniBand address.  */
#define PF_MPLS		28	/* MPLS.  */
#define PF_CAN		29	/* Controller Area Network.  */
#define PF_TIPC		30	/* TIPC sockets.  */
#define PF_BLUETOOTH	31	/* Bluetooth sockets.  */
#define PF_IUCV		32	/* IUCV sockets.  */
#define PF_RXRPC	33	/* RxRPC sockets.  */
#define PF_ISDN		34	/* mISDN sockets.  */
#define PF_PHONET	35	/* Phonet sockets.  */
#define PF_IEEE802154	36	/* IEEE 802.15.4 sockets.  */
#define PF_CAIF		37	/* CAIF sockets.  */
#define PF_ALG		38	/* Algorithm sockets.  */
#define PF_NFC		39	/* NFC sockets.  */
#define PF_VSOCK	40	/* vSockets.  */
#define PF_KCM		41	/* Kernel Connection Multiplexor.  */
#define PF_QIPCRTR	42	/* Qualcomm IPC Router.  */
#define PF_SMC		43	/* SMC sockets.  */
#define PF_MAX		44	/* For now..  */

地址族

地址族就是一个协议族所使用的地址集合,也是用宏来表示不同的地址族,这个宏的形式是AF开头,比如IP地址族为AF_INET,AF的意思是ADDRESS FAMILY,这些宏定义在/usr/include/bits/socket.h

/* Address families.  */
#define AF_UNSPEC	PF_UNSPEC
#define AF_LOCAL	PF_LOCAL
#define AF_UNIX		PF_UNIX
#define AF_FILE		PF_FILE
#define AF_INET		PF_INET
#define AF_AX25		PF_AX25
#define AF_IPX		PF_IPX
#define AF_APPLETALK	PF_APPLETALK
#define AF_NETROM	PF_NETROM
#define AF_BRIDGE	PF_BRIDGE
#define AF_ATMPVC	PF_ATMPVC
#define AF_X25		PF_X25
#define AF_INET6	PF_INET6
#define AF_ROSE		PF_ROSE
#define AF_DECnet	PF_DECnet
#define AF_NETBEUI	PF_NETBEUI
#define AF_SECURITY	PF_SECURITY
#define AF_KEY		PF_KEY
#define AF_NETLINK	PF_NETLINK
#define AF_ROUTE	PF_ROUTE
#define AF_PACKET	PF_PACKET
#define AF_ASH		PF_ASH
#define AF_ECONET	PF_ECONET
#define AF_ATMSVC	PF_ATMSVC
#define AF_RDS		PF_RDS
#define AF_SNA		PF_SNA
#define AF_IRDA		PF_IRDA
#define AF_PPPOX	PF_PPPOX
#define AF_WANPIPE	PF_WANPIPE
#define AF_LLC		PF_LLC
#define AF_IB		PF_IB
#define AF_MPLS		PF_MPLS
#define AF_CAN		PF_CAN
#define AF_TIPC		PF_TIPC
#define AF_BLUETOOTH	PF_BLUETOOTH
#define AF_IUCV		PF_IUCV
#define AF_RXRPC	PF_RXRPC
#define AF_ISDN		PF_ISDN
#define AF_PHONET	PF_PHONET
#define AF_IEEE802154	PF_IEEE802154
#define AF_CAIF		PF_CAIF
#define AF_ALG		PF_ALG
#define AF_NFC		PF_NFC
#define AF_VSOCK	PF_VSOCK
#define AF_KCM		PF_KCM
#define AF_QIPCRTR	PF_QIPCRTR
#define AF_SMC		PF_SMC
#define AF_MAX		PF_MAX

区别

地址族和协议族其实是一样的,值也一样,都是用来识别不同协议的,为什么要搞两套东西呢?这是因为之前UNIX有两种风格系统:BSD系统和POSIX系统:

  • 对于BSD系统,一直用的是AF;

  • 对于POSIX系统,一直用的是PF

Linux作为后起之秀,为了兼容,所以两种都支持。

BSD系统和POSIX系统简介

  • POSIX系统:Bell实验室的Ken Thompson和Dennis Richie一起发明了Unix系统,随着Unix系统的成长,后来占领了市场,公司多了,懂得的人也多了,就分家了。后来Unix太多太乱,大家编程接口甚至命令都不一样了,为了规范大家的使用和开发,就出现了POSIX标准。典型的POSIX标准的UNIX实现有Solaris和AIX等。

  • BSD系统:BSD代表伯克利软件条件,是20世界70年代,加州大学伯克利分校对贝尔实验室UNIX进行了一系列修改后的版本,它最终发展成了一个完整的操作系统,有着自己的一套标准。现在有多个不同的BSD分支。今天,BSD并不特指任何一个BSD衍生版本,而是类UNIX操作系统的一个分支总称。典型代表是FreeBSD、NetBSD、OpenBSD等。