前言

计算网络课程设计开始,要求用C/C++,而且有些题目还要有图形界面,因此决定用Qt写图形界面,同时决定用一个程序完成所有的实验设计。

白嫖容易,创作不易,本文原创,转载请注明!!!
源码和可运行程序:
链接:https://pan.baidu.com/s/1A9KctmpP2JJgyW2wLrehIg
提取码:Lin2

计算网络课程设计:
计算机网络课程设计之网络聊天程序的设计与实现
计算网络课程设计之Tracert与Ping程序设计与实现
计算机网络课程设计之基于 IP 多播的网络会议程序
计算机网络课程设计之网络嗅探器的设计与实现
计算网络课程设计之电子邮件客户端程序设计与实现
计算网络课程设计之TELNET 终端设计与实现
计算机网络课程设计之网络代理服务器的设计与实现
计算网络课程设计之简单 Web Server 程序的设计与实现

Qt入门系列:
Qt学习之C++基础
Qt学习之Qt安装
Qt学习之Qt基础入门(上)
Qt学习之Qt基础入门(中)
Qt学习之Qt基础入门(下)

创作不易,整个课程设计程序3000多行代码,所有实验都写在了一个程序中,时间有限,能力不足,转载望注明!!!
本文链接
个人博客:https://ronglin.fun/archives/264
PDF链接:见博客网站
CSDN: https://blog.csdn.net/RongLin02/article/details/122509928

实验题目

网络聊天程序的设计与实现

实验目的

了解 Socket 通信的原理,在此基础上编写一个聊天程序。

总体设计

(含背景知识或基本原理与算法、或模块介绍、设计步骤等)
本次设计客户端用了Qt,Qt的一个很鲜明的特色就是可以使用槽函数异步处理数据。
服务器端用了Java,因为要用到多线程处理每一个客户端,同时用到了很多阻塞式方法,Java的库中对于多线程的处理十分方便,因此服务器用Java书写。

客户端

在Qt中创建一个TCP连接十分简单
参考博客:QT5实现简单的TCP通信
首先使用QT的网络套接字需要.pro文件中加入一句:

QT       += network

客户端的代码比服务器稍简单,总的来说,使用QT中的QTcpSocket类与服务器进行通信只需要以下5步:

  1. 创建QTcpSocket套接字对象
QTcpSocket* socket = new QTcpSocket();
  1. 使用这个对象连接服务器
socket->connectToHost(IP, port);
  1. 使用write函数向服务器发送数据
int result = socket->write(data);
  1. 当socket接收缓冲区有新数据到来时,会发出readRead()信号,因此为该信号添加槽函数以读取数据
QObject::connect(socket, &QTcpSocket::readyRead, this, &MainWindow::socket_Read_Data);

void MainWindow::socket_Read_Data()
{
    QByteArray buffer;
    //读取缓冲区数据
    buffer = socket->readAll();
}
  1. 断开与服务器的连接
    关于close()和disconnectFromHost()的区别,可以按F1看帮助
socket->disconnectFromHost();
//socket->close()

服务器

虽然服务器端用的是Java写的,但是还是提一下Qt中如何创建服务器

Qt TCP服务器

服务器除了使用到了QTcpSocket类,还需要用到QTcpSever类。即便如此,也只是比客户端复杂一点点,用到了6个步骤:

  1. 创建QTcpSever对象
QTcpSocket server = new QTcpServer();
  1. 侦听一个端口,使得客户端可以使用这个端口访问服务器
    这里的第一个参数是QHostAddress,在Qt的assistant(帮助文档)有详细的说明
server->listen(QHostAddress::Any, port);
  1. 当服务器被客户端访问时,会发出newConnection()信号,因此为该信号添加槽函数,并用一个QTcpSocket对象接受客户端访问
connect(server,&QTcpServer::newConnection,this,&MainWindow::server_New_Connect);
void MainWindow::server_New_Connect()
{
    //获取客户端连接
    socket = server->nextPendingConnection();
}
  1. 使用socket的write函数向客户端发送数据
int result = socket->write(data);
  1. 当socket接收缓冲区有新数据到来时,会发出readRead()信号,因此为该信号添加槽函数以读取数据
QObject::connect(socket, &QTcpSocket::readyRead, this, &MainWindow::socket_Read_Data);

void MainWindow::socket_Read_Data()
{
    QByteArray buffer;
    //读取缓冲区数据
    buffer = socket->readAll();
}
  1. 取消侦听
server->close();

