Category: 理解计算机

2 Posts

thumbnail
TCP应用编程入门
目录 1. 网络模型 2. 数据链路层 3. 应用场景 4. golang网络模型 5. tcp应用样例 6. 异常场景 7. 参考资料 1、网络模型 1.1 OSI模型 1.2 http报文格式 关键字段:URL 1.3 tcp报文格式 关键字段:端口号 1.4 ip报文格式 关键字段:源IP地址、目的IP地址 1.5 以太帧 关键字段:源MAC地址、目的MAC地址 1.6 问题 lvs、slb的基于端口转发工作在哪一层呢? nginx的域名反向代理转发工作在哪一层呢? 家用普通路由器和无路由功能的交换机呢? 2、数据链路层 通过电信号当中的某一个字段或者光纤当中的某一个固定频率的波来传递控制报文,组合成电物理拓扑和光物理拓扑图; 某一个线路中断时,通过控制报文触发告警,让中断线路的两节点用户数据走其他线路来实现用户网络不中断的目的; 智能化的全自动交换的光网络(ASON); 3、应用场景 Http应用通常用于无状态一次性请求,请求之后连接就关闭的短连接,开发简单; Tcp应用通常用于长连接,客户端和服务端反复发送接收数据,需要自己定义报文格式,开发稍复杂; 4、实现原理 文件路径都在/proc/sys/net目录下 1. TCP收发缓冲区 # TCP接收缓冲(用于TCP接收滑动窗口)的最小值、默认值、最大值 cat /proc/sys/net/ipv4/tcp_rmem 4096 87380 6291456 # TCP发送缓冲(用于TCP发送滑动窗口)的最小值、默认值、最大值 cat /proc/sys/net/ipv4/tcp_wmem 4096 16384 4194304 每个TCP套接字有一个发送和一个接收缓冲区,当某个应用进程调用write时,内核从该应用进程的缓冲区复制发送缓冲区;同样的调用read时,内核从接收缓冲区复制到应用进程的缓冲区。 2. 内核缓冲区 网卡接收到的数据存放到内核缓冲区内,然后内核缓冲区存放的数据根据TCP信息将数据移动到具体的某一个TCP连接上的接收缓冲区内,也就是接收滑动窗口内,整个完成从IP层到传输层的数据传递。 3. 滑动窗口 发送窗口 接收窗口 5、golang网络模型 常见网络模型比较 golang用户态模型 绿色goroutine是接受TCP链接 当完成握手accept返回conn对象后,使用一个单独的goroutine来阻塞读(紫色),使用一个单独的goroutine来阻塞写(红色) 读到的数据通过解码后放入读channel,并由蓝色的goroutine来处理 需要写数据时,蓝色的goroutine将数据写入写channel,从而触发红色的goroutine编码并写入conn 汇总: Golang实利用了OS提供的非阻塞IO访问模式,并配合epoll/kqueue等IO事件监控机制;为了弥合OS的异步机制与Golang接口的差异,而在runtime上做的一层封装,这层封装就是netpoller。 最终的效果就是用户层阻塞,底层非阻塞。 5、tcp应用样例 func HandleConn(conn net.Conn) { defer conn.Close() for { // 从连接里面循环读固定长度的数据,这里一般用封装好的io.ReadFull()接口 bytesRead, err = conn.Read(buffer) totalBytesRead += bytesRead for totalBytesRead < toRead && err == nil { bytesRead, err = conn.Read(buffer[totalBytesRead:]) totalBytesRead += bytesRead } // 向连接里循环写入指定长度的数据,实际conn.Write已经实现成循环去写,不需要应用层循环写入 for totalBytesWritten < toWriteLen && writeError == nil { bytesWritten, writeError = conn.Write(buffer[totalBytesWritten:]) totalBytesWritten += bytesWritten } } } func main() { listen, err := net.Listen("tcp", ":8888") if err != nil { fmt.Println("listen error: ", err) return } for { conn, err := listen.Accept() if err != nil { fmt.Println("accept error: ", err) break } // start a new goroutine…
thumbnail
白话原码、反码、补码的产生
数字在自然界中抽象出来的时候,一棵树,两只猪,是没有正数和负数的概念的。计算机保存最原始的数字,也是没有正和负的数字,叫没符号数字。如果我们在内存分配4位(bit)去存放无符号数字,是下面这样子的: 后来在生活中为了表示“欠别人钱”这个概念,就从无符号数中,划分出了“正数”和“负数”。正如上帝一挥手,从混沌中划分了“白天”与“黑夜”。为了表示正与负,人们发明了"原码",把生活应该有的正负概念,原原本本的表示出来。把左边第一位腾出位置,存放符号,正用0来表示,负用1来表示: 但使用“原码”储存的方式,方便了看的人类,却苦了计算机 我们希望 (+1)和(-1)相加是0,但计算机只能算出0001+1001=1010 (-2),这不是我们想要的结果 。 (╯' - ')╯︵ ┻━┻ 另外一个问题,这里有一个(+0)和(-0)。 为了解决“正负相加等于0”的问题,在“原码”的基础上,人们发明了“反码”。 “反码”表示方式是用来处理负数的,符号位置不变,其余位置相反: 当“原码”变成“反码”时,完美的解决了“正负相加等于0”的问题。过去的(+1)和(-1)相加,变成了0001+1101=1111,刚好反码表示方式中,1111象征-0。 人们总是进益求精,历史遗留下来的问题,有两个零存在,+0 和 -0。 我们希望只有一个0,所以发明了"补码",同样是针对"负数"做处理的"补码"的意思是,从原来"反码"的基础上,补充一个新的代码,(+1)我们的目标是,没有蛀牙(-0): 有得必有失,在补一位1的时候,要丢掉最高位我们要处理"反码"中的"-0",当1111再补上一个1之后,变成了10000,丢掉最高位就是0000,刚好和左边正数的0,完美融合掉了。 这样就解决了+0和-0同时存在的问题另外"正负数相加等于0"的问题,同样得到满足举例,3和(-3)相加,0011 + 1101 =10000,丢掉最高位,就是0000(0)同样有失必有得,我们失去了(-0) , 收获了(-8)以上就是"补码"的存在方式。 结论:保存正负数,不断改进方案后,选择了最好的"补码"方案。 参考 知乎用户