1.TCP通信概述
tcp是一种用于数据传输的低级网络协议,它是可靠的、面向流、面向连接的传输协议,特别适合于连续数据传输。
服务器端使用QTcpServer用于端口监听,建立服务器;QTcpSocket用于建立连接后使用套接字进行通信。
2.常用API
void close() 关闭服务器,停止网络监听
bool listen() 在给定IP地址和端口上进行监听
QTcpSocket *nextPendingConnection() 返回下一个等待接入的连接
bool waitForNewConnection() 以阻塞方式等待新连接
3.示例
服务器端程序首先使用listen()开始服务器监听,可以指定IP和端口,一般一个服务器程序只监听某个端口的网络连接。
当有新的客户端接入时,QTcpServer内部的incomingConnection()函数会创建一个与客户端连接的QTcpSocket对象,然后发射信号newConnection()。
所以在客户端与服务器建立TCP连接后,具体的数据通信是通过QTcpSocket完成的。
namespace Ui {
class Widget;
}
class Widget : public QWidget
{
Q_OBJECT
public:
explicit Widget(QWidget *parent = 0);
~Widget();
private slots:
void on_btnStart_clicked();
void on_btnStop_clicked();
void on_btnClear_clicked();
void on_newConnection();
void onConnected();
void onDisConnected();
void onStateChanged(QAbstractSocket::SocketState socketState);
void onReadyRead();
void on_btnSend_clicked();
private:
Ui::Widget *ui;
QTcpServer *m_tcpServer = nullptr;
QTcpSocket *m_tcpSocket = nullptr;
};
// WIDGET_H
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
//本地主机名
QString hostName = QHostInfo::localHostName();
//本机IP地址
QHostInfo hostInfo = QHostInfo::fromName(hostName);
//IP地址列表
QList<QHostAddress> addrList = hostInfo.addresses();
for(int i=0;i<addrList.count();i++)
{
QHostAddress host = addrList.at(i);
if(QAbstractSocket::IPv4Protocol == host.protocol())
{
QString ip = host.toString();
ui->comboBox->addItem(ip);
}
}
m_tcpServer = new QTcpServer(this);
connect(m_tcpServer,&QTcpServer::newConnection,this,&Widget::on_newConnection);
}
Widget::~Widget()
{
delete ui;
}
void Widget::on_btnStart_clicked()
{
//当前选择的ip
QString ip = ui->comboBox->currentText();
//端口
int port = ui->lineEditPort->text().toInt();
QHostAddress addr(ip);
//监听
m_tcpServer->listen(addr,port);
ui->plainTextEdit->appendPlainText("**开始监听...");
ui->plainTextEdit->appendPlainText("**服务器地址: "+m_tcpServer->serverAddress().toString());
ui->plainTextEdit->appendPlainText("**服务器端口: "+QString::number(m_tcpServer->serverPort()));
ui->btnStart->setEnabled(false);
ui->btnStop->setEnabled(true);
ui->lbListen->setText("正在监听");
}
void Widget::on_btnStop_clicked()
{
if(m_tcpServer->isListening())
{
m_tcpServer->close();
ui->btnStart->setEnabled(true);
ui->btnStop->setEnabled(false);
ui->lbListen->setText("停止监听");
ui->plainTextEdit->appendPlainText("**停止监听**");
}
}
void Widget::on_btnClear_clicked()
{
ui->plainTextEdit->clear();
}
void Widget::on_newConnection()
{
m_tcpSocket = m_tcpServer->nextPendingConnection();
connect(m_tcpSocket,&QTcpSocket::connected,this,&Widget::onConnected);
connect(m_tcpSocket,&QTcpSocket::disconnected,this,&Widget::onDisConnected);
connect(m_tcpSocket,&QTcpSocket::stateChanged,this,&Widget::onStateChanged);
connect(m_tcpSocket,&QTcpSocket::readyRead,this,&Widget::onReadyRead);
ui->plainTextEdit->appendPlainText("** client socket connected");
ui->plainTextEdit->appendPlainText("** peer address: "+m_tcpSocket->peerAddress().toString());
ui->plainTextEdit->appendPlainText("** peer port: "+QString::number(m_tcpSocket->peerPort()));
}
void Widget::onConnected()
{
ui->plainTextEdit->appendPlainText("** client socket connected");
ui->plainTextEdit->appendPlainText("** peer address: "+m_tcpSocket->peerAddress().toString());
ui->plainTextEdit->appendPlainText("** peer port: "+QString::number(m_tcpSocket->peerPort()));
}
void Widget::onDisConnected()
{
ui->plainTextEdit->appendPlainText("** client socket disconnected");
m_tcpSocket->deleteLater();
}
void Widget::onStateChanged(QAbstractSocket::SocketState socketState)
{
switch (socketState)
{
case QAbstractSocket::UnconnectedState:
ui->lbListen->setText("UnconnectedState");break;
case QAbstractSocket::HostLookupState:
ui->lbListen->setText("HostLookupState");break;
case QAbstractSocket::ConnectedState:
ui->lbListen->setText("ConnectedState");break;
case QAbstractSocket::ConnectingState:
ui->lbListen->setText("ConnectingState");break;
case QAbstractSocket::BoundState:
ui->lbListen->setText("BoundState");break;
case QAbstractSocket::ClosingState:
ui->lbListen->setText("ClosingState");break;
case QAbstractSocket::ListeningState:
ui->lbListen->setText("ListeningState");break;
}
}
void Widget::onReadyRead()
{
while(m_tcpSocket->canReadLine())
{
ui->plainTextEdit->appendPlainText("[in] "+m_tcpSocket->readLine());
}
}
void Widget::on_btnSend_clicked()
{
QString msg =ui->lineEdit->text();
ui->plainTextEdit->appendPlainText("[out]"+msg);
QByteArray str = msg.toUtf8();
str.append('\n');
m_tcpSocket->write(str);
}
一般TCP服务器程序允许多个客户端接入,本例只演示了一个客户端接入,为了使每个socket连接通信互不影响,一般采用多线程,即为一个socket连接创建一个线程。
4.与客户端通信