Linux下使用RAW SOCKET原始套接字构造UDP原始数据帧广播到局域网,在局域网的另一台计算机上显示UDP发送的信息

Linux RAW SOCKET构造UDP广播数据帧及接收解析
本文介绍了如何在Linux环境下使用RAW SOCKET构造UDP原始数据帧进行局域网广播,并在另一台计算机上使用QT程序接收显示。内容包括遇到的问题分析,如UDP校验和错误、数据帧ID的影响、同一主机无法接收广播等,以及相关源码示例。

因为使用IEC61850需要直接访问以太网数据链路层,因此需要做一些访问数据链路层的准备工作。计划使用Linux C构造UDP原始帧在局域网内广播消息,并在另一台电脑上使用QT程序接收和显示这个广播消息。

网上关于使用RAW SOCKET构造原始帧的资料很少,在外国的一个网站上找到了一份源码,经过改造后可以再局域网内广播UDP了。 在此之前遇到了很多问题,在此总结:

(1)使用wireshark可以抓取到UDP数据帧,但应用层的QT程序就是不能读到这个UDP中的数据。经测试,这可能是因为UDP段的校验和错误,wireshark并不会报错,但该UDP无法被应用层的程序接收(经过本人测试)。

(2)以为identification(ID)的值对于发送只有一个数据帧的UDP会产生影响。经测试,如果UDP只有一个数据帧,则identification可以取任意值,不会影响UDP消息的正确接收。

(3)构造UDP原始帧的Linux C程序和接收UDP消息的QT程序若放在同一台计算机下测试,则无法接收到广播的UDP消息;若将两个程序分别放在两台不同的计算机下运行,则UDP广播消息可被正确地接收。(本人分析的原因:本机上发送的UDP广播可能不会广播给自身,从而导致了在同一台计算机上不能正确接收)

(4)为何wireshark抓到的UDP原始帧中最后几位为"00",wireshark显示其属于trailer。在以太网中,规定最小的数据包为64字节(和碰撞检测有关),如果数据包不足64字节,则会由网卡填充,所以显示到后面几位就是"00"。

(5)以太网帧的最小长度到底是多少?不是64字节么?为什么看到了42字节的arp包?  是64字节,你用wireshark抓到的包是把最后4个字节的FCS丢掉的结果,在没有达到64字节时,网卡驱动会自动填充到64字节。楼主看到的42字节,可能是wireshark做了处理,去掉了填充部分。

在这个过程中其实遇到了很多问题,就不一一列举了,再好的文字都不如代码来的实在,下面是一些源码:

 

【1】接收UDP消息的QT程序

/**
*这个程序功能很简单:监听6666端口的UDP消息,如果接收到消息则显示。
*/

#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    socket=new QUdpSocket(this);
    connect(ui->startButton,SIGNAL(clicked()),this,SLOT(startDo()));
    connect(ui->clearButton,SIGNAL(clicked()),ui->historyTextEdit,SLOT(clear()));
    connect(ui->quitButton,SIGNAL(clicked()),this,SLOT(quitDo()));
}

MainWindow::~MainWindow()
{
    delete ui;
}

bool MainWindow::validatePort(QString port)
{
    bool ok=false;
    int tmp_port=port.toInt(&ok,10);
    if(ok){
        if((tmp_port>1024)&&(tmp_port<65536)){
            return true;
        }else{
            return false;
        }
    }else{
        return false;
    }
}

//初始化程序UDP相关的部分
void MainWindow::startDo()
{
    QString port=ui->portEdit->text();
    bool bo=validatePort(port);
    if(bo && (port != "")){
        bool ok=socket->bind((quint16)port.toInt());
        if(ok){
           ui->historyTextEdit->append("bind indicated port!");
        }else{
            ui->historyTextEdit->append("bind error!");
        }
    }else{
        bool ok=socket->bind(DEFAULT_PORT);
        if(ok){
            ui->historyTextEdit->append("bind default port!");
        }else{
            ui->historyTextEdit->append("bind error!");
        }
    }
    connect(socket,SIGNAL(readyRead()),this,SLOT(readData()));
    ui->startButton->setEnabled(false);
}

//读取UDP数据报中的信息
void MainWindow::readData()
{
    while(socket->hasPendingDatagrams()){
        QByteArray arr;
        arr.resize(socket->pendingDatagramSize());
        socket->readDatagram(arr.data(),arr.size());
        ui->historyTextEdit->append(arr);
    }
}

void MainWindow::quitDo()
{
    exit(0);
}


通过了测试的Linux C UDP原始帧构造程序有两个,便于参考。

【2】Linux C UDP构造参考程序一

/**
*功能:构造UDP 原始帧,并使用Linux原始套接字发送到局域网中。
*UDP数据帧中包含了一个测试用的字符串“123456789”
**/
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/if_ether.h>
#include <netinet/ip.h>
#include <linux/if_packet.h>
#include <linux/if.h>
#include <linux/sockios.h>
#include <stdio.h>
#include <unistd.h>

int main(void)
{
	int fd=0;
	char buf[256]={0};
	struct sockaddr_ll dest;
	int destlen=0;
	int ret=0;
	struct ifreq ifstruct;
	fd=socket(PF_PACKET,SOCK_RAW,htons(ETH_P_ALL));
	if(fd<0){
		printf("ERR:socket was failed.\n");
		return -1;
	}
	
	memset((char *)&dest, 0x00,sizeof(dest));
	destlen=sizeof(dest);
	strcpy(ifstruct.ifr_name, "eth0");
	ioctl(fd, SIOCGIFINDEX, &ifstruct);
	dest.sll_ifindex=ifstruct.ifr_ifindex;
	
	dest.sll_family=AF_PACKET;
    	unsigned char my_mac[]={0x08,0x00,0x27,0x93,0x68,0xe5};
	memcpy(dest.sll
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值