这里的内容主要参考《Qt Creator 快速入门》的18章节的TCP部分~
刚开始不懂TCP传输的什么服务端客户端,以及监听等概念,自己敲完一遍代码后,了解的更加清楚了~
先说一下,要实现的功能很简单:
服务器一直监听一个端口,一旦有客户端连接请求,便建立连接,并向客户端发送一个字符串,然后客户端接受该字符串并显示出来。
实现效果界面如图:
刚开始先运行服务器端的程序,界面上有一个label显示“等待连接。。。”
之后运行客户端的程序,界面上要求输入你要连的主机和端口号(我在服务器端设置的监听端口号是6666,所以这里我会填主机是“localhost”,表示本主机,端口号填6666),然后点击连接按钮(我在这个按钮的槽函数下设置了一个连接服务器的槽函数)
运行结果图:
当点击客户端的连接后,立马与服务器端进行了连接,并且此时服务器端给客户端发送了字符串,服务器端也收到了,就是图片上的“hello TCP”
主要代码:
服务器端:
项目名称为tcpserver,类名为Server
头文件
server.h
#ifndef SERVER_H
#define SERVER_H
#include<QTcpServer>
#include <QDialog>
namespace Ui {
class Server;
}
class Server : public QDialog
{
Q_OBJECT
public:
explicit Server(QWidget *parent = 0);
~Server();
private:
Ui::Server *ui;
QTcpServer *tcpServer;
private slots:
void sendMessage();
};
#endif // SERVER_H
server.cpp
#include<QtNetwork>
#include "server.h"
#include "ui_server.h"
#include<QDebug>
#include<QByteArray>
#include<QDataStream>
#include<QTcpSocket>
Server::Server(QWidget *parent) :
QDialog(parent),
ui(new Ui::Server)
{
ui->setupUi(this);
tcpServer=new QTcpServer(this);
if(!tcpServer->listen(QHostAddress::Any,6666))//开始监听任何连在6666端口的IP地址
{
qDebug()<<tcpServer->errorString();
close();
}
connect(tcpServer,&QTcpServer::newConnection,this,&Server::sendMessage);//当有客户端连接过来时,发射sendMessage
}
Server::~Server()
{
delete ui;
}
void Server::sendMessage()
{
QByteArray block;
QDataStream out(&block,QIODevice::WriteOnly);//用block来暂时存放要发送的数据
out.setVersion(QDataStream::Qt_5_5);//设置数据流的版本,客户端和服务器端的版本要相同
//在发送数据时,一定要在最开始写入实际数据的大小信息,该大小信息占用两个字节,可以使用(quint16)0来表示
out<<(quint16)0;//但是在写入数据前可能不知道实际数据大小信息,所以要在数据块的最前面留两个自己的位置,以便以后填写数据大小的信息
out<<tr("hello TCP!");//这里输入实际的数据
out.device()->seek(0);//seek(0)跳转到数据块的开头
out<<(quint16)(block.size()-sizeof(quint16));//数据块总大小-数据块开头两个字节的大小获得数据实际大小,写入开头空着的位置
QTcpSocket *clientConnection=tcpServer->nextPendingConnection();//获取已经建立连接的套接字
connect(clientConnection,&QTcpSocket::disconnected,clientConnection,&QTcpSocket::deleteLater);//当连接断开时删除该套接字
clientConnection->write(block);//用write函数,将数据发送出去
clientConnection->disconnectFromHost();//该函数表明会一直等待套接字将所有数据发送完毕,然后关闭该套接字
ui->label->setText(tr("发送数据成功!"));
}
可以看到,编写TCP服务器端程序时可以使用QTcpServer类,然后调用它的listen()函数进行监听。要对连接过来的客户端进行操作时,可以通过关联newConnection()信号在槽中进行
客户端
项目名称为tcpclient,类名为Client
client.h
#ifndef CLIENT_H
#define CLIENT_H
#include<QTcpSocket>
#include <QDialog>
namespace Ui {
class Client;
}
class Client : public QDialog
{
Q_OBJECT
public:
explicit Client(QWidget *parent = 0);
~Client();
private:
Ui::Client *ui;
QTcpSocket *tcpSocket;
QString message;
quint16 blocksize;
private slots:
void newConnect();
void readmessage();
void displayError(QAbstractSocket::SocketError);
void on_pushButton_connect_clicked();
};
#endif // CLIENT_H
client.cpp
#include "client.h"
#include "ui_client.h"
#include<QtNetwork>
#include<QDataStream>
Client::Client(QWidget *parent) :
QDialog(parent),
ui(new Ui::Client)
{
ui->setupUi(this);
tcpSocket=new QTcpSocket(this);
connect(tcpSocket,&QTcpSocket::readyRead,this,&Client::readmessage);
connect(tcpSocket,SIGNAL(error(QAbstractSocket::SocketError)),this,SLOT(displayError(QAbstractSocket::SocketError)));
}
Client::~Client()
{
delete ui;
}
void Client::newConnect()
{
blocksize=0;//初始化数据大小为0
tcpSocket->abort();//取消了当前已经存在的连接,并且重置套接字
//连接到指定主机的指定端口
tcpSocket->connectToHost(ui->lineEdit_Host->text(),ui->lineEdit_Port->text().toInt());
}
void Client::readmessage()
{
QDataStream in(tcpSocket);//数据流入套接字
in.setVersion(QDataStream::Qt_5_5);
//如果是刚开始接收数据,因为初始化大小为0
if(blocksize==0)
{
//判断所接收的数据是否大于两个字节,也就是文件的大小信息的空间
//如果是,则把文件大小信息保存到blocksize
if(tcpSocket->bytesAvailable()<(int)sizeof(quint16))
return;
in>>blocksize;//发送和接收的数据流都是有自己的格式的,比如,自己定义的quint16 blocksize;
}
//如果没有得到全部的数据,则返回,继续接收数据
if(tcpSocket->bytesAvailable()<blocksize)
return;
in>>message;
ui->label_message->setText(message);
}
void Client::displayError(QAbstractSocket::SocketError)
{
qDebug()<<"test";
qDebug()<<tcpSocket->errorString();
}
void Client::on_pushButton_connect_clicked()
{
newConnect();
}
整个过程是这样的:启动服务器端,开始监听端口,客户端单击“连接,调用newConnect()槽,然后客户端向服务器发送连接请求,这时服务器调用sendMessage()槽,发送字符串,客户端接收到字符串时,调用readMessage()槽将字符串显示出来。”