uc_15_TCP协议

1  TCP协议

        TCP提供客户机与服务器的链接。一个完整TCP通信过程需要经历三个阶段

        1)首先,客户机必须建立与服务器的连接,所谓虚电路

        2)然后,凭借已建立好的连接,通信双方相互交换数据

        3)最后,客户机与服务器双双终止连接,结束通信过程

        TCP保证数据传输的可靠性(超时重传、反向确认):

        TCP的协议栈底层在向另一端发送数据时,会要求对方在一个给定的时间窗口内返回确认。如果超过了这个时间窗口仍没有收到确认,则TCP会重传数据并等待更长的时间。只有在数次重传均告失败以后,TCP才会最终放弃。TCP含有用于动态估算数据往返时间(Round-Trip Time, RTT)的算法,因此它知道等待一个确认需要多长时间。

        TCP保证数据传输的有序性

        TCP的协议栈底层在向另一端发送数据时,会为所发送数据的每个字节指定一个序列号。即使这些数据字节没有能够按照发送时的顺序到达接收方,接收方的TCP也可以根据它们的序列号重新排序,再把最后的结果交给应用程序。

        TCP是全双工的:

        在给定的连接上,应用程序在任何时候都既可以发送数据也可以接收数据。因此,TCP必须跟踪每个方向上数据流的状态信息,如序列号和通告窗口的大小。

1.1  三次握手(建立连接)

                         

                                         三次握手                                                          交换数据

        1)客户机的TCP协议栈向服务器发送一个SYN分节(SYN比特位是1),告知对方自己将在连接中发送数据的初始序列号,称为主动打开。

        2)服务器的TCP协议栈向客户机发送一个单个分节,其中不仅包括对客户机SYN分节的ACK应答,还包含服务器自己的SYN分节(ACK和SYN比特位均是1),以告知对方自己在同一连接中发送数据的初始序列号。

        3)客户机的TCP协议栈向服务器返回ACK应答,以表示对服务器所发SYN的确认。
 

        交换数据

        - 一旦连接建立,客户机即可构造请求包,并发往服务器。服务器接收并处理来自客户机的请求包,构造响应包。

        - 服务器向客户机发送响应包,同时捎带对客户机请求包的ACK应答(反向确认)。

        - 客户机接收来自服务器的响应包,同时向对方发送ACK应答(反向确认)。

1.2  四次挥手(关闭连接)

                                                        

        1)客户机或者服务器主动关闭连接,TCP协议向对方发送FIN分节,表示数据通信结束。如果此时尚有数据滞留于发送缓冲区中,则FIN分节跟在所有未发送数据之后。

        2)接收到FIN分节的另一端执行被动关闭,一方面通过TCP协议栈向对方发送ACK应答,另一方面向应用程序传递文件结束符。

        3)一段时间后,方才接收到FIN分节的进程关闭自己的链接,同时通过TCP协议栈向对方发送FIN分节。

        4)对方在收到FIN分节后发送ACK应答。

2  TCP函数

2.1  listen()

        #include <sys/socket.h>

        int  listen( int socket,   int backlog );

                功能:启动侦听,在指定套接字上启动对连接请求的侦听功能

                sockfd:套接字描述符,在调用此函数之前是一个主动套接字,是不能感知连接请求的

                               在调用此函数并成功返回后,是一个被动套接字,具有感知连接请求的能力。

                backlog:未决连接请求队列的最大长度,一般取不小于1024的值

                返回值:成0-1 

2.2  accept()

        三次握手始于connect(),终于accept()结束

        #include <sys/socket.h>

        int accept( int sockfd,   struct sockaddr* addr,   socklen_t* addrlen );

                功能:等待并接受连接请求,在指定套接字上阻塞,直到连接建立完成

                sockfd:侦听套接字描述符

                addr:输出连接请求发起方的地址信息

                addrlen:输出连接请求发起方的地址信息字节数

                返回值:成功返回可用于后续通信的连接套接字描述符,失败返回-1 

