【linux】深入了解TCP与UDP

认识端口号

端口号(port)是传输层协议的内容.
  • 端口号是一个2字节16位的整数;
  • 端口号用来标识一个进程, 告诉操作系统, 当前的这个数据要交给哪一个进程来处理;
  • IP地址 + 端口号能够标识网络上的某一台主机的某一个进程;
  • 一个端口号只能被一个进程占用

理解 "端口号" "进程ID"

一个进程可以绑定多个端口号 ; 但是一个端口号不能被多个进程绑定 ;
传输层协议 (TCP UDP) 的数据段中有两个端口号 , 分别叫做源端口号和目的端口号 . 就是在描述 " 数据是谁发的 , 要发给谁 ";
认识 TCP 协议
此处我们先对 TCP(Transmission Control Protocol 传输控制协议 ) 有一个直观的认识
        传输层协议
        有连接
        可靠传输
        面向字节流
认识 UDP 协议
此处我们也是对 UDP(User Datagram Protocol 用户数据报协议 ) 有一个直观的认识
        传输层协议
        无连接
        不可靠传输
        面向数据报
网络字节序
我们已经知道 , 内存中的多字节数据相对于内存地址有大端和小端之分 , 磁盘文件中的多字节数据相对于文件中的偏移地址也有大端小端之分 , 网络数据流同样有大端小端之分 . 那么如何定义网络数据流的地址呢 ?
  • 发送主机通常将发送缓冲区中的数据按内存地址从低到高的顺序发出;
  • 接收主机把从网络上接到的字节依次保存在接收缓冲区中,也是按内存地址从低到高的顺序保存;
  • 因此,网络数据流的地址应这样规定:先发出的数据是低地址,后发出的数据是高地址.
  • TCP/IP协议规定,网络数据流应采用大端字节序,即低地址高字节.
  • 不管这台主机是大端机还是小端机, 都会按照这个TCP/IP规定的网络字节序来发送/接收数据;
  • 如果当前发送主机是小端, 就需要先将数据转成大端; 否则就忽略, 直接发送即可

为使网络程序具有可移植性,使同样的C代码在大端和小端计算机上编译后都能正常运行,可以调用以下库函数做网络字节序和主机字节序的转换

这些函数名很好记,h表示host,n表示network,l表示32位长整数,s表示16位短整数
例如htonl表示将32位的长整数从 主机字节序转换为网络字节序 ,例如将IP地址转换后准备发送。
如果主机是小端字节序,这些函数将参数做相应的大小端转换然后返回;
如果主机是大端字节序,这些函数不做转换,将参数原封不动地返回。

socket编程接口

socket 常见API

// 创建 socket 文件描述符 (TCP/UDP, 客户端 + 服务器)
int socket(int domain, int type, int protocol);
// 绑定端口号 (TCP/UDP, 服务器) 
int bind(int socket, const struct sockaddr *address,
 socklen_t address_len);
// 开始监听socket (TCP, 服务器)
int listen(int socket, int backlog);
// 接收请求 (TCP, 服务器)
int accept(int socket, struct sockaddr* address,
 socklen_t* address_len);
// 建立连接 (TCP, 客户端)
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

sockaddr结构

socket API 是一层抽象的网络编程接口 , 适用于各种底层网络协议 , IPv4 IPv6, 以及后面要讲的UNIX Domain Socket. 然而, 各种网络协议的地址格式并不相同
  • IPv4和IPv6的地址格式定义在netinet/in.h中,IPv4地址用sockaddr_in结构体表示,包括16位地址类型, 16位端口号和32位IP地址.
  • IPv4、IPv6地址类型分别定义为常数AF_INET、AF_INET6. 这样,只要取得某种sockaddr结构体的首地址, 不需要知道具体是哪种类型的sockaddr结构体,就可以根据地址类型字段确定结构体中的内容.
  • socket API可以都用struct sockaddr *类型表示, 在使用的时候需要强制转化成sockaddr_in; 这样的好处是程序的通用性, 可以接收IPv4, IPv6, 以及UNIX Domain Socket各种类型的sockaddr结构体指针做为参数

sockaddr 结构sockaddr_in 结构 

