基于QT平台利用TCP协议实现两个应用程序进行通信,是典型的c/s架构,这里仅仅使用单线程,目的是为了了解QT下socket套接字的使用,其实与linux中的套接字使用流程很接近。QT提供QTcpServer类和QTcpSocket类用于建立Tcp通信,服务器端的程序必须使用QTcpServer用于端口监听;使用QTcpSocket用于建立连接后使用套接字可以进行通信。
首先列出框架:
服务器端(server): 客户端(client):
#ifndef SERVER_H
#define SERVER_H
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define DEBUG
#define BUFSIZE 10000
using namespace std;
namespace Ui {
class Server;
}
class Server : public QWidget
{
Q_OBJECT
public:
explicit Server(QWidget *parent = 0);
~Server();
void getHostInfo();
public slots:
void newConnectSlot();//对应于 QTcpServer中有客户端连接会触发newConnect()信号
// void newClientConnectSlot();//对应于 有客户端使用connectToHost()函数连接服务器之后,会发出connect()信号
void readyReadSlot();//对应于 缓冲区中有数据,会发生readyRead信号
void clientDisconnectSlot();
void initUI();//初始化窗口控件
void setIpAndPort(QString ip,QString port);
private slots:
void on_btn_Send_clicked();
private:
Ui::Server *ui;
QTcpServer *server;//服务器
QTcpSocket *currentClient;//临时建立连接的客户端
QString serverIP;//服务器ip
uint serverPort = 9999;//服务器port
char *readBuf; //读缓冲区
QString writeBuf;//写缓冲区
int serverReceiveBufSize = 1000;
protected:
void closeEvent(QCloseEvent *event);
};
#endif // SERVER_H
#include "server.h"
#include "ui_server.h"
Server::Server(QWidget *parent) :
QWidget(parent),
ui(new Ui::Server)
{
ui->setupUi(this);
/* 1.获取主机ip
* 2.创建QTcpServer
* 3.建立连接,还要开始监听
* 4.一旦有连接之后,会发出newConnect信号,立马获取currentClient
*/
/* 初始化串口控件 */
initUI();
/* 1.获取主机ip */
getHostInfo();
/* 创建QTcpServer */
server = new QTcpServer(this);
/* 3.建立连接,还要开始监听 */
server->listen(QHostAddress::Any,serverPort);//开始侦听
connect(server,SIGNAL(newConnection()),this,SLOT(newConnectSlot()));//有新的client连接会触发newConnetSlot槽
readBuf = (char*)calloc(BUFSIZE,sizeof(char));
}
Server::~Server()
{
delete ui;
free(readBuf);
}
/* 初始化串口控件 */
void Server::initUI()
{
/* 设置服务器端每次最多只能读取多少个Bytes */
ui->receiver_num->setValue(serverReceiveBufSize);
ui->receiver_num->setMaximum(BUFSIZE);//一次最多可以读多少
// ui->comboBox_ip->addItem("0.0.0.0");
// ui->comboBox_port->addItem("0000");
}
/* 设置服务器Ip和port */
void Server::setIpAndPort(QString ip,QString port)
{
ui->comboBox_ip->addItem(ip);
ui->comboBox_port->addItem(port);
}
/* 1.获取主机ip */
void Server::getHostInfo()
{
QString pcHostName = QHostInfo::localHostName();//获取主机名
QHostInfo pcHostInfo = QHostInfo::fromName(pcHostName);//获取主机host信息
QList addList = pcHostInfo.addresses();//本机ip地址列表
if(!addList.isEmpty())//如果是空,isEmpty()返回true,flase表示非空
{
for(int i=0; i < addList.count(); i++)
{
QHostAddress hostIP = addList.at(i);//依次遍历出addList中的每一项,也就是ip信息
if(QAbstractSocket::IPv4Protocol == hostIP.protocol())//需要ipv4
{
serverIP = hostIP.toString();//把获取的ip信息从QHostAddress转到QString类型
qDebug() << "\nserverIP" << serverIP;
qDebug() << "port " << serverPort;
setIpAndPort(serverIP,QString::number(serverPort));
}
}
}
}
/* 一旦有新的client来连接server之后
* QTcpServer内部的incomingConnection()函数
* 会创建一个与客户端连接的QTcpSocket对象然后会
* 发newConnect信号 */
void Server::newConnectSlot()
{
#ifdef DEBUG
cout << "new client connect" << endl;
#endif
currentClient = new QTcpSocket(this);
currentClient = server->nextPendingConnection();//获取临时连接的客户端socket
// QHostAddress clientip = this->currentClient->peerAddress();
// QString ip = clientip.toString();
// qDebug() << " ------------ client ip = " << ip;
//仅仅在客户端使用connectToHost()之后才会触发connected()信号,服务器端并不会触发这个信号
// connect(currentClient,SIGNAL(connected()),this,SLOT(newClientConnectSlot()));
connect(currentClient,SIGNAL(readyRead()),this,SLOT(readyReadSlot()));
connect(currentClient,SIGNAL(disconnected()),this,SLOT(clientDisconnectSlot()));
ui->client_status->setText("client connected");
int originalReadBufSize = currentClient->readBufferSize();
qDebug() << "originalReadBufSize = " << originalReadBufSize << endl;
}
/* 有客户端使用connectToHost()函数连接服务器之后,会发出connect()信号
* newClientConnectSlot()这个槽就是接收连接信号的
*/
void Server::clientDisconnectSlot()
{
ui->client_status->setText("no connected! ");
}
/* 缓冲区中有数据,会发生readyRead信号,此槽函数读出缓冲区中数据 */
void Server::readyReadSlot()
{
/* 设置内部读缓冲区大小:5 Bytes,如果不设置好像会无限大(至于是不是无线大,反正qt帮助文档是这么说的)
* currentClient->setReadBufferSize(5*sizeof(char));
*/
currentClient->read(readBuf,ui->receiver_num->value());//这里的数字5是我需要读出的个数
/* 如果客户端发送的数据多余我需要读取的数量,也就是5个,那么把剩余的不要的全部读出去丢掉。
* 不然剩余的数量任何在缓冲区中,再次在读取接着读而不会读新来的数据,新来的数量排在后面 */
currentClient->readAll();
ui->receive_info->setText(readBuf);//在控件中显示接收到的数据
ui->receive_info->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);//可垂直滚动
ui->receive_info->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn);//可水平滚动
QTextCursor text_cursor(ui->receive_info->textCursor());//设置光标的位置
text_cursor.movePosition(QTextCursor::End);
ui->receive_info->setTextCursor(text_cursor);
#ifdef DEBUG
qDebug() << "new data:" << readBuf;
#endif
memset(readBuf,0,BUFSIZE);
}
void Server::on_btn_Send_clicked()
{
this->writeBuf = (ui->send_info->toPlainText());
this->currentClient->write(writeBuf.toUtf8().data(),this->writeBuf.length());
}
/* 窗口关闭事件 */
void Server::closeEvent(QCloseEvent *event)
{
if(this->server->isListening())
this->server->close();
if(this->currentClient->isOpen())
{
this->currentClient->abort();
}
}
#ifndef CLIENT_H
#define CLIENT_H
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define DEBUG
#define BUFSIZE 10000
namespace Ui {
class client;
}
class client : public QWidget
{
Q_OBJECT
public:
explicit client(QWidget *parent = 0);
~client();
void initUi();//初始化UI
public slots:
// void connectedSlot();//用于接收connectToHost()之后发送的connected()信号
void on_btn_connect_clicked();
void readData();//当客户端socket缓冲区有数据,会触发readRead()信号,该槽用于接收此信号
private slots:
void on_btn_Send_clicked();
void on_btn_abort_clicked();
private:
Ui::client *ui;
QTcpSocket *myclient;//用于连接服务器
QString serverIp = "192.168.1.104";
int serverPort = 9999;
char *readBuf;
QString writeBuf;
protected:
void closeEvent(QCloseEvent *event);
};
#endif // CLIENT_H
#include "client.h"
#include "ui_client.h"
client::client(QWidget *parent) :
QWidget(parent),
ui(new Ui::client)
{
ui->setupUi(this);
initUi();//初始化UI
myclient = new QTcpSocket(this);//实例化一个QTcpSocket对象
// connect(myclient,SIGNAL(connected()),this,SLOT(connectedSlot()));//用于接收connectToHost()之后发送的connected()信号
connect(this->myclient,SIGNAL(readyRead()),this,SLOT(readData()));//客户端有缓冲区时,会触发readyRead()
this->readBuf = (char *)calloc(BUFSIZE,sizeof(char));
}
client::~client()
{
delete ui;
free(this->readBuf);
}
void client::initUi()
{
ui->server_ip->setText(this->serverIp);
ui->server_port->setText(QString::number(this->serverPort));
/* 设置按钮状态 */
ui->btn_abort->setEnabled(false);
ui->btn_connect->setEnabled(true);
/* 设置服务器端每次最多只能读取多少个Bytes */
ui->receive_num->setValue(100);
ui->receive_num->setMaximum(BUFSIZE);//一次最多可以读多少
}
/* 用于接收readyRead信号 */
void client::readData()
{
/* 如果客户端发送的数据多余我需要读取的数量,比如就是5个,那么把剩余的不要的全部读出去丢掉。
* 不然剩余的数量任何在缓冲区中,再次在读取接着读而不会读新来的数据,新来的数量排在后面 */
int len = ui->receive_num->value();//界面限制的数量
this->myclient->read(this->readBuf,len);//接收设置的数量
this->myclient->readAll();//把剩余的全部丢掉
ui->receive_info->setText(this->readBuf);//在控件中显示接收到的数据
ui->receive_info->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);//可垂直滚动
ui->receive_info->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn);//可水平滚动
QTextCursor text_cursor(ui->receive_info->textCursor());//设置光标的位置
text_cursor.movePosition(QTextCursor::End);
ui->receive_info->setTextCursor(text_cursor);
}
/* 连接按钮 */
void client::on_btn_connect_clicked()
{
/* 从界面获取服务器ip和port */
this->serverIp = ui->server_ip->text();
this->serverPort = ui->server_port->text().toInt();
/* 设置按钮显示状态 */
ui->btn_abort->setEnabled(true);
ui->btn_connect->setEnabled(false);
this->myclient->connectToHost(this->serverIp,this->serverPort);//连接到服务器
if(this->myclient->waitForConnected(1500))//连接失败,返回false
{
ui->server_status->setText("Yes !");
}
}
/* 发送按钮 */
void client::on_btn_Send_clicked()
{
/* 设置按钮状态 */
ui->btn_abort->setEnabled(true);
ui->btn_connect->setEnabled(false);
this->writeBuf = ui->send_info->toPlainText();//从文本框中获取数据
myclient->write(writeBuf.toUtf8().data(),this->writeBuf.length());//通过socket写
}
/* 关闭连接按钮 */
void client::on_btn_abort_clicked()
{
/* 断开客户端的连接 */
this->myclient->abort();
/* 设置按钮状态 */
ui->btn_abort->setEnabled(false);
ui->btn_connect->setEnabled(true);
ui->server_status->setText("No !");
}
/* 窗口关系事件 */
void client::closeEvent(QCloseEvent *event)
{
/* 断开客户端的连接 */
if(this->myclient->isOpen())
this->myclient->abort();
}