简单的通信(QUdpSocket、 QTcpSocket、QWebSocket)--Qt网络

 

    Qt实现简单的通信,一般用到UDP和TCP协议,前者是不可靠的协议,后者需要经过三次握手才能创建连接,故为可靠地传输协议;而Websocket协议是基于TCP的一种新的网络协议,它实现了浏览器与服务器全双工(full-duplex)通信——允许服务器主动发送信息给客户端。
    简单的通信,是创建了一个客户端和一个服务端。对基于不同协议的进行简单的学习、总结和协议之间对比。

 

    通信,最简单的说法就是发消息,无外乎创建连接、客户端向服务端发送消息或者两者互发、接收消息、和断开连接,这几个步骤。理解透了比较简单,总之我觉得记住几个点就可以了。

 

WebSocket

    WebSocket一种在单个 TCP 连接上进行全双工通讯的协议。WebSocket 使得客户端和服务器之间的数据交换变得更加简单,允许服务端直接向客户端推送数据而不需要客户端进行请求,在 WebSocket API 中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并允许数据进行双向传送。——- 《维基百科》

    WebSocket 协议使用 ws: 和 wss: URL 协议,以分别代表不安全和安全的 WebSocket 请求。

 

server服务端

    用Qt建立服务端用到了类QWebSocketServer、和QWebSocket 。码参考是Qt自带的实例。大概流程就是:

(1)监听端口port,即告诉服务器监听地址和端口的传入连接情况,当有连接将新连接信号和处理槽绑定,关闭信号依然,方便后面的处理。

if (m_pWebSocketServer->listen(QHostAddress::Any, port))
{
        if (m_debug)
            qDebug() << "Echoserver listening on port" << port;
        connect(m_pWebSocketServer, &QWebSocketServer::newConnection,
                this, &EchoServer::onNewConnection);
        connect(m_pWebSocketServer, &QWebSocketServer::closed, this, &EchoServer::closed);
    }

在当发出有新连接信号时,槽获取客户端的连接

QWebSocket *pSocket = m_pWebSocketServer->nextPendingConnection();

(2)在创建好连接,可以用pSocket接收数据,发送数据等。

    connect(pSocket, &QWebSocket::textMessageReceived, this, &EchoServer::processTextMessage);
    connect(pSocket, &QWebSocket::binaryMessageReceived, this, &EchoServer::processBinaryMessage);
    connect(pSocket, &QWebSocket::disconnected, this, &EchoServer::socketDisconnected);

    在实例代码中,看到了QObject的函数 sender()。简单提一句:当某一个目标emit一个信号的时候,它就是一个sender,系统会记录下当前是谁emit出这个signal的,所以在对应的slot里就可以通过 sender()得到当前是谁调用了了这个槽,对应的是QObject->d->sender。

 

client客户端

 

    使用WebSocket API创建一个简单的客户端。这个过程更简单:根据给的url地址打开连接,从而进行一系列操作。

简单粗暴,上代码,同样参考Qt实例

    值得注意的是,千万记得要在pro文件里添加  QT     += websockets

    Qt详细的对此类的实例,在这里

 

TcpSocket

TCP传输控制协议,面向连接、可靠地传输协议。

代码与websocket很类似。这个参考的文章是:这个,介绍的详细而更加简单。

 

客户端

客户端还是很简单,用到了类QTcpSocket。用到还是那几个:

(1)连接服务器(在创建好套接字的基础上)

tcpSocket->connectToHost(*serverIP,port);

 

(2)发送信息,这个和WebSocket有点不一样,用的是Write()函数。类似这样:

tcpSocket->write(msg.toLatin1();

 

(3)接收信息 ,看到两种接收方式,都贴出来。一个用的read(char * data, qint64 maxSize),一个用的readAll()。

方式一:

    while(tcpSocket->bytesAvailable()>0)
    {
        QByteArray datagram;
        datagram.resize(tcpSocket->bytesAvailable());
        tcpSocket->read(datagram.data(),datagram.size());
        QString msg=datagram.data();
        contentListWidget->addItem(msg.left(datagram.size()));
    }

 

方式二: 

 

void TcpClient::dataReceived()
{
    QByteArray buffer;  
      //读取缓冲区数据  
      buffer = socket->readAll();  
      if(!buffer.isEmpty())  
      {  
          QString str = ui->textEdit_Recv->toPlainText();  
          str+=tr(buffer);  
          //刷新显示  
          ui->textEdit_Recv->setText(str);  
      } 
}

 

 

服务端

 

服务端比客户端稍复杂点,多了类QTcpServer。感觉除了多了监听,也没多大区别。

 

(1)监听端口

server->listen(QHostAddress::Any,port);


(2)当服务器被客户端访问时,发出信号,转到槽。

connect(server,&QTcpServer::newConnection,this,&MainWindow::server_New_Connect);  
  
void MainWindow::server_New_Connect()  
{  
    //获取客户端连接  
    socket = server->nextPendingConnection();  
}  

(3)发送信息

socket->write(data);

(4)当socket接收缓冲区有新数据到来时,会发出readRead()信号,因此为该信号添加槽函数以读取数据。

void MainWindow::socket_Read_Data()  
{  
    QByteArray buffer;  
    //读取缓冲区数据  
    buffer = socket->readAll();  
}  

 

 

(5) 取消监听

 


 
  1. server->close();

 

Udp

 

    UDP 是User Datagram Protocol的简称, 中文名是用户数据报协议,是OSI(Open System Interconnection,开放式系统互联) 参考模型中一种无连接传输层协议,提供面向事务的简单不可靠信息传送服务。-------百度百科

 

客户端

 

udp客户端只用到一个类 ,即QUdpSocket。因为udp是一种无连接的传输协议,所以无需像上面的协议建立连接过程更加简单。

(1)建立套接字

    udpSocket=new  QUdpSocket(this);

 

(2)通过信号readyRead()发出,可调用槽来接收数据。

connect(udpSocket,SIGNAL(readyRead()),this,SLOT(dataReceived()));
void UdpClient::dataReceived()
{
    while(udpSocket->hasPendingDatagrams())  //有未处理的报文
    {
        QByteArray datagram;
        datagram.resize(udpSocket->pendingDatagramSize());
        udpSocket->readDatagram(datagram.data(),datagram.size());
        QString msg=datagram.data();
        receiveTextEdit->insertPlainText(msg);
    }
}

 

 

 

(3)绑定端口

    bool result=udpSocket->bind(port);  //绑定端口
    if(!result)
    {
        QMessageBox::information(this,tr("error"),tr("udp socket create error"));
        return;
    }

 

 

 

 

服务端

 

(1)建立套接字

    udpSocket=new QUdpSocket(this);

 

(2)发送数据

udpSocket->writeDatagram(msg.toLatin1(),QHostAddress::Broadcast,port);

 

从上面可以看出,这几个协议都差不多,可靠地协议就是要建立连接,断开连接,而 无连接的不可靠的协议,无需建立连接,直接绑定端口,就可以了。

 

 

附加一句,可能错误是有的,但是慢慢改。这只是一个记录。

 

发布了27 篇原创文章 · 获赞 15 · 访问量 4万+
展开阅读全文

UDP给指定IP和端口传输数据包,为什么一直显示Network is unreachable: Datagram send failed

11-25

**_ UDPClient.java** package com.bill.udp.client; import java.io.IOException; import java.io.RandomAccessFile; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetAddress; import java.net.InetSocketAddress; import com.bill.udp.util.UDPUtils; public class UDPClient { private static final String SEND_FILE_PATH = "D:/imgmap.jpg"; public static void main(String[] args){ long startTime = System.currentTimeMillis(); byte[] buf = new byte[UDPUtils.BUFFER_SIZE]; byte[] receiveBuf = new byte[1]; RandomAccessFile accessFile = null; DatagramPacket dpk = null; DatagramSocket dsk = null; int readSize = -1; try { accessFile = new RandomAccessFile(SEND_FILE_PATH,"r"); //构造数据报包,用来将长度为 length 的包发送到指定主机上的指定端口号。 dpk = new DatagramPacket(buf, buf.length,new InetSocketAddress(InetAddress.getByName("192.168.1.106"), 5000)); //创建数据报套接字,将其绑定到指定的本地地址。 dsk = new DatagramSocket(5001, InetAddress.getByName("192.168.1.106")); int sendCount = 0; while((readSize = accessFile.read(buf,0,buf.length)) != -1){ dpk.setData(buf, 0, readSize); dsk.send(dpk); // wait server response { while(true){ dpk.setData(receiveBuf, 0, receiveBuf.length); dsk.receive(dpk); // confirm server receive if(!UDPUtils.isEqualsByteArray(UDPUtils.successData,receiveBuf,dpk.getLength())){ System.out.println("resend ..."); dpk.setData(buf, 0, readSize); dsk.send(dpk); }else break; } } System.out.println("send count of "+(++sendCount)+"!"); } // send exit wait server response while(true){ System.out.println("client send exit message ...."); dpk.setData(UDPUtils.exitData,0,UDPUtils.exitData.length); dsk.send(dpk); dpk.setData(receiveBuf,0,receiveBuf.length); dsk.receive(dpk); if(!UDPUtils.isEqualsByteArray(UDPUtils.exitData, receiveBuf, dpk.getLength())){ System.out.println("client Resend exit message ...."); dsk.send(dpk); }else break; } }catch (Exception e) { e.printStackTrace(); } finally{ try { if(accessFile != null) accessFile.close(); if(dsk != null) dsk.close(); } catch (IOException e) { e.printStackTrace(); } } long endTime = System.currentTimeMillis(); System.out.println("time:"+(endTime - startTime)); } } **UDPUtils.java** package com.bill.udp.util; public class UDPUtils { private UDPUtils(){} /** transfer file byte buffer **/ public static final int BUFFER_SIZE = 50 * 1024; /** controller port **/ public static final int PORT = 50000; /** mark transfer success **/ public static final byte[] successData = "success data mark".getBytes(); /** mark transfer exit **/ public static final byte[] exitData = "exit data mark".getBytes(); public static void main(String[] args) { byte[] b = new byte[]{1}; System.out.println(isEqualsByteArray(successData,b)); } public static boolean isEqualsByteArray(byte[] compareBuf,byte[] buf){ if (buf == null || buf.length == 0) return false; boolean flag = true; if(buf.length == compareBuf.length){ for (int i = 0; i < buf.length; i++) { if(buf[i] != compareBuf[i]){ flag = false; break; } } }else return false; return flag; } public static boolean isEqualsByteArray(byte[] compareBuf,byte[] buf,int len){ if (buf == null || buf.length == 0 || buf.length < len || compareBuf.length < len) return false; boolean flag = true; int innerMinLen = Math.min(compareBuf.length, len); for (int i = 0; i < innerMinLen; i++) { if(buf[i] != compareBuf[i]){ flag = false; break; } } return flag; } } 问答

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 编程工作室 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览