虽然socket api的接口是sockaddr, 但是我们真正在基于IPv4编程时, 使用的数据结构是sockaddr_in; 这个结构里主要有三部分信息: 地址类型, 端口号, IP地址.

in_addr结构

  in_addr用来表示一个IPv4IP地址. 其实就是一个32位的整数 


TCP socket API 详解socket API 详解

socket():

  • socket()打开一个网络通讯端口,如果成功的话,就像open()一样返回一个文件描述符;
  • 应用程序可以像读写文件一样用read/write在网络上收发数据;
  • 如果socket()调用出错则返回-1;
  • 对于IPv4, family参数指定为AF_INET;
  • 对于TCP协议,type参数指定为SOCK_STREAM, 表示面向流的传输协议
  • protocol参数的介绍从略,指定为0即可 

bind():

  • 服务器程序所监听的网络地址和端口号通常是固定不变的,客户端程序得知服务器程序的地址和端口号后就可以向服务器发起连接; 服务器需要调用bind绑定一个固定的网络地址和端口号;
  • bind()成功返回0,失败返回-1。
  • bind()的作用是将参数sockfdmyaddr绑定在一起, 使sockfd这个用于网络通讯的文件描述符监听myaddr所描述的地址端口号
  • 前面讲过,struct sockaddr *是一个通用指针类型,myaddr参数实际上可以接受多种协议的sockaddr结构体,而它们的长度各不相同,所以需要第三个参数addrlen指定结构体的长度

 我们的程序中对myaddr参数是这样初始化的

  1.  将整个结构体清零;
  2. 设置地址类型为AF_INET;
  3. 网络地址为INADDR_ANY, 这个宏表示本地的任意IP地址,因为服务器可能有多个网卡,每个网卡也可能绑定多个IP 地址, 这样设置可以在所有的IP地址上监听,直到与某个客户端建立了连接时才确定下来到底用哪个IP 地址;
  4. 端口号为SERV_PORT, 我们定义为9999;

listen():

  • listen()声明sockfd处于监听状态, 并且最多允许有backlog个客户端处于连接等待状态, 如果接收到更多的连接请求就忽略, 这里设置不会太大(一般是5)
  • listen()成功返回0,失败返回-1

 accept():

  • 三次握手完成后, 服务器调用accept()接受连接;
  • 如果服务器调用accept()时还没有客户端的连接请求,就阻塞等待直到有客户端连接上来;
  • addr是一个传出参数,accept()返回时传出客户端的地址和端口号;
  • 如果给addr 参数传NULL,表示不关心客户端的地址;
  • addrlen参数是一个传入传出参数(value-result argument), 传入的是调用者提供的, 缓冲区addr的长度以避免缓冲区溢出问题, 传出的是客户端地址结构体的实际长度(有可能没有占满调用者提供的缓冲区);
我们的服务器程序结构是这样的:

connect 

  • 客户端需要调用connect()连接服务器;
  • connectbind的参数形式一致, 区别在于bind的参数是自己的地址, connect的参数是对方的地址;
  • connect()成功返回0,出错返回-1; 

TCP流程

 服务器初始化:

  • 调用socket, 创建文件描述符;
  • 调用bind, 将当前的文件描述符和ip/port绑定在一起; 如果这个端口已经被其他进程占用了, 就会bind失败;
  • 调用listen, 声明当前这个文件描述符作为一个服务器的文件描述符, 为后面的accept做好准备;
  • 调用accecpt, 并阻塞, 等待客户端连接过来

建立连接的过程:

  • 调用socket, 创建文件描述符;
  • 调用connect, 向服务器发起连接请求;
  • connect会发出SYN段并阻塞等待服务器应答; (第一次)
  • 服务器收到客户端的SYN, 会应答一个SYN-ACK段表示"同意建立连接"; (第二次)
  • 客户端收到SYN-ACK后会从connect()返回, 同时应答一个ACK段; (第三次)
  • 这个建立连接的过程, 通常称为 三次握手;

数据传输的过程

