网通实验——QT实现基于TCP协议的Socket通信(含源码)

本项目为本科课程作业,仅供参考学习,有问题或建议欢迎与作者交流


前言

TCP连接:Socket套接字基础知识

TCP(Transmission Control Protocol 传输控制协议)把连接作为最基本的抽象。TCP的许多特性都与TCP是面向连接的这个基本特征有关。因此我们对TCP连接需要有更清楚的了解。
每一条TCP连接有两个端点

那么TCP连接的端点是什么呢?


一、TCP连接的端点

TCP连接的端点不是主机的IP地址,不是应用进程,也不是运输层的协议端口。
TCP实现两个终端的通信,故除了需要获取目标主机的IP地址外,还需要目标主机的对应应用进程端口号Port,
所以TCP连接的端点叫做套接字(socket)或插口。根据RFC 793定义:端口号拼接到IP地址即构成了套接字
所以TCP使用Socket套接字作为端点。

套接字socket = (IP地址:端口号)

每一条TCP连接唯一的被通信两端的两个端点(即套接字对 socket pair)所确定。即:

TCP连接:: = { socket1,socket2 } = { (IP1, Port1), (IP2, Port2) }

这里IP1和IP2分别是两个端点主机的IP地址,而Port1和Port分别是两个端点主机中的端口号

一种理解是Socket就是该模式的一个实现:即socket是一种特殊的文件,一些socket函数就是对其进行的操作(读/写IO、打开、关闭)。
socket抽象层

TCP/IP与Socket详解参考资料


二、代码分析

代码架构

在这里插入图片描述

(一)头文件

1.client.h

主要include一些库,定义一些函数,创建socket对象
代码如下:

#ifndef CLIENT_H
#define CLIENT_H

#include <QMainWindow>
#include <QTcpSocket>
#include <QKeyEvent>
#include <QWidget>

namespace Ui {
    
    
class Client;
}

class Client : public QMainWindow
{
    
    
    Q_OBJECT

public:
    explicit Client(QWidget *parent = nullptr);
    ~Client();

private slots:
    void on_connect_button_clicked(bool checked);

    void on_send_button_clicked();

    void readyRead_SLOT();

    void connected_SLOT();

private:
    Ui::Client *ui;
    QTcpSocket *socket;
};

#endif // CLIENT_H

2.server.h

代码如下:

#ifndef SERVER_H
#define SERVER_H

#include <QMainWindow>
#include <QTcpServer>
#include <QTcpSocket>
#include <QString>

QT_BEGIN_NAMESPACE
namespace Ui {
    
     class Server; }
QT_END_NAMESPACE

class Server : public QMainWindow
{
    
    
    Q_OBJECT

public:
    Server(QWidget *parent = nullptr);
    ~Server();

private slots:
    void on_send_button_clicked();

    void on_startorstop_Listen_clicked(bool checked);

    void readyRead_SLOT();

    void newConnection_SLOT();

private:
    Ui::Server *ui;
    QTcpSocket *socket;
    QTcpServer *server;
};
#endif // SERVER_H

(二)源文件

1、client.cpp

主要include一些库,定义一些函数,创建socket和server对象

#include "client.h"
#include "ui_client.h"
#include "stdio.h"

#include "QString"

Client::Client(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::Client)
{
    
    
    ui->setupUi(this);
    //设置clicked(bool checked)点击反转状态打开
    ui->connect_button->setCheckable(true);

    socket = new QTcpSocket();

    //信号:客户端申请连接成功 槽函数:允许写入数据
    connect(socket,SIGNAL(connected()),this,SLOT(connected_SLOT()));
}

Client::~Client()
{
    
    
    delete ui;
}

//信号:客户端申请连接成功 槽函数:允许写入数据
void Client::connected_SLOT()
{
    
    
    connect(socket,SIGNAL(readyRead()),this,SLOT(readyRead_SLOT()));//如果socket中有缓存消息,触发槽函数
}

//接收消息并显示到接收框
void Client::readyRead_SLOT()
{
    
    
    qDebug() << "Client Received!";
    QString buffer;
    //读取缓冲区数据
    buffer = socket->readAll();
    if(!buffer.isEmpty())
    {
    
    
        //刷新显示
        ui->receiver->appendPlainText(buffer);
    }
}

