このプロジェクトは学部のコースワークであり、参照のみを目的としています。ご質問やご提案がある場合は、著者にご連絡ください。
記事ディレクトリ
序文
TCP连接:Socket套接字基础知识
TCP (Transmission Control Protocol 伝送制御プロトコル) は、接続を最も基本的な抽象概念とみなします。TCP の多くの特性は、TCP が接続指向であるという基本特性に関連しています。したがって、TCP 接続についてより明確に理解する必要があります。
すべての TCP 接続には 2 つのエンドポイントがあります。
では、TCP 接続のエンドポイントは何でしょうか?
1. TCP 接続のエンドポイント
TCP 接続のエンドポイントは、ホストの IP アドレス、アプリケーション プロセス、トランスポート層のプロトコル ポートではありません。
TCP は 2 つの端末間の通信を実装するため、ターゲット ホストの IP アドレスの取得に加えて、ターゲット ホストの対応するアプリケーション プロセスのポート番号も必要となるため、TCP 接続のエンドポイントはソケットまたはソケットと呼ば
れます。によるとRFC 793定義:ポート番号は IP アドレスに結合されてソケットを形成する
ため、TCP は Socket ソケットをエンドポイントとして使用します。
套接字socket = (IP地址:端口号)
各 TCP 接続は、通信の両端の 2 つのエンドポイント (つまり、ソケット ペア) によって一意に決定されます。たった今:
TCP连接:: = { socket1,socket2 } = { (IP1, Port1), (IP2, Port2) }
ここで、IP1 と IP2 はそれぞれ 2 つのエンドポイント ホストのIP アドレスであり、Port1 と Port はそれぞれ 2 つのエンドポイント ホストのポート番号です。
1 つの理解は、ソケットはこのモードの実装であるということです。つまり、ソケットは特別なファイルであり、一部のソケット関数はそのファイルに対する操作 (読み取り/書き込み IO、オープン、クローズ) です。
2. コード分析
コード構造
(1) ヘッダファイル
1.client.h
主にいくつかのライブラリをインクルードし、いくつかの関数を定義し、ソケット オブジェクトを作成します。
コードは次のとおりです。
#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
(2) ソースファイル
1、クライアント.cpp
主にいくつかのライブラリを組み込み、いくつかの関数を定義し、ソケットとサーバーオブジェクトを作成します
#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、サーバー.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();
}
(3) UIデザインファイル
client.ui
server.ui は
手動で設計でき、対応するテキスト ボックスまたはウィンドウ、キー名、コードは一貫している必要があります。
(デザインするのが面倒で効果を確認したい場合は、下部にあるソースコードを直接ダウンロードできます)
3. 概要とソースコードのリンク
コード内のコメントは非常に完全なので、修正する必要があります。読んでいただきありがとうございます。皆様のご支援が私にとって最大の助けです。
ソースコードはこちら