建立连接后,TCP协议提供 全双工 的通信服务;
所谓全双工的意思是: 在同一条连接中, 同一时刻, 通信双方可以同时写数据;
相对的概念叫做半双工 :同一条连接在同一时刻, 只能由一方来写数据;
服务器从accept()返回后立刻调 用read(), 读socket就像读管道一样, 如果没有数据到达就阻塞等待;
这时客户端调用write()发送请求给服务器, 服务器收到后从read()返回,对客户端的请求进行处理, 在此期间客户端调用read()阻塞等待服务器的应答;
服务器调用write()将处理结果发回给客户端, 再次调用read()阻塞等待下一条请求;
客户端收到后从read()返回, 发送下一条请求,如此循环下去

断开连接的过程

  • 如果客户端没有更多的请求了, 就调用close()关闭连接, 客户端会向服务器发送FIN段(第一次);
  • 此时服务器收到FIN后, 会回应一个ACK, 同时read会返回0 (第二次);
  • read返回之后, 服务器就知道客户端关闭了连接, 也调用close关闭连接, 这个时候服务器会向客户端发送 一个FIN; (第三次)
  • 客户端收到FIN, 再返回一个ACK给服务器; (第四次)
这个断开连接的过程 , 通常称为 四次挥手

TCP与UDP协议的对比

可靠传输 vs 不可靠传输
有连接 vs 无连接
字节流 vs 数据报

 

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

数据结构MySQL —— 索引

目录 一、索引概述 二、索引结构 三、索引分类 四、索引语法 五、SQL性能分析 1. 查看执行频次 2. 慢查询日志 3. show profiles指令 4. explain执行计划 六、索引使用规则 1. 验证索引效率 2. 最左前缀法则 3. 范围查询 4. 索引失效情况 5. SQL提示 6. …

记录springboot+vue+fastdfs实现简易的文件(上传、下载、删除、预览)操作

前言说明:springboot vue FastDFS实现文件上传(支持预览)升级版 FASTDFS部分 FASTDFS安装过程:基于centos 7安装FastDFS文件服务器 SpringBoot部分 springboot源码实现 package com.core.doc.controller;import com.baomid…

Spring Boot集成RocketMQ实现普通、延时、事务消息发送接收、PULL消费模式及开启ACL | Spring Cloud 30

一、前言 在前面我们通过以下章节对RocketMQ有了基础的了解: docker-compose 搭建RocketMQ 5.1.0 集群(双主双从模式) | Spring Cloud 28 docker-compose 搭建RocketMQ 5.1.0 集群开启ACL权限控制 | Spring Cloud 29 现在开始我们正式学习…

LORA: LOW-RANK ADAPTATION OF LARGE LAN-GUAGE MODELS

Paper name LORA: LOW-RANK ADAPTATION OF LARGE LAN-GUAGE MODELS Paper Reading Note Paper URL: https://arxiv.org/pdf/2106.09685.pdf Code URL: huggingface 集成: https://github.com/huggingface/peft官方代码: https://github.com/microsof…

C++11新特性

C11新特性 1统一列表初始化 1.1{} 在C98中,标准允许使用花括号{}对数组或者结构体元素进行统一的列表初始值设定。 C11扩大了用大括号括起的列表(初始化列表)的使用范围,使其可用于所有的内置类型和用户自定义的类型,**使用初始化列表时&…

安全防御之入侵检测篇

目录 1.什么是IDS? 2.IDS和防火墙有什么不同?3.IDS的工作原理? 4.IDS的主要检测方法有哪些?请详细说明 5.IDS的部署方式有哪些? 6.IDS的签名是什么意思?签名过滤器有什么用?例外签名的配置作…

【数据结构】栈与队列:后进先出与先进先出到底是啥?

👑专栏内容:数据结构⛪个人主页:子夜的星的主页💕座右铭:日拱一卒,功不唐捐 文章目录一、前言二、栈的概念1、定义2、操作三、栈的实现1、定义2、栈的初始化3、栈的销毁4、栈的判空5、查看栈顶元素6、入栈与…

vue3 解决各场景 loading过度 ,避免白屏尴尬!

