二層廣播,Socket的收發

1.二層是指算機網絡七層模型中的二層,及鏈路層。

2.以太幀格式如下:

Ethernet II類型以太網幀的最小長度爲64字節(6+6+2+46+4),最大長度爲1518字節(6+6+2+1500+4)。其中前12字節分別標識出發送數據幀的源節點MAC地址和接收數據幀的目標節點MAC地址。(注:ISL封裝後可達1548字節,802.1Q封裝後可達1522字節)

3.注意項:1.如果是廣播,目的mac需要爲全f。2.客戶端和服務端不能同時綁定到同一個網卡上。3.如果客戶端和服務端是經過交換器獲取其他網卡轉接的,需要注意是否會隔離數據傳輸。

4.demo代碼如下:

頭文件如下:

server.h

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <unistd.h>           // close()
#include <string.h>           // strcpy, memset(), and memcpy()
#include <netdb.h>            // struct addrinfo
#include <sys/types.h>        // needed for socket(), uint8_t, uint16_t, uint32_t
#include <sys/socket.h>       // needed for socket()
#include <netinet/in.h>       // IPPROTO_TCP, INET_ADDRSTRLEN
#include <netinet/ip.h>       // struct ip and IP_MAXPACKET (which is 65535)
#include <netinet/tcp.h>      // struct tcphdr
#include <arpa/inet.h>        // inet_pton() and inet_ntop()
#include <sys/ioctl.h>        // macro ioctl is defined
#include <bits/ioctls.h>      // defines values for argument "request" of ioctl.
#include <net/if.h>           // struct ifreq
#include <linux/if_ether.h>   // ETH_P_IP = 0x0800, ETH_P_IPV6 = 0x86DD
#include <linux/if_packet.h>  // struct sockaddr_ll (see man 7 packet)
#include <net/ethernet.h>
#include <linux/types.h>
#include <errno.h>
#include <sys/signal.h>

#define ETH_ALEN 6


#define ETH_ALEN 6
#define MD5_LEN 4

#define device_id_len 8 
#define TRUE 1
#define FALSE 0
#define BIND_IF_NAME  "eth1"

__u8 stbMac[ETH_ALEN];
#define PRO_MAX_SIZE 92



#define SERVER_SOCKET_NAME "server_socket"
#define ETH_P_DEBUG 0x9979
#define PACKET_LEN(a) (sizeof(a) - sizeof(a.stMsgBody.payload) + ntohs(a.stMsgBody.length))

typedef char __S8;
typedef short __S16;
typedef int __S32;
typedef unsigned char __U8;
typedef unsigned short __U16;
typedef unsigned int __U32;
typedef unsigned long long __U64;
typedef void __VOID;

/**消息包頭*/
struct eth_header{
    __S8   ether_dhost[6]; /*6*/
    __S8   ether_shost[6]; /*6*/
    __U16 ether_type;  /*2*/
	__U8  md5[4];   /**md5校驗4*/
} __attribute__((packed)); 

/**消息體*/
typedef struct
{
	__U16 action;	/** PRIV_PACKET_TYPE 2*/
    __U32 session;	/** PPPoE session  4*/
	__U8 sad[8];	/** mac-uuid(3+5) */ 
	__U16 length;	/** Payload length  2*/
	__S8 payload[1480]; /* A bit of room to spare */
}__attribute__((packed))MESSAGE_BODY_ST;

/**packet = 1514bit*/
typedef struct PrivPacketStruct {
    struct eth_header ethHdr;   /** Ethernet header  18*/
    MESSAGE_BODY_ST stMsgBody; /** Ethernet body 1486*/
}__attribute__((packed))PrivPacket;

服務端代碼如下:

#include "server.h"

static __S32 ug_open_socket(__U16 u16Type, __S8 *ps8Ifname)
{
    __S32 fd = socket(PF_PACKET,SOCK_RAW,htons(u16Type));
    if (fd < 0) {
		printf("###### open socket fail");
        return -1;
    }

    __S32 optval = 1;
    if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &optval, sizeof(optval)) < 0) {
		printf(" ###### setsockopt fail");
		close (fd);
        return -1;
    }
    __S32 nRecvBuf=64*1024;  // 2KB
    if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF,(const char*)&nRecvBuf, sizeof(__S32)) < 0) {
		printf(" ###### setsockopt fail");
		close (fd);
        return -1;
    }
    
    struct sockaddr_ll sa;
    memset(&sa,0,sizeof(sa));
    sa.sll_family = AF_PACKET;
    sa.sll_protocol = htons(u16Type);
    sa.sll_ifindex = if_nametoindex(ps8Ifname);
    
    if(bind(fd,(struct sockaddr *)&sa,sizeof(sa)) < 0) {
		printf(" ###### bind fail");
		close (fd);
        return -1;
    }
    return fd;
}

int main(int argc, char const *argv[])
{
	int fd = -1;
   	int s32Len =-1;
    PrivPacket packet;
    char mac_buff[ETH_ALEN] = {0}; /**mac buff*/

	fd = ug_open_socket(ETH_P_DEBUG, BIND_IF_NAME);
	if(fd < 0){
		printf("fd create fail: %d",fd);
		return -1;
	}

    printf("server: read data from client ... \n");
 
    while (1)
    { 
	    memset(&packet,0,sizeof(packet)); 
        s32Len = read(fd,&packet,sizeof(packet));
        if (s32Len<=0) {
		    continue;
	    }    
	    printf("read s32Len: %d, action: %d\n",s32Len,ntohs(packet.stMsgBody.action));
    }
    
    return 0;
}