2.3  recv()

        #include <sys/socket.h>

        ssize_t recv( int sockfd,   void* buf,   size_t count,   int flags );

                功能:接收数据

                flags:取0则与read()函数等价。另外也可取以下值

                           MSG_DONTWAIT  以非阻塞方式接收数据

                           MSG_OOB             接收带外数据

                           MSG_WAITALL      等待所有数据,即不接收到count个字节就不返回

                返回值:成功返回实际接收到的字节数,失败返回-1  

2.4  send()

        #include <sys/socket.h>

        ssize_t send( int sockfd,   void const* buf,   size_t count,   int flags );

                功能:发送数据

                flags:取0则与write()函数等价。另外也可取以下值

                           MSG_DONTWAIT  以非阻塞方方式发送数据

                           MSG_OOB             接收带外数据

                           MSG_DONTROUTE 不查路由表,直接在本地网络中寻找目的主机

                返回值:成功返回实际发送的字节数,失败返回-1 

3  TCP编程模型

        

4  通信终止

4.1  客户机主动终止

        在某个时刻,客户机认为已经不再需要服务器继续为其提供服务器了,于是它在接收完最后一个响应包以后,通过close()函数关闭与服务器通信的套接字。

        客户机的TCP协议栈向服务器发送FIN分节并得到对方的ACK应答。

        服务器专门负责与客户机通信的子进程,此刻正视图通过recv()接收下一个请求包,结果却因为收到来自客户机的FIN分节而返回0。

        于是该子进程退出收发循环,同时通过close()关闭连接套接字,导致服务器的TCP协议栈向客户机发送FIN分节,使对方进入TIME_WAIT状态,并在收到对方ACK应答以后,自己进入CLOSED状态。

        随着收发循环的退出,服务器子进程终止,并在服务器主进程的SIGCHLD(17)信号处理函数中被回收。

        通信过程宣告结束。

4.2  服务器主动终止

        服务器专门负责和某个特定客户机通信的子进程,在运行过程中出现错误,不得不调用close()函数关闭连接套接字,或者直接退出,甚至被信号杀死。于是服务器的TCP协议栈向客户机发送FIN分节并得到对方的ACK应答。

        A、如果客户机这时正视图通过recv()接收响应包,那么该函数会返回0。客户机可据此判断服务器已宕机,直接通过close()关闭与服务器通信的套接字,终止通信进程。

        B、如果客户机这时正视图通过send()发送请求包,那么该函数并不会失败,但会导致对方以RST分节做出响应,该响应分节甚至会先于FIN分节被紧随其后的recv()收到并返回-1,同时置errno为ECONNRESET。这也是终止通信的条件之一。

4.3  服务器主机不可达(主机崩溃、网络中断、路由失效...)

        在服务器主机不可达的情况下,无论是客户机还是服务器,它们的TCP协议栈都不可能再有任何数据分节的交换。因此,客户机通过send()函数发送完请求包以后,会阻塞在recv()上等待来自服务器的响应包。

        这是客户机的TCP协议栈会持续地重传数据分节,视图得到对方的ACK应答。源自伯克利的实现最多重传12次,最长等待9分钟。

        当TCP最终决定放弃时,会通过recv()向用户进程返回失败,并置errno为ETIMEOUT或EHOSTUNREACH或ENETUNREACH。

        此后,即使服务器主机被重启,或者通信线路被恢复,由于TCP协议栈已丢失了与先前连接有关的信息,通信依然无法继续,对所接收到的一切数据一律响应RST分节,只有在重新建立TCP连接后,才能继续通信。

5  域名解析

        IP地址是网络上标识站点的数字地址,为了方便记忆,采用域名来代替IP地址标识站点地址。

        域名解析就是域名IP地址的转换过程,由DNS服务器完成。

        当应用过程需要将一个主机域名映射为IP地址时,就调用域名解析函数,解析函数将待转换的域名放在DNS请求中,以UDP报文方式发给本地域名服务器。本地的域名服务器查到域名后,将对应的IP地址放在应答报文中返回。