Ⅰ、前言 当我们每次打卡页面,切换路由,甚至于异步组件,都会有一个等待的时间 ;为了不白屏,提高用户体验,添加一个 loading 过度动画是 非常 常见的 ;那么这几种场景我们应该把 loading 加在哪…

C语言番外-------《函数栈帧的创建和销毁》知识点+基本练习题+完整的思维导图+深入细节+通俗易懂建议收藏

绪论 书接上回,上回我们已经将C语言的所有知识点进行了总结归纳到同一个思维导图中,而本章是一个番外篇,将具体讲述一些更深入的知识。 话不多说安全带系好,发车啦(建议电脑观看)。 附:红色&…

软件架构常用设计

一、分层架构1.四层架构:分层架构(layered architecture)是最常见的软件架构,也是事实上的标准架构。如果你不知道要用什么架构,那就用它。这种架构将软件分成若干个水平层,每一层都有清晰的角色和分工&…

【c++】:list模拟实现“任意位置插入删除我最强ƪ(˘⌣˘)ʃ“

文章目录 前言一.list的基本功能的使用二.list的模拟实现总结前言 1. list是可以在常数范围内在任意位置进行插入和删除的序列式容器,并且该容器可以前后双向迭代。2. list的底层是双向链表结构,双向链表中每个元素存储在互不相关的独立节点中&#xff0…

【Linux】进程理解与学习Ⅲ-环境变量

环境:centos7.6,腾讯云服务器Linux文章都放在了专栏:【Linux】欢迎支持订阅🌹相关文章推荐:【Linux】冯.诺依曼体系结构与操作系统【Linux】进程理解与学习Ⅰ-进程概念浅谈Linux下的shell--BASH【Linux】进程理解与学习…

【李宏毅】-各种各样的self-attention

如何使自注意力机制变得高效? 在这篇博客中有讲关于注意力机制,其中,我们需要计算三个矩阵——Q,K,V ,如果序列长度为N,那么三个矩阵的大小都是NxN,这将导致注意力机制计算量很大&a…

Linux上搭建Discuz论坛

一.准备工作 1.下载php*,mariadb-server 2.上传Discuz3.5压缩包并解压 二.搭建过程 基于redhat 9 版本和Discuz3.5,php8.0,mariadb10.5演示 一.准备工作 1.下载php*,mariadb-server [rootredhat9 aaa]# yum install -y php*…

软件测试基础篇

一、软件测试的生命周期 需求分析计划阶段:范围、时间、人员、工具;测试设计/开发:编写测试用例;测试执行:执行并补充测试用例;测试评估:覆盖范围(测试了哪些功能,哪些没…

QCefView编译配置(Windows-MSVC)(11)

QCefView编译配置(Windows-MSVC) 文章目录QCefView编译配置(Windows-MSVC)1、概述2、准备工作3、添加环境变量4、更换cef源码版本5、CMake构建6、Visual Studio编译7、安装编译后的文件8、验证编译结果更多精彩内容👉个…

jwt 学习笔记

概述 JWT,Java Web Token,通过 JSON 形式作为 Web 应用中的令牌,用于在各方之间安全地将信息作为 JSON 对象传输,在数据传输过程中还可以完成数据加密、签名等相关处理 JWT 的作用如下: 授权:一旦用户登…

ChatGPT常用开源项目汇总

❤️觉得内容不错的话,欢迎点赞收藏加关注😊😊😊,后续会继续输入更多优质内容❤️👉有问题欢迎大家加关注私戳或者评论(包括但不限于NLP算法相关,linux学习相关,读研读博…

动态代理原理

一、案例分析 1、引出问题 回到Spring之初控制事务繁琐的问题。 回到Spring之初控制事务繁琐的问题. 考虑一个应用场景∶需要对系统中的某些业务方法做事务管理,拿简单的save和update操作举例。没有加上事务控制的代码如下。 加上事务代码,如下&#x…

【备战蓝桥杯】----01背包问题(动态规划)

🌹作者:云小逸 📝个人主页:云小逸的主页 📝Github:云小逸的Github 🤟motto:要敢于一个人默默的面对自己,强大自己才是核心。不要等到什么都没有了,才下定决心去做。种一颗树,最好的时间是十年前…
最新文章