对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界面是这样的:

 1 #ifndef TCPCLIENT_H
 2 #define TCPCLIENT_H
 3
 4 #include <QWidget>
 5 #include <QMessageBox>
 6 #include <QTcpSocket>
 7 #include <QDebug>
 8 #include <QHostAddress>
 9 #include <QTextStream>
10
11 namespace Ui {
12 class TcpClient;
13 }
14
15 class TcpClient : public QWidget
16 {
17     Q_OBJECT
18
19 public:
20     explicit TcpClient(QWidget *parent = 0);
21     ~TcpClient();
22
23 private slots:
24     void on_connectPushButton_clicked();    //connect按键槽函数
25
26     void slotConnect();
27     void slotErr(QAbstractSocket::SocketError err);
28     void slotDisconnect();
29     void slotReadData();
30
31     void on_sendPushButton_clicked();     //send按键槽函数
32     void on_sendLineEdit_returnPressed();    //sendLineEdit的回车槽
33
34 private:
35     Ui::TcpClient *ui;
36     QTcpSocket *socket;   //客户端的文件描述符
37     bool isConnect;      //connect按键的状态变换值
38 };
39
40 #endif // TCPCLIENT_H

tcpclient.h

  1 #include "tcpclient.h"
  2 #include "ui_tcpclient.h"
  3
  4 TcpClient::TcpClient(QWidget *parent) :
  5     QWidget(parent),
  6     ui(new Ui::TcpClient)
  7 {
  8     ui->setupUi(this);
  9     isConnect = true;
 10 }
 11
 12 TcpClient::~TcpClient()
 13 {
 14     delete ui;
 15 }
 16
 17 void TcpClient::on_connectPushButton_clicked()
 18 {
 19     QString hostip;
 20     quint16 port;
 21     if(isConnect)
 22     {
 23         /*判断ip和端口是否填写,并获取该ip和端口*/
 24         if(ui->ipLineEdit->text().isEmpty())
 25         {
 26             QMessageBox::information(this,"ip","The ip is empty,please input a ip");
 27             return ;
 28         }
 29         hostip = ui->ipLineEdit->text();
 30         if(ui->portLineEdit->text().isEmpty())
 31         {
 32             QMessageBox::information(this,"port","The port is empty,please input a port");
 33             return ;
 34         }
 35         port = ui->portLineEdit->text().toShort();
 36
 37         /*连接服务器,并在这之前连接几个必要的信号*/
 38         socket = new QTcpSocket;
 39         //已连接信号
 40         connect(socket,SIGNAL(connected()),this,SLOT(slotConnect()));
 41         //连接出现错误信号
 42         connect(socket,SIGNAL(error(QAbstractSocket::SocketError)),this,SLOT(slotErr(QAbstractSocket::SocketError)));
 43         //连接断开信号
 44         connect(socket,SIGNAL(disconnected()),this,SLOT(slotDisconnect()));
 45         //准备读取数据信号
 46         connect(socket,SIGNAL(readyRead()),this,SLOT(slotReadData()));
 47         socket->connectToHost(QHostAddress(hostip),port);
 48     }
 49     else
 50     {
 51         socket->disconnectFromHost();
 52         ui->connectPushButton->setText("connect");
 53         isConnect = true;
 54     }
 55 }
 56
 57 //槽函数定义
 58 void TcpClient::slotConnect()
 59 {
 60     QMessageBox::information(this,"connect","host connect sucess");
 61     ui->ipLineEdit->setEnabled(false);
 62     ui->portLineEdit->setEnabled(false);
 63     ui->connectPushButton->setText("disconnect");
 64     isConnect = false;
 65 }
 66
 67 void TcpClient::slotErr(QAbstractSocket::SocketError err)
 68 {
 69     qDebug("error is %d",err);
 70     ui->connectPushButton->setText("connect");
 71 }
 72
 73 void TcpClient::slotDisconnect()
 74 {
 75     QMessageBox::information(this,"disconnect","host disconnect sucess");
 76     ui->ipLineEdit->setEnabled(true);
 77     ui->portLineEdit->setEnabled(true);
 78     ui->connectPushButton->setText("connect");
 79     isConnect = true;
 80 }
 81
 82 void TcpClient::slotReadData()
 83 {
 84     qDebug()<<"have data";
 85     if(socket->bytesAvailable()>0)
 86     {
 87         QString msg;
 88         QByteArray ba;
 89         ba.resize(socket->bytesAvailable());
 90         socket->read(ba.data(),ba.size());
 91         msg = QString(ba);
 92
 93 //        QTextStream out(socket);
 94 //        QString msg;
 95 //        out >> msg;
 96         ui->msgListWidget->addItem(msg);
 97     }
 98 }
 99
