计算机网络再次整理————tcp周边[八]
前言
tcp的包的格式可以看我以前的计算机网络整理,下面这些周边只是为了开发时候我们能用到一些理论知识。
正文
首先要介绍的就是域名,为啥有域名这东西呢?单纯站在网络的角度上讲这属于应用层的东西了。
如果站在万物互联的角度上讲,把互联网看做是一台大型电脑的话,那么域名就相当于句柄了。
句柄这东西解释一下,句柄(Handle)是一个是用来标识对象或者项目的标识符,可以用来描述窗体、文件等,值得注意的是句柄不能是常量。
Windows之所以要设立句柄,根本上源于内存管理机制的问题,即虚拟地址。简而言之数据的地址需要变动,变动以后就需要有人来记录、管理变动,因此系统用句柄来记载数据地址的变更。在程序设计中,句柄是一种特殊的智能指针,当一个应用程序要引用其他系统(如数据库、操作系统)所管理的内存块或对象时,就要使用句柄
比如我们要访问某个服务,但是服务器的ip地址是可以变动的,那么如果是这样的话,那么就有一个问题,那就是这个ip要一直属于你,但是这是不可能的。
那么就有了域名这东西,ip再怎么变化,同一个域名代表着某种服务,域名不变服务就不变了。
那么我们知道源计算机到目标计算机的是通过ip协议的,也就是说传输跟域名是无关的,那么域名如何应对变化的ip呢?
那么就有人想出了DNS这东西,Domain name system(域名系统),它作为将域名和IP地址相互映射的一个分布式数据库,能够使人更方便地访问互联网。DNS使用UDP端口53。当前,对于每一级域名长度的限制是63个字符,域名总长度则不能超过253个字符。
大概是这么一个东西,使用的是udp,那么我们浏览器访问域名,实际上内部会帮助我们将域名转换为ip然后发送出去,那么域名标志就在我们应用层才知道是啥,比如说http将域名放在header 里面的host位置上。
上面是dns抓包。这里可以看到我配置了DNS 服务配置是192.168.0.1,也就是我的网关。
那么如果192.168.0.1 如果无法解析出域名,那么就会传到下一级DNS服务器了。
static void Main(string[] args)
{
var addresses = Dns.GetHostEntry("www.baidu.com").AddressList;
foreach (var item in addresses)
{
Console.WriteLine($"ip地址:{item.ToString()}");
}
Console.ReadLine();
}
我们代码中获取也是相当简单。
然后这里可以看到,我们一个域名会返回AddressList,也就是多个地址。
这个是一个常规现象,怎么说呢,可能多个ip配置一个域名,可以做到负载均衡的作用。
那么多个ip配置一个域名,那么访问域名访问的是哪个ip呢?
在域名解析过程中,通过层层解析,必将域名对应到 IP(逐级授权、中间 CNAME 层层转发,此处按下不表)对于对应到的IP,可能1个,也可能13个(不建议超过13个),DNS 会一股脑将这(些)个 IP 给解析客户端(如浏览器、操作系统的解析服务)最终的应用或系统解析服务,从中随机挑选一个(如果是定制的应用,还可以通过后续的应用层访问对获取到的 IP 做 HA 或权重)
也就是说你用哪个ip是应用程序自己决定的,当然有些库是帮你轮询了,也有些库只选用第一个,但这不是DNS帮你干的事情,DNS 只会返回你在服务商配置的信息。
一些大型网站或CDN服务商为了实现负载均衡,他们的DNS服务器会动态改变多个IP地址的顺序,使得每个IP地址都有机会成为解析结果中的第一个IP地址。
既然域名能解析成ip,那么ip是否能反向解析为域名呢?也是可以的。
https://baike.baidu.com/item/域名反向解析/9327917
平时也没过反向解析这东西,上面也是写道是邮件识别安全问题,不做过多的评论,总之是可以的。
那么对于我们的应用来说,现在ip修改的问题解决了,那么是否直接一个socket,然后通信编写我们自己的应用协议就行呢?
因为我们的应用各不相同,那么就有不同的场景,也就有了不同的需求,那么可以通过对socket的配置来满足我们的需求。
static void Main(string[] args)
{
var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
socket.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.ReceiveBuffer, 4098*2);
}
比如tcp是流,我们可以设置输入缓冲区的大小,当然也可以设置输出缓冲区的大小了。
但是值得注意的是,这个设置操作系统可能并不会100%执行,比如说我设置了两倍,那么操作系统不一定按照两倍,可能是1点多倍,这个操作系统有自己的算法的。
那么是否配置是否只影响性能呢?答案其实不是的,还可能短时间无法重启问题。
这就是著名的 time-wait 问题。
这上面标红的部分就是time-wait。
为啥有time-wait呢? time-wait 一定是在首先发送断开的一方。
下面把首先发送端口的比作是A,后面断开的比作是B。
为什么有time-wait呢? 因为A断开的一方没有time-wait,也就是说发送最后的ack这个socket销毁了,那么可能发送的ack没有到达B(丢失)。B就不知道A收到了FIN信号,那么B就会进行重试,但是A的socket消失了,然后B永远收不到Ack,无法正常关闭。
那么这个time-wait有什么问题呢? 比如我们的服务应用宕机了,如果socket 在time-wait 那么我们指定socket bind 某个指定的端口了,那么如果该socket处于time-wait状态,那么服务重启就会报告被端口占用情况。
那么有没有办法解决呢?
这个是允许绑定time-wait socket使用的端口。
Q:编写 TCP/SOCK_STREAM 服务程序时,SO_REUSEADDR到底什么意思?
A:这个套接字选项通知内核,如果端口忙,但TCP状态位于 TIME_WAIT ,可以重用端口。如果端口忙,而TCP状态位于其他状态,重用端口时依旧得到一个错误信息,指明"地址已经使用中"。如果你的服务程序停止后想立即重启,而新套接字依旧使用同一端口,此时SO_REUSEADDR 选项非常有用。必须意识到,此时任何非期望数据到达,都可能导致服务程序反应混乱,不过这只是一种可能,事实上很不可能。
前面几篇文章中提及到nagle这个词,nagle 是一个算法,主要是优化小数据网络流量问题。
nagle 是在什么条件下诞生的呢? 我们指定tcp 是建立在ip协议的基础上,ip协议和tcp协议都是占用字节的,如果你传输一个字节就要给你发送一次的话,也就是说协议的大小要大于数据的大小,那么可以想象流量多么浪费。
那么nagle 就是这样的,比如要发送nagle,n、a、g、l,这个一个字节一个字节发送的话,那么首先n传入到缓存中去,然后发送,这个时候等n传输的ack回来后,那么此时缓存中可能就有了agl,那么就会把agl全部发送出去。
也就是说要等上一个消息的ack确定对方收到后,才开始发送下一条消息。如果设置socket 为TCP_NODELAY为true,那么不会等上一个ack收到就会发送新的数据。
那么nagle 算法是不是百分百适用呢? 答案肯定是否定的,没有任何一种算法是100%的。
如果我们在传输大文件的时候,很快就会填充掉io的缓存,如果使用nagle 算法,那么要等待ack回来后才能发送,就有点影响效率了。
Nagle算法的规则(可参考tcp_output.c文件里tcp_nagle_check函数注释):
(1)如果包长度达到MSS,则允许发送;
(2)如果该包含有FIN,则允许发送;
(3)设置了TCP_NODELAY选项,则允许发送;
(4)未设置TCP_CORK选项时,若所有发出去的小数据包(包长度小于MSS)均被确认,则允许发送;
(5)上述条件都未满足,但发生了超时(一般为200ms),则立即发送。
那么可能就有人问了,那么tcp 都没有ack确认后再进行发送,那么对方收到的包的顺序不就是乱的啊,怎么能保证数据流顺序正常呢?
这个问题的其实吧,tcp 的包里面的数据是有编号的,并不是说发送方发完消息,然后发送方收到了接收方收到了消息(ack),然后保证数据顺序的。
那这就太局限了,其实tcp保证顺序是有编号的,这就要从tcp格式说起了,以前整理的也有,后面再次整理的时候会补充。
socket的配置还有很多,比如TCP_CORK啊,可以百度看看。
结
上面就是为了说明一下tcp是一个非常复杂的协议, 我们在做应用的时候需要了解一些配置,或许有利于性能以及稳定性。下一节,多进程的tcp服务。
计算机网络再次整理————tcp周边[八]的更多相关文章
- 计算机网络再次整理————tcp的关闭[七]
前言 tcp的关闭不是简单粗暴的,相对而言是友好优雅的,好聚好散吧. 那么友好的关闭方式是这样的: 假设这里是客户端请求关闭的,服务端倒过来. 客户端:我要请求关闭 服务端:我接收到你的请求了,等我把 ...
- 计算机网络再次整理————tcp例子第二前奏[四]
前言 前文我们介绍了网络协议的各层,同时也介绍了一下我们在编写代码时候的服务端的accept.bind.listen.connect.send做了什么. 可以说是从宏观的角度,或者代码开发的角度来说的 ...
- 计算机网络再次整理————tcp例子[五]
前言 本文介绍一些tcp的例子,然后不断完善一下. 正文 服务端: // See https://aka.ms/new-console-template for more information us ...
- 计算机网络再次整理————tcp[二]
前言 本文不会去介绍tcp的具体协议,因为这个tcp 应该不能说是单纯的连接和传输数据这么简单,里面还有很多机制. 正文 首先介绍一下什么是协议族(protocal Family),举个例子PF_IN ...
- 计算机网络再次整理————tcp例子前奏[三]
前言 简单编写一下tcp例子. 正文 我们常说IOS有7层,实际上也只有4层,或者这样说简单的说是4层. 首先是数据链路层,首先这一层解决了什么问题呢?为什么要有这一层呢? 首先要抛开有操作系统的意识 ...
- 计算机网络再次整理————socket[一]
前言 以前也整理过吧,写了几篇之后,感觉没啥整理的必要了然后就放弃了,最近又想整理一下. 正文 这篇对应的是:https://www.cnblogs.com/aoximin/p/12235333.ht ...
- 计算机网络再次整理————UDP例子[六]
前言 简单的说,UDP 没有 TCP 用的广泛,但是还有很多是基于UDP的程序的,故而简单介绍一下. 正文 秉承节约脑容量的问题,只做简单的介绍和例子,因为自己几乎也没怎么用过UDP. 只是了解和知晓 ...
- 计算机网络基础之TCP/IP 协议栈
计算机网络基础之TCP/IP 协议栈 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.TCP/IP 协议栈概述 1>.什么是TCP/IP协议栈 Transmission C ...
- 计算机网络知识(TCP连接,TCP/UDP区别,HTTP与HTTPS,Socket原理等等)
1.网络七层协议包含,物理层.数据链路层.网络层(ip协议).传输层(TCP传输控制协议.UDP用户数据报协议).会话层.表示层.应用层(http协议).是一个提供的概念架构协议. 2.TCP/IP协 ...
随机推荐
- unittest_skip跳过用例执行(3)
在执行测试用例时,有时候有些用例是不需要执行的,比如版本迭代用例弃用,测试周期短只需要执行优先级高的用例,那我们怎么办呢?难道删除这些用例?那下次执行时如果又需要执行这些用例时,又把它补回来?这样操作 ...
- win10 配置maven
1.官网下载后,解压到需要的文件夹 2.进入文件夹,获取根目录的路径 3.配置环境变量 两个都要 M2_HOME MAVEN_HOME 3.配置path ,以相对路径的方式配置bin目录 4.测试是否 ...
- Maven自定义jar包名
一.默认命名 <finalName>${project.artifactId}-${project.version}</finalName> 二.自定义包名 <build ...
- Zookeeper绍二(分布式锁介)
一.为什么会有分布式锁? 在多线程环境下,由于上下文的切换,数据可能出现不一致的情况或者数据被污染,我们需要保证数据安全,所以想到了加锁. 所谓的加锁机制呢,就是当一个线程访问该类的某个数据时,进行保 ...
- Three.js之绘制物体的边框及修改lineWidth
本博文主要记录如何使用three.js绘制物体的边框及修改其lineWidth.three.js是个技术点比较多,查询资料又比较少的框架,单单就这个修改lineWidth就是一个坑.先放一个动态的效果 ...
- winfrom 双缓冲
在窗体load函数中 this.DoubleBuffered = true; //控件,需要反射的方式设置 Type dgvType = this.dgv.GetType(); PropertyInf ...
- C++构造函数语义学(一)(基于C++对象模型)
如果一个类没有自己的构造函数,编译器会在需要的时候为其合成一个出来,俗称:合成默认构造函数.但是请注意是在需要的时候,并不是所有情况. 请看下面代码: 1 #include<iostream&g ...
- Linux下Makefile的编写及四个特殊符号的意义@、$@、$^、$
转自:https://blog.csdn.net/runfarther/article/details/50036115# 我们先看三段C++程序: 一.line1的源码 line1.h #ifnde ...
- VS2017:win32项目与win32控制台应用程序的转换方法
原文:https://www.cnblogs.com/asuser/articles/12297251.html 刚开始使用VS2017新建项目工程时,有时把应用类型的工程建成控制台类型的工程,在编译 ...
- golang中字符串-字节切片,字符串-字符切片的互转
package main import ( "fmt" "reflect" ) func B2S(bs []uint8) string { // 将字节切片转换 ...