Linux下的网络编程——C/S模型TCP(二)

前言:

TCP协议被广泛应用 其根本原因就是提供了详尽的可靠性保证 基于TCP的上层应用非常多 比如HTTP、HTTPS、FTP、SSH、MySQL等。TCP是一种面向连接的单播协议,在发送数据前,通信双方必须在彼此间建立一条连接。所谓“连接”,其实是客户端和服务器端内存里保持的一份关于对方的信息(IP地址、端口号)下面让我们学习一下什么是TCP协议吧

TCP协议

1.TCP通信时序

下图是一次TCP通讯的时序图。TCP连接建立断开。包含大家熟知的三次握手和四次握手

三次握手建立连接、四次握手根据半关闭断开连接

Linux下的网络编程——C/S模型TCP(二)_第1张图片

 (1).三次握手

        1)客户端向主机端发送申请建立连接

        2)主机端向客户端发送接收到申请请求并确定连接

        3)客户端接收到主机端并确定是最近的一次确定连接申请后,确定连接

        主动发起连接请求端,发送 SYN 标志位,请求建立连接。 携带序号号、数据字节数(0)、滑动窗口大小。

       被动接受连接请求端,发送 ACK 标志位,同时携带 SYN 请求标志位。携带序号、确认序号、数据字节数(0)、滑动窗口大小。

       主动发起连接请求端,发送 ACK 标志位,应答服务器连接请求。携带确认序号。
 

Linux下的网络编程——C/S模型TCP(二)_第2张图片

 (2).四次握手

        1)客户端向主机端发送申请断开连接

        2)主机端向客户端发送信息,说明已经接收到客户端的请求

        3)待主机端处理完线路上两者之间的数据后主动推送数据给客户端代表此时已经处理完所有数据

        4)客户端确定后,最后确定并释放自己的连接

           主动关闭连接请求端, 发送 FIN 标志位。 

            被动关闭连接请求端, 应答 ACK 标志位。          ----- 半关闭完成


            被动关闭连接请求端, 发送 FIN 标志位。

            主动关闭连接请求端, 应答 ACK 标志位。         ----- 连接全部关闭

Linux下的网络编程——C/S模型TCP(二)_第3张图片

注意:不能三次挥手的原因:第四次挥手用于确定主机的数据已经发送完毕的确认

2.滑动窗口(TCP流量控制)

        介绍UDP时我们描述了这样的问题:如果发送端发送的速度较快,接收端接收到数据后处理的速度较慢,而接收缓冲区的大小是固定的,就会丢失数据。TCP协议通过“滑动窗口(Sliding Window)”机制解决这一问题。看下图的通讯过程:

  1. 发送方仅能发送发送窗口内的数据,发送窗口之外的数据不允许发送
  2. 接收方只能接收接收窗口内的数据,接收窗口之外的数据不能被接收;
  3. 窗口的大小由接收方控制,用于调整传输的速率

Linux下的网络编程——C/S模型TCP(二)_第4张图片

  1. 发送端发起连接,声明最大段尺寸是1460,初始序号是0,窗口大小是4K,表示“我的接收缓冲区还有4K字节空闲,你发的数据不要超过4K”。接收端应答连接请求,声明最大段尺寸是1024,初始序号是8000,窗口大小是6K。发送端应答,三方握手结束。
  2. 发送端发出段4-9,每个段带1K的数据,发送端根据窗口大小知道接收端的缓冲区满了,因此停止发送数据。
  3. 接收端的应用程序提走2K数据,接收缓冲区又有了2K空闲,接收端发出段10,在应答已收到6K数据的同时声明窗口大小为2K。
  4. 接收端的应用程序又提走2K数据,接收缓冲区有4K空闲,接收端发出段11,重新声明窗口大小为4K。
  5. 发送端发出段12-13,每个段带2K数据,段13同时还包含FIN位。
  6. 接收端应答接收到的2K数据(6145-8192),再加上FIN位占一个序号8193,因此应答序号是8194,连接处于半关闭状态,接收端同时声明窗口大小为2K。
  7. 接收端的应用程序提走2K数据,接收端重新声明窗口大小为4K。
  8. 接收端的应用程序提走剩下的2K数据,接收缓冲区全空,接收端重新声明窗口大小为6K。
  9. 接收端的应用程序在提走全部数据后,决定关闭连接,发出段17包含FIN位,发送端应答,连接完全关闭。

        上图在接收端用小方块表示1K数据,实心的小方块表示已接收到的数据,虚线框表示接收缓冲区,因此套在虚线框中的空心小方块表示窗口大小,从图中可以看出,随着应用程序提走数据,虚线框是向右滑动的,因此称为滑动窗口。

发送给连接对端,本端的缓冲区大小(实时),保证数据不会丢失 

3.TCP状态转换

Linux下的网络编程——C/S模型TCP(二)_第5张图片

结合三次握手、四次挥手 理解记忆。 

Linux下的网络编程——C/S模型TCP(二)_第6张图片

   1). 主动发起连接请求端:    

