对Qt下对话服务器客户端的总结(MyTcpServer与MyTcpClient)
在汇文培训老师给讲了这个例子。讲的挺好的
Qt编写聊天服务器与客户端主要用到下面两个类:
- QTcpSocket --- 处理连接的
- QTcpServer --- 处理服务器,对接入进行响应,创建每个链接的QTcpSocket实例
编写网络程序需要在 .pro 文件中加上 network,如下
QT += core gui network
1.客户端的编写
客户端需要做的事:
- 获取服务器的主机ip和端口(port)
- 链接主机(connectToHost)
- 链接状态下等待一些信号(signal)的产生并作出相应的回应(slot)
主要等待的信号由一下几个:
void connected()
void disconnected()
void error(QAbstractSocket::SocketError socketError)
void readyRead()
并为这几个信号写相应的槽函数对该情况作出处理
以下为我的具体实现代码:
我的ui界面是这样的:
#ifndef TCPCLIENT_H
#define TCPCLIENT_H #include <QWidget>
#include <QMessageBox>
#include <QTcpSocket>
#include <QDebug>
#include <QHostAddress>
#include <QTextStream> namespace Ui {
class TcpClient;
} class TcpClient : public QWidget
{
Q_OBJECT public:
explicit TcpClient(QWidget *parent = );
~TcpClient(); private slots:
void on_connectPushButton_clicked(); //connect按键槽函数 void slotConnect();
void slotErr(QAbstractSocket::SocketError err);
void slotDisconnect();
void slotReadData(); void on_sendPushButton_clicked(); //send按键槽函数
void on_sendLineEdit_returnPressed(); //sendLineEdit的回车槽 private:
Ui::TcpClient *ui;
QTcpSocket *socket; //客户端的文件描述符
bool isConnect; //connect按键的状态变换值
}; #endif // TCPCLIENT_H
tcpclient.h
#include "tcpclient.h"
#include "ui_tcpclient.h" TcpClient::TcpClient(QWidget *parent) :
QWidget(parent),
ui(new Ui::TcpClient)
{
ui->setupUi(this);
isConnect = true;
} TcpClient::~TcpClient()
{
delete ui;
} void TcpClient::on_connectPushButton_clicked()
{
QString hostip;
quint16 port;
if(isConnect)
{
/*判断ip和端口是否填写,并获取该ip和端口*/
if(ui->ipLineEdit->text().isEmpty())
{
QMessageBox::information(this,"ip","The ip is empty,please input a ip");
return ;
}
hostip = ui->ipLineEdit->text();
if(ui->portLineEdit->text().isEmpty())
{
QMessageBox::information(this,"port","The port is empty,please input a port");
return ;
}
port = ui->portLineEdit->text().toShort(); /*连接服务器,并在这之前连接几个必要的信号*/
socket = new QTcpSocket;
//已连接信号
connect(socket,SIGNAL(connected()),this,SLOT(slotConnect()));
//连接出现错误信号
connect(socket,SIGNAL(error(QAbstractSocket::SocketError)),this,SLOT(slotErr(QAbstractSocket::SocketError)));
//连接断开信号
connect(socket,SIGNAL(disconnected()),this,SLOT(slotDisconnect()));
//准备读取数据信号
connect(socket,SIGNAL(readyRead()),this,SLOT(slotReadData()));
socket->connectToHost(QHostAddress(hostip),port);
}
else
{
socket->disconnectFromHost();
ui->connectPushButton->setText("connect");
isConnect = true;
}
} //槽函数定义
void TcpClient::slotConnect()
{
QMessageBox::information(this,"connect","host connect sucess");
ui->ipLineEdit->setEnabled(false);
ui->portLineEdit->setEnabled(false);
ui->connectPushButton->setText("disconnect");
isConnect = false;
} void TcpClient::slotErr(QAbstractSocket::SocketError err)
{
qDebug("error is %d",err);
ui->connectPushButton->setText("connect");
} void TcpClient::slotDisconnect()
{
QMessageBox::information(this,"disconnect","host disconnect sucess");
ui->ipLineEdit->setEnabled(true);
ui->portLineEdit->setEnabled(true);
ui->connectPushButton->setText("connect");
isConnect = true;
} void TcpClient::slotReadData()
{
qDebug()<<"have data";
if(socket->bytesAvailable()>)
{
QString msg;
QByteArray ba;
ba.resize(socket->bytesAvailable());
socket->read(ba.data(),ba.size());
msg = QString(ba); // QTextStream out(socket);
// QString msg;
// out >> msg;
ui->msgListWidget->addItem(msg);
}
} void TcpClient::on_sendPushButton_clicked()
{
QTextStream in(socket);
in << ui->sendLineEdit->text();
ui->sendLineEdit->clear();
} void TcpClient::on_sendLineEdit_returnPressed()
{
QTextStream in(socket);
in << ui->sendLineEdit->text();
ui->sendLineEdit->clear();
}
tcpcilent.cpp
#include "tcpclient.h"
#include <QApplication> int main(int argc, char *argv[])
{
QApplication a(argc, argv);
TcpClient w;
w.show(); return a.exec();
}
main.cpp
总结:客户端的编写比较容易,只要连接服务器,发送信息给服务器就可以
2.服务器的编写
服务器需要做的事:
- Qt对于服务器有专门的类 QTcpServer封装,所以不需要去自己配置服务器(socket,bind),可以直接侦听(listen)
- 当有客户端连接的时候分配文件描述符和地址标示客户端
- 然后等待客户端发送信息(这里会得到一些信号,通过信号来处理),接收信号,转发(发送给链接在服务器的其他人)或者回馈信息。
- 服务器需要支持多客户端,所以要有保存和删除客户端信息的处理。
因为需要实现支持多客户端接入的服务器,所以需要将显示层(ui),服务器层,socket层分开。可以看下面这个图来理解
简述下这里的流程,首先,客户端发送请求,服务器分配(创建)一个socket,然后incomingConnection()函数捕捉到这个socket,并在此响应客户端(接收消息,转发消息)。然后在这里创建一个信息转发消息到ui界面
通过这样的模型,我们就可以创建一个多客户端的服务器。
接下来的一张图我来解释下我的程序中信号与槽函数之间连接的关系(人类总是对视觉的感官来的更直接点)
结合上面的图和我的代码就很清晰的能看出信号与槽在这三层之间的传递关系
(1)由MyTcpSocket socket产生两个信号,readyread()和disconnected(),然后通过connect连接到两个槽函数readDataSlot()与disconnectSlot()
(2)在上面的两个槽函数中发送emit两个信号sockReadDataSignal(qintptr,QString),disconnectSignal(qintptr),附带信息的信号,使得MyTcpServer能够访问到信息
(3)在MyTcpServer类中通过socket接受这两个信号,并连接到相应的槽函数,把信息返回给其他客户端。
(4)并且我把信号sockReadDataSignal()连接到MyTcpServer内显示到ui界面
下面是的我的代码区
#ifndef MYTCPSOCKET_H
#define MYTCPSOCKET_H #include <QObject>
#include <QTcpSocket>
#include <QTextStream>
#include <QHostAddress> class MyTcpSocket : public QTcpSocket
{
Q_OBJECT
public: MyTcpSocket();
~MyTcpSocket(); signals:
void disconnectSignal(qintptr);
void sockReadDataSignal(qintptr,QString); private slots:
void readDataSlot();
void disconnectSlot();
}; #endif // MYTCPSOCKET_H
mytcpsocket.h
#include "mytcpsocket.h" MyTcpSocket::MyTcpSocket()
{
QObject::connect(this,SIGNAL(readyRead()),this,SLOT(readDataSlot()));
QObject::connect(this,SIGNAL(disconnected()),this,SLOT(disconnectSlot()));
} MyTcpSocket::~MyTcpSocket()
{ } void MyTcpSocket::readDataSlot()
{
//读取数据的两种方式
qDebug()<<"read data";
QString msg;
QByteArray ba;
ba.resize(this->bytesAvailable());
this->read(ba.data(),ba.size());
msg = QString(ba);
qDebug()<<"ip:"<<this->peerAddress().toString(); // QTextStream out(this),in(this);
// QString msg;
// out >> msg;
emit sockReadDataSignal(this->socketDescriptor(),msg);
} void MyTcpSocket::disconnectSlot()
{
qDebug()<<"disconnect";
emit disconnectSignal(this->socketDescriptor());
}
mytcpsocket.cpp
#ifndef MYTCPSERVER_H
#define MYTCPSERVER_H #include <QObject>
#include <QHostAddress>
#include <QTcpServer>
#include <QDebug>
#include <QList>
#include "mytcpsocket.h" class MyTcpServer : public QTcpServer
{
Q_OBJECT
public:
MyTcpServer();
~MyTcpServer(); protected:
void incomingConnection(qintptr socketDescriptor); private slots:
void disconnectSlot(qintptr);
void answerMsgSlot(qintptr,QString); signals:
void serverReadDataSignal(qintptr,QString); private:
QList<MyTcpSocket *> clients; }; #endif // MYTCPSERVER_H
mytcpserver.h
#include "mytcpserver.h" MyTcpServer::MyTcpServer()
{
listen(QHostAddress::Any,);
} MyTcpServer::~MyTcpServer()
{ } void MyTcpServer::incomingConnection(qintptr socketDescriptor)//when a new connection is available.
{
qDebug()<<"connect success";
MyTcpSocket *sock = new MyTcpSocket;
sock->setSocketDescriptor(socketDescriptor);
QObject::connect(sock,SIGNAL(disconnectSignal(qintptr)),this,SLOT(disconnectSlot(qintptr)));
QObject::connect(sock,SIGNAL(sockReadDataSignal(qintptr,QString)),this,SIGNAL(serverReadDataSignal(qintptr,QString)));
QObject::connect(sock,SIGNAL(sockReadDataSignal(qintptr,QString)),this,SLOT(answerMsgSlot(qintptr,QString)));
clients << sock;
} void MyTcpServer::disconnectSlot(qintptr sockfd)
{
// MyTcpSocket *sock = new MyTcpSocket;
// sock->setSocketDescriptor(socketDescriptor);
qDebug()<<"delete client "<<sockfd;
for (int i = ; i < clients.size(); ++i)
{
if (clients.at(i)->socketDescriptor() == sockfd)
clients.removeAt(i);
} // delete sock;
} void MyTcpServer::answerMsgSlot(qintptr sockfd, QString msg)
{
for (int i = ; i < clients.size(); ++i)
{
if(clients.at(i)->socketDescriptor() != sockfd)
{
QTextStream in(clients.at(i));
in << sockfd << ":" << msg << endl;
}
}
}
mytcpserver.cpp
#ifndef MYTCPSERVERWIDGET_H
#define MYTCPSERVERWIDGET_H #include <QWidget>
#include "mytcpserver.h"
#include <QObject> namespace Ui {
class MyTcpServerWidget;
} class MyTcpServerWidget : public QWidget
{
Q_OBJECT public:
explicit MyTcpServerWidget(QWidget *parent = );
~MyTcpServerWidget(); private slots:
void widgetReadDataSlot(qintptr,QString); private:
Ui::MyTcpServerWidget *ui;
MyTcpServer *tcpserver;
}; #endif // MYTCPSERVERWIDGET_H
mytcpserverwidget.h
#include "mytcpserverwidget.h"
#include "ui_mytcpserverwidget.h" MyTcpServerWidget::MyTcpServerWidget(QWidget *parent) :
QWidget(parent),
ui(new Ui::MyTcpServerWidget)
{
ui->setupUi(this);
tcpserver = new MyTcpServer; //connect to the server
QObject::connect(tcpserver,SIGNAL(serverReadDataSignal(qintptr,QString)),this,SLOT(widgetReadDataSlot(qintptr,QString))); } MyTcpServerWidget::~MyTcpServerWidget()
{
delete ui;
} void MyTcpServerWidget::widgetReadDataSlot(qintptr sock, QString msg)
{
QString id;
id = QString::number(sock);
ui->listWidget->addItem(id+":"+msg);
}
mytcpserverwidget.cpp
#include "mytcpserverwidget.h"
#include <QApplication> int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MyTcpServerWidget w;
w.show(); return a.exec();
}
main.cpp
视频教程在汇文的网校上也是有的:www.huiwen.com
最后附带一个写的很好的blog
对Qt下对话服务器客户端的总结(MyTcpServer与MyTcpClient)的更多相关文章
- 【Echo】实验 -- 实现 C/C++下TCP, 服务器/客户端 通讯
本次实验利用TCP/IP, 语言环境为 C/C++ 利用套接字Socket编程,实现Server/CLient 之间简单的通讯. 结果应为类似所示: 下面贴上代码(参考参考...) Server 部分 ...
- 【Chat】实验 -- 实现 C/C++下TCP, 服务器/客户端 "多人聊天室"
本次实验利用TCP/IP, 语言环境为 C/C++ 利用套接字Socket编程,以及线程处理, 实现Server/CLient 之间多人的聊天系统的基本功能. 结果大致如: 下面贴上代码(参考参考.. ...
- 【Echo】实验 -- 实现 C/C++下UDP, 服务器/客户端 通讯
本次实验利用UDP协议, 语言环境为 C/C++ 利用套接字Socket编程,实现Server/CLient 之间简单的通讯. 结果应为类似所示: 下面贴上代码(参考参考...) Server 部分: ...
- windows下的mysql客户端mysqlworkbench链接虚拟机上CentOS的mysql服务器
本人在虚拟机上CentOS的Linux环境下安装了mysql服务器,在本地Windows下安装了mysql的客户端mysqlworkbench ,所以就想让windows下的mysql客户端mysql ...
- c++下基于windows socket的单线程服务器客户端程序(基于TCP协议)
今天自己编写了一个简单的c++服务器客户端程序,注释较详细,在此做个笔记. windows下socket编程的主要流程可概括如下:初始化ws2_32.dll动态库-->创建套接字-->绑定 ...
- c++下基于windows socket的服务器客户端程序(基于UDP协议)
前天写了一个基于tcp协议的服务器客户端程序,今天写了一个基于UDP协议的,由于在上一篇使用TCP协议的服务器中注释已经较为详细,且许多api的调用是相同的,故不再另外注释. 使用UDP协议需要注意几 ...
- CentOS 6.3下Samba服务器的安装与配置方法(图文详解)
这篇文章主要介绍了CentOS 6.3下Samba服务器的安装与配置方法(图文详解),需要的朋友可以参考下 一.简介 Samba是一个能让Linux系统应用Microsoft网络通讯协议的软件, ...
- CentOS 6.3下Samba服务器的安装与配置(转)
CentOS 6.3下Samba服务器的安装与配置 一.简介 Samba是一个能让Linux系统应用Microsoft网络通讯协议的软件,而SMB是Server Message Block的缩写, ...
- CentOS 7下Samba服务器的安装与配置
文基于<CentOS 6.3下Samba服务器的安装与配置>,参照原博文,自己在CentOS7环境上实现,并按照自己的环境修改博文内容 一.简介 Samba是一个能让Linux系统应用Mi ...
随机推荐
- 【疑问】css
1.p{float:left}好像意思是p后面的元素往左浮动的意思啊!!2.ff下,button的文字好像没法垂直居中2.1 button垂直对齐,其line-height似乎需要减2px(依边框宽度 ...
- Asp.net mvc 知多少(十)
本系列主要翻译自<ASP.NET MVC Interview Questions and Answers >- By Shailendra Chauhan,想看英文原版的可访问http:/ ...
- 关于JavaScript简单描述
1.什么是JavaScript? JavaScript 是一种专门用于与网页交互而设计的脚本语言,它有三部分组成,那是哪三部分呢?分别是ECMAScript(提供核心语言功能),DOM(提供访问和操作 ...
- Tp框架 之对控制器的一些操作等
在浏览器中输入tp框架入口文件的地址,如图 要注意,localhost/后面跟的是www的下一级,tp文件的上一级,因为我直接把tp文件做成了www目录的下一级,所以我写的地址localhost后面跟 ...
- C#基础笔记---浅谈XML读取以及简单的ORM实现
背景: 在开发ASP.NETMVC4 项目中,虽然web.config配置满足了大部分需求,不过对于某些特定业务,我们有时候需要添加新的配置文件来记录配置信息,那么XML文件配置无疑是我们选择的一个方 ...
- WPF: 在 MVVM 设计中实现对 ListViewItem 双击事件的响应
ListView 控件最常用的事件是 SelectionChanged:如果采用 MVVM 模式来设计 WPF 应用,通常,我们可以使用行为(如 InvokeCommandAction)并结合命令来实 ...
- HTTP请求错误400、401、402、403、404、405、406、407、412、414、500、501、502解析
HTTP 错误 400 400 请求出错 由于语法格式有误,服务器无法理解此请求.不作修改,客户程序就无法重复此请求. HTTP 错误 401 401.1 未授权:登录失败 此错误表明传输给服务器的证 ...
- MySql Table错误:is marked as crashed and last (automatic?) 和 Error: Table "mysql"."innodb_table_stats" not found
一.mysql 执行select 的时候报Table错误:is marked as crashed and last (automatic?) 解决方法如下: 找到mysql的安装目录的bin/myi ...
- NGINX下配置CACHE-CONTROL
HTTP协议的Cache -Control指定请求和响应遵循的缓存机制.在请求消息或响应消息中设置 Cache-Control并不会影响另一个消息处理过程中的缓存处理过程.请求时的缓存指令包括no-c ...
- Oracle子查询中any、some、all之间的区别
用some,any和all对子查询中返回的多行结果进行处理. 下面我们来简单介一下这几个关键词的含义. * Some在此表示满足其中一个的意义,是用or串起来的比较从句. * Any也表示满足其中一个 ...