5.1  gethostbyname()

        #include <netdb.h>

        struct hostnet* gethostbyname( char const* host_name );

                功能:通过参数所传的主机域名,获取主机信息

                host_name:主机域名

                返回值:成功返回表示主机信息的结构体指针,失败返回NULL 

        注意,该函数需要在联网情况下使用。

        

        struct hostent {

                char   *h_name;        // 主机官方名

                char   **h_aliases;    // 主机别名表

                int   h_addrtype;       // 地址类型

                int   h_length;           // 地址长度

                int   **h_addr_list;    // IP地址表

        };

        对于WEB服务器而言,主机官方名有一个,而主机别名可能有多个,这些别名都是为了便于用户记忆。同时IP地址也可能有多个。

        h_aliases   ->   *   ->   "xxx\n"

                          ->   *   ->   "xxx\n"

                          NULL;

        h_addr_list ->   *   ->   in_addr

                          ->   *   ->   in_addr

                          NULL

//tcpser.c  基于tcp的服务器
#include<stdio.h>
#include<string.h>
#include<ctype.h> // toupper()
#include<sys/socket.h>//网络
#include<sys/types.h>//网络
#include<arpa/inet.h>//网络
#include<signal.h>
#include<sys/wait.h>
#include<errno.h>
#include<unistd.h>
//信号处理函数,收尸int main(void){//捕获17号信号printf("服务器:创建套接字\n");int sockfd = socket(AF_INET,SOCK_STREAM,0);if(sockfd == -1){perror("socket");}//组织地址结构printf("服务器:组织地址结构\n");struct sockaddr_in ser;ser.sin_family = AF_INET;ser.sin_port = htons(8980); //8980由小端转大端//ser.sin_addr.s_addr = inet_addr("192.168.222.136");ser.sin_addr.s_addr = INADDR_ANY;//绑定地址结构和套接字printf("服务器:绑定套接字和地址结构\n");if(bind(sockfd,(struct sockaddr*)&ser,sizeof(ser)) == -1){perror("bind");return -1;}//开启侦听 套接字 主动-->被动-->感知连接请求printf("服务器:启动侦听\n");if(listen(sockfd,1024) == -1){perror("listen");return -1;}for(;;){//等待并建立通信连接printf("服务器:等待并建立通信连接\n");struct sockaddr_in cli;//用来输出客户端的地址结构socklen_t len = sizeof(cli);//用来输出地址结构大小int conn = accept(sockfd,(struct sockaddr*)&cli,&len);if(conn == -1){perror("accept");return -1;}printf("服务器:接收到%s:%hu的客户端的连接\n",inet_ntoa(cli.sin_addr),ntohs(cli.sin_port));//创建子进程 fork//子进程服务业务处理//业务处理printf("服务器:业务处理\n");for(;;){//接受客户端发来的小写的串char buf[64] = {};ssize_t size = read(conn,buf,sizeof(buf)-1);if(size == -1){perror("read");return -1;}if(size == 0){printf("服务器:客户端断开连接\n");break;}//转成大写for(int i = 0;i < strlen(buf);i++){buf[i] = toupper(buf[i]);}//将转成大写的串回传客户端if(write(conn,buf,strlen(buf)) == -1){perror("write");return -1;}}printf("服务器:关闭套接字\n");close(conn);}close(sockfd);  return 0;
}#include<stdio.h>
#include<string.h>
#include<ctype.h> // toupper()
#include<sys/socket.h>//网络
#include<sys/types.h>//网络
#include<arpa/inet.h>//网络
#include<signal.h>
#include<sys/wait.h>
#include<errno.h>
#include<unistd.h>
//信号处理函数,收尸
void sigchild(int signum){printf("服务器:捕获到%d号信号\n",signum);for(;;){pid_t pid = waitpid(-1,NULL,WNOHANG);if(pid == -1){if(errno == ECHILD){printf("服务器:没有子进程\n");break;}else{perror("waitpid");return ;}}else if(pid == 0){printf("服务器:子进程在运行\n");break;}else{printf("服务器:回收了%d进程的僵尸\n",pid);}}
}int main(void){//捕获17号信号if(signal(SIGCHLD,sigchild) == SIG_ERR){perror("signal");return -1;}printf("服务器:创建套接字\n");int sockfd = socket(AF_INET,SOCK_STREAM,0);if(sockfd == -1){perror("socket");}//组织地址结构printf("服务器:组织地址结构\n");struct sockaddr_in ser;ser.sin_family = AF_INET;ser.sin_port = htons(8980);//ser.sin_addr.s_addr = inet_addr("192.168.222.136");ser.sin_addr.s_addr = INADDR_ANY;//接受任意IP地址的数据(服务器可能有多个地址)//绑定地址结构和套接字printf("服务器:绑定套接字和地址结构\n");if(bind(sockfd,(struct sockaddr*)&ser,sizeof(ser)) == -1){perror("bind");return -1;}//开启侦听 套接字 主动-->被动-->感知连接请求printf("服务器:启动侦听\n");if(listen(sockfd,1024) == -1){perror("listen");return -1;}for(;;){//等待并建立通信连接printf("服务器:等待并建立通信连接\n");struct sockaddr_in cli;//用来输出客户端的地址结构socklen_t len = sizeof(cli);//用来输出地址结构大小int conn = accept(sockfd,(struct sockaddr*)&cli,&len);if(conn == -1){perror("accept");return -1;}printf("服务器:接收到%s:%hu的客户端的连接\n",inet_ntoa(cli.sin_addr),ntohs(cli.sin_port));//创建子进程 forkpid_t pid = fork();if(pid == -1){perror("fork");return -1;}//子进程服务业务处理//业务处理if(pid == 0){close(sockfd);printf("服务器:业务处理\n");for(;;){//接受客户端发来的小写的串char buf[64] = {};ssize_t size = read(conn,buf,sizeof(buf)-1);if(size == -1){perror("read");return -1;}if(size == 0){printf("服务器:客户端断开连接\n");break;}//转成大写for(int i = 0;i < strlen(buf);i++){buf[i] = toupper(buf[i]);}//将转成大写的串回传客户端if(write(conn,buf,strlen(buf)) == -1){perror("write");return -1;}}printf("服务器:关闭套接字\n");close(conn);return 0;//!!!!!!}//父进程代码,关闭通信套接字close(conn);//通信套接字(1个通信套接字对应1个客户端)}close(sockfd);//侦听套接字(整个服务器只有1个)return 0;
}//结合tcpcli,1台虚拟机做服务器执行本代码,另一台做客户端执行tcpcli
//tcpcli.c  基于的客户端
#include<stdio.h>
#include<string.h>
#include<unistd.h>
#include<sys/socket.h>
#include<sys/types.h>
#include<arpa/inet.h>int main(void){//创建套接字printf("客户端:创建套接字\n");int sockfd = socket(AF_INET,SOCK_STREAM,0);if(sockfd == -1){perror("socket");return -1;}//准备服务器的地址结构printf("客户端:准备服务器的地址结构\n");struct sockaddr_in ser;ser.sin_family = AF_INET;ser.sin_port = htons(8980);ser.sin_addr.s_addr = inet_addr("192.168.222.136");//发起连接printf("客户端:发起连接\n");if(connect(sockfd,(struct sockaddr*)&ser,sizeof(ser)) == -1){perror("connect");return -1;}//业务处理for(;;){//通过键盘获取小写的串char buf[64] = {};fgets(buf,sizeof(buf),stdin);// ! 退出if(strcmp(buf,"!\n") == 0){break;            }//将小写的串发送给服务器if(send(sockfd,buf,strlen(buf),0) == -1){perror("send");return -1;}//接受服务器回传大写的串if(recv(sockfd,buf,sizeof(buf)-1,0) == -1){perror("recv");return -1;}//显示printf("%s",buf);}printf("客户端:关闭套接字\n");close(sockfd);return 0;
}

1

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/210634.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

智能优化算法应用:基于粒子群算法3D无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用&#xff1a;基于粒子群算法3D无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用&#xff1a;基于粒子群算法3D无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.粒子群算法4.实验参数设定5.算法结果6.参考文…

Python---异常的综合案例

☆ 异常的传递 需求&#xff1a; ① 尝试只读方式打开python.txt文件&#xff0c;如果文件存在则读取文件内容&#xff0c;文件不存在则提示用户即可。 ② 读取内容要求&#xff1a;尝试循环读取内容&#xff0c;读取过程中如果检测到用户意外终止程序&#xff0c;则except捕…

个人博客网站如何实现https重定向(301)到http

对于个人网站站注册比较少的&#xff0c;服务器配置不是很好的&#xff0c;没必要https,https跳转到http是要时间的&#xff0c;会影响网站打开的速度。免费的https每年都要更换。个人博客网站https有一段时间了&#xff0c;而且很多页面都有收录排名&#xff0c;现在已去掉htt…

基于JavaWeb+SSM+Vue实习记录微信小程序系统的设计和实现

基于JavaWebSSMVue实习记录微信小程序系统的设计和实现 源码获取入口Lun文目录前言主要技术系统设计功能截图订阅经典源码专栏Java项目精品实战案例《500套》 源码获取 源码获取入口 Lun文目录 目 录 摘 要 III Abstract 1 1 系统概述 1 1.1 概述 2 1.2课题意义 3 1.3 主要内…

