网上有很多教程,他们说发送使用
sendto
,接收使用recvfrom
就可以,确实如此。但是你会用吗?
我们以QT(C++)为例,来使用这个udp实现自发自收的功能(途中我们会用到QThread来开启我们的线程)
服务端(线程)
#include "udpserverthread.h"
#include <QDebug>
#include <string>
UdpServerThread::UdpServerThread(QObject *parent)
:QThread(parent)
{
if(WSAStartup(MAKEWORD(2,2),&wsaData) != 0){
qDebug("server init faild");
}
isStop = true;
}
//SOCKET serverSocket
//sockaddr_in serverAddr
//int serverLen = sizeof(serverAddr)
void UdpServerThread::run(){
qDebug() << "server thread is run";
uint8_t recv[896]; //接收字符
isStop = false; //控制循环标志位
while(!isStop){
int size = recvfrom(serverSocket,(char *)recv,sizeof(recv),
0,(SOCKADDR *)&serverAddr,&serverLen); //接收
if(size > 0){
qDebug() << "size:" << size; //打印接收到数据的大小
}
}
}
void UdpServerThread::stop(){
isStop = true;
}
UdpServerThread::~UdpServerThread(){
}
我们这里只是一个接收到数据就打印的案例
客户端(线程)
#include "udpclientthread.h"
#include <QDebug>
UdpClientThread::UdpClientThread(QObject *parent)
:QThread(parent)
{
if(WSAStartup(MAKEWORD(2,2),&wsaData) != 0){
qDebug("client init faild");
}
isStop = true;
}
void UdpClientThread::run(){
qDebug() << "client thread is run";
while (!isStop) {
//发送,没什么效果,不用去管
/*int dataSize = sendto(clientSocket,buffer_char,sizeof(buffer),0,
(SOCKADDR *)&clientAddr,clientLen);
if(dataSize <= 0){
closesocket(clientSocket);
}*/
}
}
void UdpClientThread::stop(){
isStop = true;
}
UdpClientThread::~UdpClientThread(){
}
这里也没什么,就一个线程
主函数
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
//实例化客户端和服务端(线程)
clientThread = new UdpClientThread;
serverThread = new UdpServerThread;
//信号和槽
connect(ui->pushButton,&QPushButton::clicked,this,&MainWindow::on_pushButton_network);
connect(ui->pushButton_testSend,&QPushButton::clicked,this,&MainWindow::on_pushButton_TestSend);
}
void MainWindow::on_pushButton_network(){
//client
clientThread->clientSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
//udpThread->sockWIN: SOCKET sockWIN;
if(clientThread->clientSocket != INVALID_SOCKET){//INVALID_SOCKET:检查错误以确保套接字是有效的套接字
closesocket(clientThread->clientSocket);//关闭套接字
}
clientThread->clientSocket = socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP);
if(clientThread->clientSocket == INVALID_SOCKET){//INVALID_SOCKET:检查错误以确保套接字是有效的套接字
QMessageBox::critical(this, tr("错误"), tr("无法创建 Socket!"));//显示提示
return;
}
clientThread->clientAddr.sin_family = AF_INET;
clientThread->clientAddr.sin_addr.S_un.S_addr = inet_addr("192.168.1.191");
clientThread->clientAddr.sin_port = htons(8080);
clientThread->serverAddr.sin_family = AF_INET;
clientThread->serverAddr.sin_addr.S_un.S_addr = inet_addr("192.168.1.191");
clientThread->serverAddr.sin_port = htons(5505);
if (bind(clientThread->clientSocket, (sockaddr *)&clientThread->clientAddr,
sizeof(clientThread->clientAddr)) == SOCKET_ERROR)
{//绑定出错
closesocket(clientThread->clientSocket);
clientThread->clientSocket = INVALID_SOCKET;
QMessageBox::critical(this, tr("错误"), tr("端口无法打开或被占用!"));
return;
}
//server
serverThread->serverSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
//udpThread->sockWIN: SOCKET sockWIN;
if(serverThread->serverSocket != INVALID_SOCKET){//INVALID_SOCKET:检查错误以确保套接字是有效的套接字
closesocket(serverThread->serverSocket);//关闭套接字
}
serverThread->serverSocket = socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP);
if(serverThread->serverSocket == INVALID_SOCKET){//INVALID_SOCKET:检查错误以确保套接字是有效的套接字
QMessageBox::critical(this, tr("错误"), tr("无法创建 Socket!"));//显示提示
return;
}
serverThread->clientAddr.sin_family = AF_INET;
serverThread->clientAddr.sin_addr.S_un.S_addr = inet_addr("192.168.1.191");
serverThread->clientAddr.sin_port = htons(5505);
serverThread->serverAddr.sin_family = AF_INET;
serverThread->serverAddr.sin_addr.S_un.S_addr = inet_addr("192.168.1.191");
serverThread->serverAddr.sin_port = htons(8080);
if (bind(serverThread->serverSocket, (sockaddr *)&serverThread->clientAddr,
sizeof(serverThread->clientAddr)) == SOCKET_ERROR)
{//绑定出错
closesocket(serverThread->serverSocket);
serverThread->serverSocket = INVALID_SOCKET;
QMessageBox::critical(this, tr("错误"), tr("端口无法打开或被占用!"));
return;
}
clientThread->start();
serverThread->start();
}
void MainWindow::on_pushButton_TestSend(){
//发送
uint8_t buffer[896];
uint8_t recv[896];
buffer[0] = 0x66;
buffer[1] = 0xff;
buffer[2] = 0xec;
char *buff_char = reinterpret_cast<char *>(buffer);
char *recv_char = reinterpret_cast<char *>(recv);
int dataSize = sendto(clientThread->clientSocket,buff_char,sizeof(buff_char),0,
(SOCKADDR *)&clientThread->serverAddr,clientThread->serverLen);
qDebug() << "main test send size:" << dataSize;
// int size = recvfrom(serverThread->serverSocket,(char *)recv,sizeof(recv),0,
// (SOCKADDR *)&serverThread->clientAddr,&serverThread->clientLen);
// qDebug() << "main test recv size" << size;
}
MainWindow::~MainWindow()
{
delete ui;
}
关于信号和槽这里你只需要知道,我们通过一个ui界面的按键,按下按键触发on_pushButton_network(打开线程和网路)
和on_pushButton_TestSend(客户端发送固定数据)
俩个函数
关于Qt线程 | 关于C++线程
注意点
我们这个接收
recvfrom
和发送sendto
的俩个函数中的内容
参数一
: 发送的绑定的是发送的socket套接字; 接收的是接收的socket套接字参数二
: 发送的是发送的数组(缓存);接收的是接收的数组(缓存)参数三
:发送的是发送的数组(缓存)大小;接收的是接收的数组(缓存)大小参数四
:先不用管,默认给它0参数五
: 是一个SOCKADDR
。注意发送端是发送端绑定的对方地址
和发送端绑定的对方地址的大小
;服务端是服务端绑定的对方(可以是发送端)地址
和服务端绑定的对方(可以是发送端)地址的大小
在客户端和服务端进行自我绑定的时候,俩个端口要想
Tx->Rx
,Rx->Tx
那样要反正来,不能Tx->Tx
,Rx->Rx
即便是有客户端和线程的加护,也不能直接在
run()
中一直发送数据