TCP/IP

TCP三次握手,四次挥手

5、三次握手文字总结

三次握手是 TCP 连接的建立过程。在握手之前,主动打开连接的客户端结束 CLOSE 阶段,被动打开的服务器也结束 CLOSE 阶段,并进入 LISTEN 阶段。随后进入三次握手阶段:

① 首先客户端向服务器发送一个 SYN 包,并等待服务器确认,其中:

标志位为 SYN,表示请求建立连接;

序号为 Seq = x(x 一般取随机数);

随后客户端进入 SYN-SENT 阶段。

② 服务器接收到客户端发来的 SYN 包后,对该包进行确认后结束 LISTEN 阶段,并返回一段 TCP 报文,其中:

标志位为 SYN 和 ACK,表示确认客户端的报文 Seq 序号有效,服务器能正常接收客户端发送的数据,并同意创建新连接;

序号为 Seq = y;

确认号为 Ack = x + 1,表示收到客户端的序号 Seq 并将其值加 1 作为自己确认号 Ack 的值,随后服务器端进入 SYN-RECV 阶段。

③ 客户端接收到发送的 SYN + ACK 包后,明确了从客户端到服务器的数据传输是正常的,从而结束 SYN-SENT 阶段。并返回最后一段报文。其中:

标志位为 ACK,表示确认收到服务器端同意连接的信号;

序号为 Seq = x + 1,表示收到服务器端的确认号 Ack,并将其值作为自己的序号值;

确认号为 Ack= y + 1,表示收到服务器端序号 seq,并将其值加 1 作为自己的确认号 Ack 的值。

随后客户端进入 ESTABLISHED。

当服务器端收到来自客户端确认收到服务器数据的报文后,得知从服务器到客户端的数据传输是正常的,从而结束 SYN-RECV 阶段,进入 ESTABLISHED 阶段,从而完成三次握手。

public class ListNode { int val; ListNode next; ListNode(int x) { val = x; } } class MyLinkedList { int size; ListNode head; // sentinel node as pseudo-head public MyLinkedList() { size = 0; head = new ListNode(0); } /** Get the value of the index-th node in the linked list. If the index is invalid, return -1. */ public int get(int index) { // if index is invalid if (index < 0 || index >= size) return -1; ListNode curr = head; // index steps needed // to move from sentinel node to wanted index for(int i = 0; i < index + 1; ++i) curr = curr.next; return curr.val; } /** Add a node of value val before the first element of the linked list. After the insertion, the new node will be the first node of the linked list. */ public void addAtHead(int val) { addAtIndex(0, val); } /** Append a node of value val to the last element of the linked list. */ public void addAtTail(int val) { addAtIndex(size, val); } /** Add a node of value val before the index-th node in the linked list. If index equals to the length of linked list, the node will be appended to the end of linked list. If index is greater than the length, the node will not be inserted. */ public void addAtIndex(int index, int val) { // If index is greater than the length, // the node will not be inserted. if (index > size) return; // [so weird] If index is negative, // the node will be inserted at the head of the list. if (index < 0) index = 0; ++size; // find predecessor of the node to be added ListNode pred = head; for(int i = 0; i < index; ++i) pred = pred.next; // node to be added ListNode toAdd = new ListNode(val); // insertion itself toAdd.next = pred.next; pred.next = toAdd; } /** Delete the index-th node in the linked list, if the index is valid. */ public void deleteAtIndex(int index) { // if the index is invalid, do nothing if (index < 0 || index >= size) return; size--; // find predecessor of the node to be deleted ListNode pred = head; for(int i = 0; i < index; ++i) pred = pred.next; // delete pred.next pred.next = pred.next.next; } }

这里普及一些基本知识,后续文章不会再提:大家提到安全老是说签名和加密,他们的作用是不同的。

签名主要功能是防篡改,是无限集合(真实内容)向有限集合(签名)的映射,根据签名是无法还原内容的,接收方收到内容后,按约定的算法和密钥算出签名,和收到的签名一样就是没被篡改。

加密主要功能是防泄漏,是无线集合向无限集合的映射,因为陌生人即使拿到了整个消息,依旧看不懂写的什么,而接收方解密拿到明文可以进行处理。

至于签名和加密的算法的种类本系列也不会再科普,可以自己上网搜一下。

避免过多的上下文切换开销

多线程调度过程中必然需要在 CPU 之间切换线程上下文 context,而上下文的切换又涉及程序计数器、堆栈指针和程序状态字等一系列的寄存器置换、程序堆栈重置甚至是 CPU 高速缓存、TLB 快表的汰换,如果是进程内的多线程切换还好一些,因为单一进程内多线程共享进程地址空间,因此线程上下文比之进程上下文要小得多,如果是跨进程调度,则需要切换掉整个进程地址空间。

如果是单线程则可以规避进程内频繁的线程切换开销,因为程序始终运行在进程中单个线程内,没有多线程切换的场景。

避免同步机制的开销

如果 Redis 选择多线程模型,又因为 Redis 是一个数据库,那么势必涉及到底层数据同步的问题,则必然会引入某些同步机制,比如锁,而我们知道 Redis 不仅仅提供了简单的 key-value 数据结构,还有 list、set 和 hash 等等其他丰富的数据结构,而不同的数据结构对同步访问的加锁粒度又不尽相同,可能会导致在操作数据过程中带来很多加锁解锁的开销,增加程序复杂度的同时还会降低性能。

  • 4
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值