100
101 void TcpClient::on_sendPushButton_clicked()
102 {
103     QTextStream in(socket);
104     in << ui->sendLineEdit->text();
105     ui->sendLineEdit->clear();
106 }
107
108
109 void TcpClient::on_sendLineEdit_returnPressed()
110 {
111     QTextStream in(socket);
112     in << ui->sendLineEdit->text();
113     ui->sendLineEdit->clear();
114 }

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 #ifndef MYTCPSOCKET_H
 2 #define MYTCPSOCKET_H
 3
 4 #include <QObject>
 5 #include <QTcpSocket>
 6 #include <QTextStream>
 7 #include <QHostAddress>
 8
 9
10 class MyTcpSocket : public QTcpSocket
11 {
12     Q_OBJECT
13 public:
14
15     MyTcpSocket();
16     ~MyTcpSocket();
17
18 signals:
19     void disconnectSignal(qintptr);
20     void sockReadDataSignal(qintptr,QString);
21
22 private slots:
23     void readDataSlot();
24     void disconnectSlot();
25 };
26
27 #endif // MYTCPSOCKET_H

mytcpsocket.h

 1 #include "mytcpsocket.h"
 2
 3 MyTcpSocket::MyTcpSocket()
 4 {
 5     QObject::connect(this,SIGNAL(readyRead()),this,SLOT(readDataSlot()));
 6     QObject::connect(this,SIGNAL(disconnected()),this,SLOT(disconnectSlot()));
 7 }
 8
 9 MyTcpSocket::~MyTcpSocket()
10 {
11
12 }
13
14 void MyTcpSocket::readDataSlot()
15 {
16     //读取数据的两种方式
17     qDebug()<<"read data";
18     QString msg;
19     QByteArray ba;
20     ba.resize(this->bytesAvailable());
21     this->read(ba.data(),ba.size());
22     msg = QString(ba);
23     qDebug()<<"ip:"<<this->peerAddress().toString();
24
25 //    QTextStream out(this),in(this);
26 //    QString msg;
27 //    out >> msg;
28     emit sockReadDataSignal(this->socketDescriptor(),msg);
29 }
30
31 void MyTcpSocket::disconnectSlot()
32 {
33     qDebug()<<"disconnect";
34     emit disconnectSignal(this->socketDescriptor());
35 }

mytcpsocket.cpp

 1 #ifndef MYTCPSERVER_H
 2 #define MYTCPSERVER_H
 3
 4 #include <QObject>
 5 #include <QHostAddress>
 6 #include <QTcpServer>
 7 #include <QDebug>
 8 #include <QList>
 9 #include "mytcpsocket.h"
10
11 class MyTcpServer : public QTcpServer
12 {
13    Q_OBJECT
14 public:
15     MyTcpServer();
16     ~MyTcpServer();
17
18 protected:
19     void incomingConnection(qintptr socketDescriptor);
20
21
22 private slots:
23     void disconnectSlot(qintptr);
24     void answerMsgSlot(qintptr,QString);
25
26 signals:
27     void serverReadDataSignal(qintptr,QString);
28
29 private:
30     QList<MyTcpSocket *> clients;
31
32 };
33
34 #endif // MYTCPSERVER_H

