linux小作业,在虚拟机中安装qt并使用实现服务端和客户端的连接,实现局域网聊天的内容
环境:centos7(适用于绝大多数本科教学环境)
总体设计
系统架构:
客户端-服务器架构:聊天室采用典型的客户端-服务器架构,客户端负责用户交互和显示,服务器负责消息处理和转发。
服务器端设计:
网络监听:使用socket编程接口创建TCP监听套接字。定监听地址和端口,开始监听客户端连接。
连接管理:采用多线程方式处理客户端连接。为每个连接创建一个会话,记录用户信息和连接状态。
消息处理:接收客户端发送的消息,解析消息内容。根据消息类型进行相应处理,如广播消息、私聊消息等。封装响应消息并发送给目标客户端。
用户管理:维护在线用户列表,记录用户名、连接状态等信息。实现用户登录、注销、查找等功能。
日志记录:记录系统运行日志和用户行为日志,方便后续分析和问题排查。
性能优化:使用多路复用技术提高I/O处理效率。优化数据结构,减少内存占用和查找时间。合理使用缓存和异步处理,提高系统响应速度。
客户端设计:
用户交互:设计简洁明了的用户界面,支持文本输入。实现用户登录等交互操作。
消息发送/接收:与服务器端建立连接,发送和接收消息。解析服务器返回的响应消息,更新用户界面。
消息展示:在聊天窗口中展示接收到的消息。
异常处理:处理网络连接异常、消息解析错误等异常情况。给出友好提示信息,引导用户重新连接或重新发送消息。
以下为示例代码:
(读者重点掌握socket编程和通信)
//服务器server:
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow)
{
ui->setupUi(this);
mserver = new QTcpServer(this);
msocket = NULL;
connect(mserver,&QTcpServer::newConnection,this,&MainWindow::new_client);
}
MainWindow::~MainWindow()
{
delete ui;
}
//监听——启动服务器
void MainWindow::on_pushButton_clicked()
{
if( ui->textEdit->toPlainText() == "" ){ //判断是否为空
ui->textBrowser->append("服务器启动失败,请输入要监听的端口号!");
}else{
mserver->listen(QHostAddress::Any,ui->textEdit->toPlainText().toUShort());
ui->textBrowser->append("服务器启动成功,正在监听端口"+ui->textEdit->toPlainText()+"!");
}
}
//接受连接
void MainWindow::new_client()
{
//获取与客户端通信的套接字
msocket = mserver->nextPendingConnection();
socketlist.append(msocket); //将客户端的套接字加入容器中
//4.使用socket的write函数向客户端发送数据
msocket->write("服务器连接成功!");
//获取客户端IP并显示
QString ip = msocket->peerAddress().toString();
ip.remove("::ffff:");
quint16 port = msocket->peerPort();
QString tmp = QString("客户端[%1:%2] 已上线!").arg(ip).arg(port);
ui->textBrowser->append(tmp);
connect(msocket,&QTcpSocket::readyRead,this,&MainWindow::read_data);
}
//读取数据
void MainWindow::read_data()
{
//获取信号发送者
QTcpSocket *msocket1 = dynamic_cast<QTcpSocket*>(sender());
//读取数据
QString msg = msocket1->readAll();
//获取对方IP和端口
QString ip = msocket1->peerAddress().toString();
quint16 port = msocket1->peerPort();
ip.remove("::ffff:");
//quint16 port = msocket1->peerPort();
//QString tmp = QString("客户端[%1:%2]:%3").arg(ip).arg(port).arg(msg);
if( msg != "@断开连接@" ){ //如果传过来的不是断开连接的信息
QString tmp = msg;
ui->textBrowser->append(tmp);
//传送到每个客户端里面
for(int i = 0 ; i < socketlist.size() ; i ++ ){
socketlist[i]->write(tmp.toUtf8());
}
}else{ //如果传过来是断开连接的信息
//num--; //在线人数减少
QString tmp = QString("客户端[%1:%2] 已下线!").arg(ip).arg(port);
ui->textBrowser->append(tmp);
//QString tmp = "系统信息:"+ui->textEdit_2->toPlainText();
//send_data(tmp);;
}
}
//断开连接
void MainWindow::on_pushButton_2_clicked()
{
//断开连接
ui->textBrowser->append("服务器已经关闭!");
mserver->disconnect();
//num = 0;
}
//发送按钮
void MainWindow::on_pushButton_3_clicked()
{
//向客户端发送信息
//获取与客户端通信的套接字
//QTcpSocket *msocket = mserver->nextPendingConnection();
//使用socket的write函数向客户端发送数据
QString tmp = "系统信息:"+ui->textEdit_2->toPlainText();
//注意这里要toUtf8,否则会报错
//msocket->write(tmp.toUtf8());
send_data(tmp);
}
//发送信息到客户端
void MainWindow::send_data(QString which){
//向每个客户端发送
for(int i = 0 ; i < socketlist.size() ; i++ ){
socketlist[i]->write(which.toUtf8());
}
//服务器本身日志添加
ui->textBrowser->append(which);
}
//客户端client:
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
}
MainWindow::~MainWindow()
{
delete ui;
}
//连接服务器
void MainWindow::on_pushButton_2_clicked()
{
if( ui->textEdit_4->toPlainText() == "" ){
QMessageBox::warning(this,tr("提示"),tr("请输入聊天用户名"),tr("确定"));
return;
}else if( ui->textEdit->toPlainText() == "" ){
QMessageBox::warning(this,tr("提示"),tr("请输入IP地址"),tr("确定"));
return;
}else if( ui->textEdit_2->toPlainText() == "" ){
QMessageBox::warning(this,tr("提示"),tr("请输入端口"),tr("确定"));
return;
}
//存储id名字
idname = ui->textEdit_4->toPlainText();
msocket = new QTcpSocket(this);
//关联读数据信号(当QTcpSocket有数据可读会发送readyRead信号--关联槽函数)
connect(msocket,&QTcpSocket::readyRead,this,&MainWindow::read_data);
//调用对象方法链接服务器(connectToHost(服务器的IP, 端口))
//msocket->connectToHost(ui->textEdit->toPlainText(),ui->textEdit_2->toPlainText().toUShort());
msocket->connectToHost( QHostAddress::LocalHost,ui->textEdit_2->toPlainText().toUShort());
//禁用按钮以及输入框(412,2),防止重复点击
ui->pushButton_2->setDisabled(true);
ui->textEdit->setDisabled(true);
ui->textEdit_2->setDisabled(true);
ui->textEdit_4->setDisabled(true);
}
void MainWindow::read_data()
{
//读取数据
QString msg = msocket->readAll();
ui->textBrowser->append(msg);
}
//发送
void MainWindow::on_pushButton_clicked()
{
if( msocket->state() == QAbstractSocket::ConnectedState ){ //判断连接状态:如果连接
//向服务器发送数据
QString data = QString("%1:%2").arg(idname).arg(ui->textEdit_3->toPlainText());
msocket->write(data.toUtf8());
}
//客户端日志
//QString tmp = QString("客户端:%1").arg(data);
//ui->textBrowser->append(tmp);
}
//断开连接
void MainWindow::on_pushButton_3_clicked()
{
if( msocket->state() == QAbstractSocket::ConnectedState ){ //判断连接状态:如果连接
//断开前发送信息给客户端
QString data = "@断开连接@";
msocket->write(data.toUtf8());
//主动和对方断开连接
msocket->disconnectFromHost();
msocket->close();
}
//解除禁用按钮以及输入框(412,2)
ui->pushButton_2->setDisabled(false);
ui->textEdit->setDisabled(false);
ui->textEdit_2->setDisabled(false);
ui->textEdit_4->setDisabled(false);
}
如图所示 简单的UI就制作完成啦