阿里一面,反正也进不去,那就瞎聊聊,哈哈哈哈
- TCP(Transmission Control Protocol,传输控制协议)和UDP(User Data Protocol,用户数据报协议)的差别==>TCP三次握手==>TCP如何实现有序发送,而UDP不行==》TCP吞吐量与什么有关
TCP与UDP的优缺点上的区别:
TCP的优点: 可靠,稳定 TCP的可靠体现在TCP在传递数据之前,会有三次握手来建立连接,而且在数据传递时,有确认、窗口、重传、拥塞控制机制,在数据传完后,还会断开连接用来节约系统资源,发送数据端和接收端在数据正式传输前就有了交互。
简单概括(三次“对话”的目的是使数据包的发送和接收同步,经过三次“对话”之后,主机A才向主机B正式发送数据**)
1)主机A向主机B发出连接请求数据包:“我想给你发数据,可以吗?”,这是第一次对话;
2)主机B向主机A发送同意连接和要求同步(同步就是两台主机一个在发送,一个在接收,协调工作)的数据包:“可以,你什么时候发?”,这是第二次对话;
3)主机A再发出一个数据包确认主机B的要求同步:“我现在就发,你接着吧!”,**这是第三次对话。
TCP三次握手过程
1) 主机A通过向主机B 发一个含同步序列号的标志位的数据段给主机B ,向主机B请求建立连接,通过这个数据段,A告诉B两件事:我想和你通信;你可用个序列号作为起始数据段来回应我.
2) B收到A的请求后,用一带有确认应答(ACK)和同步序列号(SYN)标志位的数据段响应A,也告诉A两件事:我已收到你的请求,你可以传输数据了;你要用个序列号作为起始数据段来回应我
(ACK TCP报头的控制位之一,对数据进行确认.确认由目的端发出,来告诉发送端这个序列号之前的数据段都收到了.比如,确认号为X,则表示前X-1个数据段都收到了,只有当ACK=1时,确认号才有效,当ACK=0时,确认号无效,这时会要求重传数据,保证数据的完整性.) 3)主机A收到这个数据段后,再发送一个确认应答,确认已收到主机B 的数据段:"我已收到回复,我现在要开始传输实际数据了**这样3次握手就完成了,主机A和主机B 就可以传输数据了.
(SYN 同步序列号 这个标志位只有在TCP建立连接时才会被置1握手完成后SYN标志位被置0)
TCP四次断开连接
1 当主机A完成数据传输后,将控制位FIN置1,提出停止TCP连接的请求
2 主机B收到FIN后对其作出响应,确认这一方向上的TCP连接将关闭,将ACK置1
3 由B 端再提出反方向的关闭请求,将FIN置1
TCP的缺点: 慢,效率低,占用系统资源高,易被攻击 TCP在传递数据之前,要先建连接,这会消耗时间,而且在数据传递时,确认机制、重传机制、拥塞控制机制等都会消耗大量的时间,而且要在每台设备上维护所有的传输连接,事实上,每个连接都会占用系统的CPU、内存等硬件资源。 而且,因为TCP有确认机制、三次握手机制,这些也导致TCP容易被人利用,实现DOS、DDOS、CC等攻击。
TCP通信的最大吞吐量*由窗口大小和往返时间决定**,假定最大吞吐量为Tmax,窗口大小为W,往返时间是RTT的话,那么最大吞吐量Tmax=W/RTT,
如果想要提高吞吐量得建立更多的连接,基于 给定的窗口大小 和 往返延迟,变快==>增加窗口大小/缩小延迟,带宽(bits每秒) 往返延迟(秒) = TCP窗口大小(bits) / 8 = TCP窗口大小(字节),
服务器增加 TCP窗口大小,其中一个缺点是需要更多的缓冲内存,因为未应答的数据必须存储在内存中为了应对可能的重传。另一个潜在的缺陷是性能,发生在包丢失时,因为窗口内任何的包丢失都会导致整个窗口重传 - 除非你的 TCP协议栈使用一种 TCP增强技术,叫做选择性应答(selective acknowledgements),但是大部分服务器没有使用此种技术。
UDP的优点: 快,比TCP稍安全 没有TCP的握手、确认、窗口、重传、拥塞控制等机制,UDP是一个无状态的传输协议,所以它在传递数据时非常快。没有TCP的这些机制,UDP被攻击者利用的漏洞就要少一些。但UDP也是无法避免攻击的,比如:UDP Flood攻击……
(UDP Flood攻击检测 短时间内向特定目标不断发送 UDP 报文,致使目标系统负担过重而不能处理合法的传输任务,就发生了 UDP Flood。启用 UDP Flood 攻击检测功能时,要求设置一个连接速率阈值,一旦发现保护主机响应的 UDP 连接速率超过该值,防火墙会输出发生 UDP Flood 攻击的告警日志,并且根据用户的配置可以阻止发往该主机的后续连接请求**)
UDP的缺点: 不可靠,不稳定 因为UDP没有那些可靠的机制,在数据传递时,如果网络质量不好,就会很容易丢包。
TCP应用场景: 当对网络通讯质量有要求的时候,比如:整个数据要准确无误的传递给对方,要求可靠的应用,比如HTTP、HTTPS、FTP等传输文件的协议,POP、SMTP等邮件传输的协议。 在日常生活中,常见使用TCP协议的应用如下: 浏览器,用的HTTP FlashFXP,用的FTP Outlook,用的POP、SMTP Putty,用的Telnet、SSH QQ文件传输 …………
TCP与UDP的理论上的区别:
- 基于连接与无连接——TCP面向连接(如打电话要先拨号建立连接);UDP是无连接的,即发送数据之前不需要建立连接,当它想传送时就简单地去抓取来自应用程序的数据,并尽可能快地把它扔到网络上。在发送端,UDP传送数据的速度仅仅是受应用程序生成数据的速度、计算机的能力和传输带宽的限制;在接收端,UDP把每个消息段放在队列中,应用程序每次从队列中读一个消息段
- 对系统资源的要求(TCP较多,UDP少)——Tcp首部开销20字节,Udp首部开销小只有8个字节
- UDP程序结构较简单——TCP的逻辑通信信道是全双工的可靠信道,UDP主机不需要维持复杂的链接状态表(这里面有许多参数)是不可靠信道;每条TCP连接只能有两个端点endpoint,每一条TCP连接只能点对点一对一,UDP支持一对一、一对多、多对一、多对多的交互通信(由于传输数据不建立连接,因此也就不需要维护连接状态,包括收发状态等,因此一台服务机可同时向多个客户机传输相同的消息);
-
流模式与数据报模式 ——Tcp面向字节流,实际上是Tcp把数据看成一连串无结构的字节流;Udp面向报文的,发送方的UDP对应用程序交下来的报文,
在添加首部后就向下交付给IP层。既不拆分,也不合并,而是保留这些报文的边界,Udp没有拥塞控制,因此网络出现拥塞不会使源主机的发送速率降低,对实时很有用,如IP电话、实时视频会议 - TCP保证数据正确性,通过Tcp连接传送的数据,无差错、不丢失、不重复,且按序到达;UDP可能丢包,UDP不保证,同时也不使用拥塞控制。
(有序:*简单说就是协议中定义的报文头不一样,在TCP头中有发送帧序号和期望接收的帧序号,收发双方通过滑动窗口协议保证仅收到下一个需要的帧时才提交给上层应用。**一直保持连接是在TCP协议层中有保活定时器,超过一段时间没有发送的应用报文,TCP层会自动发送一个握手帧告诉对方我还在正常工作。**UDP的协议简单,这些东西都没有。*
*主机每次发送数据时,TCP就给每个数据包分配一个序列号并且在一个特定的时间内等待接收主机对分配的这个序列号进行确认,如果发送主机在一个特定时间内没有收到接收主机的确认,则发送主机会重传此数据包。接收主机利用序列号对接收的数据进行确认,以便检测对方发送的数据是否有丢失或者乱序等,接收主机一旦收到已经顺序化的数据,它就将这些数据按正确的顺序重组成数据流并传递到高层进行处理。
具体步骤如下:
(1)为了保证数据包的可靠传递,发送方必须把已发送的数据包保留在缓冲区;
(2)并为每个已发送的数据包启动一个超时定时器;
(3)如在定时器超时之前收到了对方发来的应答信息(可能是对本包的应答,也可以是对本包后续包的应答),则释放该数据包占用的缓冲区;
(4)否则,重传该数据包,直到收到应答或重传次数超过规定的最大次数为止。
TCP与UDP的编程上的区别:
socket()的参数不同 UDP Server不需要调用listen和accept UDP收发数据用sendto/recvfrom函数 TCP:地址信息在connect/accept时确定
UDP:在sendto/recvfrom函数中每次均需指定地址信息 UDP:shutdown函数无效 UDP:shutdown函数无效
编程区别
网络编程默认TCP编程,socket函数创建一个socket用于TCP通讯,函数参数SOCK_STREAM,socket(PF_INET,SOCK_STREAM,0):建立一个socket用于流式网络通讯
SOCK_STREAM面向连接,每次收发数据前得通过connect建立连接,任何一方都可以收发数据
UDP函数参数是SOCK_DGRAM,双方发送数据不知道对方是否已经收到数据,任何一方建立一个socket都可以用sentdo发送数据,也可用recvfrom接收数据
TCP编程的服务器端一般步骤是:
用函数socket()创建一个socket--> 用函数setsockopt()设置socket属性(可选) -->用函数bind()绑定IP地址、端口等信息到socket上-->用函数listen()开启监听-->用函数accept()接收客户端上来的连接-->用函数send()和recv()/read()和write()收发数据-->关闭网络连接-->关闭监听;
TCP编程的客户端一般步骤是:
用函数socket()创建一个socket--> 用函数setsockopt()设置socket属性(可选) -->用函数bind()绑定IP地址、端口等信息到socket上(可选)-->设置要连接的对方的IP地址和端口等属性-->用函数connect()连接服务器-->用函数send()和recv()/read()和write()收发数据-->关闭网络连接
UDP编程的服务器端一般步骤是:
用函数socket()创建一个socket--> 用函数setsockopt()设置socket属性(可选) -->用函数bind()绑定IP地址、端口等信息到socket上- ->用函数recvfrom()循环接收数据-->关闭网络连接
UDP编程的客户端一般步骤是:
TCP/IP协议是一个协议簇。里面包括很多协议的。UDP只是其中的一个。**之所以命名为TCP/IP协议,因为TCP,IP协议是两个很重要的协议,就用他两命名了。
TCP/IP协议集包括应用层,传输层,网络层,网络访问层。
为了实现连接到互联网上的结点之间的通信,必须为每个结点(入网的计算机)分配一个地址,并且应当保证这个地址是全网唯一的,这便是IP地址。
目前的IP地址(IPv4:IP第4版本)由32个二进制位表示,每8位二进制数为一个整数,中间由小数点间隔,整个IP地址空间有4组8位二进制数,由表示主机所在的网络的地址以及主机在该网络中的标识共同组成。 为了便于寻址和层次化的构造网络,IP地址被分为A、B、C、D、E五类,商业应用中只用到A、B、C三类。
* A类地址:网络标识由第一组8位二进制数表示,网络中的主机标识占3组8位二进制数,网络标识的第一位二进制数取值必须为"0"。A类地址允许有126个网段,每个网络大约允许有1670万台主机,通常分配给拥有大量主机的网络(如主干网)。 1.0.0.1-127.255.255.254
* B类地址:网络标识由前两组8位二进制数表示,网络中的主机标识占两组8位二进制数,网络标识的前两位二进制数取值必须为"10"。B类地址允许有16384个网段,每个网络允许有65533台主机,适用于结点比较多的网络(如区域网)。 128.1.0.1-191.255.255.254
* C类地址:网络标识由前3组8位二进制数表示,网络中主机标识占1组8位二进制数,网络标识的前3位二进制数取值必须为"110"。具有C类地址的网络允许有254台主机,适用于结点比较少的网络(如校园网)。 192.0.1.1-223.255.255.254
为了便于记忆,通常习惯采用4个十进制数来表示一个IP地址,十进制数之间采用句点"."予以分隔。这种IP地址的表示方法也被称为点分十进制法。
OSI:
物理层:EIA/TIA-232, EIA/TIA-499, V.35, V.24, RJ45, Ethernet, 802.3, 802.5, FDDI, NRZI, NRZ, B8ZS
数据链路层:Frame Relay, HDLC, PPP, IEEE 802.3/802.2, FDDI, ATM, IEEE 802.5/802.2
网络层:IP,IPX,AppleTalk DDP
传输层:TCP,UDP,SPX
会话层:RPC,SQL,NFS,NetBIOS,names,AppleTalk,ASP,DECnet,SCP
表示层:TIFF,GIF,JPEG,PICT,ASCII,EBCDIC,encryption,MPEG,MIDI,HTML
-
GET在浏览器回退时是无害的,而POST会再次提交请求。
-
GET产生的URL地址可以被Bookmark,而POST不可以。
-
GET请求会被浏览器主动***,而POST不会,除非手动设置。
-
GET请求只能进行url编码,而POST支持多种编码方式。
-
GET请求参数会被完整保留在浏览器历史记录里,而POST中的参数不会被保留。
-
GET请求在URL中传送的参数是有长度限制的,而POST么有。
-
对参数的数据类型,GET只接受ASCII字符,而POST没有限制。
-
GET比POST更不安全,因为参数直接暴露在URL上,所以不能用来传递敏感信息。
-
GET参数通过URL传递,POST放在Request body中。
GET和POST还有一个重大区别,简单的说:GET产生一个TCP数据包;POST产生两个TCP数据包。
长的说:对于GET方式的请求,浏览器会把http header和data一并发送出去,服务器响应200(返回数据);
而对于POST,浏览器先发送header提示,服务器响应100 continue,浏览器再发送data,服务器响应200 ok(返回数据)。
因为POST需要两步,时间上消耗的要多一点,看起来GET比POST更有效。
1. GET与POST都有自己的语义,不能随便混用。2. 据研究,在网络环境好的情况下,发一次包的时间和发两次包的时间差别基本可以无视。而在网络环境差的情况下,两次包的TCP在验证数据包完整性上,有非常大的优点。3. 并不是所有浏览器都会在POST中发送两次包,Firefox就只发送一次。
(1).队列先进先出,栈先进后出。
(2). 对插入和删除操作的"限定"。栈是限定只能在表的一端进行插入和删除操作的线性表。队列是限定只能在表的一端进行插入和在另一端进行删除操作的线性表。
(3).遍历数据速度不同。栈只能从头部取数据,也就最先放入的需要遍历整个栈最后才能取出来,而且在遍历数据的时候还得为数据开辟临时空间,保持数据在遍历前的一致性。队列则不同,它基于地址指针进行遍历,而且可以从头或尾部开始遍历,但不能同时遍历,无需开辟临时空间,因为在遍历的过程中不影像数据结构,速度要快的多
实现public int length() { int count = 0; Node current = head; while (current != null) { count++; current = current.next(); } return count; }
public int length(Node current) { if (current == null) { return 0; } return 1 + length(current.next()); }
如果没有环,则fast首先到达链表结尾; 链表有环的情况下:fast与slow两次相遇,slow中间走过的节点处即为环的长度;
碰撞点p到连接点的距离=头指针到连接点的距离,分别从碰撞点、头指针开始走,相遇的那个点就是连接点。已经求出连接点距离头指针的长度,加上环的长度,就是带环单链表的长度
[public]interface 接口名称 [extends父接口名列表] { //静态常量 [public] [static] [final] 数据类型变量名=常量值; //抽象方法 [public] [abstract] [native] 返回值类型方法名(参数列表); }
3.接口中的变量默认都会被final修饰为常量,亦可以加static修饰符将其定义成静态常量,
4.接口中的变量由于是常量,须在定义接口的时候就为其初始化
5.接口方法的修饰符有public abstract均为默认修饰
6.关于接口中方法的实现:并不所有的子类都必须实现接口的方法,当子类是抽象类的时候,可以不实现接口中的方法
抽象类就是为了继承而存在的,如果你定义了一个抽象类,却不去继承它,那么等于白白创建了这个抽象类,因为你不能用它来做任何事情。对于一个父类,如果它的某个方法在父类中实现出来没有任何意义,必须根据子类的实际需求来进行不同的实现,那么就可以将这个方法声明为abstract方法,此时这个类也就成为abstract类了。
2.接口支持多继承,而抽象类只能单继承
3.抽象类中可以有自己的数据成员,接口中的变量都是共有的常量
4.抽象类中可以有部分成员函数的实现,而接口中只有函数的定义
1)抽象方法必须为public或者protected(因为如果为private,则不能被子类继承,子类便无法实现该方法),缺省情况下默认为public。
2)抽象类不能用来创建对象;
当我们往HashMap中put元素的时候,先根据key的hashCode重新计算hash值,根据hash值得到这个元素在数组中的位置(即下标), 如果数组该位置上已经存放有其他元素了,那么在这个位置上的元素将以链表的形式存放,新加入的放在链头,最先加入的放在链尾。如果数组该位置上没有元素,就直接将该元素放到此数组中的该位置上。
HashMap里实现了一个静态的内部类Entry,重要的属性有key,value,next,hash。map中的数据就存储在Entry[]中,每次我们put数据时,通过该key的hashcode,得到该key在Entry[]中的索引,然后以e.next遍历,如果存在对应value,返回value,不存在就将其添加到Entry[]。1.put(key,value)时,如果Entry[]的size超过threshold,则进行扩容,即table.length*2。
2.get(key) 时先定位到该数组元素,再遍历该元素处的链表。
如果我们再次放入同样的key会怎样呢?逻辑上,它应该替换老的value。事实上,它确实是这么做的。在迭代的过程中,会调用equals()方法来检查key的相等性(key.equals(k)),如果这个方法返回true,它就会用当前Entry的value来替换之前的value。
- 梯度下降法找目标函数最小值,梯度下降法的计算过程就是沿梯度下降的方向求解极小值(也可以沿梯度上升方向求解极大值)
- 梯度从几何意义上讲,就是函数变化增加最快的地方。具体来说,对于函数f(x,y),在点(x0,y0),沿着梯度向量的方向就是(∂f/∂x0, ∂f/∂y0)T的方向是f(x,y)增加最快的地方。或者说,沿着梯度向量的方向,更加容易找到函数的最大值。反过来说,沿着梯度向量相反的方向,也就是 -(∂f/∂x0, ∂f/∂y0)T的方向,梯度减少最快,也就是更加容易找到函数的最小值。
- 单纯的梯度下降算法是收敛于局部最优解的,如果要求实现全局最优解的话可以考虑加入退火算法或者遗传算法之类的思想,简单说就是在搜索过程中不但有基于梯度下降的方向,同时也融入少量的逆向搜索,最终设定一个收敛域即可。
- 主要是初始化θ0,θ1...,θnθ0,θ1...,θn,算法终止距离εε以及步长αα。在没有任何先验知识的时候,我喜欢将所有的θθ初始化为0, 将步长初始化为1。在调优的时候再 优化