//连接和断开按键
void Client::on_connect_button_clicked(bool checked)
{
    
    
    if(checked)
    {
    
    
        QString IP = ui->ipnum->text();
        int Port = ui->portnum->text().toUInt();
        //取消已有的连接
        socket->abort();
        //连接服务器
        socket->connectToHost(IP,Port);
        //如果等待超过1000ms
        if(!socket->waitForConnected(1000))
        {
    
    
            qDebug() << "Connect failed, please try again later!";
            //连接失败,再次点击则重新连接,将checked恢复为true
            ui->connect_button->toggle();
            return;
        }
        qDebug() << "Connect Successfully! Connect with IP:" << IP << "; port:" << Port;
        //修改按键文字
        ui->connect_button->setText("断开连接");
        //发送键使能
        ui->send_button->setEnabled(true);
    }
    else
    {
    
    
        qDebug() << "Disconnect!";
        //断开连接
        socket->disconnectFromHost();
        //修改按键文字&发送键静默
        ui->connect_button->setText("连接");
        ui->send_button->setEnabled(false);
    }
}

//发送消息,写入socket缓存区
void Client::on_send_button_clicked()
{
    
    
    qDebug() << "Client Send: " << ui->sender->toPlainText().toLatin1();
    //将输入框的内容写入socket缓冲区
    socket->write(ui->sender->toPlainText().toLatin1());
    //刷新socket缓冲区
    socket->flush();
}


2、server.cpp

#include "server.h"
#include "ui_server.h"

Server::Server(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::Server)
{
    
    
    ui->setupUi(this);
    //设置clicked(bool checked)点击反转状态打开
    ui->startorstop_Listen->setCheckable(true);

    socket = new QTcpSocket();
    server = new QTcpServer();

    //信号:新的客户端连接建立 槽函数:获取客户端套接字,允许写入数据
    connect(server,SIGNAL(newConnection()),this,SLOT(newConnection_SLOT()));
}

Server::~Server()
{
    
    
    delete ui;
}

//信号:新的客户端连接建立 槽函数:获取客户端套接字,允许写入数据
void Server::newConnection_SLOT()
{
    
    
    socket = server->nextPendingConnection(); //获取已经连接的客户端套接字
    connect(socket,SIGNAL(readyRead()),this,SLOT(readyRead_SLOT()));//如果socket中有缓存消息,触发槽函数
}

//接收消息并显示到接收框
void Server::readyRead_SLOT()
{
    
    
    qDebug() << "Server Received!";
    QString buffer;
    //读取缓冲区数据
    buffer = socket->readAll();
    if(!buffer.isEmpty())
    {
    
    
        //刷新显示
        ui->receiver->appendPlainText(buffer);
    }
}


//开始监听和停止监听按键
void Server::on_startorstop_Listen_clicked(bool checked)
{
    
    
    if(checked)
    {
    
    
        int port = ui->portnum->text().toUInt();
        //如果未监听到
        if(!server->listen(QHostAddress::Any, port))
        {
    
    
            qDebug() << server->errorString();
            //连接失败,再次点击则重新连接,将checked恢复为true
            ui->startorstop_Listen->toggle();
            return;
        }
        qDebug() << "Listen Successfully! Message from port:" << port;
        //修改按钮文字
        ui->startorstop_Listen->setText("停止监听");
        //发送键使能
        ui->send_button->setEnabled(true);
    }
    else
    {
    
    
         qDebug() << "Stop Listening!";
        //如果已经连接则断开连接
        if(socket->state() == QAbstractSocket::ConnectedState)
        {
    
    
            //断开连接
            socket->disconnectFromHost();
        }
        //关闭倾听服务
        server->close();
        //修改按钮文字&发送键静默
        ui->startorstop_Listen->setText("开始监听");
        ui->send_button->setEnabled(false);
    }
}

//发送消息,写入socket缓存区
void Server::on_send_button_clicked()
{
    
    
    qDebug() << "Server Send: " << ui->sender->toPlainText().toLatin1();
    //将输入框的内容写入socket缓冲区
    socket->write(ui->sender->toPlainText().toLatin1());
    //刷新socket缓冲区
    socket->flush();
}

3、main.cpp

#include "server.h"
#include "client.h"

#include <QApplication>

int main(int argc, char *argv[])
{
    
    
    QApplication a(argc, argv);
    Server w1;
    Client w2;

    //Client窗口通过鼠标单机获得聚焦
    w2.setFocusPolicy(Qt::ClickFocus);
    //将客户端和服务端窗口移动到屏幕合适位置
    w1.move(320,340);
    w2.move(960,340);

    //打开客户端和服务端窗口
    w1.show();
    w2.show();

    return a.exec();
}

(三)UI设计文件

client.ui
server.ui

可以手动设计,对应文本框或窗口、按键名与代码保持一致即可。
(如果实在懒得设计想看效果,可以直接下载最下面的源码
服务器Server界面
服务器Server对象命名

客户端Client界面
客户端Client对象命名


三、总结及源码链接

代码内注释很完整,有待指正,感谢阅读,您的支持是对我最大的帮助!
源代码在这

猜你喜欢

转载自blog.csdn.net/huangweijie0426/article/details/127327905