【QT学习】11.TCP协议

一。【window为例】TCP协议的解释(记忆方法)

物理结构(自己理解)

1.服务器端

服务器端:        

        首先使用套接字函数创建 套接字 (Socket),并使用 绑定 函数绑定到本地地址(bind)。 使用 listen 指定传入连接的积压工作(listen),然后使用 accept 函数接受连接(accept)。最后接收数据(recv)。

1.创建套接字socket().(确定协议)
int socket( int af, int type, int protocol);

        af:协议族。支持AF_INET格式(ipv4)和AF_INET6格式(ipv6)

        type:指定socket类型。如TCP(SOCK_STREAM)和UDP(SOCK_DGRAM)

        rotocol:就是指定协议,如调用者不想指定,可用0。常用的协议有,IPPROTO_TCP、IPPROTO_UDP。

        返回值:若无错误发生,socket()返回引用套接字的描述字。否则的话,返回INVALID_SOCKET错误,应用程序可通过WSAGetLastError()获取相应错误代码。

2. 绑定bind() :(确定具体ip与端口)
        服务器必须在一个已知的名称(ip地址、端口)监听,属于显性绑定。客户端不需要调用bind绑定端口,而是由内核自定选择临时端口,属于隐性绑定。

        int PASCAL FAR bind( SOCKET sockaddr, const struct sockaddr FAR* my_addr,int addrlen);

        sockaddr表示已经建立的socket编号(描述符);

        FAR是一个指向sockaddr结构体类型的指针;

        addrlen表示my_addr结构的长度,可以用sizeof操作符获得。

        ip地址使用的是网络字节顺序,所以需要调用htons、htonl 函数主机把ip地址和端口的主机字节序转换网络字节序。对服务器而言 对于IPv4来说,通配地址通常由INADDR_ANY来指定,其值一般为0。它告知内核去选择IP地址。

        如无错误发生,则bind()返回0。否则的话,将返回-1。

        如果tcp服务器没有把ip地址捆绑到套接字上,内核就把客户端发送的syn的目的ip地址作为服务器的源ip地址。服务器可以通过getsockname函数获取该地址。

        在bind函数调用的sockaddr 类型指针,可以直接用前面的sockaddr_in变量转换替换。

iPv4通配地址由INADDR_ANY指定,如果端口号为0表示由内核选择一个临时端口。

        套接字分为UDP套接字和TCP套接字,对于前者,其由(ip地址,端口号)来标识,后者由(源ip,源端口号,目的ip,目的端口号)标识。

        tcp:如多个不同的客户端连接服务端,会产生多个套接字,这些套接字实际上是共用了相同的服务端端口号,但源ip地址不一样。

        疑问:如果是多个套接字,或者是多个进程应用绑定在同一ip,端口上,会怎样?tcp、udp都可以吗?

        默认的情况下,如果一个套接字 绑定了一个端口,这时候,别的套接字就无法使用这个端口。但是端口复用允许在一个应用程序可以把 n 个套接字绑在一个端口上而不出错。

3.监听 listen(激活套接字)
int listen( int sockfd, int backlog);

sockfd:用于标识一个已捆绑未连接套接口的描述字。

backlog:等待连接队列的最大长度。

如无错误发生,listen()返回0。否则的话,返回-1,应用程序可通过WSAGetLastError()获取相应错误代码。

该函数应该在socket和bind函数之后,accept函数之前。套接字会从closed状态转换到listen状态。

4.接受连接 accept (三次握手成功)
SOCKET accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

默认是阻塞函数

sockfd:套接字描述符,该套接口在listen()后监听连接。

addr:(可选)指针,指向一缓冲区,其中接收为通讯层所知的连接实体的地址(即客户端的地址)。Addr参数的实际格式由套接口创建时所产生的地址族确定。

addrlen:(可选)指针,输入参数,配合addr一起使用,指向存有addr地址长度的整型数。

