一、TCP和UDP区别:
1、TCP:
(1)、面向连接的,连接需要3次握手,断连需要4次挥手,即双向连接、双向断开。
(2)、流式传输协议,发送端和接收端处理数据量可以不均等,比如发送端一次发送10M数据,接收端每次接收1M数据,分10次接收。
(3)、数据可靠的,有数据校验机制,若数据包丢失则自动重传。
2、UDP:
(1)、面向无连接的,双方直接通信,无需连接。
(2)、报文式传输协议,发送端和接收端处理数据量均等,比如发送端一次发送1M报文,接收端要么一次接收1M报文,要么丢包,不存在接收一半报文的情况。
(3)、数据不可靠的,报文丢失就丢了。
二、简介
1、 在标准C++中,没有提供专门用于套接字通信的类,所以只能使用操作系统提供的基于C的API函数。
2、 基于C的API函数,我们可以封装自己的类。
3、 Qt是C++的一个框架,并且封装了用于套接字通信的类。
4、 在Qt中使用标准C的API进行套接字通信也是可以的。
**5、**网络通信和语言没有关系,只要搞清楚是TCP还是UDP。
三、TCP的C通信流程
cpp
TCP客户端 TCP服务器端
socket()
|
bind()
socket() |
| listen()
connect() |
| \ 建立连接 accept()
| \ \ \ \ \ |
| 阻塞直到有客户端连接
| 请求数据 |
send()------------------> recv()
| 应答数据 |
recv()<------------------ send()
| 结束连接 |
| / / / / / / / / recv()
| / |
close() close()
四、UDP的C通信流程(没有严格意义上的客户端和服务器端)
cpp
SERVER CLIENT
socket() socket()
| |
bind() |
| request // sendto()
recvfrom() // |
| |
sendto() \\\\\\\\\\\ |
| response \\\\\\ recvfrom()
| |
close() close()
五、使用Qt提供的类进行基于TCP的套接字通信需要用到两个类:
1、QTcpServer:
服务器类,用于监听客户端连接以及和客户端建立连接。
2、QTcpSocket:
通信的套接字类,客户端、服务器端都需要使用。
注意: 这些通信类都属于网络模块network。
六、QTcpServer常用API函数:
1、QTcpServer(QObject * parent = nullptr)
(1)、构造函数
2、bool listen(const QHostAddress & address = QHostAddress::Any, quint16 port = 0)
(1)、告诉服务器监听地址address和端口port上的连接。
(2)、如果端口为0则会自动选择端口。
(3)、如果地址为QHostAddress::Any则服务器将监听所有网络接口。
(4)、成功返回true,失败返回false。
3、bool isListening() const
(1)、如果当前服务器正在监听则返回true,否则返回false。
4、virtual QTcpSocket * nextPendingConnection()
(1)、得到和客户端建立连接后用于通信的QTcpSocket对象。
(2)、注意,QTcpSocket对象是QTcpServer对象的子对象,当QTcpServer对象被销毁时,QTcpSocket对象会自动销毁。
(3)、但是,QTcpSocket通信完后,明确删除它是比较推荐的,以避免浪费内存,因为QTcpServer对象可能要继续监听很久。
(4)、注意,QTcpSocket对象不能在其它线程中使用,如果非要这样做,得重写incomingConnection()。
5、QHostAddress serverAddress() const
(1)、如果服务器正在监听连接,则返回服务器的地址,否则返回QHostAddress::Null。
(2)、这个地址就是C接口bind()的地址。
6、quint16 serverPort() const
(1)、如果服务器正在监听连接,则返回服务器的端口,否则返回0。
(2)、这个端口就是C接口bind()的端口。
7、bool waitForNewConnection(int msec = 0, bool *timedOut = nullptr)
(1)、阻塞等待客户端发起的连接请求。
(2)、不推荐在单线程程序中使用,建议使用非阻塞方式处理连接,即使用信号newConnection()。
(3)、最多等待msec毫秒,timedOut是出参,如果超时为true,没有超时为false。
(4)、如果有连接返回true,没有连接返回false。
8、void newConnection()
(1)、信号。
(2)、每次有新连接可用时都会发出此信号,无论它是否已添加到挂起的连接队列中。
9、void acceptError(QAbstractSocket::SocketError socketError)
(1)、信号。
(2)、当接受新连接导致错误时,将发射如下信号。
(3)、socketError参数描述了发生错误的信息。
七、QTcpSocket常用API函数:
1、 在服务器端,QTcpSocket对象是QTcpServer对象创建的,可以直接通信。
在客户端,需要程序员自己创建QTcpSocket对象,并且得先和服务器端建立连接,才能通信。
2、 继承关系:
QTcpSocket --> QAbstractSocket --> QIODevice。
QTcpSocket的读、写函数是继承QIODevice类中通用的。
3、QTcpSocket(QObject *parent = nullptr)
(1)、构造函数。
4、void connectToHost(const QHostAddress &address, quint16 port, QIODeviceBase::OpenMode openMode = ReadWrite)
(1)、连接服务器,需要指定服务器端绑定的IP和端口信息。
(2)、还有一个重载函数,其它参数一般默认。
5、QByteArray readAll()
(1)、从设备中读取所有剩余数据,并将其作为字节数组返回。
(2)、此函数无报错。
(3)、若返回的QByteArray对象为空,意味着当前没有可供读取的数据,或者发生了错误。
6、QByteArray read(qint64 maxSize)
(1)、从设备中读取最多maxSize字节数据,并将读取的数据作为QByteArray返回。
(2)、此函数无报错。
(3)、若返回的QByteArray对象为空,意味着当前没有可供读取的数据,或者发生了错误。
7、qint64 write(const char *data)
(1)、将数据内容写入设备,遇到'\0'结束。
(2)、返回实际写入的字节数。
(3)、如果发生错误,则返回-1。
8、qint64 write(const QByteArray &data)
(1)、将数据内容写入设备。
(2)、返回实际写入的字节数。
(3)、如果发生错误,则返回-1。
9、void connected()
(1)、信号。
(2)、此信号在调用connectToHost()并成功建立连接后发出。
10、void disconnected()
(1)、信号。
(2)、当连接断开时会发出此信号。
(3)、注意,若想在disconnected()信号对应的槽函数中删除QTcpSocket对象,只能使用deleteLater(),否则软件会崩溃。
11、void readyRead()
(1)、信号。
(2)、每当有新数据可从设备的当前读取通道读取时,就会发出一次此信号。
(3)、注意,只有新数据到达才会再次发出信号,而不是缓冲区中还有数据发出信号。
八、说明:
1、 在Qt中不管调用读函数接收数据,还是调用写函数发送数据,操作的对象都是本地由Qt框架维护的一块内存。
2、 调用了写函数数据不一定马上被发送到网络中,调用了接收函数也不是直接从网络中接收数据,因此才有QIODeviceBase::OpenMode这个参数。
**3、**关于底层的这些操作不需要开发者来维护。
九、基于TCP通信流程:
1、服务器端流程:
(1)、创建套接字服务器QTcpServer对象。
(2)、通过QTcpServer对象设置监听,即调用QTcpServer::listen()。
(3)、基于QTcpServer::newConnection()信号检测是否有新的客户端连接。
(4)、如果有新的客户端连接,在newConnection()信号对应的槽函数中调用QTcpServer::nextPendingConnection()得到通信的套接字对象。
(5)、使用通信的套接字对象QTcpSocket和客户端进行通信。
2、客户端通信流程:
(1)、创建通信的套接字类QTcpSocket对象。
(2)、使用服务器端绑定的IP和端口连接服务器QAbstractSocket::connectToHost()。
(3)、使用QTcpSocket对象和服务器进行通信。