详细了解STM32----GPIO

提示&#xff1a;永远支持免费开源知识文档&#xff0c;喜欢的点个关注吧&#xff01;谢谢&#xff01; 文章目录 一、什么是GPIO&#xff1f;二、GPIO基本结构三、GPIO的输入输出模式1、推挽输出2、开漏输出3、复用推挽4、复用开漏1、浮空输入2、上拉输入&#xff13;、下拉输…

FastAPI之嵌套模型

请求体 - 嵌套模型 使用 FastAPI&#xff0c;你可以很随意的实现模型的嵌套、定义、校验、记录文档&#xff0c;并使用任意深度嵌套的模型&#xff0c;这其实都是FastAPI的核心模块P一单提成进行做的。。 List 字段 from fastapi import FastAPI from pydantic import BaseM…

基于JavaWeb+SSM+Vue童装商城小程序系统的设计和实现

基于JavaWebSSMVue童装商城小程序系统的设计和实现 源码获取入口Lun文目录前言主要技术系统设计功能截图订阅经典源码专栏Java项目精品实战案例《500套》 源码获取 源码获取入口 Lun文目录 目 录 摘 要 III Abstract 1 1 系统概述 2 1.1 概述 3 1.2课题意义 4 1.3 主要内容 5…

BearPi Std 板从入门到放弃 - 先天篇(1)(阶段 : 智慧城市 - 智慧路灯)