返回值:如果没有错误产生,则accept()返回一个描述所接受包的SOCKET类型的值。否则的话,返回INVALID_SOCKET错误,应用程序可通过调用WSAGetLastError()来获得特定的错误代码

参数的sockaddr 类型指针,可以直接用前面的sockaddr_in变量转换替换。该地址变量是输出参数,表示的是客户端的地址。

同时返回新的套接字,用于传输数据用。原来的套接字继续用来监听。这时候,注意3次握手的过程,还要考虑是否是阻塞,同步的状态。默认是阻塞。

accept()函数成功返回时,完成了关联的建立,即3次握手成功结束。

检测到达的连接请求:accept()函数调用成功,或者是select()函数指示监听socket上有可写数据。

5.接收数据 recv()函数
默认是阻塞函数

buf缓冲区的类型都是char   最后一个变量flag 一般是0 在tcp中,要注意缓冲区不够,导致要么重发要么重收。所有要判断返回值。

int recv( _In_ SOCKET s, _Out_ char *buf, _In_ int len, _In_ int flags);

返回值:

若无错误发生,recv()返回读入的字节数。如果连接已中止,返回0。否则的话,返回SOCKET_ERROR错误,应用程序可通过WSAGetLastError()获取相应错误代码。

6.发送数据send() 函数
int send( SOCKET s, const char FAR *buf, int len, int flags );

若无错误发生,send()返回所发送数据的总数(请注意这个数字可能小于len中所规定的大小,该值应该不可以为0)。否则的话,返回SOCKET_ERROR错误,应用程序可通过WSAGetLastError()获取相应错误代码。

7.shutdown 
只适合在tcp上,udp这函数没有意义。值关闭连接,不释放socket资源。

shutdown会切断进程共享的套接字的所有连接,不管这个套接字的引用计数是否为零

SHUT_RD:关闭连接的读端

SHUT_WR:关闭连接的写端

SHUT_RDWR:连接的读端和写端都关闭。 这与调用shutdown两次等效。第一次调用指定SHUT_RD,第二次调用指定SHUT_WR

8.关闭closesocket ()(四次挥手)
4次握手,该函数默认是非阻塞函数。只有对一个阻塞socket,并且调用setsockopt设置了非0的超时值来使能SO_DONTLINGER。它才是阻塞的。

int PASCAL FAR closesocket( SOCKET s);

如无错误发生,则closesocket()返回0。否则的话,返回SOCKET_ERROR错误,应用程序可通过WSAGetLastError()获取相应错误代码。

在linux中关闭套接字使用的函数是close().

close把描述符的引用计数减1,仅在该计数为0时才关闭套接字,如果有其他的进程共享着这个套接字,那么它仍然是打开的,这个连接仍然可以用来读和写。

close终止读写两个方向的数据传输。

可以通过setsockopt函数设置SO_DONTLINGER  、SO_LINGER选项。

二。客户端

1创建套接字socket()

2.连接connect()函数(地址簇name要连接套接字s<服务器套接字>)
int connect(SOCKET s, const struct sockaddr * name, int namelen);

s:标识一个未连接socket

name:指向要连接套接字的sockaddr 结构体的指针

namelen:sockaddr结构体的字节长度

返回值:成功则返回0, 失败返回-1.

成功后,套接字的状态从closed->syn_sent ->established.

注意阻塞函数,握手3步.connect函数由系统决定超时时间,一般是75s.

失败的多种原因:

        1.具体流程是:发送一个syn,若无响应的等待几秒再发送一个,连续好几次,等到75s仍不响应。

        在windows 则返回SOCKET_ERROR(也即-1).调用WSAGetLastError(),返回出错码是WSAETIMEDOUT。

        在linux 则返回-1.通过全局变量errno,返回出错码是ETIMEDOUT。

        2.若对客户端返回的是RST,表明在服务器主机指定的端口没有进程在等待与之连接。