CLOSE  --》 发送SYN  --》  SEND_SENT   --》   接收 ACK 、SYN   --》   SEND_SENT   --》  发送 ACK   --》    ESTABLISHED(数据通信态)

Linux下的网络编程——C/S模型TCP(二)_第7张图片

  server:
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include "wrap.h"

#define MAXLINE 8192
#define SERV_PORT 8000

void do_sigchild(int num)
{
    while (waitpid(0, NULL, WNOHANG) > 0)
        ;
}

int main(void)
{
    struct sockaddr_in servaddr, cliaddr;
    socklen_t cliaddr_len;
    int listenfd, connfd;
    char buf[MAXLINE];
    char str[INET_ADDRSTRLEN];
    int i, n;
    pid_t pid;
    struct sigaction newact;

    newact.sa_handler = do_sigchild;
    sigemptyset(&newact.sa_mask);
    newact.sa_flags = 0;
    sigaction(SIGCHLD, &newact, NULL);

    listenfd = Socket(AF_INET, SOCK_STREAM, 0);

    int opt = 1;
    setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));

    bzero(&servaddr, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    servaddr.sin_port = htons(SERV_PORT);

    Bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr));

    Listen(listenfd, 20);

    printf("Accepting connections ...\n");
    while (1) {
        cliaddr_len = sizeof(cliaddr);
        connfd = Accept(listenfd, (struct sockaddr *)&cliaddr, &cliaddr_len);
        printf("-------------------------%d\n", connfd);

        pid = fork();
        if (pid == 0) {
            Close(listenfd);
            while (1) {
                n = Read(connfd, buf, MAXLINE);
                if (n == 0) {
                    printf("the other side has been closed.\n");
                    break;
                }
                printf("received from %s at PORT %d\n",
                        inet_ntop(AF_INET, &cliaddr.sin_addr, str, sizeof(str)),
                        ntohs(cliaddr.sin_port));

                for (i = 0; i < n; i++)
                    buf[i] = toupper(buf[i]);

                Write(STDOUT_FILENO, buf, n);
                Write(connfd, buf, n);
            }
            Close(connfd);
            return 0;
        } else if (pid > 0) {
            Close(connfd);
        }  else
            perr_exit("fork");
    }
    return 0;
}
  client:
/* client.c */
#include 
#include 
#include 
#include 
#include 

#include "wrap.h"

#define MAXLINE 8192
#define SERV_PORT 8000

int main(int argc, char *argv[])
{
    struct sockaddr_in servaddr;
    char buf[MAXLINE];
    int sockfd, n;

    sockfd = Socket(AF_INET, SOCK_STREAM, 0);

    bzero(&servaddr, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    inet_pton(AF_INET, "127.0.0.1", &servaddr.sin_addr);
    servaddr.sin_port = htons(SERV_PORT);

    Connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr));

    while (fgets(buf, MAXLINE, stdin) != NULL) {
        Write(sockfd, buf, strlen(buf));
        n = Read(sockfd, buf, MAXLINE);
        if (n == 0) {
            printf("the other side has been closed.\n");
            break;
        }
        else
            Write(STDOUT_FILENO, buf, n);
    }

    Close(sockfd);

    return 0;
}

Linux下的网络编程——C/S模型TCP(二)_第8张图片

    2). 主动关闭连接请求端:

ESTABLISHED(数据通信态)  --》   发送 FIN  --》  FIN_WAIT_1    --》   接收ACK     --》 FIN_WAIT_2(半关闭) 

接收对端发送 FIN    --》    FIN_WAIT_2(半关闭) --》   回发ACK    -- 》  TIME_WAIT(只有主动关闭连接方,会经历该状态) --》   等 2MSL时长    --》    CLOSE 

Linux下的网络编程——C/S模型TCP(二)_第9张图片

Linux下的网络编程——C/S模型TCP(二)_第10张图片

    3). 被动接收连接请求端:

CLOSE    --》   LISTEN    -- 》   接收 SYN    --》     LISTEN    -- 》   发送 ACK、SYN    --》 SYN_RCVD    --》    接收ACK     --》     ESTABLISHED(数据通信态)

Linux下的网络编程——C/S模型TCP(二)_第11张图片

    4). 被动关闭连接请求端:

ESTABLISHED(数据通信态) --》   接收 FIN    --》   ESTABLISHED(数据通信态)   --》    发送ACK    --》     CLOSE_WAIT (说明对端【主动关闭连接端】处于半关闭状态)      --》   发送FIN  --》     LAST_ACK     --》    接收ACK     -- 》    CLOSE   

Linux下的网络编程——C/S模型TCP(二)_第12张图片

 

重点记忆: ESTABLISHED、FIN_WAIT_2 <--> CLOSE_WAIT、TIME_WAIT(2MSL)

                   netstat -apn | grep  端口号

4.   2MSL时长:

一定出现在【主动关闭连接请求端】。 --- 对应 TIME_WAIT 状态。

保证,最后一个 ACK 能成功被对端接收。(等待期间,对端没收到我发的ACK,对端会再次发送FIN请求。)

你可能感兴趣的:(网络,linux,TCP,三次握手,四次握手,滑动窗口,TCP状态转化)