客戶端代碼如下:

#include "server.h"


int getGwMac(char *ifname,char *gwMac)
{
	struct ifreq ifr;
	int sd;
	printf("--------------\n");
	printf("ifname: %s\n",ifname);
	if ((sd = socket (PF_PACKET, SOCK_RAW, htons(ETH_P_ALL))) < 0) {//第一次創建socket是爲了獲取本地網卡信息
	    	printf("socket() failed to get socket descriptor for using ioctl()\n");
			printf("sd: %d\n",sd);
	    	return -1;
	}
	// Use ioctl() to look up interface name and get its MAC address.
	memset (&ifr, 0, sizeof (ifr));
	snprintf (ifr.ifr_name, sizeof (ifr.ifr_name), "%s", ifname);
	if (ioctl (sd, SIOCGIFHWADDR, &ifr) < 0) { 
	    	printf("ioctl() failed to get source MAC address\n");
			close (sd);
	    	return -1;
	}
	
	// Copy source MAC address.
	memcpy (gwMac, ifr.ifr_hwaddr.sa_data, ETH_ALEN * sizeof (__u8));
	printf("\n ###### get gwMac = %02X:%02X:%02X:%02X:%02X:%02X \n", gwMac[0], gwMac[1], gwMac[2], gwMac[3], gwMac[4], gwMac[5]);
	close (sd);
	return 0;
}
int  set_packet_head(PrivPacket *packet,const __u8 *src_hwaddr, const __u8 *dst_hwaddr,int len)
{
	unsigned char md5_buf[4];
	memset(md5_buf,0,sizeof(md5_buf));
	if (NULL == src_hwaddr) {
		printf("pstMsgBody fail\n");
		return -1;
	}
	memcpy(packet->ethHdr.ether_shost, src_hwaddr, ETH_ALEN);
	memcpy(packet->ethHdr.ether_dhost, dst_hwaddr, ETH_ALEN);
    packet->ethHdr.ether_type = htons(ETH_P_DEBUG);
	
	/**計算出消息體的md5*/
	memcpy(packet->ethHdr.md5,md5_buf,4);
	return 0;
}

/**發上報設備信息請求*/
void send_device_info(PrivPacket *packet)
{
	packet->stMsgBody.action = htons(0x0101);
	packet->stMsgBody.session = htonl(1);
	strcpy(packet->stMsgBody.sad,"NULL");
    packet->stMsgBody.length = htons(0);
	return ;
}

int main(int argc, char const *argv[])
{
    PrivPacket packet;
    char mac_buff[ETH_ALEN] = {0}; /**mac buff*/
    int ret = getGwMac(BIND_IF_NAME,mac_buff);
	if (ret < 0) {
		printf("getGwMac fail: %d",ret);
		return ret;
	}
	printf("getGwMac ok!\n");
    
    
    //創建socket
    __S32 serverFD = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_DEBUG));
    printf("client: create socket serverFD=%d\n", serverFD);

    if (serverFD < 0)
    {
        printf("client: error: create socket failed ... \n");
        return -1;
    }

    int optval = 1;
    if (setsockopt(serverFD, SOL_SOCKET, SO_KEEPALIVE, &optval, sizeof(optval)) < 0)
    {

        printf("client: error: setsockopt SO_KEEPALIVE  failed ... \n");
        close(serverFD);
        return -1;
    }
    int nRecvBuf = 2 * 1024; // 2KB
    if (setsockopt(serverFD, SOL_SOCKET, SO_RCVBUF, (const char *)&nRecvBuf, sizeof(int)) < 0)
    {
        printf("client: error: setsockopt SO_RCVBUF failed ... \n");
        close(serverFD);
        return -1;
    }


    struct sockaddr_ll sa;
    memset(&sa, 0, sizeof(sa));
    sa.sll_family = AF_PACKET;
    sa.sll_protocol = htons(ETH_P_DEBUG);
    sa.sll_ifindex = if_nametoindex(BIND_IF_NAME);

    if (bind(serverFD, (struct sockaddr *)&sa, sizeof(sa)) < 0)
    {
        printf("client: error:  bind failed ... \n");
        close(serverFD);
        return -1;
    }

    int writeRet = 0;
    memset(&packet,0,sizeof(packet));
    while (1)
    {
       	int n = 0;
		printf("enter select: \n");
		scanf("%d",&n);
		memset(&packet,0,sizeof(packet));  
		int len = 0;
		switch(n)
		{
			case 1:
				send_device_info(&packet);
				len = PACKET_LEN(packet) - 18;
				printf("PACKET_LEN(packet): %d,len: %d\n",PACKET_LEN(packet),len);
				set_packet_head(&packet,mac_buff,stbMac,len);
				ret = write(serverFD,&packet,PACKET_LEN(packet));
				if (ret <= 0) {
					printf("\n ######## write ret= %d\n",ret);
					break;
				}
				printf("\n ######## write ret= %d\n",ret);
				break;
			}
    }
    
    return 0;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章