1.回顾linux的网络编程
tcp服务器: socket--》bind-->listen-->accept-->read/write-->close
tcp客户端: socket--》bind-->connect-->read/write-->close2.QT中的tcp
如果用到QT的网络编程,必须在QT工程中添加 QT += network(打开.pro文件,把这个库加进去)
(1)涉及到类
QTcpServer --》表示tcp服务器
QTcpSocket --》表示tcp套接字
(2)流程思路
服务器流程
第一步:创建QTcpServer对象,然后调用listen方法绑定并监听
QTcpServer::QTcpServer(QObject *parent = nullptr)
//绑定并监听
bool QTcpServer::listen(const QHostAddress &address = QHostAddress::Any, quint16 port = 0)
参数: address --》你要绑定的ip地址 QHostAddress类是用来表示ip地址的一个类
QHostAddress::QHostAddress(const QString &address)
参数:address --》ip地址
port --》你要绑定的端口号
第二步:如果有新的客户端连接服务器,QTcpServer对象会自动触发[signal] void QTcpServer::newConnection()信号,程序员主动关联这个信号,在槽函数中调用QTcpSocket *QTcpServer::nextPendingConnection()产生新的套接字
第三步:使用刚才第二步产生的新套接字去跟对应的客户端通信
发送信息: write() --》跟前面学习文件IO是一模一样
接收信息: read()/readAll() --》跟前面学习文件IO是一模一样
注意:接收信息必须通过信号与槽来接收,当对方有发送信息过来,QTcpSocket对象会自动触发[signal] void QIODevice::readyRead(),程序员需要关联该信号,在槽函数中调用read/readAll去接收对方的信息
第四步:关闭套接字
void QAbstractSocket::close()客户端流程QTcpClient
第一步:创建QTcpSocket对象,然后调用bind方法绑定自己的ip和端口号
QTcpSocket(QObject *parent = nullptr)
//绑定自己的ip和端口号
bool QAbstractSocket::bind(const QHostAddress &address, quint16 port = 0)
参数: address --》你要绑定的ip地址 QHostAddress类是用来表示ip地址的一个类
QHostAddress::QHostAddress(const QString &address)
参数:address --》ip地址
port --》你要绑定的端口号
第二步:连接服务器
void QAbstractSocket::connectToHost(const QHostAddress &address, quint16 port)
参数:address --》服务器的ip地址
port --》服务器的端口号
第三步:使用第一步创建的套接字去跟服务器通信
发送信息: write() --》跟前面学习文件IO是一模一样
接收信息: read()/readAll() --》跟前面学习文件IO是一模一样
注意:接收信息必须通过信号与槽来接收,当对方有发送信息过来,QTcpSocket对象会自动触发[signal] void QIODevice::readyRead(),程序员需要关联该信号,在槽函数中调用read/readAll去接收对方的信息
第四步:关闭套接字
void QAbstractSocket::close()
(3)其他常用的方法
第一个:获取对方的ip和端口号
//获取对方的ip
QHostAddress QAbstractSocket::peerAddress() const
返回值: 返回对方的ip地址
QString QHostAddress::toString() const //可以把ip地址转换成熟悉的字符串格式(点分十进制ip)
//获取对方的端口号
quint16 QAbstractSocket::peerPort() const
返回值:返回对方的端口号
(4)多个客户端连接同一个服务器
需要把所有连接成功的客户端套接字保存起来
(5)判断客户端/服务器断开连接
客户端/服务器断开连接,QTcpSocket对象会自动触发[signal] void QAbstractSocket::disconnected()信号,我们只需要关联这个信号,在槽函数中去处理断开以后的事情就可以了
tcp单向通信
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QDebug>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
//设置标题
this->setWindowTitle("我是服务器");
//绑定并监听
server.listen(QHostAddress("192.168.26.2"),10000);
//关联newConnection()信号
connect(&server,SIGNAL(newConnection()),this,SLOT(newclientlink()));
}
MainWindow::~MainWindow()
{
delete ui;
}
//如果有新的客户端连接服务器,这个槽函数就会被自动调用
void MainWindow::newclientlink()
{
qDebug()<<"新的客户端连接服务器了";
//产生新的套接字用于跟这个客户端通信
p=server.nextPendingConnection();
//获取连接成功的这个客户端的ip和端口号
QString ipstr=p->peerAddress().toString();
quint16 portnum=p->peerPort();
//在横向列表框中把这个客户端的信息显示出来
QString str=QString("%1@%2").arg(ipstr).arg(portnum);
ui->listWidget->addItem(str);
//接收信息--》反面教材,直接调用read/readAll接收
//QByteArray buf=p->readAll(); //根本收不到信息
//关联readyRead信号
connect(p,SIGNAL(readyRead()),this,SLOT(recvclientmsg()));
}
//接收客户端发送过来的信息
void MainWindow::recvclientmsg()
{
QByteArray buf=p->readAll();
//把接收的信息在文本编辑框中显示出来
ui->textEdit->setText(buf);
}
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
//设置标题
this->setWindowTitle("我是客户端张三");
//绑定ip和端口号
sock.bind(QHostAddress("192.168.26.2"),20000);
}
MainWindow::~MainWindow()
{
delete ui;
}
//连接服务器
void MainWindow::on_linkbt_clicked()
{
//获取输入的ip和端口号
QString ipstr=ui->iple->text();
QString portstr=ui->portle->text();
sock.connectToHost(QHostAddress(ipstr),portstr.toInt());
}
//发送信息给服务器
void MainWindow::on_sendbt_clicked()
{
//获取文本编辑框中输入的内容
QString str=ui->textEdit->toPlainText();
//发送给服务器
sock.write(str.toUtf8());
}
tcp双向通信
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QDebug>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
//设置标题
this->setWindowTitle("我是服务器");
//绑定并监听
server.listen(QHostAddress("192.168.26.2"),10000);
//关联newConnection()信号
connect(&server,SIGNAL(newConnection()),this,SLOT(newclientlink()));
}
MainWindow::~MainWindow()
{
delete ui;
}
//如果有新的客户端连接服务器,这个槽函数就会被自动调用
void MainWindow::newclientlink()
{
qDebug()<<"新的客户端连接服务器了";
//产生新的套接字用于跟这个客户端通信
p=server.nextPendingConnection();
//获取连接成功的这个客户端的ip和端口号
QString ipstr=p->peerAddress().toString();
quint16 portnum=p->peerPort();
//在横向列表框中把这个客户端的信息显示出来
QString str=QString("%1@%2").arg(ipstr).arg(portnum);
ui->listWidget->addItem(str);
//接收信息--》反面教材,直接调用read/readAll接收
//QByteArray buf=p->readAll(); //根本收不到信息
//关联readyRead信号
connect(p,SIGNAL(readyRead()),this,SLOT(recvclientmsg()));
}
//接收客户端发送过来的信息
void MainWindow::recvclientmsg()
{
QByteArray buf=p->readAll();
//把接收的信息在文本编辑框中显示出来
ui->textEdit->setText(buf);
}
//发送信息给客户端
void MainWindow::on_pushButton_clicked()
{
//获取文本编辑框中输入的内容
QString buf=ui->textEdit_2->toPlainText();
p->write(buf.toUtf8());
}
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
//设置标题
this->setWindowTitle("我是客户端张三");
//绑定ip和端口号
sock.bind(QHostAddress("192.168.26.2"),20000);
//关联readyRead信号
connect(&sock,SIGNAL(readyRead()),this,SLOT(recvservermsg()));
}
MainWindow::~MainWindow()
{
delete ui;
}
//连接服务器
void MainWindow::on_linkbt_clicked()
{
//获取输入的ip和端口号
QString ipstr=ui->iple->text();
QString portstr=ui->portle->text();
sock.connectToHost(QHostAddress(ipstr),portstr.toInt());
}
//发送信息给服务器
void MainWindow::on_sendbt_clicked()
{
//获取文本编辑框中输入的内容
QString str=ui->textEdit->toPlainText();
//发送给服务器
sock.write(str.toUtf8());
}
//接收服务器发送过来的信息
void MainWindow::recvservermsg()
{
QByteArray recvbuf=sock.readAll();
//把接收的信息在文本编辑框中显示出来
ui->textEdit_2->setText(recvbuf);
}
tcp多个客户端连接服务器
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QDebug>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
//设置标题
this->setWindowTitle("我是服务器");
//绑定并监听
server.listen(QHostAddress("192.168.26.2"),10000);
//关联newConnection()信号
connect(&server,SIGNAL(newConnection()),this,SLOT(newclientlink()));
}
MainWindow::~MainWindow()
{
delete ui;
}
//如果有新的客户端连接服务器,这个槽函数就会被自动调用
void MainWindow::newclientlink()
{
qDebug()<<"新的客户端连接服务器了";
//产生新的套接字用于跟这个客户端通信
p=server.nextPendingConnection();
//顺便把这个套接字存放到容器中
clientlist.append(p);
//获取连接成功的这个客户端的ip和端口号
QString ipstr=p->peerAddress().toString();
quint16 portnum=p->peerPort();
//在横向列表框中把这个客户端的信息显示出来
QString str=QString("%1@%2").arg(ipstr).arg(portnum);
ui->listWidget->addItem(str);
//接收信息--》反面教材,直接调用read/readAll接收
//QByteArray buf=p->readAll(); //根本收不到信息
//关联readyRead信号,为了接收信息
connect(p,SIGNAL(readyRead()),this,SLOT(recvclientmsg()));
//关联disconnected(),为了判断客户端是否断开了
connect(p,SIGNAL(disconnected()),this,SLOT(someclientunlink()));
}
//接收客户端发送过来的信息
void MainWindow::recvclientmsg()
{
//清空字符串
allclientmsg.clear();
//获取信号的发送者
QTcpSocket *somesock=qobject_cast<QTcpSocket *>(sender());
QByteArray buf=somesock->readAll();
QString msgstr(buf);
//判断收到的信息
if(msgstr=="getlist") //说明某个客户端想让服务器发送在线好友信息
{
allclientmsg.append("getlist@");
//遍历容器,把所有的客户端信息拼接成字符串然后发送给对应的客户端
//ip@端口号\nip@端口号\nip@端口号
for(int i=0; i<clientlist.size(); i++)
{
QString ipstr=clientlist.at(i)->peerAddress().toString();
quint16 portnum=clientlist.at(i)->peerPort();
QString str=QString("%1@%2@").arg(ipstr).arg(portnum);
allclientmsg.append(str);
}
//把拼接好的所有客户端信息发送给对应的客户端
somesock->write(allclientmsg.toUtf8());
}
else //其它信息
{
//得到这个客户端的ip以及端口号,拼接显示
QString ipstr=somesock->peerAddress().toString();
quint16 portnum=somesock->peerPort();
QString str=QString("%1@%2@%3").arg(ipstr).arg(portnum).arg(msgstr);
//把接收的信息在文本编辑框中显示出来
//ui->textEdit->setText(str); //setText方法会覆盖之前显示的内容
ui->textEdit->append(str); //append会把新信息追加到后面
}
}
//发送信息给客户端
void MainWindow::on_pushButton_clicked()
{
int i;
//遍历容器,找到我刚才在横向列表框中点击的那个客户端对应的套接字
for(i=0; i<clientlist.size(); i++)
{
QString ipstr=clientlist.at(i)->peerAddress().toString();
quint16 portnum=clientlist.at(i)->peerPort();
QString str=QString("%1@%2").arg(ipstr).arg(portnum);
//跟刚才你点击进行比较
if(str==someclientmsg)
break;
}
//获取文本编辑框中输入的内容
QString buf=ui->textEdit_2->toPlainText();
clientlist.at(i)->write(buf.toUtf8());
}
//某个客户端断开连接了
void MainWindow::someclientunlink()
{
//获取信号的发送者
QTcpSocket *somesock=qobject_cast<QTcpSocket *>(sender());
QString ipstr=somesock->peerAddress().toString();
quint16 portnum=somesock->peerPort();
QString str=QString("%1@%2").arg(ipstr).arg(portnum);
//从在线客户端列表框中删除这个客户端的信息
//先找到这个列表项
QList<QListWidgetItem *> itemlist=ui->listWidget->findItems(str,Qt::MatchContains);
int row=ui->listWidget->row(itemlist.at(0));
ui->listWidget->takeItem(row);
//从QList容器中删除这个客户端的套接字
clientlist.removeOne(somesock);
}
//单击某个列表项
void MainWindow::on_listWidget_itemClicked(QListWidgetItem *item)
{
someclientmsg=item->text();
}
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
//设置标题
this->setWindowTitle("我是客户端张三");
//绑定ip和端口号
sock.bind(QHostAddress("192.168.26.2"),20000);
//关联readyRead信号
connect(&sock,SIGNAL(readyRead()),this,SLOT(recvservermsg()));
}
MainWindow::~MainWindow()
{
delete ui;
}
//连接服务器
void MainWindow::on_linkbt_clicked()
{
//获取输入的ip和端口号
QString ipstr=ui->iple->text();
QString portstr=ui->portle->text();
sock.connectToHost(QHostAddress(ipstr),portstr.toInt());
}
//发送信息给服务器
void MainWindow::on_sendbt_clicked()
{
//获取文本编辑框中输入的内容
QString str=ui->textEdit->toPlainText();
//发送给服务器
sock.write(str.toUtf8());
}
//接收服务器发送过来的信息
void MainWindow::recvservermsg()
{
QByteArray recvbuf=sock.readAll();
QString str(recvbuf);
//把收到的信息做字符串的切割
QStringList msglist=str.split('@');
//判断服务器发送过来的信息类型
if(msglist.at(0)=="getlist") //说明收到是服务器发过来好友列表
{
//清空横向列表框
ui->friendlist->clear();
//在横向列表框中显示好友信息
for(int i=1; i<msglist.size()-1; i++)
ui->friendlist->addItem(msglist.at(i));
}
else if(msglist.at(0)=="msg") //说明服务器发过来的是字符串信息
{
}
else if(msglist.at(0)=="emoji") //说明服务器发过来的是表情包序号
{
}
else if(msglist.at(0)=="file") //说明服务器发过来的是文件
{
}
//把接收的信息在文本编辑框中显示出来
//ui->textEdit_2->setText(recvbuf);
ui->textEdit_2->append(recvbuf);
}
//刷新好友信息
void MainWindow::on_flushbt_clicked()
{
//给服务器发送指定的字符串
sock.write("getlist");
}
udp通信
1.流程
发送/接收端
第一步: 创建QUdpSocket对象,绑定ip和端口号
QUdpSocket::QUdpSocket(QObject *parent = nullptr)
//绑定自己的ip和端口号
bool QAbstractSocket::bind(const QHostAddress &address, quint16 port = 0)
参数: address --》你要绑定的ip地址 QHostAddress类是用来表示ip地址的一个类
QHostAddress::QHostAddress(const QString &address)
参数:address --》ip地址
port --》你要绑定的端口号
第二步: 利用刚才的套接字收发信息
发送信息
qint64 QUdpSocket::writeDatagram(const char *data, qint64 size, const QHostAddress &address, quint16 port)
qint64 QUdpSocket::writeDatagram(const QByteArray &datagram, const QHostAddress &host, quint16 port)
返回值:成功发送的字节数
参数:data --》你要发送的字符串
size --》发送的字节数
address --》对方的ip
port --》对方的端口号
接收信息
qint64 QUdpSocket::readDatagram(char *data, qint64 maxSize)
返回值:成功收到的字节数
参数:data --》存放接收的信息
maxSize --》接收的字节数
第三步:关闭
void QAbstractSocket::close()