出错代码是WSAECONNREFUSED(windows),ECONNREFUSED(linux)。

        3.中间某个路由引发icmp错误。按照1方式联系75s发送syn包,错误码是: WSAENETUNREACH

        所以需要把套接字改成非阻塞模式、并且使用select函数,自定义超时时间。可参考下面的做法。

        调用的sockaddr 类型指针,可以直接用前面的sockaddr_in变量转换替换。需要知道服务端的ip地址和端口。

        注意:客户端没必要调用bind进行绑定命名,主要原因是:

        如果没事前调用bind()函数,connect()函数会隐式对本地socket命名,并且是任取一个端口,避免端口冲突。

        同时,如果同时运行多个客户端,给套接字指定端口容易导致端口冲突。

        如果调用connect失败,推荐先调用closesocket()和socket()获取一下新的socket。

补充:

TCP协议中,接收方成功接收到数据后,会回复一个ACK数据包,表示已经确认接收到ACK确认号前面的所有数据。

发送方在一定时间内没有收到服务端的ACK确认包后,就会重新发送TCP数据包。

接收方在接收到数据后,不是立即会给发送方发送ACK的。这可能由以下原因导致:
1、收到数据包的序号前面还有需要接收的数据包。因为发送方发送数据时,并不是需要等上次发送数据被Ack就可以继续发送TCP包,而这些TCP数据包达到的顺序是不保证的,这样接收方可能先接收到后发送的TCP包(注意提交给应用层时是保证顺序的)。
2、为了降低网络流量,ACK有延迟确认机制。
3、ACK的值到达最大值后,又会从0开始。

接收方在收到数据后,并不会立即回复ACK,而是延迟一定时间。一般ACK延迟发送的时间为200ms,但这个200ms并非收到数据后需要延迟的时间。系统有一个固定的定时器每隔200ms会来检查是否需要发送ACK包。这样做有两个目的。
1、这样做的目的是ACK是可以合并的,也就是指如果连续收到两个TCP包,并不一定需要ACK两次,只要回复最终的ACK就可以了,可以降低网络流量。
2、如果接收方有数据要发送,那么就会在发送数据的TCP数据包里,带上ACK信息。这样做,可以避免大量的ACK以一个单独的TCP包发送,减少了网络流量。
 

MSS 是在建立连接时通过SYN数据包中的MSS选项里进行协商的(以太网的MTU能到1500,所以MSS可以为1460),如果没有协商,默认为536,MSS是数据净负荷,协议保证最小支持536(加上TCP和IP的头部后packet为576)

TCP中在发送的数据的ACK未回来前,能继续发送其他数据包吗

能不能发,取决于下面的条件是否满足:

1. 如果包长度达到MSS,则再根据CWND、AWND来做决定;
2. 如果该包含有FIN,则允许发送;
3. 如果没达到MSS且不包含FIN:
  
3.1. 设置了TCP_NODELAY选项,则允许发送;
3.2. 没设置TCP_NODELAY, 未设置TCP_CORK选项时,若所有发出去的小数据包(包长度小于MSS)均被确认,则允许发送(nagel算法起作用);设置了TCP_CORK选项时,需要包长度到MSS。

int on = 1;
setsockopt(fd,SOL_TCP,TCP_CORK,&on,sizeof(on));
4. 上述条件都未满足,但发生了超时(一般为200ms),则立即发送
————————————————

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。                        
原文链接:https://blog.csdn.net/baidu_16370559/article/details/104646624

二。QT的TCP连接(基于事件(异常,中断)处理)

服务器

  1. 创建QTcpServer对象  (相当于windows的第一步)
  2. 监听   (相当于windows的二三四步)

QTcpServer类的listen函数

  1. 等待客户端连接
    1. QTcpServer类的newConnection信号
    2. QTcpServer类的nextPendingConnection函数返回客户端QTcpSocket

    通信

    1. 接受数据:   QTcpSocket   的 readyRead 信号

                                       QTcpSocket   的 read函数

    1. 发送数据:   QTcpSocket   的 write函数

