MENU

局域网IP多播

December 24, 2019 • 阅读: 1611 • 笔记&折腾



目录:

知识储备

接收端重点:

(1)、创建一个 SOCK_DGRAM 类型的 Socket。

(2)、将此 Socket 绑定到本地的一个端口上,为了接收服务器端发送的多播数据。

(3)、加入多播组。

(4)、接收多播数据。

发送端重点:

(1)、创建一个 SOCK_DGRAM 类型的 Socket。

(2)、加入多播组。

(3)、发送多播数据。

当一台主机想要同时接收或者发送时,必须绑定相同的端口号。

在网络中,线程的控制是非常重要的,在此实验中我采用了双线程,主线程用于监听发送端,副线程用于监听接收端。

我编不下去了,自己看代码吧。

采用QT creator平台,项目文件目录如下:

首先添加库:在untitled.pro文件中任意空白行插入 LIBS += -lWs2_32

GUI布局为:

代码

mainwindow.h 用于声明一下全局变量。

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include "thread.h"

#include <iostream>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <stdio.h>
#include <stdlib.h>
class TestThread;
//全局变量声明
extern struct sockaddr_in remote,local,from;
extern SOCKET sock,sockM;

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT


public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();

private slots:
    void on_pushButton_clicked();
    void receiver(char*,char*);
    void on_pushButton_2_clicked();

private:
    Ui::MainWindow *ui;
    TestThread *Thread;
};

#endif // MAINWINDOW_H

thread.h 用于创建线程类:

#ifndef TESTTHREAD_H
#define TESTTHREAD_H

#include <QThread>

#include "mainwindow.h"

class TestThread : public QThread
{
    Q_OBJECT
public:
    explicit TestThread(QObject *parent = 0);

protected:
    void run();

signals:
    void TestSignal(char*,char*);

private:
    int tnc=4;
};

#endif // TESTTHREAD_H

main.cpp 无需改动

mainwindow.cpp 中绑定按钮监听事件和发送消息

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

#include <iostream>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <stdio.h>
#include <stdlib.h>
#define MCASTADDR "233.0.0.1" //本例使用的多播组地址。
#define MCASTPORT 5150 //本地端口号。
#define BUFSIZE 1024 //发送数据缓冲大小。

//#pragma comment(lib,"ws2_32")
using namespace std;
SOCKET sock,sockM;
struct sockaddr_in remote,local;


void MainWindow::receiver(char* recvbuf,char* ipadd){
    cout<<ipadd << "+"<<recvbuf <<endl;
    QString recvip = QString::fromLocal8Bit(ipadd);
    QString recvtext = QString::fromLocal8Bit(recvbuf);
    ui->textEdit->textCursor().insertText("<"+recvip+"> : \n  "+recvtext+"\n");
}

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    WSADATA wsd;
    if( WSAStartup( MAKEWORD(2,2),&wsd) != 0 ){
        cout << "WSAStartup() failed\n" << endl;
     }
    if((sock=WSASocket(AF_INET,SOCK_DGRAM,0,NULL,0,WSA_FLAG_MULTIPOINT_C_LEAF|WSA_FLAG_MULTIPOINT_D_LEAF|WSA_FLAG_OVERLAPPED)) == INVALID_SOCKET)
    {
        cout<< "socket failed with:" << WSAGetLastError() <<endl;
        WSACleanup();
    }
    //将 sock 绑定到本机某端口上。
    local.sin_family = AF_INET;
    local.sin_port = htons(MCASTPORT);
    local.sin_addr.s_addr = INADDR_ANY;
    if( bind(sock,(struct sockaddr*)&local,sizeof(local)) == SOCKET_ERROR )
    {
        cout<< "bind failed with:" << WSAGetLastError() <<endl;
        closesocket(sock);
        WSACleanup();
    }
     remote.sin_family = AF_INET;
     remote.sin_port = htons(MCASTPORT);
     remote.sin_addr.s_addr = inet_addr( MCASTADDR );
     if(( sockM = WSAJoinLeaf(sock,(SOCKADDR*)&remote,sizeof(remote),NULL,NULL,NULL,NULL,JL_BOTH)) == INVALID_SOCKET)
     {
            cout<< "WSAJoinLeaf() failed: " << WSAGetLastError() <<endl;
            closesocket(sock);
            WSACleanup();
     }
     Thread = new TestThread();//新建线程
     connect(Thread, SIGNAL(TestSignal(char*,char*)), this, SLOT(receiver(char*,char*)));
     Thread->start();
}

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

void MainWindow::on_pushButton_clicked()
{
    QString sendtext = ui->lineEdit->text();
    char sendbuf[BUFSIZE];
    strcpy(sendbuf,sendtext.toLatin1().data());
    cout <<"sendbuf: "<< sendbuf <<endl;

    if( sendto(sockM,(char*)sendbuf,strlen(sendbuf),0,(struct sockaddr*)&remote,sizeof(remote))==SOCKET_ERROR)
     {
            printf("sendto failed with: %d\n",WSAGetLastError());
            closesocket(sockM);
            closesocket(sock);
            WSACleanup();
     }
    cout <<"send sucessfully."<<endl;
    ui->lineEdit->clear();
}

void MainWindow::on_pushButton_2_clicked()
{
    exit(0);
    closesocket(sockM);
    closesocket(sock);
    WSACleanup();
}

thread.cpp 用于副线程接收消息:

#include "thread.h"
#include "mainwindow.h"

#include <iostream>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <stdio.h>
#include <stdlib.h>
#define MCASTADDR "233.0.0.1" //本例使用的多播组地址。
#define MCASTPORT 5150 //本地端口号。
#define BUFSIZE 1024 //发送数据缓冲大小。
using namespace std;

struct sockaddr_in from;

TestThread::TestThread(QObject *parent) :
    QThread(parent)
{
}

void TestThread::run()
{
    //触发信号
    char recvbuf[BUFSIZE];
    /*struct ip_mreq mcast; // Winsock1.0 */
    int len = sizeof( struct sockaddr_in);
    int ret;
    while(1){
        if(( ret = recvfrom(sock,recvbuf,BUFSIZE,0,(struct sockaddr*)&from,&len)) == SOCKET_ERROR){
            printf("recvfrom failed with:%d\n",WSAGetLastError());
            closesocket(sockM);
            closesocket(sock);
            WSACleanup();
        }
        recvbuf[ret] = '\0';
        emit TestSignal(recvbuf,inet_ntoa(from.sin_addr));
    }
}

运行效果

仅适用于局域网,可将改程序打包在局域网的其他PC上用于通信。

Last Modified: February 6, 2021