目录:
知识储备
接收端重点:
(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上用于通信。