mytcpserver.h

 1 #include "mytcpserver.h"
 2
 3 MyTcpServer::MyTcpServer()
 4 {
 5     listen(QHostAddress::Any,8080);
 6 }
 7
 8 MyTcpServer::~MyTcpServer()
 9 {
10
11 }
12
13 void MyTcpServer::incomingConnection(qintptr socketDescriptor)//when a new connection is available.
14 {
15     qDebug()<<"connect success";
16     MyTcpSocket  *sock = new MyTcpSocket;
17     sock->setSocketDescriptor(socketDescriptor);
18     QObject::connect(sock,SIGNAL(disconnectSignal(qintptr)),this,SLOT(disconnectSlot(qintptr)));
19     QObject::connect(sock,SIGNAL(sockReadDataSignal(qintptr,QString)),this,SIGNAL(serverReadDataSignal(qintptr,QString)));
20     QObject::connect(sock,SIGNAL(sockReadDataSignal(qintptr,QString)),this,SLOT(answerMsgSlot(qintptr,QString)));
21     clients << sock;
22 }
23
24
25 void MyTcpServer::disconnectSlot(qintptr sockfd)
26 {
27 //    MyTcpSocket *sock = new MyTcpSocket;
28 //    sock->setSocketDescriptor(socketDescriptor);
29     qDebug()<<"delete client "<<sockfd;
30     for (int i = 0; i < clients.size(); ++i)
31     {
32         if (clients.at(i)->socketDescriptor() == sockfd)
33             clients.removeAt(i);
34     }
35
36    // delete sock;
37 }
38
39 void MyTcpServer::answerMsgSlot(qintptr sockfd, QString msg)
40 {
41     for (int i = 0; i < clients.size(); ++i)
42     {
43         if(clients.at(i)->socketDescriptor() != sockfd)
44         {
45             QTextStream in(clients.at(i));
46             in << sockfd << ":" << msg << endl;
47         }
48     }
49 }

mytcpserver.cpp

 1 #ifndef MYTCPSERVERWIDGET_H
 2 #define MYTCPSERVERWIDGET_H
 3
 4 #include <QWidget>
 5 #include "mytcpserver.h"
 6 #include <QObject>
 7
 8 namespace Ui {
 9 class MyTcpServerWidget;
10 }
11
12 class MyTcpServerWidget : public QWidget
13 {
14     Q_OBJECT
15
16 public:
17     explicit MyTcpServerWidget(QWidget *parent = 0);
18     ~MyTcpServerWidget();
19
20 private slots:
21     void widgetReadDataSlot(qintptr,QString);
22
23 private:
24     Ui::MyTcpServerWidget *ui;
25     MyTcpServer *tcpserver;
26 };
27
28 #endif // MYTCPSERVERWIDGET_H

mytcpserverwidget.h

 1 #include "mytcpserverwidget.h"
 2 #include "ui_mytcpserverwidget.h"
 3
 4 MyTcpServerWidget::MyTcpServerWidget(QWidget *parent) :
 5     QWidget(parent),
 6     ui(new Ui::MyTcpServerWidget)
 7 {
 8     ui->setupUi(this);
 9     tcpserver = new MyTcpServer; //connect to the server
10     QObject::connect(tcpserver,SIGNAL(serverReadDataSignal(qintptr,QString)),this,SLOT(widgetReadDataSlot(qintptr,QString)));
11
12 }
13
14 MyTcpServerWidget::~MyTcpServerWidget()
15 {
16     delete ui;
17 }
18
19
20 void MyTcpServerWidget::widgetReadDataSlot(qintptr sock, QString msg)
21 {
22     QString id;
23     id = QString::number(sock);
24     ui->listWidget->addItem(id+":"+msg);
25 }

mytcpserverwidget.cpp

 1 #include "mytcpserverwidget.h"
 2 #include <QApplication>
 3
 4 int main(int argc, char *argv[])
 5 {
 6     QApplication a(argc, argv);
 7     MyTcpServerWidget w;
 8     w.show();
 9
10     return a.exec();
11 }

main.cpp

最后附带一个写的很好的blog

Qt浅谈之十六:TCP和UDP(之一)

时间: 2024-08-08 14:50:05

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

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

本次实验利用TCP/IP, 语言环境为 C/C++ 利用套接字Socket编程,实现Server/CLient 之间简单的通讯. 结果应为类似所示: 下面贴上代码(参考参考...) Server 部分: 1 /* TCPServer.cpp - main */ 2 3 #include <stdlib.h> 4 #include <stdio.h> 5 #include <winsock2.h> 6 #include <time.h> 7 #include

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

