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

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

前言:

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

TCP协议

1.TCP通信时序

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

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

(1).三次握手

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

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

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

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

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

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

 

(2).四次握手

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

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

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

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

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

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

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

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

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

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

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

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

  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状态转换

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

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

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

 server:
#include <stdio.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <signal.h>
#include <sys/wait.h>
#include <ctype.h>
#include <unistd.h>
 
#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 <stdio.h>
#include <string.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>
 
#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;
}

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

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

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

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

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

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

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

 

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

                  netstat -apn | grep  端口号

4.   2MSL时长:

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

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

相关实践学习
容器服务Serverless版ACK Serverless 快速入门:在线魔方应用部署和监控
通过本实验,您将了解到容器服务Serverless版ACK Serverless 的基本产品能力,即可以实现快速部署一个在线魔方应用,并借助阿里云容器服务成熟的产品生态,实现在线应用的企业级监控,提升应用稳定性。
云原生实践公开课
课程大纲 开篇:如何学习并实践云原生技术 基础篇: 5 步上手 Kubernetes 进阶篇:生产环境下的 K8s 实践 相关的阿里云产品:容器服务&nbsp;ACK 容器服务&nbsp;Kubernetes&nbsp;版(简称&nbsp;ACK)提供高性能可伸缩的容器应用管理能力,支持企业级容器化应用的全生命周期管理。整合阿里云虚拟化、存储、网络和安全能力,打造云端最佳容器化应用运行环境。 了解产品详情:&nbsp;https://www.aliyun.com/product/kubernetes
目录
相关文章
|
4天前
|
机器学习/深度学习 缓存 监控
linux查看CPU、内存、网络、磁盘IO命令
`Linux`系统中,使用`top`命令查看CPU状态,要查看CPU详细信息,可利用`cat /proc/cpuinfo`相关命令。`free`命令用于查看内存使用情况。网络相关命令包括`ifconfig`(查看网卡状态)、`ifdown/ifup`(禁用/启用网卡)、`netstat`(列出网络连接,如`-tuln`组合)以及`nslookup`、`ping`、`telnet`、`traceroute`等。磁盘IO方面,`iostat`(如`-k -p ALL`)显示磁盘IO统计,`iotop`(如`-o -d 1`)则用于查看磁盘IO瓶颈。
|
2天前
|
网络协议 Linux Shell
【linux网络(一)】初识网络, 理解四层网络模型
【linux网络(一)】初识网络, 理解四层网络模型
|
2天前
|
安全 Ubuntu Linux
Linux 网络操作命令Telnet
Linux 网络操作命令Telnet
14 0
Linux 网络操作命令Telnet
|
2天前
|
Ubuntu Linux
Linux(22) Linux设置网络优先级顺序
Linux(22) Linux设置网络优先级顺序
3 0
|
3天前
|
Ubuntu 网络协议 Linux
Linux(20) Ubuntu 20.04 网络接口自动切换路由配置
Linux(20) Ubuntu 20.04 网络接口自动切换路由配置
26 0
|
10天前
|
网络协议 Ubuntu Unix
Linux 下使用 socket 实现 TCP 客户端
Linux 下使用 socket 实现 TCP 客户端
|
1月前
|
运维 网络协议 安全
【Shell 命令集合 网络通讯 】Linux 网络抓包工具 tcpdump命令 使用指南
【Shell 命令集合 网络通讯 】Linux 网络抓包工具 tcpdump命令 使用指南
44 0
|
1月前
|
网络协议 Linux 网络安全
curl(http命令行工具):Linux下最强大的网络数据传输工具
curl(http命令行工具):Linux下最强大的网络数据传输工具
48 0
|
6月前
|
监控 网络协议 Ubuntu
Linux网络监控工具 - iftop
Linux网络监控工具 - iftop
65 1
|
4月前
|
Linux
百度搜索:蓝易云【Linux系统下获取系统、BIOS、进程、网络等相关信息的方法和工具。】
综上所述,通过使用命令行工具和图形化工具,可以在Linux系统下获取系统、BIOS、进程和网络等相关信息。根据具体的需求和使用场景,选择合适的工具和命令可以帮助你更好地了解和管理Linux系统。
65 2

热门文章

最新文章