# socket编程 OSI模型如下: ![](media/image-20240422105652627.png) ## socket编程参数区别 网卡对数据帧进行硬过滤,根据网卡的模式不同采取不同的操作: - 如果设置了混杂模式,则不做任何过滤直接交给下一层; - 否则非本机mac或者广播mac的会被直接丢弃。 ### 套接字创建方式 1. 发送接收IP层数据包 ```c 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, ðreq); ``` ## 协议族和地址族区别 ![](media/430e01d5bb14495cb2285022d67d3e38.png) ### 协议族 协议族就是不同协议的集合,在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等。