TCP如何保证可靠传输

1、确认应答机制-----发送的每条数据都需要确认回复一下
2、超时重传机制-----发送方等待一段时间后还没有收到回复,就认为传输失败了,将数据重传。这个超时时间是递增的,次数有限,超过重传次数就认为网络断开了。
3、序号/确认序号------保证数据有序传输

恋爱表白(上)--TCP

tcp因为要保证可靠传输,因此性能有很大的消耗,为了提高tcp传输性能,有需要有其他的一些机制:

滑动窗口机制、流量控制

1、滑动窗口机制:一次发送多条数据,集中等待(快速重传):如果发送方没有接收到第一条的ACK,但是收到了第二条数据的ACK,那么他认为第一条也发送成功了,不需要重传,假如第一条数据丢了,那么接受方会连续发送3次ACK说要的是第一条数据,但是并不会发送第二条已经收到的ACK,发送方接收到重传请求就会进行数据重传。一次发送的数据有多少取决于协议中的窗口大小字段----窗口大小在进行数据传输前会进行双方协商。
窗口大小实际也决定了TCP的传输吞吐量。
流量控制:通过不断设定窗口大小来告诉对方我能一次接收多少数据,最终达到流量控制的效果,避免因为发送太快,而处理太慢,导致接收方缓冲区塞满引起的大量的数据宝重传。
一开始的时候,因为窗口比较大,发送方一次发送的数据比较多,但是网络状况不好,就有可能会大量丢包,丢包就需要重传,造成性能下降。
2、拥塞控制
双方进行窗口大小协商之后,发送方还有一个东西叫拥塞窗口,并且拥塞窗口大小一开始的时候非常小,而实际发送大小取小的那一个,也就意味着拥塞窗口控制了tcp传输的一个慢启动,快速增长,一旦丢包,回到很低的窗口位置,再次轮回。
1. 停止等待协议:一请求一应答(网络不好)
2. 回退n步协议:丢了某条,该条之后的数据都重传
3. 选择重传协议:丢了那条传那条
3、延时应答机制:
对处理速度比较自信,对接收到的数据能够快速处理,只要稍微延时一下进行ACK应答,那么能够设置的窗口大小就会尽可能的大,一直保证一个tcp的最大吞吐量,加入是立即应答,那么有可能回复的这个窗口大小就会比较小。
4、捎带应答机制:
将确认应答直接标记在即将发送的数据包内,那么这样不仅传输了数据还对上一次接受的数据进行应答(少传输一个应答包)

为什么TCP需要“三次握手”、“四次挥手”?

1、连接过程最新的一次连接都不可靠,因为最新的这次连接可能丢包、损坏等。
2、如果是两次握手,服务器在接收到连接请求后,需要建立数据结构来维护连接请求,如果服务器的ACK丢失的状况大量出现,那么会出现SYN攻击服务器
3、三次握手保护服务器,第三次ACK丢失,客户端认为连接成功,服务器处于半连接状态,服务器认为连接没有成功,只有客户端会建立数据结构维护链接,如此服务器可以达到高效、安全。客户端给服务器发送数据,服务器的数据结构没有建立好,RST复位报文重新建立连接。

恋爱表白(上)--TCP

1、挥手是双向的,不仅要拆除客户端的对于服务器的连接,还要拆除服务器对于客户端的连接。
2、客户端发送FIN包给服务器,服务器知道了客户端要把连接断开了,服务器把自己的连接状态改为半连接状态,继续接受客户端的数据,但不再发送数据给客户端,并且给客户端发送ACK确认包,客户端收到ACK之后,客户端到服务端的连接就拆除了。由于网络中可能还存在双方之前发送的数据,因此,服务器在保证数据传输完毕了,给客户端发送FIN包拆除服务器给客户端建立的连接。
3、客户端收到服务端的FIN包之后,拆除客户端对服务端建立的连接,此时双向连接均已拆除,可是服务器并不知道自己的FIN包是否被客户端接收,客户端的ACK也有可能丢失,因此,进入TIME_WAIT状态。

syn泛洪攻击

SYN攻击利用的是TCP的三次握手机制,攻击端利用伪造的IP地址向被攻击端发出请求(SYN),而被攻击端发出的响应报文(ACK+SYN)将永远发送不到目的地,那么被攻击端在等待关闭这个连接的过程中(半连接挂起等待状态,接收不到攻击端的ACK,就重发ACK+SYN,直到超过时间SYN timeout,才会丢弃连接,释放资源)消耗了资源,如果有成千上万的这种连接,主机资源将被耗尽,从而达到攻击的目的。