客户端

  1. 创建QTcpSocket对象  (拿到服务器的ip地址和端口号,方式:自己写入)
  2. QTcpSocket类的connectToHost 函数连接服务器

通过QTcpSocket类的connected信号知道已经连接到服务器了

  1. 通信

接受数据:QTcpSocket   的 readyRead 信号

                  QTcpSocket   的 read函数

发送数据:QTcpSocket   的 write函数

2.使用QT写一个TCP客户端与TCP服务器端

1.TCP服务器端

页面制作:

1.服务器端类

private slots:
    void on_pushButtonSend_clicked();

    void on_pushButtonClose_clicked();

private:
    Ui::Widget *ui;
    //服务器socket套接字
    QTcpServer* pTcpServer;
    //服务器这边代表 客户端的socket套接字
    QTcpSocket* pTcpSocket;
};

2.服务器实现

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    this->setWindowTitle("服务器端 端口9527");

    //创建QTcpServer对象
    pTcpServer=new QTcpServer(this);//指定父对象,自动回收
    pTcpSocket=NULL;
    //监听
    pTcpServer->listen(QHostAddress::Any,9527);
    //连接
    //客户端连接服务器--》触发(服务器端)NewConnection信号--》触发(客户端)connected信号
    connect(pTcpServer,&QTcpServer::newConnection,
            [=](){
                //获取客户端的socket
                pTcpSocket=pTcpServer->nextPendingConnection();
                //获取客户端端口与ip
                QString ipStr=pTcpSocket->peerAddress().toString();
                quint16 portUint=pTcpSocket->peerPort();
                //显示端口与ip
                QString buff=QString("客户端连接服务器成功:ip:%1,port:%2").arg(ipStr).arg(portUint);
                ui->textEditRecv->setText(buff);

                //只有客户端连接上服务器后,才会有此监听
                //触发服务器端接受数据
                connect(pTcpSocket,&QTcpSocket::readyRead,
                        [=](){
                        QByteArray data=pTcpSocket->readAll();
                        ui->textEditRecv->append(data);
                });
    });
}

void Widget::on_pushButtonSend_clicked()
{
    if(pTcpSocket){
        QString str=ui->textEditSend->toPlainText();
        //向客户端的套接字写数据str
        pTcpSocket->write(str.toUtf8().data());
    }
}

void Widget::on_pushButtonClose_clicked()
{
    if(pTcpSocket){
        pTcpSocket->disconnectFromHost();//断开网络连接
        pTcpSocket->close();
    }
}

2.TCP客户端

页面制作:

1.创建一个新的页面

2.页面为

3.main中添加页面,并展示

4.结果:

1.客户端类

2.客户端实现

Form::Form(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Form)
{
    ui->setupUi(this);
    this->setWindowTitle("客户端");

    //创建socket
    pTcpSocket = new QTcpSocket(this);
    //连接服务器
    connect(pTcpSocket,&QTcpSocket::connected,
            [=](){
        ui->textEditRecv->setText("连接服务器成功");
    });

    //放在连接服务器内外是不是都可以?
    connect(pTcpSocket,&QTcpSocket::readyRead,
            [=](){
        QByteArray data = pTcpSocket->readAll();
        ui->textEditRecv->append(data);
    });
}

void Form::on_pushButtonConnect_clicked()
{
    QString ipStr=ui->lineEditIp->text();
    quint16 portUint=ui->lineEditPort->text().toUInt();
    //客户端连接服务器--》触发(服务器端)NewConnection信号--》触发(客户端)connected信号
    pTcpSocket->connectToHost(ipStr,portUint);
}

void Form::on_pushButtonSend_clicked()
{
    QString str=ui->textEditSend->toPlainText();
    pTcpSocket->write(str.toUtf8().data());
}

void Form::on_pushButtonClose_clicked()
{
    pTcpSocket->disconnectFromHost();//断开网络连接
    pTcpSocket->close();
}

