socket抛砖引玉

为何要讲socket呢?

其实也没什么特别的缘由,主要是本身想了解一下网络通讯这块的东西,而后一直以为socket高深难懂,因此决定挑战一下本身,作一个简单的学习了解。web

之前由于项目缘由接触过socket,可是当时是使用的socket.io的这个框架,对于socket的底层是如何实现的,以及框架是如何封装的并无进行深刻的研究。编程

因此此次就抛砖引玉简单的讲一下我对socket的理解,很是欢迎各位大佬多多指教。数组

什么是socket?

字面上socket又成为“套接字”安全

实际上:网络上 的 两个程序 经过一个双向的通讯链接 实现数据的交换,这个链接的一端称为一个socket服务器

客户端和服务端的socket创建一个链接,经过两端创建的这个通讯管道进行网络的请求和响应。socket能够理解成通讯管道(隧道)的两个端口,一个入口,一个出口网络

Socket通讯.jpg

网络通讯的三要素

  • IP地址(网络上主机设备的惟一标识框架

    • 用来寻找对应服务器的主机
  • 端口号(定位程序)socket

    • 用于标示进程的逻辑地址,不一样进程的有不一样的端口号
    • 至关于当前服务器(当前电脑)上对应的 web应用程序
    • 有效端口:0 ~ 65535,其中0 ~ 1024由系统使用或者称做保留端口,开发中建议使用1024以上的端口
  • 传输协议(使用什么样的方式进行交互,通讯规则)函数

    • 常见协议:TCP、UDP

传输协议

TCP(传输控制协议)

  • 须要创建链接,造成传输数据的通道
  • 在链接中进行大数据传输(数据的大小不受限制
  • 经过3次握手完成链接,是可靠协议,安全送达
  • 必须创建链接,效率会稍低

UDP(用户数据报协议)

  • 数据 及 源和目的 封装成数据包中,不须要创建链接
  • 每一个数据报的大小限制在64K以内
  • 由于无需链接,所以是不可靠协议
  • 不须要创建链接,速度快

套接字

套接字的类型有不少种,好比 DARPA Internet 地址(Internet 套接字)、本地节点的路径名(Unix套接字)、CCITT X.25地址(X.25 套接字)等。涉及到网络请求方面的主要指的都是Internet套接字学习

根据数据的传输方式,能够将Internet套接字分红两种类型

流格式套接字(SOCK_STREAM)也叫“面向链接的套接字”

  • SOCK_STREAM 是一种可靠的、双向的通讯数据流,数据能够准确无误地到达另外一台计算机,若是损坏或丢失,能够从新发送。

  • 流格式套接字有本身的纠错机制

  • SOCK_STREAM 有如下几个特征:

    • 数据在传输过程当中不会消失;
    • 数据是按照顺序传输的;
    • 数据的发送和接收不是同步的(有的也称“不存在数据边界”)
  • 能够将 SOCK_STREAM 比喻成一条传送带,只要传送带自己没有问题(也就是不断网),就能够保证数据不会丢失;同时,比较晚传送的数据不会先到达,比较早传送的数据不会晚到达,保证了数据的传递是按照顺序传递的。

SOCK_STREAM传送带.png

  • 流式套接字使用的是传输控制协议(TCP),进而保证了数据能够 准确无误的顺序到达,而且接收者不须要和发送者保持相同的节奏接收数据。

    • 流格式套接字的内部有一个缓冲区(也就是字符数组),经过 socket 传输的数据将保存到这个缓冲区。接收端在收到数据后并不必定当即读取,只要数据不超过缓冲区的容量,接收端有可能在缓冲区被填满之后一次性地读取,也可能分红好几回读取。
    • 也就是说,无论数据分几回传送过来,接收端只须要根据本身的要求读取,不用非得在数据到达时当即读取。传送端有本身的节奏,接收端也有本身的节奏,它们是不一致的。
  • 应用场景:HTTP协议就是基于面向链接的套接字(TCP服务),数据信息必需要准确无误的进行传输

数据报格式套接字(SOCK_DGRAM)也叫“无链接的套接字”

  • 计算机只管传输数据,不做数据校验,若是数据在传输中损坏,或者没有到达另外一台计算机,是没有办法补救的。也就是说,数据错了就错了,没法重传。

  • SOCK_DGRAM 有如下几个特征:

    • 强调快速传输而非传输顺序;
    • 传输的数据可能丢失也可能损毁;
    • 限制每次传输的数据大小;
    • 数据的发送和接收是同步的(有的教程也称“存在数据边界”)。
  • 能够将SOCK_DGRAM理解为快递行业,用货车发往同一地点的两件包裹无需保证顺序,只要以最快的速度送到客户手中便可。这种方式存在损坏或者丢失的风险,并且对快递包裹的大小也是有必定限制的。因此当传递大量包裹的时候就须要分批发送。

SOCK_DGRAM快递小哥.png

  • 用两辆车分别发送的两件包裹,接收者也须要分两次接收,因此数据的发送和接收必须是同步的。(也能够理解为:接收次数和发送次数是相同的)

  • 总之,数据报套接字是一种不可靠的、不按顺序传递的、以追求速度为目的的套接字,使用的是UDP协议。

  • 应用场景:QQ视频聊天 & QQ语音 都是使用SOCK_DGRAM来进行数据传输的。

面向链接和无链接的套接字的理解

网络数据传输.png

面向链接的套接字

  • 对于面向链接的套接字则是在通讯以前先肯定好一条路径,没有特殊状况,之后固定走这条路径进行数据的传递,固然若是这条路径出问题或者被破坏的话则会在发送数据以前从新创建新的路径。
  • 为了确保数据的准确性以及发送顺序,须要在收到数据包的确认消息以后才进行下一次数据的发送,不然进行数据的重发。
  • 很是可靠,确保万无一失

无链接的套接字

  • 对于无链接的套接字,每一个数据包能够选择不一样的路径,固然也能够选择相同的路径,不管中途走那条通道或者经历了什么,最终到达便可,不管前后以及最终到达是否成功
  • 尽最大努力交付 原则

Socket通讯流程

socket起源于Unix,而Unix/Linux基本哲学之一就是“一切皆文件”,均可以用“打开open -> 读写write/read -> 关闭close”模式来操做。也能够理解为socket就是该模式的一个实现,socket便是一种特殊的文件

流程图

socket通讯-TCP.jpg

socket通讯-UDP.png

接口简介

  • socket(): 建立socket套接字

  • bind(): 将套接字和IP、端口绑定,一般由服务端调用

  • listen(): TCP专用,开启监听状态,等待用户发起请求,套接字一直处于“睡眠”中,直到客户端发起请求才会被“唤醒”

  • accept(): TCP专用,服务器等待客户端链接请求,通常是阻塞态(暂停运行),直到客户端发起请求

  • connect(): TCP专用,客户端向服务器主动发起链接请求,直到服务器传回数据后,connect() 才运行结束

  • send(): TCP专用,发送数据

  • recv(): TCP专用,接收数据

  • sendto(): UDP专用,发送数据到指定的IP地址和端口,数据信息包含目标地址

  • recvfrom(): UDP专用,接收数据,返回数据远端的IP地址和端口,接收数据请求的时候将目标地址信息保存

  • shutdown(): TCP专用,优雅的断开流链接,断开输入流 断开输出流 同时断开I/O流,会等缓冲区的数据发送完毕以后再断开。用来关闭链接,而不是套接字,因此须要在调用以后再调一次close(),才能将套接字从内存中清除

  • closesocket(): 关闭socket套接字,不只会关闭服务端的socket,还会通知客户端链接已断开,客户端也会清理 socket 相关资源,该操做会丢失输出缓冲区中的数据

不一样协议下socket通讯的注意点

基于TCP的socket通讯

  • TCP的服务端与客户端必须一对一的创建链接才能够进行数据的通讯

  • 在TCP中,套接字是一对一的关系,若是要向10个客户端提供服务的话,那么除了负责监听的套接字以外,还须要建立10个套接字

  • 建立好TCP套接字以后,传输数据时无需添加地址信息,由于TCP的套接字与对方套接字相链接,知道数据的目标地址信息

基于UDP的socket通讯

  • UDP中的服务器端和客户端没有链接,无需在链接状态下交换数据

  • UDP 套接字不会保持链接状态,每次传输数据都要添加目标地址信息,这至关于在邮寄包裹前填写收件人地址

  • 在UDP中,无论是服务端仍是客户端都只须要1个套接字便可

  • UDP不存在严格的服务端和客户端的区分,只是由于其提供服务而称为服务端

问题探讨

http 的长短链接 & 长短轮询

长链接 & 短链接

首先须要强调一点:HTTP协议是基于请求/响应模式的,所以只要服务端给了响应,本次HTTP链接就结束了。 也能够理解为是本次HTTP请求就结束了,根本没有长链接 短链接这一说

对于网络上说的HTTP分为长链接和短链接,实际上是指TCP链接。TCP链接是一个双向的通道,它是能够保持一段时间不关闭的,所以TCP链接才是真正有长链接和短链接之分的

对于HTTP链接的说法 实际上是HTTP请求和HTTP响应更为准确,因此 长链接是指的TCP链接,而不是HTTP链接

怎样算是把HTTP变成长链接?

首先要明白 长链接意味着链接会被重复使用,是指TCP的链接通道被重复使用,并非HTTP请求的复用,是多个HTTP请求能够复用同一个TCP链接,这样节省了不少TCP链接创建和断开的握手消耗。

HTTP1.0协议不支持长链接,从HTTP1.1协议之后默认是长链接,咱们能够看到平时的Web应用的HTTP头部Connection也确实是keep-alive,可是须要服务端和客户端都设置才算是长链接

长链接也并非永久链接的,若是一段时间内(具体的时间长短,是能够在header当中进行设置的,也就是所谓的超时时间),这个链接没有HTTP请求发出的话,那么这个长链接就会被断掉。

长链接优点: 假设打开一个网页,里面确定包含不少CSS、JS等一系列资源,若是是短链接的话(每次都要从新创建TCP链接),因此一个网页的打开须要耗费几个甚至几十个的TCP链接。若是是长链接的话,那么这么屡次HTTP请求(这些请求包括请求网页内容,CSS文件,JS文件,图片等等),其实使用的都是同一个TCP链接,很显然这样能够节省不少的消耗。

长轮询 & 短轮询

假设一个场景:淘宝界面显示库存的剩余个数,客户端写一个死循环,不停的去请求服务端的数据

短轮询

  • 服务端马上返回请求的结果

长轮询

  • 当服务端收到客户端的请求的时候,并非马上去返回结果,而是检查一下请求的数据有没变化,检测到有变化则当即返回,不然一直等到超时为止。

对于客户端来讲,长轮询和短轮询是同样的,就是不停的去请求;对于服务端来讲,短轮询的状况下服务端每次请求无论有没有变化都会当即返回结果信息,而长轮询的状况下是有变化才返回结果信息,没变化的话则不会返回信息,直到超时为止

长短轮询和长短链接的区别

  • 1.决定方式

    • 一个TCP链接是否为长链接,是经过设置HTTP的Connection Header来决定的,并且是须要两边都设置才有效
    • 一种轮询方式是否为长轮询,是根据服务端的处理方式来决定的,与客户端没有关系
  • 2.实现方式

    • 链接的长短是经过协议来规定来实现的
    • 轮询的长短,是服务器经过编程的方式手动挂起请求来实现的

Socket 的短链接 长链接

Socket的长短链接的差异:整个客户端和服务端的请求是经过一个socket仍是多个socket进行的

  • 长链接是整个通讯过程当中,服务端和客户端只用一个socket对象,长期保持socket的链接
  • 短链接是每次请求的时候都新建一个socket,处理完一个请求就直接关闭socket

关于链接的关闭问题

Socket链接中 断开流 其实就是断开链接了,由于Socket原本就是依靠流进行通讯的

在关闭链接的时候 必定要显示关闭socket,而不是经过调用shutDown方法关闭某个流,这样容易形成内存泄漏

顺序是先关流再关socket

如何实现长链接?

首先要知道:要实现长链接,通常须要发送结束标记符号来告诉客户端 - 服务端的某段消息已经发送完毕,不然客户端会一直阻塞在read方法

因此 客户端须要本身主动退出读取的动做,为了防止客户端一直阻塞在read()方法处

咱们能够在服务端每发送完一段消息而且刷新前就进行一个写入结束符号的标志,当客户端解析到结束符号时,就可直接退出read的循环读取操做,避免一直阻塞。

Socket如何保持长链接?

方法1:应用层本身实现心跳包

节点(防火墙)会自动把必定时间以内没有数据交互的链接给断掉,因此要保持长链接,须要保活,须要发送心跳包,防止长时间没有数据交互,致使链接被断掉

客户端和服务端制定一个通讯协议,每隔必定时间(通常15秒左右),由一方发起,向对方发送协议包;对方收到这个包后,按指定好的通讯协议回一个。若没收到回复,则判断网络出现问题,服务器可及时的断开链接,客户端也能够及时重连。

总的来讲:心跳包主要也就是用于长链接的保活和断线处理。通常的应用下,断定时间在30-40秒比较不错。若是实在要求高,那就在6-9秒

方法2:TCP的KeepAlive保活机制

经过TCP协议层发送KeepAlive包。这个方法只需设置好你使用的TCP的KeepAlive项就好,其余的操做系统会帮你完成。操做系统会按时发送KeepAlive包,一发现网络异常,立刻断开。

开启KeepAlive功能须要消耗额外的宽带和流量,因此TCP协议层默认并不开启KeepAlive功能,另外一方面,KeepAlive设置不合理时可能会由于短暂的网络波动而断开如今的TCP链接。而且,默认的KeepAlive超时须要7,200,000 MilliSeconds, 即2小时,探测次数为5次。对于不少服务端应用程序来讲,2小时的空闲时间太长。所以,咱们须要手工开启KeepAlive功能并设置合理的KeepAlive参数。

TCP的 SO_KEEPALIVE。系统默认是设置的2小时的心跳频率。可是它检查不到机器断电、网线拔出、防火墙这些断线。并且逻辑层处理断线可能也不是那么好处理。通常,若是只是用于保活仍是能够的。

TCP粘包问题

客户端同一时间发送几条数据,而服务端接收的是这几条数据合在一块儿的一条数据,通过TCP的传输,三条数据被合并成一条了,这就是数据的粘包问题。 也称数据的无边界性,read()/recv()函数不知道数据包的开始或结束标志(实际上也没有任何开始或结束标志),只把它们当作连续的数据流来处理。

假设咱们但愿客户端每次发送一位学生的学号,让服务器端返回该学生的姓名、住址、成绩等信息,这时候可能就会出现问题,服务器端不能区分学生的学号。例如第一次发送 1,第二次发送 3,服务器可能当成 13 来处理,返回的信息显然是错误的。

解决办法

封包的时候给每一个包的数据加一个标记,来标明数据的长度和类型(类型显然是须要的,咱们须要知道它是文本、图片、仍是录音等等,来用正确的方式处理这个数据)。

拆包的时候,先获取到咱们给每一个包的标记,而后根据标记的数据长度,去获取数据。最后再根据标记的类型去处理数据。(文字输出、图片展现、录音播放等等)

相关文章
相关标签/搜索