为什么会有TIME_WAIT?

1、主动关闭方在最后一次发送ACK之后,有可能这个ACK会丢掉,被动方等待一段时间之后,没有接收到ACK会认为FIN请求没有发送成功,然后重新发送一个FIN到主动方,如果主动方发送ACK之后直接关闭,释放资源,那么被动方会维持这个无效链接很长时间,浪费被动断开方资源。如果主动方又重新建立socket,会对新的socket造成影响。
2、因此我们让主动方等待2个MSL时间,确保ACK已经发送到对方,或者就算有FIN请求,那在2个MSL时间后也已经被丢弃,不会再传递到主动方。
3、TIME_WAIT状态,等待时间是为了保护主动关闭方
4、有TIME_WAIT,就可以保证最后一个报文在某种程度达到基本可靠,“TIME_WAIT期间没有消息就是好消息”
5、有TIME_WAIT,把通信信道双方以前的合法数据尽量消散。

恋爱表白(上)--TCP

TCP服务端

恋爱表白(上)--TCP
恋爱表白(上)--TCP
恋爱表白(上)--TCP

创建套接字——>绑定地址——>监听——>accept——>recv——>send——>close。
//tcp服务端的代码
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>
#include<sys/socket.h>
#include<arpa/inet.h>

int main()
{
    //1、创建socket套接字
    int sockfd=socket(AF_INET,SOCK_STREAM,0);
    if(sockfd<0){
        perror("socket error");
        return -1;
    }
    struct sockaddr_in ser;
    ser.sin_family=AF_INET;
    ser.sin_port=htons(atoi("9000"));
    ser.sin_addr.s_addr=inet_addr("192.168.117.130");

    //2、绑定地址
    socklen_t len=sizeof(struct sockaddr_in);
    if(bind(sockfd,(struct sockaddr*)&ser,len)<0)
    {
        perror("bind error");
        close(sockfd);
        return -1;
    }

    //3、监听
    if(listen(sockfd,5)<0)
    {
        perror("listen error");
        close(sockfd);
        return -1;
    }
    while(1)
    {
        int n_sockfd;
        struct sockaddr_in cli;
        //4、accept
        n_sockfd=accept(sockfd,(struct sockaddr*)&cli,&len);
        if(n_sockfd<0){
            perror("accept error");
            continue;
        }
        //5、recv
        while(1){
            //6、send
            char buff[1024]={0};
            int ret=recv(n_sockfd,buff,1023,0);
            if(ret<0){
                perror("recv error");
                continue;
            }
            printf("client %s[%d]say:%s",inet_ntoa(cli.sin_addr),ntohs(cli.sin_port),buff);

            memset(buff,0x00,1024);
            scanf("%s",buff);
            send(n_sockfd,buff,sizeof(buff),0);
            //7、close(sockfd)
        }
        close(n_sockfd);
    }
}

TCP客户端

不推荐手动绑定地址
创建套接字——>connect——>send——>recv——>close。
//tcp客户端端的代码
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>
#include<sys/socket.h>
#include<arpa/inet.h>

int main()
{
    //1、创建socket套接字
    int sockfd=socket(AF_INET,SOCK_STREAM,0);
    if(sockfd<0){
        perror("socket error");
        return -1;
    }
    struct sockaddr_in ser;
    ser.sin_family=AF_INET;
    ser.sin_port=htons(atoi("9000"));
    ser.sin_addr.s_addr=inet_addr("192.168.117.130");
    //2、连接
    socklen_t len=sizeof(struct sockaddr_in);
    if(connect(sockfd,(struct sockaddr*)&ser,len)<0)
    {
        perror("connect error");
        return -1;
    }
    //3、发送数据
    while(1)
    {
            char buff[1024]={0};
            scanf("%s",buff);
            send(sockfd,buff,sizeof(buff),0);
            memset(buff,0x00,1024);

            int ret=recv(sockfd,buff,1023,0);
            if(ret<0){
                perror("recv error");
                continue;
            }
            printf("client %s[%d]say:%s",inet_ntoa(ser.sin_addr),ntohs(ser.sin_port),buff);
    }
    close(sockfd);
}

恋爱表白(上)--TCP

相关文章:

  • 2021-06-08
  • 2022-12-23
  • 2022-12-23
  • 2023-03-25
  • 2021-11-20
  • 2022-12-23
  • 2021-05-25
猜你喜欢
  • 2021-11-04
  • 2022-12-23
  • 2021-08-18
  • 2021-11-25
  • 2021-12-13
  • 2021-11-07
相关资源
相似解决方案