TCP模型是一个常见的网络协议参考模型,也称为TCP/IP模型或互联网模型。它是指TCP/IP协议族中的一组协议,用于在计算机网络中进行数据通信。TCP模型由四个层次组成,分别是:
应用层(Application Layer):
应用层是最靠近用户的层次,为用户提供各种网络应用服务。
包括常见的应用层协议,如HTTP、FTP、SMTP、DNS等。
该层定义了应用程序之间的通信规则和数据格式。
传输层(Transport Layer):
传输层负责提供可靠的端到端通信和数据传输服务。
最常用的传输层协议是TCP和UDP。
TCP提供可靠的、面向连接的通信服务,保证数据的可靠性、有序性和流量控制。
UDP是一种无连接的传输协议,提供简单的、不可靠的数据传输服务。
网络层(Network Layer):
网络层负责将数据从源主机传输到目标主机,处理主机之间的路径选择和数据包转发。
最常用的网络层协议是IP(Internet Protocol)。
IP协议定义了数据在网络中的寻址和路由机制,将数据分割成数据包进行传输。
链路层(Link Layer):
链路层是最底层的层次,负责实际的物理链路传输和网络适配。
包括以太网、Wi-Fi、PPP等不同的链路层协议。
链路层将数据包封装成帧,并通过物理介质进行传输。
TCP模型是一个分层结构,每个层次具有特定的功能和责任,通过不同层次的协议进行通信。它提供了一个标准化的网络通信框架,使不同的计算机和网络设备可以互相通信和交换数据。TCP/IP模型被广泛应用于互联网和许多局域网中,成为现代计算机网络的基础。
第一次握手(SYN):
客户端向服务器发送一个SYN(同步)包,表示客户端请求建立连接,并指定初始序列号。
客户端将SYN包的Sequence Number设置为一个随机数,用于后续数据传输的序号标识。
验证了客户端的发送能力和服务端的接收能力
第二次握手(SYN-ACK):
服务器收到客户端的SYN包后,向客户端发送一个SYN-ACK(同步-确认)包作为响应。
服务器将自己的Sequence Number设置为一个随机数,用于后续数据传输的序号标识。
服务器同时将ACK标志和确认序号设置为客户端的初始序列号加1,并将自己的初始序列号放入ACK包中,表示服务器接受客户端的连接请求。
验证了服务端的发送能力和客户端的接收能力
第三次握手(ACK):
客户端收到服务器的SYN-ACK包后,向服务器发送一个ACK(确认)包,确认连接请求。
客户端将自己的Sequence Number设置为服务器的初始序列号加1,并将ACK标志和确认序号设置为服务器的初始序列号加1,表示客户端确认服务器的连接响应。
小节:通过这三次握手,客户端和服务器都能确认彼此的通信能力,并建立起可靠的双向连接。每次握手过程都包含了序列号和确认序列号的交换,用于确保数据的可靠传输。同时,三次握手也能防止旧的连接请求在网络中滞留导致的错误连接建立。
TCP的四次挥手是在关闭TCP连接时使用的挥手过程,用于确保双方完成数据的传输并安全关闭连接。下面是TCP四次挥手的详细过程:
第一次挥手(FIN):
客户端向服务器发送一个FIN(结束)包,表示客户端没有更多的数据要发送。
客户端进入FIN_WAIT_1状态,等待服务器的确认。
第二次挥手(ACK):
服务器收到客户端的FIN包后,向客户端发送一个ACK(确认)包作为响应。
服务器进入CLOSE_WAIT状态,表示已经关闭了客户端到服务器的数据传输。
第三次挥手(FIN):
等到服务端处理完数据后,服务器向客户端发送一个FIN包,表示服务器也没有更多的数据要发送。
服务器进入LAST_ACK状态,等待客户端的确认。
第四次挥手(ACK):
客户端收到服务器的FIN包后,向服务器发送一个ACK包作为确认。
客户端进入TIME_WAIT状态,等待一段时间,以确保服务器收到了确认,并允许时间足够长来处理可能到达的延迟的数据。
服务器收到客户端的确认后,关闭连接,进入CLOSED状态。
客户端等待一段时间后,关闭连接,也进入CLOSED状态。
小节:通过这四次挥手,双方确认彼此不再发送数据,并完成了连接的关闭过程。这样可以确保双方都有足够的时间来处理未处理的数据,并避免数据丢失或中断。四次挥手过程中,每次挥手都包含了序列号和确认序列号的交换,用于确保数据的可靠传输和连接的正确关闭。
与TCP协议相比,UDP数据传输是建立在非链接的基础之上的。udp 传输数据就是将数据简单的封装一下然后在通过网卡发送出去即可,数据包之间并没有状态上的链接。
创建套接字(Socket):
在客户端和服务器端都需要创建一个套接字对象,用于建立TCP连接。
在C/C++中,可以使用socket()函数创建套接字对象,并指定协议族(如AF_INET)和套接字类型(如SOCK_STREAM)。
绑定地址和端口(服务器端):
在服务器端,需要将套接字绑定到一个特定的IP地址和端口上,以便监听客户端的连接请求。
可以使用bind()函数将套接字与地址和端口绑定。
监听连接请求(服务器端):
在服务器端,需要开始监听来自客户端的连接请求。
可以使用listen()函数将套接字设置为监听状态,指定最大连接数(backlog)。
发起连接(客户端):
在客户端,需要与服务器建立连接。
可以使用connect()函数将套接字连接到服务器的地址和端口。
接受连接(服务器端):
当服务器端收到客户端的连接请求时,需要接受连接。
可以使用accept()函数接受客户端的连接请求,并返回一个新的套接字对象用于与该客户端通信。
数据传输:
一旦连接建立,客户端和服务器端可以通过套接字进行数据传输。
在客户端,可以使用send()函数发送数据到服务器端。
在服务器端,可以使用recv()函数接收客户端发送的数据。
关闭连接:
当数据传输完成或连接不再需要时,需要关闭连接。
在客户端和服务器端,可以使用close()或closesocket()函数关闭套接字。
创建套接字(Socket):
在客户端和服务器端都需要创建一个UDP套接字对象,用于进行UDP通信。
在C/C++中,可以使用socket()函数创建套接字对象,并指定协议族(如AF_INET)和套接字类型(如SOCK_DGRAM)。
绑定地址和端口(服务器端):
在服务器端,需要将套接字绑定到一个特定的IP地址和端口上,以便监听客户端的连接请求。
可以使用bind()函数将套接字与地址和端口绑定。
发送数据(客户端):
在客户端,可以使用sendto()函数向服务器发送数据包。
需要指定服务器的IP地址和端口,以及要发送的数据。
接收数据(服务器端):
在服务器端,可以使用recvfrom()函数接收客户端发送的数据包。
可以通过该函数获取客户端的IP地址和端口,以及接收到的数据。
处理数据:
在接收到数据后,可以对其进行处理和解析。
根据应用需求,可以对数据进行解析、验证、处理等操作。
发送响应(服务器端):
在服务器端,可以使用sendto()函数向客户端发送响应数据包。
需要指定客户端的IP地址和端口,以及要发送的响应数据。
关闭套接字:
当UDP通信完成或不再需要时,需要关闭套接字。
可以使用close()或closesocket()函数关闭套接字。
实验由一个客户端程序和一个服务器端程序组成,用QT实现连接通信。运行界面如下所示:
server.h:
#ifndef SERVER_H
#define SERVER_H
#include
#include
#include
#include
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_pushButton_listen_clicked();
void server_new_connect();
void socket_read_data();
void read_data();
private:
Ui::server *ui;
QUdpSocket *uServer;
QTcpServer *mServer;
QTcpSocket *mSocket;
int i1,i2;
int j1,j2;
};
#endif // SERVER_H
server.cpp
#include "server.h"
#include "ui_server.h"
#include
server::server(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::server)
{
ui->setupUi(this);
ui->portEdit->setText("8080");
mServer=new QTcpServer;
connect(mServer,&QTcpServer::newConnection,this,&server::server::server_new_connect);
uServer=new QUdpSocket;
uServer->bind(QHostAddress("192.168.1.107"),8080);
QObject::connect(uServer,&QUdpSocket::readyRead,this,&server::read_data);
}
void server::read_data()
{
qDebug()<<"recv_UDP";
if(ui->radioButton_UDP->isChecked())
{
qDebug()<<"recv_UDP";
QByteArray arr;//创建QByteArray对象存储缓冲区数据
arr.resize(uServer->bytesAvailable());//设置字符串大小
uServer->readDatagram(arr.data(),arr.size());//读取缓冲区数据
qDebug()<<uServer->readDatagram(arr.data(),arr.size());
QString str ;//创建QString对象用于进行缓冲数据的处理
str.prepend(arr);//QByteArray转QString
qDebug()<<"str="<<str;
ui->textEdit->setText("udp:"+str);
}
}
server::~server()
{
uServer->close();
uServer->deleteLater();
mServer->close();
mServer->deleteLater();
delete ui;
}
void server::on_pushButton_listen_clicked()
{
if(ui->pushButton_listen->text() == QString::fromLocal8Bit("侦听"))
{
int port = ui->portEdit->text().toInt();
if(!mServer->listen(QHostAddress("192.168.1.107"),port))//判断侦听本机端口和地址是否成功
{
ui->portEdit_state->setText(QString::fromLocal8Bit("侦听失败!"));//侦听失败
qDebug()<<mServer->errorString();
return;
}
ui->pushButton_listen->setText(QString::fromLocal8Bit("取消侦听"));//改变按键功能
ui->portEdit_state->setText(QString::fromLocal8Bit("侦听成功!"));//侦听成功
qDebug()<<"Listen successful!";
}
else//侦听失败
{
mSocket->abort();//销毁TCP套接字对象
mServer->close();
ui->portEdit_state->setText(QString::fromLocal8Bit("侦听失败!"));//返回侦听失败
ui->pushButton_listen->setText(QString::fromLocal8Bit("侦听"));//改变按键功能
}
}
void server::server_new_connect()
{
mSocket = mServer->nextPendingConnection();
QObject::connect(mSocket,&QTcpSocket::readyRead,this,&server::socket_read_data);//连接函数槽
ui->portEdit_state->setText(QString::fromLocal8Bit("连接成功!"));
qDebug()<<"A client connect!";
}
void server::socket_read_data()
{
if(ui->radioButton_TCP->isChecked())
{
QString str= mSocket->readAll();
qDebug()<<str;
qDebug()<<"recv_TCP";
ui->textEdit->setText("tcp:"+str);
}
}
client.h
#ifndef CLIENT_H
#define CLIENT_H
#include
#include
#include
#include
#include
#include
#include
#include
QT_BEGIN_NAMESPACE
namespace Ui { class client; }
QT_END_NAMESPACE
class client : public QMainWindow
{
Q_OBJECT
public:
client(QWidget *parent = nullptr);
~client();
private slots:
void socket_disconnected();
void on_pushButtonconnect_clicked();
void on_pushButton_send_clicked();
private:
Ui::client *ui;
QTcpSocket *mSocket;
QUdpSocket *uSocket;
QTimer *tim;
};
#endif // CLIENT_H
client.cpp
#include "client.h"
#include "ui_client.h"
#include
client::client(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::client)
{
ui->setupUi(this);
ui->ipEdit->setText("192.168.1.107");//默认显示ip为192.168.1.44
ui->portEdit->setText("8080");//默认显示端口为8080
mSocket = new QTcpSocket();//TCP套接字初始化
ui->pushButton_send->setEnabled(true);
//QObject::connect(mSocket,&QTcpSocket::readyRead,this,&client::on_pushButton_send_clicked);
QObject::connect(mSocket,&QTcpSocket::disconnected,this,&client::socket_disconnected);
uSocket = new QUdpSocket(this);//UDP套接字初始化
tim = new QTimer();//初始化计时器对象
tim->setInterval(1000);//设置计时器间隔时间为1s
connect(tim,SIGNAL(timeout()),this,SLOT(onTimeOut()));//连接信号槽
tim->start();//初始化计时器状态
}
client::~client()
{
delete this->uSocket;
delete this->mSocket;
delete ui;
}
/*TCP连接*/
void client::on_pushButtonconnect_clicked()
{
if(ui->pushButtonconnect->text() == QString::fromLocal8Bit("连接"))
{
QString IP;
int port;
IP = ui->ipEdit->text(); //获取IP地址
port = ui->portEdit->text().toInt(); //获取端口号
mSocket->abort();//取消已有的连接
mSocket->connectToHost(IP, port);//连接服务器
if(!mSocket->waitForConnected(3000))//等待连接成功
{
qDebug() << "Connection failed!";
ui->lineEdit_state->setText(QString::fromLocal8Bit("连接失败!"));
return;
}
qDebug() << "Connect successfully!";
ui->lineEdit_state->setText(QString::fromLocal8Bit("连接成功!"));
ui->pushButton_send->setEnabled(true);//发送按键使能
ui->pushButtonconnect->setText(QString::fromLocal8Bit("断开连接"));//修改按键文字
}
else
{
mSocket->disconnectFromHost();//断开连接
ui->pushButtonconnect->setText(QString::fromLocal8Bit("连接"));//修改按键文字
ui->pushButton_send->setEnabled(true);//发送按键使能
}
}
/*发送数据*/
void client::on_pushButton_send_clicked()
{
qDebug()<<"Send: "<<ui->textEdit_enter->toPlainText().toLatin1();
if(ui->radioButton_TCP->isChecked())//判断使用TCP协议
{
qDebug()<<"TCP";
mSocket->write(ui->textEdit_enter->toPlainText().toLatin1());//tcp协议下客户端发送框内容写入缓冲
mSocket->flush();
}
else if(ui->radioButton_UDP->isChecked())//判断使用UDP协议
{
qDebug()<<"UDP";
uSocket->writeDatagram(ui->textEdit_enter->toPlainText().toLatin1(),QHostAddress("192.168.1.107"),8080);//udp协议下客户端发送框内容写入缓冲
uSocket->flush();
}
else
{
ui->lineEdit_state->setText("choose TCP or UDP ");//未选择协议
}
}
/*断开连接*/
void client::socket_disconnected()
{
ui->pushButton_send->setEnabled(false);//关闭发送按键
ui->pushButtonconnect->setText(QString::fromLocal8Bit("连接"));//显示
qDebug()<<"Disconnected!";
}
TCP(Transmission Control Protocol)和UDP(User Datagram Protocol)是两种常用的传输层协议,用于在计算机网络中传输数据。它们具有不同的特点和适用场景,以下是对TCP和UDP的总结:
TCP
UDP
选择使用TCP还是UDP取决于应用的需求和特点。如果应用程序需要可靠传输、有序性和拥塞控制,应选择TCP。而如果应用程序对实时性要求较高,可以容忍一些数据丢失或顺序混乱,且需要较低的延迟,那么选择UDP更合适。