读《《图解TCP-IP》》有感
读《《图解TCP/IP》》有感
TCP/IP
近期几天读完《《图解TCP/IP》》,收获蛮多,记得上学时读stevens的《《TCP/IP具体解释》》时那是一个囫囵吞枣,没认真看也看不下去。等有时间再拜读下《《TCP/IP具体解释》》吧,预计能有不少共鸣。
如今认为。要想比較透彻理解TCP/IP,还得须要有server编程经验,学校应该同一时候开设《socket编程》》相关课程,最好同一个老师教,能够串讲,不然光理论不实战就是花架子,并且也不easy真正理解TCP/IP协议。
TCP/UDP一览
仅仅有较为透彻的理解了TCP/UDP才知道为啥TCP叫传输控制协议。UDP叫用户数据包协议。TCP在背后帮你做了非常多事,连接管理,保存时序,超时重发,拥塞控制。流控。通过mss自己主动帮你做分片,以降低传输途中路由器分片消耗(IPV6路由器是不会分片的),这叫传输控制协议,突出“控制”二字。而UDP什么都不帮你做,不保证时序。不做超时重传,是须要上层协议来保证,也就是用户来写规则,这也是第一个U(user)的含义。同一时候UDP没有mss,所以一次性发送数据最好不要太多。以免分片导致性能损耗。
那UDP为什么叫用户数据包协议呢。说到数据包就得说说TCP和UDP的面向连接和无连接了。
我们知道TCP是面向连接的,而UDP是无连接的。这就直接导致了TCP会通过三次握手连接server,假设serverport不正确TCP是知道,但UDP不一样。压根不知道server段的情况。即使server不在线,client照样发送数据。
In [22]: import socket
In [23]: s=socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
In [24]: s.sendto("hello world",("127.0.0.1",9999))
Out[24]: 11
没有服务端。我们照样发送数据,sendto()照样返回。假设你之前不知道。这会让你大吃一惊,这也正说明了UDP不做数据包确认。
TCP传输的是流式数据,也就是说中间是没有分段的,这也就导致了我们常有的粘包问题,所以我们须要封包和解包,常见的都是字节长度+数据的方式,如websocket。也就是说我们接受一次,可能是对方多次发送后的数据集合,也可能是发送一次的部分数据,这都有可能,不能假设是哪种情况。这不经让我想起曾经上学时写的简单socketclient,server程序。都是一发一收。我们没封包解包不也正常吗?这就要说说Linux recv系统调用了。
int recv( SOCKET s, char FAR *buf, int len, int flags);
recv运行流程(来自网上)
1.recv等待s发送缓冲区数据发送完成,假设中途出现网络错误,recv函数返回SOCKET_ERROR2.recv检查s的接受缓冲区。假设接受缓冲区没有数据或者
正在接受数据。那么recv一直等待,直到把数据接受完成。当接受完成后,recv会把s接受缓冲区的数据copy到buf中(缓冲区数据可能大于buf长度。所以须要多次调用recv。recv仅仅是copy数据,真正接受数据是底层协议来完成的)
那这就好理解了,因为我们client一般是先send再recv,再发送完成之后recv会等待,并且我们数据量比較小,每次间隔比較大。所以有数据了,recv就返回了。当然这也是存在隐患的,发送数据大。一次recv可能接受不了。那就须要多次recv。这时就须要封包了,不然你怎么知道什么时候该返回。
就拿Http来说,服务端通过Content-Length和chunked编码来封包,让浏览器解析数据。
当然http1.0事实上Content-Length是非必须的,因为每次发送完数据后。服务端都会close,clientrecv会返回0,这时候也就知道数据结束了,也就是close作为了EOF标志。一帮下载都是通过这样的方式,所以也不会有粘包。
而UDP是不会出现粘包的,接收肯定能完整接收到发送的数据,世界瞬间清净了,这也是UDP**数据包**的概念,一个一个的数据包,接收的都是完整的数据包。
校验和
TCP/IP中校验和校验算法都几乎相同,也就是所谓的
反码求和
步骤例如以下
1.将校验和置为0
2.将数据转换为16字节整形,不足补零,求和(採用32位加法,将高16位和低16位相加,再将可能产生的进位与低16位相加)
3.和求反
最后发送者将校验和替换为求反的结果,将数据发给接受者,接受者忽略第一步採用相同算法。假设最后结果不为0,将丢弃该数据包。
手痒了一下。于是用golang模拟了下
//校验和算法
//对每16位(2字节)进行二进制反码求和,反码求和的意思是先对每16位求和。再将得到的和转为反码
func CheckSum(data []byte) uint16 {
//padding 16
all := len(data) / 2
if all*2 < len(data) {
all++
data = append(data, 0)
}
u16 := (*[0xffff]uint16)(unsafe.Pointer(&data[0]))[:all]
var sum uint32
for _, u := range u16 {
sum += uint32(u)
}
sum = (sum >> 16) + (sum & 0xffff) //把高位的进位,加到低八位
sum += sum >> 16 //前一步可能有进位
return (uint16)(^sum)
}
func main() {
check := make([]byte, 2)
data := []byte("tcp/ip checksum test1")
//首位作为校验和字段
all := []byte(string(check) + string(data))
sum := CheckSum(all)
fmt.Println(all, sum)
//校验和写入首位
copy(all, (*[2]byte)(unsafe.Pointer(&sum))[:])
fmt.Print(all, CheckSum(all))
}
结果:
[0 0 116 99 112 47 105 112 32 99 104 101 99 107 115 117 109 32 116 101 115 116 49] 22732
[204 88 116 99 112 47 105 112 32 99 104 101 99 107 115 117 109 32 116 101 115 116 49] 0
我们发现最后的确是0。说明我们算法没问题,至于为什么应该是0,小伙伴们想想就知道了。
TCP序列号与滑动窗体
以下说说TCP的seq,ack,在两方通信时seq開始是随机生成的,
假设A開始seq100,ack 1,发送数据size 100
| 通信 | seq | ack | size |
|---|---|---|---|
| A->B | 100 | 1 | 10 |
| B->A | 200 | 111 | 0 |
| A->B | 111 | 201 | 1000 |
| B->A | 201 | 1112 | 0 |
1.A->B 初始随机seq 100,ack 1,发送数据10字节
2.B->A 初始睡觉seq200,告诉A下一个发送111。前面的数据都收到了,因为B仅仅是回应包 size 0
3.A->B A收到B的回应。看到ack为111。知道前面的10个自己都已经成功发送了。所以seq为111,发送1000个字节,并回应B下一个開始发送201
4.B->A ack为1000+111+1。告诉A下次从1112開始发送
仅仅要对方回应了ack。那么就代表ack之前的数据都已经成功接收了。这是对方就能够把自己缓冲区的相应数据给清空了,不然须要保存以待重传。因为每次都单项传输。吞吐量有点低,所以TCP引用了滑动窗体。也就是在没有明确收到ack之前能够连续发送w个包。w就是滑动窗体的大小。因为引用了滑动窗体。有些ack丢失也没有关系。仅仅要收到了兴许的ack确认就代表之前的数据都已经成功接收了。
CSMA/CD
通信专业的对CSMA/CD肯定都不陌生。我发现老师特别喜欢说这玩意,但我曾经始终不明确这玩意有啥用?事实上CSMA/CD是用于共享网络的,也就是通过hub或同轴电缆等连接的总线型或星型拓扑结构。因为这东西我们基本都没见过(过时了),因为须要冲突检查所以是半双工通信。当交换机出来时,CSMA/CD就被淘汰了。通过交换机的port转发实现了全双工通信。不知道是当初老师的问题还是我太弱,如今才明确应用场景。
如今的无线也是半双工的。是CSMA/CD的改良版,叫CSMA/CA。
读《《图解TCP-IP》》有感的更多相关文章
- 读<走出软件作坊>有感
1.成功的人都是在不可能完成任务的情况下完成的,成功的人也从来不会抱怨客观条件多么糟糕. 2.公司给你的资源,永远小于你做事需要的资源,这就是现实,就这么多人,就这样的素质,必须在现状中想出做事的办法 ...
- 为什么DIY报价----走出软件作坊:三五个人十来条枪 如何成为开发正规军(十二)[转]
前段时间,写了一个开发.实施.服务费用计算三部曲. 水清则无鱼--走出软件作坊:三五个人十来条枪 如何成为开发正规军(八) 实施费用也能DIY--走出软件作坊:三五个人十来条枪 如何成为开发正规军(九 ...
- 将服务费用DIY到底----走出软件作坊:三五个人十来条枪 如何成为开发正规军(十)[转]
前一段时间,讲了一系列开发经理.实施经理.服务经理的工具箱:开发经理的工具箱---走出软件作坊:三五个人十来条枪 如何成为开发正规军(三) ,实施经理的工具箱--走出软件作坊:三五个人十来条枪 如何成 ...
- 《走出软件作坊》//TODO
目录 简介 结束语 简介 作者吕建伟(@阿朱),研发管理专家,原京东技术学院院长,中国互联网技术联盟发起人,历任首席架构师.技术总监以及CTO等职位.目前已接受用友集团董事长王文京邀请,加入用友组建研 ...
- 实施费用也能DIY--走出软件作坊:三五个人十来条枪 如何成为开发正规军(九)[转]
上次咱们讲完了开发费用的计算,很多人在后面跟帖在那里算费用. 有人说:你把程序员都不当人,94天,一天都不休息啊. 我想答曰:94天,是工作时间.不算双休日在里面.也就是说,实际的开发周期长度是94+ ...
- Sql Server查询性能优化之走出索引的误区
据了解绝大多数开发人员对于索引的理解都是一知半解,局限于大多数日常工作没有机会.也什么没有必要去关心.了解索引,实在哪天某个查询太慢了找到查询条件建个索引就ok,哪天又有个查询慢了,再建立个索引就是, ...
- 走出MFC子类化的迷宫
走出MFC子类化的迷宫 KEY WORDS:子类化 SUBCLASSWINDOW MFC消息机制 许多Windows程序员都是跳过SDK直接进行RAD开发工具[或VC,我想VC应不属于RAD]的学习 ...
- ofo走出校园观察:市场定位导致产品错位?
Ofo和摩拜单车虽然同样都是做单车共享,但实际上两者在最初的市场定位是有明显的差异的,因此提供的产品方案也存在巨大的差异. 市场定位不同,导致产品方案的巨大差异 摩拜单车一开始就定位于开放市场,充分的 ...
- 对于家政020 APP平台如何走出资本寒冬?
成都亿合科技小编了解到,随着O2O烧钱大战过去,网络上流传的一份O2O项目死亡名单上显示,近年来,汽车.社区.旅游.教育等16个领域的多个O2O项目关门大吉,仅外卖餐饮O2O项目倒闭的就有十几个.只有 ...
- 走出测试,走向CEO
飞测说:大家好,我们又见面了,我是黑夜小怪.不巧,今晚加班回来路上,湿身了,淋了个落汤鸡,不过明天也许可以看海了,也就呵呵了,原本想回来后聊些技术的,现在突然想先聊聊我的一些想法,仅供交流. 走出测试 ...
随机推荐
- 使用ASIHTTPRequest 编译提示找不到"libxml/HTMLparser.h"的解决方法
使用ASIHTTPRequest xcode编译提示找不到"libxml/HTMLparser.h",解决方法如下: 1>.在xcode中左边选中项目的root节点,在中间编 ...
- 去掉wget烦人的 “eta(英国中部时间)” 提示
gentoo 里的 wget ,从1.12版本开始,就一直有个不影响功能的小毛病:由于中文翻译的失误,进度提示的时候,会被拉成很多行.原因就是原来英文的ETA这3个字母,被翻译成了 “eta(英国中部 ...
- win10 创建安卓模拟器及启动的方法
一打开 安卓 studio 然后点击AVD manager 创建一个模拟器 二 通过命令行快速启动模拟器 D:\Android\sdk\tools\emulator.exe -netdelay non ...
- MIT算法导论笔记
详细MIT算法导论笔记 (网络链接) 第一讲:课程简介及算法分析 (Sheridan) 第二讲:渐近符号.递归及解法 (Sheridan) 第三讲:分治法(1)(Sheridan) 第四讲:快排及随 ...
- 2017.3.31 spring mvc教程(三)拦截器
学习的博客:http://elf8848.iteye.com/blog/875830/ 我项目中所用的版本:4.2.0.博客的时间比较早,11年的,学习的是Spring3 MVC.不知道版本上有没有变 ...
- NYOJ82 迷宫寻宝(一)【BFS】
迷宫寻宝(一) 时间限制:1000 ms | 内存限制:65535 KB 难度:4 描写叙述 一个叫ACM的寻宝者找到了一个藏宝图.它依据藏宝图找到了一个迷宫,这是一个非常特别的迷宫,迷宫里有N个 ...
- PS如何使用自定义画笔
1 没有杂色的白背景不用抠图,GIF格式的透明背景不用抠图,有背景但是不想抠图都可以直接定义为画笔.先选中需要定义的画笔(得到选区),然后单击编辑-定义画笔预设. 2 随后就可以找到我们的画笔工具 ...
- ejs - 初试
官方API: https://www.npmjs.com/package/ejs - 模板引擎 ejs文件(和普通html文件没什么区别,只是多增加了变量) <!DOCTYPE html> ...
- #include <>与#include""区别
<>先去系统目录中找头文件,如果没有在到当前目录下找.所以像标准的头文件 stdio.h.stdlib.h等用这个方法. 而""首先在当前目录下寻找,如果找不到,再到系 ...
- Android学习(十) SQLite 基于SQL语句的操作方式
main.xml <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns ...