TCP IP网络编程(五) 基于TCP的服务器端、客户端 (补充)

文章目录

    • 回声客户端的完美实现
      • 回声客户端出现的问题
      • 回声客户端问题解决方法
    • TCP原理
      • TCP套接字中的I/O缓冲
      • TCP内部工作原理1:与对方套接字的连接
      • TCP内部工作原理2:与对方主机的数据交换
      • TCP内部工作原理3:断开与套接字的连接

回声客户端的完美实现

回声客户端出现的问题

在上一节基于TCP的服务器端、回声客户端中,存在问题:

如果数据太大,操作系统就有可能把数据分成多个数据包发送到客户端,客户端有可能在尚未收到全部数据包时就调用read函数

问题出在客户端,而不是服务器端,先来对比一下客户端与服务器端I/O相关代码

服务器端

while((str_len = read(cknt_sock, message, BUF_SIZE)) != 0)write(clnt_sock, message, str_len);

客户端

write(sock, message, strlen(message))
str_len = read(sock, message, BUF_SIZE - 1);

客户端与服务器端都调用read和write函数,实际上回声客户端将100%接受自己传输的数据,只不过接收数据时单位出现问题

回声客户端传输的是字符串,而且是通过调用write函数一次性发送的,之后调用read函数,等待接收自己传输的字符串

这里的问题就出现在客户端收到所有字符串数据,需要等待多长时间?等待之后调用read函数是否可以一次性读取完毕?

回声客户端问题解决方法

因为可以提前确定接收数据的大小,所以这个问题不难解决,如果之前传输了100字节长的字符串,则在接收时循环调用read函数读取100字节结课

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>#define BUF_SIZE 1024
void error_handling(char *message);int main(int argc, char *argv[])
{int sock;char message[BUF_SIZE];int str_len, recv_len, recv_cnt;struct sockaddr_in serv_adr;if(argc!=3) {printf("Usage : %s <IP> <port>\n", argv[0]);exit(1);}sock=socket(PF_INET, SOCK_STREAM, 0);   if(sock==-1)error_handling("socket() error");memset(&serv_adr, 0, sizeof(serv_adr));serv_adr.sin_family=AF_INET;serv_adr.sin_addr.s_addr=inet_addr(argv[1]);serv_adr.sin_port=htons(atoi(argv[2]));if(connect(sock, (struct sockaddr*)&serv_adr, sizeof(serv_adr))==-1)error_handling("connect() error!");elseputs("Connected...........");while(1) {fputs("Input message(Q to quit): ", stdout);fgets(message, BUF_SIZE, stdin);if(!strcmp(message,"q\n") || !strcmp(message,"Q\n"))break;str_len=write(sock, message, strlen(message));recv_len=0;while(recv_len<str_len){recv_cnt=read(sock, &message[recv_len], BUF_SIZE-1);if(recv_cnt==-1)error_handling("read() error!");recv_len+=recv_cnt;}message[recv_len]=0;printf("Message from server: %s", message);}close(sock);return 0;
}void error_handling(char *message)
{fputs(message, stderr);fputc('\n', stderr);exit(1);
}

在之前的示例中只调用了一次read函数,上述示例为了接收所有传输的数据而循环调用read函数

while(recv_len<str_len)
{recv_cnt=read(sock, &message[recv_len], BUF_SIZE-1);if(recv_cnt==-1)error_handling("read() error!");recv_len+=recv_cnt;
}

如果将上述代码改成:

while(recv_len != str_len)
{recv_cnt=read(sock, &message[recv_len], BUF_SIZE-1);if(recv_cnt==-1)error_handling("read() error!");recv_len+=recv_cnt;
}

接收的数据大小应该和传输的相同,所以recv_len中保存的数据等于str_len中保存的数据,即可结束while循环,读者们肯定认为这种循环写法更符合逻辑,但是可能因为某种异常情况导致死循环,所以尽可能的使用 while(recv_len<str_len) ,即使发生异常情况也不会导致无限循环

TCP原理

TCP套接字中的I/O缓冲

TCP套接字的数据收发无边界,服务器端即使调用一次write函数传输40字节的数据,客户端也有可能通过4次read函数调用每次读取10字节,但是问题又出现了,服务器端一次性传输了40字节,而客户端居然可以缓慢的分批接收,,客户端接收10字节后,剩下的30字节在什么地方等着呢?

事实上,write函数调用后并非立即传输数据,read函数调用后也并非马上接收数据。更准确的来说,write函数调用瞬间,数据将移到输出缓冲;read函数调用瞬间,从输入缓冲读取数据。

在这里插入图片描述

调用write函数时,数据将移到输出缓冲,在适当的时候传向对方的输入缓冲,这时对方调用read函数从输入缓冲读取数据

I/O缓冲特性如下

  • I/O缓冲在每个TCP套接字中单独存在
  • I/O缓冲在创建套接字时自动生成
  • 即使关闭套接字也会继续传递输出缓冲中遗留的数据
  • 关闭套接字将丢失输入缓冲中的数据

TCP内部工作原理1:与对方套接字的连接

TCP套接字从创建到消失所经过程分为三步:

1.与对方套接字建立连接

2.与对方套接字交换数据

3.断开与对方套接字的连接

TCP在实际通信过程中也会经过三次对话过程,因此该过程又称为三次握手

在这里插入图片描述

套接字是以全双工方式工作的,也就是说明套接字可以双向传递数据

1、 请求连接的主机A向主机B传递如下信息:

[SYN] SEQ: 1000, ACK: -

该消息中SEQ为1000,ACK为空,SEQ为1000的含义如下:

现在传递的数据包序号为100,如果接收无误,请通知我向你传递1001号数据包

2、 这是首次请求连接时使用的消息,又称SYN,表示收发数据前传输的同步消息,接下来主机B向A传递如下消息:

[SYN+ACK] SEQ: 2000, ACK: 1001

此时SEQ为2000,ACK为1001,而SEQ为2000的含义如下:

现在传递的数据包序号为2000,如果接收无误,请通知我向你传递2001号数据包

而ACK 1001的含义如下:

刚才传输的SEQ为1000的数据包接收无误,现在请传递SEQ为1001的数据包

对主机A首次传输的的数据包确认消息(ACK 1001)和为主机B传输数据做准备的同步消息(SEQ 200)捆绑发送,因此此种类型的消息又称为 SYN + ACK

3、 收发数据前向数据包分配序号,并向对方通报此序号,这都是为了防止数据丢失所做的准备。通过向数据包分配序号并确认,可以在数据丢失时马上查看并重传丢失的数据包,所以TCP可以保证可靠的数据传输。最后观察主机A向B传输的消息:

[ACK] SEQ : 1001, ACK: 2001

TCP连接过程中发送数据包时需要分配序号,在此之前的序号100的基础上+1,此时该数据包传递如下消息:

已正确收到传输的SEQ为2000的数据包,现在可以传输SEQ为2001的数据包

TCP内部工作原理2:与对方主机的数据交换

通过第一步三次握手过程完成了数据交换准备,下面就开始正式收发数据

在这里插入图片描述

首先主机A通过1个数据包发送100个字节的数据,数据包的SEQ为1200,主机B为了确认这点,向主机A发送了ACK1301消息

此时的ACK号为1301而非1201,原因在于ACK号的增量为传输的数据字节数,假设每次ACK号不加传输的字节数,这样虽然可以确认数据包的传输,但是不能明确100字节却不正确传递还是丢失,因此按照如下公式传递ACK消息:

ACK 号 — SEQ号 + 传递的字节数 + 1

与三次握手协议相同,最后+1是为了告知对方下次要传递的SEQ号

下面分析传输过程中数据包消失的情况

在这里插入图片描述

SEQ1301数据包向主机B传递100字节数据,但中间发生错误,主机B未收到,一段时间后,主机A仍未收到对于SEQ 1301的ACK确认,因此重传改数据包,为了完成数据包重传,TCP套接字启动计时器以等待ACK回答,相应计时器发生超时则重传

TCP内部工作原理3:断开与套接字的连接

先由套接字A向套接字B传递断开连接的消息,套接字B发出确认收到的消息,然后向套接字A传递可以断开连接的消息,套接字A同样发出确认消息

在这里插入图片描述

数据包内的FIN表示断开连接,双方各发送一次FIN消息后断开连接,此过程经历四个阶段,因此又称为四次握手


这是《TCP/IP网络编程》专栏的第三篇文章,欢迎各位读者订阅!

更多资料点击 GitHub 欢迎各位读者去Star

⭐学术交流群Q 754410389 持续更新中~~~

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

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

相关文章

双向链表的实现(增删查改)——最好理解的链表

双向链表的实现 一&#xff0c;双向链表的特点二&#xff0c;双向链表的结构三&#xff0c;双向链表的内容实现3.1创建node节点3.2初始化3.3打印3.4插入3.4.1尾插3.4.2头插3.4.3在pos位置上插入 3.5删除3.5.1尾删3.5.2头删3.5.3删除pos位置上的数据 四&#xff0c;调试技巧&…

day21算法

常见的七种查找算法&#xff1a; ​ 数据结构是数据存储的方式&#xff0c;算法是数据计算的方式。所以在开发中&#xff0c;算法和数据结构息息相关。今天的讲义中会涉及部分数据结构的专业名词&#xff0c;如果各位铁粉有疑惑&#xff0c;可以先看一下哥们后面录制的数据结构…

【C++学习】继承

目录 一、继承的概念及定义 1、继承的概念 2、继承的定义 2.1 定义格式 2.2 继承关系和访问限定符 2.3 继承基类成员访问方式的变化 二、基类和派生类对象赋值转换 三、继承中的作用域 四、派生类的默认成员函数 五、继承与友元 六、继承与静态成员 七、复杂的菱形…

SpringMvc第五战-【SpringMvcJSR303和拦截器】

前言&#xff1a; 小编阐述了springmvc 中的文件下载&#xff0c;以及jrebel的使用和文件下载以及多文件下载! 在本次小编将会介绍JSR303的概念&#xff0c;应用场景和在具体实例的使用&#xff1b;和拦截器的应用 一.JSR303的介绍 1.什么是JSR303&#xff1f; JSR是Java S…

Aztec的隐私抽象:在尊重EVM合约开发习惯的情况下实现智能合约隐私

1. 引言 Aztec的架构&#xff0c;不同于当前“通过EVM兼容执行环境”所实现的区块链水平扩容趋势。Aztec内部笑称其构建的为首个非zkEVM协议。 Aztec专注于实现&#xff1a; 成为理解和需要智能合约隐私的开发者的终极解决方案。 Aztec为开发者提供构建隐私优先app所需的网…

【微信小程序开发】宠物预约医疗项目实战-环境配置与Vant UI集成

第一章 宠物预约医疗项目实战-环境配置与Vant UI集成 文章目录 前言一、Vant UI是什么&#xff1f;二、使用步骤2.1 安装 node.js2.2 通过 npm 安装vant2.3 修改 app.json2.4 修改 project.config.json2.5 构建 npm 包2.6 使用组件全局引入和局部引入全局引入局部引入 前言 Va…

基于Java+SpringBoot+Vue+uniapp点餐小程序(亮点:协同过滤算法、会员系统,购物车结算、在线聊天)

校园点餐小程序 一、前言二、我的优势2.1 自己的网站2.2 自己的小程序&#xff08;小蔡coding&#xff09;2.3 有保障的售后2.4 福利 三、开发环境与技术3.1 MySQL数据库3.2 Vue前端技术3.3 Spring Boot框架3.4 微信小程序 四、功能设计4.1 系统功能结构设计4.2 主要功能描述 五…

SpringBoot整合Flowable

1. 配置 &#xff08;1&#xff09; 引入maven依赖 <dependency><groupId>org.flowable</groupId><artifactId>flowable-spring-boot-starter</artifactId><version>6.7.2</version></dependency><!-- MySQL连接 -->&l…

MySQL--MySQL索引事务

事务的概念 事务指逻辑上的一组操作&#xff0c;组成这组操作的各个单元&#xff0c;要么全部成功&#xff0c;要么全部失败。 在不同的环境中&#xff0c;都可以有事务。对应在数据库中&#xff0c;就是数据库事务。 使用 &#xff08;1&#xff09;开启事务&#xff1a;start…

什么是接口自动化?为什么要做?和怎么做接口自动化?

服务端接口测试介绍 什么是服务端&#xff1f; 一般所说的服务端是指为用户在 APP 或 PC 使用的互联网功能提供数据服务的背后的一切。以天猫精灵智能音箱系列的产品链路为例&#xff0c;服务端便是网关&#xff08;包括网关在内&#xff09;之后的链路。 什么是接口&#xf…

