Udp编程 - 客户端和服务端的注意事项

网上有很多教程,他们说发送使用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的俩个函数中的内容

  1. 参数一: 发送的绑定的是发送的socket套接字; 接收的是接收的socket套接字
  2. 参数二: 发送的是发送的数组(缓存);接收的是接收的数组(缓存)
  3. 参数三:发送的是发送的数组(缓存)大小;接收的是接收的数组(缓存)大小
  4. 参数四:先不用管,默认给它0
  5. 参数五: 是一个SOCKADDR注意发送端是发送端绑定的对方地址发送端绑定的对方地址的大小;服务端是服务端绑定的对方(可以是发送端)地址服务端绑定的对方(可以是发送端)地址的大小

在客户端和服务端进行自我绑定的时候,俩个端口要想Tx->Rx,Rx->Tx那样要反正来不能Tx->Tx,Rx->Rx

即便是有客户端和线程的加护,也不能直接在run()中一直发送数据

猜你喜欢

转载自blog.csdn.net/yasinawolaopo/article/details/131851294
今日推荐