结果:相互发送数据正常

        端口是服务器设置的

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: qt tcp通信v2.zip是一个Qt程序文件,用于实现TCP通信功能。 Qt是一个跨平台的C++应用程序开发框架,可以方便地开发图形用户界面(GUI)和网络通信功能。TCP是一种可靠的传输协议,常用于实现网络通信。 qt tcp通信v2.zip包含了一个示例程序,是基于Qt框架的TCP通信的实现。它可能包含了与网络通信相关的源代码文件、二进制可执行文件、配置文件等资源。 使用Qt进行TCP通信,可以方便地进行客户端和服务器端之间的数据传输。通过TCP协议,可以建立可靠的连接,实现稳定的数据传输。在Qt的API中,提供了一些类和函数,可以方便地创建TCP服务器和客户端,进行数据传输和接收。 使用qt tcp通信v2.zip,我们可以学习如何在Qt框架的基础上,实现TCP通信的功能。可以学习如何创建一个简单的TCP服务器,监听指定端口,并接收客户端的连接请求。也可以学习如何创建一个TCP客户端,与服务器建立连接,并向服务器发送数据。 通过学习qt tcp通信v2.zip,我们可以了解Qt框架的网络编程能力,学习如何使用Qt进行网络通信的开发。这对于需要实现网络通信功能的应用程序开发来说,是非常有帮助的。 ### 回答2: "qt tcp通信v2.zip"是一个文件压缩包,它包含了关于QT框架下TCP通信的相关实例代码和资源。 QT是一个跨平台的C++应用程序开发框架,它提供了丰富的功能和工具,用于开发图形用户界面和网络应用程序等。而TCP通信是一种面向连接的网络通信协议,它可以在不同的设备之间进行可靠的数据传输。 在这个文件压缩包中,我们可以找到一些用QT框架实现TCP通信的示例代码,这些代码可以帮助开发人员理解和掌握TCP通信的基本原理和操作。此外,压缩包还可能包含一些必要的资源文件,如图标、配置文件等,以帮助使用者更方便地开发和测试应用程序。 要使用这个文件压缩包,我们首先需要解压缩它。解压后,我们可以使用QT开发环境打开这些示例代码,通过阅读和分析代码,了解QT框架如何实现TCP通信的功能。 同时,我们还可以基于这些示例代码进行修改和扩展,根据自己的需求开发更复杂和功能强大的TCP通信应用程序。 总之,"qt tcp通信v2.zip"是一个提供QT框架下TCP通信实例代码和资源的文件压缩包,通过使用它,我们可以学习和应用QT框架的TCP通信功能。 ### 回答3: "qt tcp通信v2.zip"是一个用于实现Qt TCP通信的代码文件压缩包。Qt是一种跨平台的应用程序开发框架,它提供了丰富的类库和工具,用于开发图形界面和网络应用程序等。 该压缩包中的代码主要涉及TCP通信的功能实现。TCP(传输控制协议)是一种面向连接的可靠的数据传输协议,常用于在计算机网络中实现数据通信。 Qt提供了用于创建TCP套接字(socket)的类库,并包含了一些常用的通信函数和信号槽机制,用于发送和接收数据。这个压缩包中的代码文件应该包含了使用Qt实现TCP通信的必要函数和类。 通过使用这个压缩包中的代码,我们可以利用Qt提供的TCP套接字类库,完成两台计算机之间的数据传输。比如,可以实现一个客户端和服务器端的通信,或者两台客户端之间的通信。 Qt TCP通信v2.zip提供了一个简单方便的方式来构建和管理TCP连接,并实现数据的发送和接收。用户可以根据自己的需求,修改和扩展这些代码,以满足特定的通信需求。 总之,Qt TCP通信v2.zip是一个包含用于实现Qt TCP通信的代码文件压缩包,可以帮助开发者轻松地在Qt框架下实现TCP通信的功能。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值