Linux网络传输协议TCP

TCP


  • 00.IP和端口
  • -

1.在说网络编程前我们先要说一下IP和端口号,为什么我们要引入端口号IP和端口号呢?

a. 会想前几篇关于进程的博客,可以知道用一个进程PID来标识一个主机上的进程,但是不同主机间没有限制了。这就导致在庞大的互联网中没有办法用PID来表示一个主机上的一个进程
b. IP可以再网络中标识唯一一个主机,而端口号则是用来标识这个主机上的唯一进程,这样就达到了的目的,完成了不同主机上某个进程的唯一标识,将这种IP+端口号的形式,称之为套接字。

下面看一个实例来了解一下不同网段主机的通信过程:
这里写图片描述

1. A主机的应用层微信进程发送了数据,向下给了传输层打上TCP头,在向下给网络层打上IP头,在向下给了数据链路层打上数据帧,源mac和目的mac,但是不知道目的mac所以就是默认网关(路由器)的mac,发送给路由器。
2. 路由器拿到数据,重新分装帧,把源mac换成自己的,目的mac换成主机B的mac,将信息发送给主机B
3. 主机B接受到消息,将数据解析帧,IP头,TCP头知道是应用层的9090端口的进程,即将数据交给端口号为9090的微信进程。
4. 这样主机B就收到了消息,完成了两个主机的通信。

2.常见端口号
这里写图片描述

  • 01.网络套接字API

1.创建socket文件描述符:
这里写图片描述
2.绑定端口号
这里写图片描述
3.监听socket
这里写图片描述
4.接受请求
这里写图片描述
5.建立连接
这里写图片描述
6.struct sockaddr sockaddr_in
这里写图片描述
7.对上述API进行简单的使用

//client.c
typedef struct sockaddr sockaddr;
typedef struct sockaddr_in sockaddr_in;

int main(int argc ,char* argv[])
{
    if(argc!=3)
        return -1;

    int sockfd= socket(AF_INET,SOCK_STREAM,0);
    if(sockfd<0)
        return -1;

    sockaddr_in server;
    server.sin_family = AF_INET;
    server.sin_port= htons(atoi(argv[2]));
    server.sin_addr.s_addr= inet_addr(argv[1]);

    socklen_t serverLen=sizeof(server);

    int ret =connect(sockfd,(sockaddr*)(&server),serverLen);
    if(ret<0)
        return -2;

    while(1)
    {
        char buf[1024]={0};
        ssize_t read_size = read(0,buf,sizeof(buf)-1);
        if(read_size<0)
            continue;
        else if(read_size==0)
            break;
        write(sockfd,buf,strlen(buf));

        char recv_buf[1024]={0};
        read_size = read(sockfd,recv_buf,sizeof(recv_buf));
        if(read_size<0)
        {
            printf("read_size < 0\n");
           continue;
        }
        else if(read_size==0)
        {
            printf("client close !\n"); 
            break;
        }
        else
        {
            printf("server say : %s\n",recv_buf);
        }

    }
}






//server.c

typedef struct sockaddr sockaddr;
typedef struct sockaddr_in sockaddr_in;

int main(int argc,char* argv[])
{
    if(argc!=3)
        return -1;


    int sockfd = socket(AF_INET,SOCK_STREAM,0);
    if(sockfd<0)
        return -2;

    sockaddr_in addr; 

    addr.sin_family =AF_INET;
    addr.sin_port=htons(atoi(argv[2]));
    addr.sin_addr.s_addr = inet_addr(argv[1]);

    socklen_t addrlen= sizeof(addr);
    int ret =bind(sockfd,(sockaddr*)(&addr),addrlen);
    if(ret<0)
        return -3;

    ret =listen(sockfd,5);
    if(ret<0)
        return -4;


    sockaddr_in client ;
    socklen_t clientlen=sizeof(client);
    while(1)
    {
        int sock = accept(sockfd,(sockaddr*)(&client),&clientlen);
        if(sock<0)
            return -5;
        while(1)
        {
            char buf[1024] ={0};
            ssize_t read_size = read(sock,buf,sizeof(buf)-1);
            if(read_size<0)
                continue;
            else if(read_size==0)
            {
                printf("client close !\n"); 
                break;
            }
            else
            {
                printf("client say :%s\n",buf);
                write(sock,buf,strlen(buf));
            }
        }

    }

}

//Makefile

.PHONY:all clean
all:client server
client:./client.c
        gcc $^ -o $@ 
server:./server.c
        gcc $^ -o $@ 
clean:
        rm -f client server

上述程序程序执行结果
这里写图片描述

  • 02.详解TCP

1.TCP三次握手
这里写图片描述


2.四次挥手
这里写图片描述


3.TCP的可靠行

a. 三次握手保证可靠的连接
b. 四次挥手保证可靠的断开连接
c. 序列号和超时重传保证了其数据的可靠

超时重传
这里写图片描述

情况一:可能A主机发送了一个数据,B主机收到了但是发送确认报文丢失,导致A主机超时,进行重传
情况二:主机A发送的数据直接丢失了,导致A一定时间内没有收到B的确认,所以超时,进行重传。
说明:不是超时就会一直发送,其几次等待时间都是二倍的增长,但是当重重到一定的次数后,就认为B主机异常,不进行重传。

序列号:
这里写图片描述


过程描述:
1. 首先A主机发送一个序列为x的数据
2. B主机收到回应ack=x+1,代表B主机x+1前的都收到了,下次希望发送序列x+1的数据,seq=y则是B主机发送给A主机的数据。
3. 主机A收到了B的确认,知道了B主机x+1前的都收到了,希望给他x+1的数据,seq代表A发送的数据序列为x+1,ack=y+1代表A收到y+1前的数据,渴望下次收到y+1的数据。


4.TCP传输如何保证其效率呢?

a. 校验和
b. 滑动窗口机制
c. 延迟应答
d. 捎带应答

滑动窗口:

1. 滑动窗口就是依次执行批量的发送,大小为窗口大小,再发送窗口大小的数据是不需要等待确认可以直接发送
2.每次著有收到确认的才可以从窗口中删除,再没有收到确认时是没有办法从滑动窗口删除任何东西的
3. 当收到了确认则滑动窗口可以向前移动,将进入的进行发送
4.异常情况一:数据包发送过去,但是确认丢失的话,可以通过后续的ACK进行确认,当1000的去确认丢了,但是收到了2000的确认,这样1000也想打予确认了不会触发重传。
5.异常情况二:数据包丢失没有发送过去,那么会在确认报文中反复收到重复的确认一个序列的报文,例如2000报文丢失了,那么会收到重复确认1000的报文,那么就会重传2000的报文,这样可能下次收到的就是5000的确认报文,因为其他报文都收到了。

延迟应答

1.假设接收端缓冲区为10M. ⼀次收到了5M的数据; 如果⽴刻应答, 返回的窗⼝就是5M;
2.但实际上可能处理端处理的速度很快, 10ms之内就把5M数据从缓冲区消费掉了;
3.在这种情况下, 接收端处理还远没有达到⾃⼰的极限, 即使窗⼝再放⼤⼀些, 也能处理过来;
4.如果接收端稍微等⼀会再应答, ⽐如等待200ms再应答, 那么这个时候返回的窗⼝⼤⼩就是10M;

捎带应答

1. 实际中我们没有必要一问一答,我们可以再我们想给对方发送消息的时候顺便把ACK带上,这样就节省了一次发送的时间,提高了效率。

猜你喜欢

转载自blog.csdn.net/skinwhite/article/details/81161763