raw_socket(原始套接字)以及普通socket使用终极总结

一、传输层socket(四层socket,普通socket)

可参考本人以下博客:

Windows Socket编程之UDP实现大文件的传输:http://blog.csdn.net/luchengtao11/article/details/71016222

Windows Socket编程之TCP实现大文件的传输:http://blog.csdn.net/luchengtao11/article/details/71012580

(1)创建

 

socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);//TCP
//或者
socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP);//UDP

AF_INEF表示TCP/IP族

 

第三个参数可以为0,由操作系统自行选择

(2)发送

 

  sendto(sd,buffer,BUFSIZ,0,(SOCKADDR*)&addrServ,sizeof(SOCKADDR));//UDP
  send(sd, buffer, BUFSIZ, 0);  //TCP

 

(3)接收

recvfrom(sd,buffer,BUFSIZ,0,(SOCKADDR*)&addrClient,sizeof(SOCKADDR));//UDP
recv(sd, buffer, BUFSIZ, 0);//TCP

 

 

sd为socket标识符,buffer为接受/发送缓冲区,BUFSIZ为接受/发送缓冲区。后两个参数为发送或接受的对方地址,可以为NULL

二、网络层socket(三层socket)

可以参考本人一下博客或代码:

Linux下的raw Socket(原始套接字)编程:http://blog.csdn.net/luchengtao11/article/details/73878760

网络层多线程收发:https://github.com/Wuchenwcf/MyCode/blob/master/C%2B%2B/Linux/computer%20network/raw_socket_%E7%BD%91%E7%BB%9C%E5%B1%82%E5%A4%9A%E7%BA%BF%E7%A8%8B%E6%94%B6%E5%8F%91.cpp

(1)创建

 

socket(AF_INET, SOCK_RAW, IPPROTO_UDP );//第三个参数可以是UDP,TCP或者ICMP

(2)接收

 

recvfrom(sd, buffer, sizeof(buffer), 0,(struct sockaddr *)&client_addr, &addrlen));// 

 

后两个参数可以为null

接受的报文是从IP数据报的第一个字节开始的。

当内核有一个需要传递到原始套接字的IP数据报时,它将检查所有进程上的原始套接字,以寻找所有匹配的套接字。每个匹配懂得套接字将被递送以该iP数据报的一个副本。(事实证明,如果进程过多,匹配的套接字过多,内核会忙于数据报的软过滤和分发,而实际的套接字却空闲,导致性能下降
内核对每个原始套接字均执行如下3个测试,只有这三个测试为真,内核才把接收到的数据报递送到这个套接字。

【1】如果创建这个套接字时制订了非0的协议参数(socket的第三个参数),那么接受到的数据报的协议字段必须匹配该值,否则数据报不递送到这个套接字

【2】如果这个原始套接字已由bind调用绑定了某个本地IP地址,那么接受到的数据报的目的IP地址必须匹配这个绑定的地址,否则该数据报不递送到这个套接字。

【3】如果这个原始套接字已由connect调用指定了某个外地IP地址,那么接受到的数据报的源IP地址必须匹配这个已连接地址,否则该数据报不递送到这个套接字。

注意,如果一个原始套接字以0值协议参数创建的,而且没有bind和connect,那么该套接字将接收可由内核传递到原始套接字的每个原始数据报的一个副本。

(3)发送

 

/*
	如果IP_HDRINCL未开启,由进程让内核发送的数据是从IP首部之后的第一个字节开始的,内核会自动构造合适的IP
	如果IP_HDRINGL开启,进程需要自行构造IP包
	*/
	/*
	if (setsockopt(sd, IPPROTO_IP, IP_HDRINCL, val, sizeof(int)))
	{
		perror("setsockopt() error");
		exit(-1);
	}
	*/
sendto(sd, buffer, request_length, 0, (sockaddr *)&client_addr, addrlen);

 

 

三、数据链路层scoket(二层socket)

代码可以参考:

raw_socket_数据链路层收发实例:https://github.com/Wuchenwcf/MyCode/blob/master/C%2B%2B/Linux/computer%20network/raw_socket_%E6%95%B0%E6%8D%AE%E9%93%BE%E8%B7%AF%E5%B1%82%E6%94%B6%E5%8F%91%E5%AE%9E%E4%BE%8B.cpp
 

(1)创建

 

 

sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_IP));//第三个参数可以为ETH_P_ALL ETH_P_IP ETH_P_ARP等

 

(2)接受

 

 

struct sockaddr_ll client;
	socklen_t addr_length = sizeof(sockaddr_ll);
recvfrom(sock, buffer, 2048, 0, (sockaddr *)&client, &addr_length);//此时的地址是数据链路层的地址


接受的报文是从以太网的帧开始的

 

(3)发送

 

sendto(sock, sendbuffer, n, 0, (struct sockaddr *) &client, sizeof(client));

 

四、小结

 

