用qt实现一个网络服务器
- 服务器程序
- 最终结果
- 客户端程序
- 成果
服务器程序
//server.h
#pragma once
#include <QTcpServer>
#include <QTcpSocket>
#include <QDebug>
#include <QList>
#include <QTimer>
#include <iostream>
class server : public QObject
{
Q_OBJECT
private:
QTcpServer tcpServer;
quint16 port; //服务器端口
QList<QTcpSocket *> tcpClientList; //保存所有和客户端通信的套接字
QTimer timer;
public:
void startServer(int port);
explicit server(QObject *parent = 0);
~server();
public slots:
void onNewConnection(); //响应客户端连接请求的函数
void onReadyRead(); //接收客户端消息的槽函数
void sendMessage(const QByteArray &buf);
//定义时间超出的槽函数
void onTimeOut(void);
};
//server.cpp
#include "server.h"
void server::onNewConnection() //响应客户端连接请求的函数
{
//保存套接字到容器
QTcpSocket *tcpClient = tcpServer.nextPendingConnection();
tcpClientList.append(tcpClient);
//当客户端向服务器发送信息时,通信套接字发送信号:readyRead
connect(tcpClient, SIGNAL(readyRead()), this, SLOT(onReadyRead()));
}
void server::onReadyRead() //接收客户端消息的槽函数
{
//遍历容器哪个客户端给服务器发送了消息
for (int i = 0; i < tcpClientList.size(); i++)
{
// bytesAvailable:获取当前套接字等待读取消息字节数
//返回0表示没有消息
//返回大于0说明当前套接字有消息到来
if (tcpClientList.at(i)->bytesAvailable())
{
//读取消息并保存
QByteArray buf = tcpClientList.at(i)->readAll();
qDebug() << buf;
sendMessage(buf);
}
}
}
//发送消息的槽函数
void server::sendMessage(const QByteArray &buf)
{
//发送给所有建立连接的人
for (int i = 0; i < tcpClientList.size(); i++)
{
//发送消息
tcpClientList.at(i)->write(buf);
}
}
void server::startServer(int port)
{
this->port = port;
if (tcpServer.listen(QHostAddress::Any, port) == true)
{
qDebug() << "Server starts success, listening port number is" << port << ".";
//开启定时器
timer.start(3000);
}
else
{
qDebug() << "The server starts failed, please confirm if the port number is occupied.";
}
}
void server::onTimeOut(void)
{
unsigned int tcpClientSize = tcpClientList.size();
qDebug()<<"当前连接数:"<<tcpClientSize;
//遍历检查容器中保存的客户端通信套接字是否已经断开连接,如果是则删除
for (unsigned int i = 0; i < tcpClientSize; i++)
{
if (tcpClientList.at(i)->state() == QAbstractSocket::UnconnectedState) // state是套接字状态,用来标识连接状态
{
tcpClientList.removeAt(i);
--i; //保证删除掉一个元素后依旧能遍历全部元素
--tcpClientSize; //删除掉一个后元素量也少了一个
}
}
//容器
}
server::server(QObject * parent):QObject(parent)
{
//当有客户端向服务器发送连接请求,发送信号:newConnection
connect(&tcpServer, SIGNAL(newConnection()),
this, SLOT(onNewConnection()));
//定时器到时发送信号:timeout
connect(&timer, SIGNAL(timeout()),
this, SLOT(onTimeOut()));
}
server::~server()
{
qDebug()<<"stop server";
}
//main.cpp
#include <QCoreApplication>
#include "dbServer.h"
int main(int argc, char *argv[])
{
QCoreApplication server(argc,argv);
dbServer *test = new dbServer();
test->startServer(8081);
return server.exec();
}
最终结果
客户端程序
//dialog.h
#ifndef DIALOG_H
#define DIALOG_H
#include <QDialog>
#include <QTcpSocket>
#include <QHostAddress>
#include <QMessageBox>
#include <QDebug>
QT_BEGIN_NAMESPACE
namespace Ui { class Dialog; }
QT_END_NAMESPACE
class Dialog : public QDialog
{
Q_OBJECT
public:
explicit Dialog(QWidget *parent = nullptr);
~Dialog();
public slots:
//发送按钮对应的槽函数
void on_sendButton_clicked();
//连接服务器按钮对应的槽函数
void on_connectButton_clicked();
//和服务器连接成功的槽函数
void onConnected();
//和服务器连接断开时执行的槽函
void onDisconnected();
//接收聊天消息的槽函数
void onReadyRead();
//网络异常执行的槽函数
void onError();
private:
Ui::Dialog *ui;
bool status;//标识状态:在线还是离线
QTcpSocket tcpSocket;//和服务器通信的套接字
QHostAddress serverIP;//服务器地址
quint16 serverPort;
QString username;
};
#endif // DIALOG_HA
//dialog.cpp
#include "dialog.h"
#include "ui_dialog.h"
Dialog::Dialog(QWidget *parent)
: QDialog(parent)
, ui(new Ui::Dialog)
{
ui->setupUi(this);
status = false;//离线状态
connect(&tcpSocket,SIGNAL(connected()),this,SLOT(onConnected()));
connect(&tcpSocket,SIGNAL(disconnected()),this,SLOT(onDisconnected()));
connect(&tcpSocket,SIGNAL(readyRead()),this,SLOT(onReadyRead()));
connect(&tcpSocket,SIGNAL(error(QAbstractSocket::SocketError)),this,SLOT(onError()));
}
Dialog::~Dialog()
{
delete ui;
}
//发送按钮对应的槽函数
void Dialog::on_sendButton_clicked()
{
qDebug()<<"1";
//获取用户输入的聊天消息
QString msg = ui->messageEdit->text();
if(msg == "")
{
return ;
}
msg = username + ":" + msg;
//发送消息
tcpSocket.write(msg.toUtf8());
//清空消息输入框
ui->messageEdit->clear();
qDebug()<<"1";
}
//连接服务器按钮对应的槽函数
void Dialog::on_connectButton_clicked()
{
qDebug()<<"2";
//如果是离线状态,则建立和服务器连接
if(status == false)
{
//获取服务器IP地址
serverIP.setAddress(ui->serverIpEdit->text());
qDebug()<<serverIP;
//获取服务器端口
serverPort = ui->serverPortEdit->text().toUShort();
qDebug()<<serverPort;
//获取聊天室称呼
username = ui->usernameEdit->text();
qDebug()<<username;
//向服务器发送连接请求
tcpSocket.connectToHost(serverIP,serverPort);
//成功发送信号:connected
//失败发送信号:error
}
//如果时在线状态,则断开连接
else
{
qDebug()<<"断开连接";
//向服务器发送离开聊天室的提示消息
QString msg = username + "离开聊天室";
tcpSocket.write(msg.toUtf8());
//关闭和服务器的连接,同时发送信号:disconnected
tcpSocket.disconnectFromHost();
}
qDebug()<<"2";
}
//和服务器连接成功的槽函数
void Dialog::onConnected()
{
qDebug()<<"3";
status = true;//更改状态为在线
ui->sendButton->setEnabled(true);
ui->serverIpEdit->setEnabled(false);
ui->serverPortEdit->setEnabled(false);
ui->usernameEdit->setEnabled(false);
ui->connectButton->setText("离开聊天室");
//向服务器发送进入聊天室提示消息
QString msg = username + "进入聊天室";
//toUtf8:QString转换为QByteArray
tcpSocket.write(msg.toUtf8());
qDebug()<<"3";
}
//和服务器连接断开时执行的槽函
void Dialog::onDisconnected()
{
qDebug()<<"4";
status = false;
ui->sendButton->setEnabled(false);
ui->serverIpEdit->setEnabled(true);
ui->serverPortEdit->setEnabled(true);
ui->usernameEdit->setEnabled(true);
ui->connectButton->setText("连接服务器");
qDebug()<<"4";
}
//接收聊天消息的槽函数
void Dialog::onReadyRead()
{
qDebug()<<"5";
if(tcpSocket.bytesAvailable())
{
//接收消息
QByteArray buf = tcpSocket.readAll();
//显示消息
ui->listWidget->addItem(buf);
ui->listWidget->scrollToBottom();
}
qDebug()<<"5";
}
//网络异常执行的槽函数
void Dialog::onError()
{
qDebug()<<"6";
//errorString():获取网络异常的原因
// QMessageBox::critical(this,"ERROR",tcpSocket.errorString());
qDebug()<<"6";
}
//main.cpp
#include "dialog.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Dialog w;
w.show();
return a.exec();
}
//dialog.ui
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Dialog</class>
<widget class="QDialog" name="Dialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>314</width>
<height>428</height>
</rect>
</property>
<property name="windowTitle">
<string>Dialog</string>
</property>
<widget class="QWidget" name="">
<property name="geometry">
<rect>
<x>30</x>
<y>40</y>
<width>258</width>
<height>360</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QListWidget" name="listWidget"/>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLineEdit" name="messageEdit"/>
</item>
<item>
<widget class="QPushButton" name="sendButton">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>send</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QFormLayout" name="formLayout">
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>服务器地址</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="serverIpEdit">
<property name="text">
<string>192.168.10.94</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>服务器端口</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="serverPortEdit">
<property name="text">
<string>8081</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>称呼</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLineEdit" name="usernameEdit">
<property name="text">
<string>a</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QPushButton" name="connectButton">
<property name="text">
<string>连接服务器</string>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
<resources/>
<connections/>
</ui>
成果