Qt异步处理数据用起来很方便,因为connect函数的存在,程序不用阻塞,可以等待槽函数的触发。

Java TCP服务器

接下来说明Java如何创建一个TCP服务器
参考博客:TCP通信的Java实现

  1. 先创建一个ServerSocket
  2. 调用accept()方法获取连接,此方法阻塞式方法
  3. 获取客户端的输入流,来读客户端数据
  4. 获取客户端的输出流,来向客户端写数据
  5. 关闭所有对象
    代码如下
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;

public class Server {
    public static void main(String[] args) {
        //1. 创建服务器ServerSocket对象
        try {
            ServerSocket serverSocket = new ServerSocket(6666);
            //2. 获取客户端请求对象Socket
            Socket socket = serverSocket.accept();
            //3. 获取字节输入流对象
            InputStream inputStream = socket.getInputStream();
            //4. 读取客户端数据
            byte[] bytes = new byte[1024];
            int len = inputStream.read(bytes);
            System.out.println(new String(bytes,0,len));
            //5. 获取字节输出流对象
            OutputStream outputStream = socket.getOutputStream();
            //6. 向客户端回写数据
            outputStream.write("I am OK.".getBytes());
            //7. 释放资源
            socket.close();
            serverSocket.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

详细设计

(含主要的数据结构、程序流程图、关键代码等)

数据格式

首先是数据的数据格式,这里我设置了一个Message类,格式如下:

TYPE: Information
FROM: 11809
TO: 11810
TIME: 2022-01-05 17:18:49
CONTENT: test测试

一共是5项数据:

  1. TYPE,表示数据的类型,类型一共有3种,一个是Heartbeat,表示心跳,心跳类的数据包只有一个Type类型的数,其余都是0或者null,第二种是UserList,表示用户列表,当有客户端连入TCP服务器或者与其断开的时候,会广播发送一次UserList数据包,用来告诉所有客户端更新用户列表。最后一个是Information数据包,用来封装消息。
  2. FROM,在Information中表示数据的发送者
  3. TO,在Information中表示数据的接收者,TCP服务器通过此来把数据发送给相应的TCPClient
  4. TIME,时间,注意虽然发送者在发送的时候会填入一个时间到Information类数据包中,但是当数据到达服务器时,服务器会以本身的时间来更新这部分,以防客户端本身的时间错误
  5. CONTENT,表示内容,即正文部分。

服务器

如果要实现聊天室,首先要先设计一个服务器,服务器的设计主要是Java多线程实现
下面简单说明一下设计过程

一共有3类线程:

  1. 第一类线程是在Main中的一个while(true),主要是向服务器中所有连接的客户端发送心跳包,每500ms发送一个心跳包,如果发送失败则说明客户端已经断开,然后广播一个UserList类型的数据,来告诉客户端更新列表。
  2. 第二类线程是TCPServer线程,该线程的主要作用就是用来处理阻塞的accept()方法,当连接成功一个客户端之后,将其添加到一个维护的ArrayList,然后再次while(true)阻塞accept()方法。
  3. 第三类线程就是用来处理所有的客户端的数据,主要是处理阻塞的read()方法,用来监听客户端的输入。

同时每一个客户端的标识是用他们本身的本地Port数据来标识。

客户端

在这里插入图片描述
界面如上,当点击登录的时候,socket开始连接,ip地址为自行写死的"127.0.0.1"端口也是写死的"60001",然后登录之后收到来自服务器的UserList类型的数据包,然后更新自己的在线用户栏目,复位就是清0一个计数变量,清空按钮是清空上下的textEdit控件,启动新进程是为了调试,主要是用来启动一个新的子进程,发送按钮的逻辑是先清空发送栏,然后调用write()方法,把数据打包成Message格式,然后发送给服务器。
需要注意的是,要时刻注意判定是否在登录的状态下。

实验结果与分析

下面展示一下过程
在这里插入图片描述
可以看到连接成功,然后进行数据交换
在这里插入图片描述

可以看到数据发送成功,同时在服务器端也能获得数据
当第二个客户端断开:
在这里插入图片描述
当第二个断开的时候,可以在服务器端看到14760已经断开,同时第一个客户端可以看到14760已经断开了。

小结与心得体会

因为本实验用的是Qt和Java已经封装好的TCP库,所以用起来很方便,因为以前已经有TCP服务开发经验所以本实验开发比较简单。
个人觉得本次设计亮点在于设计Message类型和添加了心跳包。
=w=

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