引用:http://blog.csdn.net/firefoxbug/article/details/7561159

     网卡对该数据帧进行硬过滤(根据网卡的模式不同会有不同的动作,如果设置了promisc混杂模式的话,则不做任何过滤直接交给下一层输入例程,否则非本机mac或者广播mac会被直接丢弃).按照上面的例子,如果成功的话,会进入ip输入例程.但是在进入ip输入例程之前,系统会检查系统中是否有通过socket(AF_PACKET, SOCK_RAW, ..)创建的套接字.如果有的话并且协议相符,在这个例子中就是需要ETH_P_IP或者ETH_P_ALL类型.系统就给每个这样的socket接收缓冲区发送一个数据帧拷贝.然后进入下一步.

  其次,进入了ip输入例程(ip层会对该数据包进行软过滤,就是检查校验或者丢弃非本机ip或者广播ip的数据包等,具体要参考源代码),例子中就是如果成功的话会进入udp输入例程.但是在交给udp输入例程之前,系统会检查系统中是否有通过socket(AF_INET, SOCK_RAW, ..)创建的套接字.如果有的话并且协议相符,在这个例子中就是需要IPPROTO_UDP类型.系统就给每个这样的socket接收缓冲区发送一个数据帧拷贝.然后进入下一步。

最后,进入udp输入例程 ...

ps:如果校验和出错的话,内核会直接丢弃该数据包的.而不会拷贝给sock_raw的套接字,因为校验和都出错了,数据肯定有问题的包括所有信息都没有意义了.

 

 

  • 15
    点赞
  • 142
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 在Linux中,可以使用raw_socket发送LLDP(链路层发现协议)数据包。LLDP是一种链路层协议,用于在网络设备之间交换信息,以便发现相邻设备的识别信息。 要发送LLDP数据包,首先需要创建一个raw_socket。在Linux中,可以使用socket函数来创建套。需要指定套类型为AF_PACKET,并且设置协议为ETH_P_ALL以收所有的原始帧数据。示例代码如下: ```c #include <sys/socket.h> #include <linux/if_packet.h> #include <net/ethernet.h> int main() { int sockfd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); if (sockfd < 0) { perror("Failed to create socket"); return -1; } // 准备LLDP数据包 // ... // 发送LLDP数据包 // ... close(sockfd); return 0; } ``` 创建了套后,我们可以准备LLDP数据包。LLDP数据包包含了一些标准的TLV(类型、长度和值)段,用于传输设备的相关信息。具体的LLDP数据包格式可以参考LLDP协议的相关文档。 准备好LLDP数据包后,我们可以使用sendto函数发送数据包。需要指定套文件描述符、目标设备的mac地址和数据包长度。示例代码如下: ```c struct sockaddr_ll dest_addr; memset(&dest_addr, 0, sizeof(struct sockaddr_ll)); dest_addr.sll_family = AF_PACKET; dest_addr.sll_protocol = htons(ETH_P_ALL); dest_addr.sll_halen = ETH_ALEN; dest_addr.sll_ifindex = if_nametoindex("eth0"); if (sendto(sockfd, lldp_packet, lldp_packet_len, 0, (struct sockaddr*)&dest_addr, sizeof(struct sockaddr_ll)) < 0) { perror("Failed to send LLDP packet"); close(sockfd); return -1; } ``` 在发送数据包之前,我们需要指定目标设备的mac地址和发送数据包的网络口,这里使用了if_nametoindex函数来获取eth0对应的口索引。 发送完成后,记得关闭套。以上就是使用raw_socket在Linux上发送LLDP数据包的基本步骤和示例代码。 ### 回答2: 在Linux系统上,可以使用Raw Socket来发送LLDP(Link Layer Discovery Protocol)数据包。LLDP是一种链路层发现协议,用于发现连的设备和交换机之间的信息。 首先,需要创建一个原始,并设置套的协议为ETH_P_ALL。这样可以收所有链路层的数据包,包括LLDP数据包。 下来,需要构造LLDP数据包的数据结构,并填充相应的段。LLDP数据包包含了发送方和收方的一些基本信息,比如系统名称、端口号、VLAN ID等。 为了构造LLDP数据包,可以使用C语言或Python等编程语言,使用网络编程库如socket或scapy。根据LLDP协议的规范,构造LLDP数据包的格式并填充相应段。其中,需要设置目标MAC地址为LLDP的组播地址(01:80:C2:00:00:0E)。 构造完LLDP数据包后,通过原始将数据包发送出去。在发送时,需要将套的发送选项设置为SOCK_DGRAM,以保证数据包能够在链路层发送。 发送LLDP数据包后,可以通过其他设备或交换机来收,并解析收到的LLDP数据包,获取其中的信息。LLDP数据包中的信息可以用于拓扑发现、设备间连性的确认等。 总结而言,使用Linux的Raw Socket发送LLDP数据包的过程包括创建原始、构造LLDP数据包、设置目标MAC地址、发送数据包。通过这种方式,可以实现LLDP数据包的发送和链路层的信息交互。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值