在汇文培训老师给讲了这个例子。讲的挺好的

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浅谈之十六:TCP和UDP(之一)

对Qt下对话服务器客户端的总结(MyTcpServer与MyTcpClient)的更多相关文章

  1. 【Echo】实验 -- 实现 C/C++下TCP, 服务器/客户端 通讯

    本次实验利用TCP/IP, 语言环境为 C/C++ 利用套接字Socket编程,实现Server/CLient 之间简单的通讯. 结果应为类似所示: 下面贴上代码(参考参考...) Server 部分 ...

  2. 【Chat】实验 -- 实现 C/C++下TCP, 服务器/客户端 "多人聊天室"

    本次实验利用TCP/IP, 语言环境为 C/C++ 利用套接字Socket编程,以及线程处理, 实现Server/CLient 之间多人的聊天系统的基本功能. 结果大致如: 下面贴上代码(参考参考.. ...

  3. 【Echo】实验 -- 实现 C/C++下UDP, 服务器/客户端 通讯

    本次实验利用UDP协议, 语言环境为 C/C++ 利用套接字Socket编程,实现Server/CLient 之间简单的通讯. 结果应为类似所示: 下面贴上代码(参考参考...) Server 部分: ...

  4. windows下的mysql客户端mysqlworkbench链接虚拟机上CentOS的mysql服务器

    本人在虚拟机上CentOS的Linux环境下安装了mysql服务器,在本地Windows下安装了mysql的客户端mysqlworkbench ,所以就想让windows下的mysql客户端mysql ...

  5. c++下基于windows socket的单线程服务器客户端程序(基于TCP协议)

    今天自己编写了一个简单的c++服务器客户端程序,注释较详细,在此做个笔记. windows下socket编程的主要流程可概括如下:初始化ws2_32.dll动态库-->创建套接字-->绑定 ...

  6. c++下基于windows socket的服务器客户端程序(基于UDP协议)

    前天写了一个基于tcp协议的服务器客户端程序,今天写了一个基于UDP协议的,由于在上一篇使用TCP协议的服务器中注释已经较为详细,且许多api的调用是相同的,故不再另外注释. 使用UDP协议需要注意几 ...

  7. CentOS 6.3下Samba服务器的安装与配置方法(图文详解)

    这篇文章主要介绍了CentOS 6.3下Samba服务器的安装与配置方法(图文详解),需要的朋友可以参考下   一.简介  Samba是一个能让Linux系统应用Microsoft网络通讯协议的软件, ...

  8. CentOS 6.3下Samba服务器的安装与配置(转)

    CentOS 6.3下Samba服务器的安装与配置   一.简介 Samba是一个能让Linux系统应用Microsoft网络通讯协议的软件,而SMB是Server Message Block的缩写, ...

  9. CentOS 7下Samba服务器的安装与配置

    文基于<CentOS 6.3下Samba服务器的安装与配置>,参照原博文,自己在CentOS7环境上实现,并按照自己的环境修改博文内容 一.简介 Samba是一个能让Linux系统应用Mi ...

随机推荐

  1. smarty模板基础1

    smarty模板的作用可以让前端和后端分离(也就是前端的显示页面和后端的php代码). smarty模板的核心是一个类,下载好的模板中有这么几个重要的文件夹 (1)libs核心文件夹(2)int.in ...

  2. java程序员入门:英语好不好对编程到底有没有影响

    我想当码农,听说钱钱拿的多! 哦.是很有钱!么样? 可是我不会! 那你想么样?去学撒! 可是,我英语差-- 有多差??? 很差-- 那????? 关于英语水平对编程的影响,我们一起来看看啦!希望可以解 ...

  3. css3滚动效果

    .css{ -webkit-transition-duration: .3s;    transition-duration: .3s; }

  4. 3399: [Usaco2009 Mar]Sand Castle城堡

    3399: [Usaco2009 Mar]Sand Castle城堡 Time Limit: 3 Sec  Memory Limit: 128 MBSubmit: 37  Solved: 32[Sub ...

  5. 1197: [HNOI2006]花仙子的魔法

    1197: [HNOI2006]花仙子的魔法 Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 762  Solved: 443[Submit][Stat ...

  6. web从入门开始(2)----基础

    HTML文件结构 <html> <head>//网页头标记 <title>放置标题</title> </head> <body> ...

  7. 定时任务框架APScheduler学习详解

    APScheduler简介 在平常的工作中几乎有一半的功能模块都需要定时任务来推动,例如项目中有一个定时统计程序,定时爬出网站的URL程序,定时检测钓鱼网站的程序等等,都涉及到了关于定时任务的问题,第 ...

  8. Hadoop2.7.3+Spark2.1.0完全分布式集群搭建过程

    1.选取三台服务器(CentOS系统64位) 114.55.246.88 主节点 114.55.246.77 从节点 114.55.246.93 从节点 之后的操作如果是用普通用户操作的话也必须知道r ...

  9. javascript中常用的

    1.javascript中构造equals().trim()方法并应用 String.prototype.Trim = function() { return this.replace(/(^\s*) ...

  10. iOS开发之计算文字尺寸

    /** *  计算文字尺寸 * *  @param text    需要计算尺寸的文字 *  @param font    文字的字体 *  @param maxSize 文字的最大尺寸 */ - ( ...