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

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. 【Java基础】HashMap工作原理

    HashMap Hash table based implementation of the Map interface. This implementation provides all of th ...

  2. Yii里model验证的小技巧

    例如:需要判断,字符长度在12-20之间,最大长度的提示语用tooLong,最小提示语言用tooShort array('pay_order', 'length', 'max' => 20, ' ...

  3. 讲一个使用jquery-slick旋转木马效果插件案例

    效果展示连接 http://www.jqcool.net/demo/201405/jquery-slick/ 今天刚接触这个插件,被这插件搞的大脑风暴了 所以来记录一下使用方法 首先注意一点 不特别标 ...

  4. HttpClient filter中间转发从A tomcat转发至B tomcat

    BackFilter.java 主要解决基于HttpGet/HttpPost以及基于HttpPost的附件流转发import java.io.IOException; import java.io.I ...

  5. struts2(一) struts2入门

    首先推荐一本书,虽然我还没看过,但是我以后肯定会看的,<Struts+技术内幕>提取密码:kg6w .现在只是停留在会使用struts2的层次,自己也想继续深入研究,但是感觉自己的知识面还 ...

  6. Oracle-操作

    登录PL_SQL,输入用户名sys 密码 安装时输入的密码,选择 sysdba 打开plsqldev.exe所在目录下的PlugIns文件夹,如果没有请从其它地方拷入 打开运行命令窗口,输入命令 re ...

  7. C#与Java区别(一)

    最近学了点java,总结了一些和c#的语法区别,欢迎大家指点和补充,如下: 1.java支持跨平台,当然.net core现在也支持. 2.java中用package,c#中用namespace定义空 ...

  8. 图解Javascript——变量对象和活动对象

    span { line-height: 1.5 } 这是由一段代码引发的思考: var laterDeclaredVar = 'I am a global variable ...'; (functi ...

  9. Office 365开发概述及生态环境介绍(二)

    本文于2017年3月19日首发于LinkedIn,原文链接在这里 在上一篇 文章,我给大家回顾了Office发展过来的一些主要的版本(XP,2003,2007,2013等),以及在Office客户端中 ...

  10. 卸载php+apache+mysql

    一.卸载删除 mysql   1 sudo apt-get autoremove --purge mysql-server-5.0 2 sudo apt-get remove mysql-server ...