众所周知,一个IP提供65536个端口。假如某个端口被一个套接字占用,其他套接字就不能 再占用这个端口了。但是根据https://blog.csdn.net/fz835304205/article/details/16980163/ 的描述,在UDP组播模式下,利用SO_REUSEADDR可以实现两个不同的套接字使用同一个端口。以下是代码实例:
main.cpp
#include "mainwindow.h"
#include <QApplication>
#include "thrdudp.h"
#include "udp2.h"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
thrdUDP1 udp1;
thrdUDP2 udp2;
udp1.start();
udp2.start();
w.show();
return a.exec();
}
下面的类thrdUDP1和thrdUDP2使用的是同一个IP地址和端口号7004
thrdUDP1.h
#ifndef THRDUDP_H
#define THRDUDP_H
#include <stdio.h>
#include <WinSock2.h>
#include <ws2tcpip.h>
#include <QThread>
class thrdUDP1 : public QThread
{
Q_OBJECT
public:
explicit thrdUDP1(QObject *parent = 0);
int m_Socket;
void vSend(void);
protected:
void run();
};
#endif // THRDUDP_H
thrdUDP1.cpp
#include "thrdudp.h"
#include "mainwindow.h"
extern MainWindow * g_pMainWin;
thrdUDP1 * g_pUDP1 = NULL;
#pragma comment(lib, "Ws2_32.lib")
thrdUDP1::thrdUDP1(QObject *parent) : QThread(parent)
{
g_pUDP1 = this;
}
void thrdUDP1::run()
{
WSAData wsaData;
int iRet = WSAStartup(MAKEWORD(2,2), &wsaData);
bool m_bBind;
struct sockaddr_in m_sinOwn;
m_Socket = socket(AF_INET, SOCK_DGRAM, 0);
if(m_Socket >= 0)
{
int iOptVal = 1;
setsockopt(m_Socket, SOL_SOCKET, SO_REUSEADDR, (const char *)&iOptVal, sizeof(iOptVal));
memset(&m_sinOwn, 0, sizeof(m_sinOwn));
m_sinOwn.sin_family = AF_INET;
m_sinOwn.sin_addr.s_addr = inet_addr("192.168.8.8");
m_sinOwn.sin_port = htons(7004);
if(bind(m_Socket, (struct sockaddr *)&m_sinOwn, sizeof(struct sockaddr_in)) == 0)
{
m_bBind = true;
}
else
{
m_bBind = false;
}
}
else
{
m_bBind = false;
}
if(m_bBind)
{
struct ip_mreq m_req;
m_req.imr_multiaddr.s_addr = inet_addr("225.0.0.23");
m_req.imr_interface.s_addr = htonl(INADDR_ANY);
int err = setsockopt(m_Socket, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *)&m_req, sizeof(m_req));
if(0 == err)
{
printf("starting\n");
while(true)
{
char arrRet[20] = {0};
recv(m_Socket, arrRet, 20, 0);
g_pMainWin->vUpdateEdt1(arrRet);
}
}
}
if(m_Socket > 0)
closesocket(m_Socket);
WSACleanup();
}
void thrdUDP1::vSend(void)
{
sockaddr_in s;
s.sin_family = AF_INET;
s.sin_port = htons(4001);
s.sin_addr.S_un.S_addr = inet_addr("225.0.0.23");
::sendto(m_Socket, "456", 3, 0, (sockaddr *)&s, sizeof(s));
}
thrdUDP2.cpp
#include "udp2.h"
#include "mainwindow.h"
extern MainWindow * g_pMainWin;
thrdUDP2 * g_pUDP2 = NULL;
#pragma comment(lib, "Ws2_32.lib")
thrdUDP2::thrdUDP2(QObject *parent) : QThread(parent)
{
g_pUDP2 = this;
}
void thrdUDP2::run()
{
bool m_bBind;
WSAData wsaData;
int iRet = WSAStartup(MAKEWORD(2,2), &wsaData);
struct sockaddr_in m_sinOwn;
m_Socket = socket(AF_INET, SOCK_DGRAM, 0);
if(m_Socket >= 0)
{
int iOptVal = 1;
setsockopt(m_Socket, SOL_SOCKET, SO_REUSEADDR, (const char *)&iOptVal, sizeof(iOptVal));
memset(&m_sinOwn, 0, sizeof(m_sinOwn));
m_sinOwn.sin_family = AF_INET;
m_sinOwn.sin_addr.s_addr = inet_addr("192.168.8.8");
m_sinOwn.sin_port = htons(7004);
if(bind(m_Socket, (struct sockaddr *)&m_sinOwn, sizeof(struct sockaddr_in)) == 0)
{
m_bBind = true;
}
else
{
m_bBind = false;
}
}
else
{
m_bBind = false;
}
if(m_bBind)
{
struct ip_mreq m_req;
m_req.imr_multiaddr.s_addr = inet_addr("225.0.0.23");
m_req.imr_interface.s_addr = htonl(INADDR_ANY);
int err = setsockopt(m_Socket, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *)&m_req, sizeof(m_req));
if(0 == err)
{
printf("starting\n");
while(true)
{
char arrRet[20] = {0};
recv(m_Socket, arrRet, 20, 0);
g_pMainWin->vUpdateEdt2(arrRet);
}
}
}
if(m_Socket > 0)
closesocket(m_Socket);
WSACleanup();
}
void thrdUDP2::vSend(void)
{
sockaddr_in s;
s.sin_family = AF_INET;
s.sin_port = htons(4001);
s.sin_addr.S_un.S_addr = inet_addr("225.0.0.23");
::sendto(m_Socket, "123", 3, 0, (sockaddr *)&s, sizeof(s));
}
mainwindow.cpp
#include "thrdudp.h"
#include "udp2.h"
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow * g_pMainWin = NULL;
extern thrdUDP1 * g_pUDP1;
extern thrdUDP2 * g_pUDP2;
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
g_pMainWin = this;
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::vUpdateEdt1(char * pData)
{
ui->Edt1->setText(QString(pData));
}
void MainWindow::vUpdateEdt2(char * pData)
{
ui->Edt2->setText(QString(pData));
}
void MainWindow::on_pushButton_clicked()
{
g_pUDP1->vSend();
}
void MainWindow::on_pushButton_2_clicked()
{
g_pUDP2->vSend();
}
效果:
从另一个计算机发送数据:
两个套接字都收到了数据:
同样,两个套接字也都可以向网络助手发送信息