【自然语言处理】【大模型】RWKV:基于RNN的LLM

相关博客 【自然语言处理】【大模型】RWKV&#xff1a;基于RNN的LLM 【自然语言处理】【大模型】CodeGen&#xff1a;一个用于多轮程序合成的代码大语言模型 【自然语言处理】【大模型】CodeGeeX&#xff1a;用于代码生成的多语言预训练模型 【自然语言处理】【大模型】LaMDA&a…

深入网络底层,了解Linux系统收发网络数据包的过程、原理、流程,附图文说明

深入网络底层&#xff0c;了解Linux系统收发网络数据包的过程、原理、流程&#xff0c;附图文说明。 Linux 服务器收到网络数据包&#xff0c;需要经过哪些处理&#xff0c;一步步将数据传给应用进程的呢&#xff1f;应用进程发送数据包时&#xff0c;Linux 又是如何操作将数据…

android studio platform使用体验分享(as无法跳转c/c++等native源码的福音,强烈推荐)

hi&#xff0c;粉丝朋友们&#xff1a; 大家好&#xff01;这些天粉丝朋友们分享了一下Android Studio for Platform 这个最新的google开发的阅读aosp源码的工具&#xff0c;特别适合做原生系统开发。具体官方介绍如下地址&#xff1a; 参考链接&#xff1a;https://developer.…

react的状态管理简单钩子方法

1.recoil useProvider文件: import { atom, useRecoilState } from recoil;const initState atom({key: initState,default: {state: [],}, })// 将业务逻辑拆分到一个单独文件中&#xff0c;方便进行状态管理 export interface StateProps {id: number;text: string;isFini…

异地远程访问本地SQL Server数据库【无公网IP内网穿透】

文章目录 1. 前言2. SeaFile云盘设置2.1 Owncould的安装环境设置2.2 SeaFile下载安装2.3 SeaFile的配置 3. cpolar内网穿透3.1 Cpolar下载安装3.2 Cpolar的注册3.3 Cpolar云端设置3.4 Cpolar本地设置 4. 公网访问测试5. 结语 1. 前言 现在我们身边的只能设备越来越多&#xff…

文心一言 VS 讯飞星火 VS chatgpt (94)-- 算法导论9.2 3题

三、用go语言&#xff0c;给出 RANDOMIZED-SELECT的一个基于循环的版本。 文心一言&#xff1a; 在循环中实现 RANDOMIZED-SELECT 的一个版本可以如下所示。这个版本使用 Go 语言编写&#xff0c;利用随机化来选择一个元素&#xff0c;并在循环中不断地调整选择的元素&#x…

【探索Linux】—— 强大的命令行工具 P.8(进程地址空间)

阅读导航 前言一、内存空间分布二、什么是进程地址空间1. 概念2. 进程地址空间的组成 三、进程地址空间的设计原理1. 基本原理2. 虚拟地址空间 概念 大小和范围 作用 虚拟地址空间的优点 3. 页表 四、为什么要有地址空间五、总结温馨提示 前言 前面我们讲了C语言的基础知识&am…

HTTP协议(超级详细)

HTTP协议介绍 基本介绍&#xff1a; HTTP&#xff1a;超文本传输协议&#xff0c;是从万维网服务器传输超文本到本地浏览器的传送协议HTTP是一种应用层协议&#xff0c;是基于TCP/IP通信协议来传送数据的&#xff0c;其中 HTTP1.0、HTTP1.1、HTTP2.0 均为 TCP 实现&#xff0…

vue组件库开发,webpack打包,发布npm

做一个像elment-ui一样的vue组件库 那多好啊&#xff01;这是我前几年就想做的 但webpack真的太难用&#xff0c;也许是我功力不够 今天看到一个视频&#xff0c;早上6-13点&#xff0c;终于实现了&#xff0c;呜呜 感谢视频的分享-来龙去脉-大家可以看这个视频&#xff1a;htt…

【C语言】【数据存储】用%u打印char类型?用char存128?

1.题目一&#xff1a; #include <stdio.h> int main() {char a -128;printf("%u\n",a);return 0; }%u 是打印无符号整型 解题逻辑&#xff1a; 1. 原反补互换&#xff0c;截断 -128 原码&#xff1a;10000000…10000000 补码&#xff1a;11111111…10000000…