简介 对前面几篇整合, 做个小小汇总试验, 使用BearPi E53_SC1扩展板主芯片: STM32L431RCT6串口: Usart1扩展板与主板连接: I2C : I2C1 (光照强度传感器&#xff1a;BH1750)LED: PB9步骤 创建项目 参考 BearPi Std 板从入门到放弃 - 引气入体篇&#xff08;1&#xff09;(由零创…

浅谈Google Play ASO 优化

什么是ASO ASO即APP Store Optimization&#xff0c;是用于提高APP在应用市场排名的工具&#xff0c;其实也就是移动产品的SEO工作。 ASO是为了提高该产品的搜索结果成绩&#xff0c;提升APP的下载量&#xff0c;针对Google Play来说&#xff0c;ASO就是优化APP页面。 为什么…

Linux升级nginx版本

处于漏洞修复目的服务器所用nginx是1.16.0版本扫出来存在安全隐患&#xff0c;需要我们升级到1.17.7以上。 一般nginx默认在 /usr/local/ 目录&#xff0c;这里我的nginx是自定义的路径安装在 /app/weblogic/nginx 。 1.查看生产环境nginx版本 cd /app/weblogic/nginx/sbin/…

Redis基础入门

第1章&#xff1a;引言 大家好&#xff01;我是小黑&#xff0c;今天咱们来聊聊Redis。Redis&#xff0c;这个名字你可能在不少地方听过&#xff0c;尤其是在后端开发领域&#xff0c;它可是个大名鼎鼎的角色。&#xff0c;Redis是一个开源的内存中数据结构存储系统&#xff0…

放弃原生SQL:Python中更优雅的数据库操作

概要 在Python中&#xff0c;通过原生SQL语句进行数据库操作是一种传统的方式&#xff0c;但现代的Python开发中&#xff0c;使用ORM&#xff08;Object-Relational Mapping&#xff09;工具和数据库连接库可以更加高效和优雅地进行增删改查操作。本文将详细介绍Python中放弃原…

解决IDEA中多个项目不在同一窗口下显示的问题和添加新的git的URL

以上是添加显示多个项目 以下是给新添加的项目添加git

LeetCode算法题解(单调栈)|LeetCode84. 柱状图中最大的矩形

一、LeetCode84. 柱状图中最大的矩形 题目链接&#xff1a;84. 柱状图中最大的矩形 题目描述&#xff1a; 给定 n 个非负整数&#xff0c;用来表示柱状图中各个柱子的高度。每个柱子彼此相邻&#xff0c;且宽度为 1 。 求在该柱状图中&#xff0c;能够勾勒出来的矩形的最大…

做外贸很多时候还是要学会随机应变

马上又要到年底了&#xff0c;相信已经有一部分小伙伴开启了催单模式&#xff0c;希望客户尽量在春节前将订单落实下来&#xff0c;自然也有很多客户会在春节前的这一段时间开始陆续拜访自己观望了很久的工厂。 其实对于贸易公司来说&#xff0c;对于来看工厂的客户&#xff0…

ChatGPT,作为一种强大的自然语言处理模型,具备显著优势,能够帮助您在各个领域取得突破

2023年随着OpenAI开发者大会的召开&#xff0c;最重磅更新当属GPTs&#xff0c;多模态API&#xff0c;未来自定义专属的GPT。微软创始人比尔盖茨称ChatGPT的出现有着重大历史意义&#xff0c;不亚于互联网和个人电脑的问世。360创始人周鸿祎认为未来各行各业如果不能搭上这班车…

Kotlin:内置函数let、also、with、run、apply

前言 在Kotlin中&#xff0c;有一些用于扩展 & 方便开发者编码的内置函数&#xff0c;能大大提高开发者的开发效率。今天&#xff0c;我将主要讲解的是&#xff1a; let函数also函数with函数run函数apply函数 基础知识&#xff1a;接口回调中Lambda使用 在Kotlin中可使用…

栈和队列的互相实现

用队列实现栈 OJ链接 请你仅使用两个队列实现一个后入先出&#xff08;LIFO&#xff09;的栈&#xff0c;并支持普通栈的全部四种操作&#xff08;push、top、pop 和 empty&#xff09;。 实现 MyStack 类&#xff1a; void push(int x) 将元素 x 压入栈顶。int pop() 移除并返…

Mybatis XML增删操作(结合上文)

先来"增"操作 在UserInfoXMLMapper.xml里面写 <?xml version"1.0" encoding"UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <…

nginx多端口部署

1.配置nginx.conf文件 有几个端口需要部署就写几个server&#xff0c;我这里只部署了两个端口分别为80和81端口&#xff0c;所以有两个server文件。80端口项目入口在根目录的test文件中&#xff0c;81端口项目入口在根目录的test1文件夹中。 2.准备项目文件html文件 在/test1…