一,使用socket发送广播包的方法:

1,sock = socket(AF_INET,SOCK_DGRAM,0);构建upd套接字

setsockopt(sock, SOL_SOCKET, SO_BROADCAST,(char*)&bbroadcast,sizeof(int));设置套接字属性,使之能发送广播包:4个255或

如192.168.136.255的包。也可以接受广播包,但是得保证广播包的端口号是自己绑定的端口号。

在windows下测试结果如上,而在Ubuntu下,有时候能发送4个255的广播包,有时候就只能发送192.168.136.255的包。原因不明。

注:Ubuntu下这样也可以发送4个255的广播包,前提是ip地址是自动获取或是手动填写的,而不能用ifconfig临时分配。

    这样设置,也可以接受网络上的广播包,只能接受发往套接字bind的端口的广播包。

 

 

2,fd = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_IP)),构建链路层套接字,就可直接发送广播包。

源码:1,

#include <netinet/in.h>    // for sockaddr_in
#include <sys/types.h>    // for socket
#include <sys/socket.h>    // for socket
#include <stdio.h>        // for printf
#include <stdlib.h>        // for exit
#include <string.h>        // for bzero
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
 
 
#define HELLO_WORLD_SERVER_PORT    6660
#define BUFFER_SIZE 1024
 
 
int main(int argc, char **argv)
{
    int err = 0;
    struct sockaddr_in client_addr;
    bzero(&client_addr,sizeof(client_addr));
    client_addr.sin_family = AF_INET;
    client_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    client_addr.sin_port = htons(0);
 
    int client_socket = socket(AF_INET,SOCK_DGRAM,0);
    if( client_socket < 0)
    {
        printf("Create Socket Failed!/n");
        exit(1);
    }
    if(bind(client_socket, (struct sockaddr *)&client_addr, sizeof(struct sockaddr)) == -1)
        perror("bind");
 
    int bbroadcast=1;
    err=setsockopt(client_socket, SOL_SOCKET, SO_BROADCAST,(char*)&bbroadcast,sizeof(int));
    if(err<0)
    {
        close(client_socket);
        exit(1);
   }
 
 
 
    struct sockaddr_in server_addr;
    bzero(&server_addr,sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.s_addr = inet_addr("255.255.255.255");
//  server_addr.sin_addr.s_addr = htonl(INADDR_BROADCAST);
    server_addr.sin_port = htons(6166);
//  server_addr.sin_port = htons(HELLO_WORLD_SERVER_PORT);
    socklen_t server_addr_length = sizeof(server_addr);
 
    char buffer[BUFFER_SIZE];
    bzero(buffer,BUFFER_SIZE);
    strcpy(buffer, "hello world");
    socklen_t n = sizeof(server_addr) ;
    while(1)
        sendto(client_socket,buffer,BUFFER_SIZE,0,(struct sockaddr*)&server_addr,n);
 
    return 0;
}

源码2:

raw_packet(&packet, INADDR_ANY, CLIENT_PORT, INADDR_BROADCAST,
 SERVER_PORT, MAC_BCAST_ADDR, client_config.ifindex);
int raw_packet(struct dhcpMessage *payload, uint32_t source_ip, int source_port,
    uint32_t dest_ip, int dest_port, uint8_t *dest_arp, int ifindex)
{
 int fd;
 int result;
 struct sockaddr_ll dest;
 struct udp_dhcp_packet packet;
 
 if ((fd = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_IP))) < 0) {
 DEBUG(LOG_ERR, "socket call failed: %m");
 return -1;
 }
 
 memset(&dest, 0, sizeof(dest));
 memset(&packet, 0, sizeof(packet));
 
 dest.sll_family = AF_PACKET;
 dest.sll_protocol = htons(ETH_P_IP);
 dest.sll_ifindex = ifindex;
 dest.sll_halen = 6;
 memcpy(dest.sll_addr, dest_arp, 6);
 if (bind(fd, (struct sockaddr *)&dest, sizeof(struct sockaddr_ll)) < 0) {
 DEBUG(LOG_ERR, "bind call failed: %m");
 close(fd);
 return -1;
 }
 
 packet.ip.protocol = IPPROTO_UDP;
 packet.ip.saddr = source_ip;
 packet.ip.daddr = dest_ip;
 packet.udp.source = htons(source_port);
 packet.udp.dest = htons(dest_port);
 packet.udp.len = htons(sizeof(packet.udp) + sizeof(struct dhcpMessage)); /* cheat on the psuedo-header */
 packet.ip.tot_len = packet.udp.len;
 memcpy(&(packet.data), payload, sizeof(struct dhcpMessage));
 packet.udp.check = checksum(&packet, sizeof(struct udp_dhcp_packet));
 
 packet.ip.tot_len = htons(sizeof(struct udp_dhcp_packet));
 packet.ip.ihl = sizeof(packet.ip) >> 2;
 packet.ip.version = IPVERSION;
 packet.ip.ttl = IPDEFTTL;
 packet.ip.check = checksum(&(packet.ip), sizeof(packet.ip));
 
 result = sendto(fd, &packet, sizeof(struct udp_dhcp_packet), 0, (struct sockaddr *) &dest, sizeof(dest));
 if (result <= 0) {
 DEBUG(LOG_ERR, "write on socket failed: %m");
 }
 close(fd);
 return result;
}

3,fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)。

   bind(fd, (struct sockaddr *)&client_addr, sizeof(struct sockaddr)) 

  这样可以接受广播包,只接收那些广播包的upd目的端口号是自己绑定的端口号的广播包。

前提是保证双方的ip地址在同一网段内。

 

二,原始数据包分析:

1. socket(AF_INET, SOCK_RAW, IPPROTO_UDP);

能:该套接字可以接收协议类型为(tcp udp icmp等)发往本机的ip数据包,从上面看的就是20+8+100.

不能:不能收到非发往本地ip的数据包(ip软过滤会丢弃这些不是发往本机ip的数据包).

不能:不能收到从本机发送出去的数据包.

发送的话需要自己组织tcp udp icmp等头部.可以setsockopt来自己包装ip头部

这种套接字用来写个ping程序比较适合

注:不能接受ip地址是非同一网段的广播包。能接受同一网段的主机发送的广播包。

 可定也能接受发往本机ip地址的单播包。

 

     2. socket(PF_PACKET, SOCK_RAW, htons(x));

这个套接字比较强大,创建这种套接字可以监听网卡上的所有数据帧.从上面看就是20+20+8+100.最后一个以太网crc从来都不算进来的,因为内核已经判断过了,对程序来说没有任何意义了.

能: 接收发往本地mac的数据帧

能: 接收从本机发送出去的数据帧(第3个参数需要设置为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的所有类型ip arp rarp的数据帧, 

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

发送的时候需要自己组织整个以太网数据帧.所有相关的地址使用struct sockaddr_ll 而不是struct 

sockaddr_in(因为协议簇是PF_PACKET不是AF_INET了),比如发送给某个机器,对方的地址需要使用struct sockaddr_ll.

这种socket大小通吃,强悍

 

注:非混杂模式下,可以接收同一局域网内任意主机发的广播包,肯定也接受发往本机的单播包

   混杂模式下,可以接收非发往本地的数据包。

 

三, int client_socket = socket(AF_INET,SOCK_DGRAM,0);

使用udp创建的套接字。也可以使用connect函数绑定一个远端的主机地址,这样就可以直接使用write/read了。

     int client_socket = socket(AF_INET,SOCK_STREAM,0);

使用tcp创建的套接字,使用connect绑定远端主机地址时,如果远端主机的相应端口的套接字没有设置成listen状态的话,

会返回-1,出错。