本次实验利用TCP/IP, 语言环境为 C/C++ 利用套接字Socket编程,以及线程处理, 实现Server/CLient 之间多人的聊天系统的基本功能. 结果大致如: 下面贴上代码(参考参考...) Server 部分: 1 /* TCPdtd.cpp - main, TCPdaytimed */ 2 3 #include <stdlib.h> 4 #include <stdio.h> 5 #include <winsock2.h> 6 #include <

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

本次实验利用UDP协议, 语言环境为 C/C++ 利用套接字Socket编程,实现Server/CLient 之间简单的通讯. 结果应为类似所示: 下面贴上代码(参考参考...) Server 部分: 1 /* UDPServer.cpp */ 2 3 #include <stdlib.h> 4 #include <stdio.h> 5 #include <winsock2.h> 6 #include <string.h> 7 #include "

c++下基于windows socket的单线程服务器客户端程序

今天自己用编写了一个简单的c++服务器客户端程序,注释较详细,在此做个笔记. windows下socket编程的主要流程可概括如下:初始化ws2_32.dll动态库-->创建套接字-->绑定地址信息-->服务器进行监听/客户端连接服务器-->数据交换-->关闭套接字对象. 服务器端: 1 #include <Winsock2.h> 2 #include <Ws2tcpip.h> 3 #include <iostream> 4 5 #prag

图文:CentOS 下对 Nginx + Tomcat 配置 SSL 实现服务器 / 客户端双向认证

1. 安装 nginx 1.1 nginx 包及其依赖包下载 出于模块的依赖性,Nginx 依赖以下三个包: gzip 模块需要 zlib 库(http://www.zlib.net/): rewrite 模块需要 pcre 库(http://www.pcre.org/): ssl 功能需要 openssl 库(http://www.openssl.org/): 分别下载它们的最新稳定版(截至本文最新稳定版分别是 zlib-1.2.8.tar.gz.pcre-8.36.tar.gz.openss

(图解)windows下的mysql客户端mysqlworkbench 链接虚拟机上CentOS的mysql服务器

本人在虚拟机上CentOS的Linux环境下安装了mysql服务器,在本地Windows下安装了mysql的客户端mysqlworkbench ,所以就想让windows下的mysql客户端mysqlworkbench 链接虚拟机上CentOS的mysql服务器,整个过程记录如下: 一.安装客户端Mysql WorkBench 1.下载安装包:mysql-workbench-community-6.2.5-winx64.msi 下载路径:http://dev.mysql.com/download

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

本文基于<CentOS 6.3下Samba服务器的安装与配置>,参照原博文,自己在CentOS7环境上实现,并按照自己的环境修改博文内容 一.简介 Samba是一个能让Linux系统应用Microsoft网络通讯协议的软件,而SMB是Server Message Block的缩写,即为服务器消息块 ,SMB主要是作为Microsoft的网络通讯协议,后来Samba将SMB通信协议应用到了Linux系统上,就形成了现在的Samba软件.后来微软又把 SMB 改名为 CIFS(Common Int

windows下subversion服务器搭建

一.下载subversion服务器端和客户端软件 1.subversion下载地址:http://subversion.tigris.org/ 2.svn比较流行的客户端Tortoisesvn下载地址:http://tortoisesvn.net/downloads 3.安装subversion服务器和Tortoisesvn,若是exe文件直接安装,若是zip解压后即可(若是想方便,可以在环境变量中配置bin) 二.创建版本库(Repository) 若是想在f:\svnroot下建立版本库,需

Linux 下 Eureka 服务器的部署

1. 查看 Linux 相关信息        版本查看# cat /etc/issueCentOS release 6.5 (Final)        位数查看# getconf LONG_BIT64        第 2 - 4 步是 Eureka Server war 包的构建,如果你嫌自己构建麻烦可以去 http://mvnrepository.com/artifact/com.netflix.eureka/eureka-server 下载一个,比如作者下载的是 eureka-serv